v0.8.0 — the board replays what everyone else did, instead of arriving rearranged
TODO #13, #15 and #18 — Gitea#20 steps 2-4 pointed at a seated player's own screen. Every accepted intent, and every automatic phase that does anything, becomes an ordered presentation step. A bot's whole switching turn used to land in one push; now it arrives as a run of steps, the district panel follows whoever is acting, and a [N behind] … [Skip] row says how far the board is from the game. Solitaire runs the same path — one collector inside submit(), which both session kinds already funnel through — which is where its automatic phases finally get a visible beat. Dwell is assigned by kind: switching holds the screen, turn bookkeeping costs nothing, and the clock turning over earns the beat. Tunable per viewer without a rebuild, and off entirely at pace 0. Also: switching was the one class of action logging unattributed, and now names its train. Reasoning, measurements and the three things that turned out wrong are in CHANGELOG.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
This commit is contained in:
co-authored by
Claude Opus 5
parent
312e0301e0
commit
02289e94b8
@@ -128,32 +128,72 @@ specific paths below have not been exercised at a table. **More testing is plann
|
||||
|
||||
## The common board, and watching play happen — Gitea#20
|
||||
|
||||
**This is v0.8.0.** One shared, seatless display of the public game, usable on a TV or in OBS on its
|
||||
own and publishable into the table's Jitsi meeting. The plan is `docs/plans/jitsi-common-board.md`,
|
||||
seven steps, of which 1-4 are the useful release and 5-7 are the Jitsi publisher.
|
||||
One shared, seatless display of the public game, usable on a TV or in OBS on its own and publishable
|
||||
into the table's Jitsi meeting. The plan is `docs/plans/jitsi-common-board.md`, seven steps.
|
||||
|
||||
**THE RELEASE SPLIT, settled with Jesse 2026-09-09. Jesse: "13 is the key. Watching on a TV is the
|
||||
bonus."**
|
||||
|
||||
- **v0.8.0 — #13, #15 and #18: the watchable table.** The step collector, steps on the `Session`
|
||||
interface, **foreign-district rendering**, the client animation queue, pacing by kind, and the
|
||||
behind-counter. **The design is the plan's § v0.8.0**, which supersedes the parts of steps 2-4 it
|
||||
covers. Needs no HTTP work at all.
|
||||
- **v0.8.1 — the seatless board page.** `display.json`, `viewToken`, `/api/display/stream`,
|
||||
`/display.html`, an all-districts layout: most of step 2 and step 3 without its canvas. Cheap once
|
||||
0.8.0 lands, and nothing in it moves #13 forward.
|
||||
- **v0.9.0 — the Jitsi publisher.** Steps 5-7 plus step 3's canvas pipeline. Held off deliberately:
|
||||
it needs Chromium in the image (several hundred MB onto a 63 MB `.s9pk`) and measurement on
|
||||
`phoenix.local`, and the only self-hosted Jitsi available needs an authenticated moderator to open
|
||||
a room, so "waiting for moderator" is the ordinary path here rather than an edge case.
|
||||
|
||||
**The correction that set that split:** `Frame.cells` is ONE district — the viewer's own
|
||||
(`view.ts:510`, from `areaOf(s, viewer)`), exactly as **Reference · #13** already said. So a step
|
||||
stream alone does not answer #13; the data would arrive with nowhere to be drawn. Rendering a
|
||||
district you do not own is the core of 0.8.0, not part of the seatless page. Two other decisions
|
||||
taken with it: **no WebSocket and no new runtime dependency** (SSE down + POST up, the pattern
|
||||
`server/http.ts` already uses), and `protocolVersion` on the wire in 0.8.0.
|
||||
|
||||
**Step 1 is BUILT** — v0.7.9.2 through v0.7.9.5 (#91, #92, #95, #97), with one item struck off
|
||||
rather than implemented (#103). **The plan was reconciled against the code in v0.7.9.8** and now says
|
||||
which of its "current code findings" are history: it had drifted badly enough to send the next reader
|
||||
fixing things twice. Steps 2-7 were never implemented and their findings have NOT been re-verified —
|
||||
check each before building on it. See **Reference · #103**. Items that look
|
||||
like screen polish live here because they need step 4's ordered presentation mechanism and nothing
|
||||
cheaper.
|
||||
rather than implemented (#103). **The plan was reconciled against the code in v0.7.9.8** and again
|
||||
on 2026-09-09, and now says which of its "current code findings" are history: it had drifted badly
|
||||
enough to send the next reader fixing things twice. Steps 2-7 were never implemented; step 4's
|
||||
findings were re-verified 2026-09-09 and steps 5-7's were NOT — check each before building on it.
|
||||
See **Done · 103**. Items that look like screen polish live here because they need step 4's ordered
|
||||
presentation mechanism and nothing cheaper.
|
||||
|
||||
**The design is settled — the plan's § v0.8.0 is the authority.** In outline: public steps animate
|
||||
the board while the existing private Push supplies hand, menu and objective (so there is no new
|
||||
redaction surface); the collector is a shared `sim/` module both `LocalSession.submit()` and
|
||||
`GameSession.submit()` call, so solitaire and multiplayer run one code path; dwell is assigned **by
|
||||
kind** with switching protected at 1s and bookkeeping at zero; and a `[N behind] … [Skip]` row shows
|
||||
the lag, carries #15's caption, and never blocks input. Two measurements that decided it: a
|
||||
switching turn runs to the engine's cap of **6 moves** (bursts of 14, 6, 6, 6 in `seed-1917398`),
|
||||
and dwell-by-kind costs ~40s of animation across a 60-stage game against 3.6 minutes for a flat
|
||||
700ms.
|
||||
|
||||
- [ ] **#13** — I cannot see what the other players did — bots included. **Settled 2026-08-29 as the
|
||||
harder reading**: not log legibility but the ordered, per-action presentation of everyone else's
|
||||
turns. Jesse: "It's not fun to do my turn and have magic happen in the background." This is
|
||||
Gitea#20 step 4 pointed at a player's own screen. See **Reference · #13**.
|
||||
Gitea#20 step 4 pointed at a player's own screen. **DESIGNED 2026-09-09 — the plan's § v0.8.0.**
|
||||
The answer is foreign-district rendering with focus following the actor; without it a step
|
||||
stream has nowhere to draw, because `Frame.cells` is the viewer's district alone. See
|
||||
**Reference · #13**.
|
||||
|
||||
- [ ] **#15** — A "most recent action" line under the status block. The text already exists and is
|
||||
already correct — this is placement, not content. **Decide with #13**: in solitaire "most
|
||||
recent" is the right unit; in multiplayer what you missed is everything that happened while you
|
||||
were WAITING. See **Reference · #15**.
|
||||
were WAITING. **DESIGNED 2026-09-09 — it is the caption in the `[N behind] … [Skip]` row, not a
|
||||
separate line.** The queue IS "everything that happened while you were waiting", which is the
|
||||
unit this entry could not choose. See **Reference · #15**.
|
||||
|
||||
- [ ] **#18** — Give every phase a visible beat. Not a timing problem — `pump` runs every automatic
|
||||
phase before the page renders once, so they are never drawn at all. **A `sleep` fixes nothing;
|
||||
it needs the async stepped pump that Gitea#20 step 4 specifies**, which is why it lives here
|
||||
rather than under The screen. See **Reference · #18**.
|
||||
rather than under The screen. **DESIGNED 2026-09-09 — it is a dwell setting on the shared queue,
|
||||
not a feature.** Phases where nothing happened dwell at ZERO (Jesse: "if nothing happens during
|
||||
a phase then we shouldn't lose time to it"); a flat second per phase is rejected on the same
|
||||
arithmetic this entry already worked out. Solitaire gets it through `LocalSession`, the same
|
||||
path multiplayer gets #13 through. See **Reference · #18**.
|
||||
|
||||
- [ ] **#75** — Let the game join a call and talk to the table — the chat, audio and nudge half of the
|
||||
idea Gitea#20 took the visual half of. Long-term. See **Reference · #75**.
|
||||
@@ -1761,7 +1801,7 @@ where it belongs, and it is still open.
|
||||
Closed items, kept because several are the only record of a ruling or a lesson. Newest first within
|
||||
each group.
|
||||
|
||||
### Shipped through v0.7.9.4, from the queue
|
||||
### Shipped through v0.7.9.8, from the queue
|
||||
|
||||
Closed items, newest first. Kept because several of them are the only record of a ruling or a lesson;
|
||||
the numbers stay so cross-references above and below still resolve.
|
||||
@@ -1996,11 +2036,13 @@ the numbers stay so cross-references above and below still resolve.
|
||||
`# fail 0`. `pretest` is `tsc --noEmit && node scripts/build-web.ts` now, and the same planted
|
||||
error exits 1 with the tests never running.
|
||||
|
||||
**Why it mattered THIS week rather than generally.** v0.8.0 is steps 2-7 of the common board —
|
||||
a display stream, credentials, persistence, and a Chromium supervisor — which is almost
|
||||
entirely `src/server/` and is exactly the half the test command could not see. Found while
|
||||
answering "anything else before 0.8.0", which is the only reason it was found at all: nothing
|
||||
about a green suite would ever have said so.
|
||||
**Why it mattered THIS week rather than generally.** The next release is steps 2-4 of the
|
||||
common board — a display stream, credentials, persistence and the display-step collector, which
|
||||
is almost entirely `src/server/` and is exactly the half the test command could not see. (The
|
||||
Chromium supervisor was in this list when the entry was written; steps 5-7 became v0.9.0 on
|
||||
2026-09-09, and the point stands without it.) Found while answering "anything else before
|
||||
0.8.0", which is the only reason it was found at all: nothing about a green suite would ever
|
||||
have said so.
|
||||
|
||||
103. ~~**The common-board plan had drifted from the code it is the source for.**~~ — done 2026-09-07
|
||||
in v0.7.9.8. `docs/plans/jitsi-common-board.md` was written 2026-08-27, still said "No
|
||||
|
||||
Reference in New Issue
Block a user