v0.7.9.8 — the test command did not typecheck, and the plan had gone stale
Housekeeping before v0.8.0: the answer to "anything else that should be looked at first". One real hole, one stale document, and my own leavings. #102 — `npm test` passed green on a type error. `pretest` ran `scripts/build-web.ts`, which invokes `tsc --ignoreConfig` against three web entry points, so it saw only what those three transitively import and under a WEAKER configuration than tsconfig.json — no `noUncheckedIndexedAccess`, no `exactOptionalPropertyTypes`, `--types ''`. It never saw `src/server/` or a single file under `test/`. Demonstrated rather than argued: a planted `const DELIBERATE_TYPE_ERROR: number = 'not a number';` in src/server/session.ts gives `npm run typecheck` a TS2322 and `npm test` a clean `# fail 0`. `pretest` is `tsc --noEmit && node scripts/build-web.ts` now, and the same error exits 1 with the tests never running. This mattered THIS week rather than generally: v0.8.0 is steps 2-7 of the common board — display stream, credentials, persistence, Chromium supervisor — which is almost entirely src/server/, exactly the half the test command could not see. #103 — the plan had drifted from the code it is the source for. docs/plans/jitsi-common-board.md was written 2026-08-27, still said "No implementation has been performed", and is what steps 2-7 get built from. Step 1 shipped across four releases since, so every "current code finding" under it described a fault that is now fixed — a document reading as present tense and nine days stale sends the next reader to fix things twice. Measured: its PublicFrame sketch lists four properties never built (protocolVersion, config, scoring, deckCounts) and omits 28 that exist, and the shape is the real difference — the implementation is FLAT where the plan grouped things into objects, so a renderer written from the sketch would not compile. The plan now says so at the top and at step 1, names src/sim/view.ts and the redaction allow-list as the authority, keeps the original sketch for its reasoning, and calls out `protocolVersion` as unbuilt rather than dropping it quietly — step 2 is the reconnecting display stream and is the first thing that would want one. One step-1 item is STRUCK OFF rather than built: "add the Red Flag holder to the public player projection". The premise does not hold here. `decks.redFlags` is written once, in setup.ts, from `optionalRules.emergencyToolbox`, and never again — `redFlag.play` emits `phaseEnded` and does not spend it — so every player holds one or none does, decided before the deal. A per-player `redFlagHeld` would be one already-public option copied N times, while telling every reader of the common board that it varies by player and might change mid-game. Worse than the absence. Pinned by test so it is not re-raised from the plan. Four dead imports removed, all mine: `HAND_LIMIT` left unused in apply.ts, view.ts and web/game.ts when 0.7.9.6 consolidated the three copies of the §6.2 test, and `actingPlayer` in web/game.ts, dead since 0.7.9.5 made `currentActor` delegate. Finding them re-measured #46: `tsc --noUnusedLocals` now reports 40, up from 29 on 2026-08-30. That entry's "without the flag this list simply regrows" is a measurement rather than a forecast now. The other 36 and the flag stay open. 946 tests pass, up from 943. No behaviour changes: three new tests pin an invariant, and the rest is a build command, dead imports and a document. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y5boPxP6JHRYMm8adXaF5R
This commit is contained in:
co-authored by
Claude Opus 5
parent
88a42ae9e2
commit
312e0301e0
@@ -1,6 +1,17 @@
|
||||
# Station Master Jitsi Common Board Implementation Plan
|
||||
|
||||
**Status:** Final planning document. No implementation has been performed.
|
||||
**Status (2026-09-07):** **STEP 1 IS BUILT AND SHIPPED. Steps 2-7 are unimplemented.**
|
||||
|
||||
Step 1 landed across four releases rather than one — v0.7.9.2 (the two narration leaks), v0.7.9.4
|
||||
(the projection helpers and the redaction net), v0.7.9.5 (the narration path), and v0.7.9.8 (this
|
||||
reconciliation). One of its items is struck off rather than built; see § Public game projection.
|
||||
|
||||
**This document has drifted from the code and is no longer the authority on what exists.** It was
|
||||
written on 2026-08-27 against the code of that date, and the "Current code findings" under each step
|
||||
describe faults that were then real — several are now fixed, and reading them as present tense will
|
||||
send you to fix things twice. Where a step is marked built, `src/sim/view.ts`, `src/server/`, and the
|
||||
tests named in TODO.md are the authority. Steps 2-7 were never implemented and their findings have
|
||||
NOT been re-verified against the current code; check each before building on it.
|
||||
|
||||
## Summary
|
||||
|
||||
@@ -44,6 +55,38 @@ The sibling repository had unrelated local modifications and untracked files. Th
|
||||
|
||||
Introduce dedicated allow-listed types. Do not derive them with `Omit<Frame, ...>` because new private `Frame` fields could then leak automatically.
|
||||
|
||||
> **RECONCILED 2026-09-07 (v0.7.9.8).** Step 1 is BUILT, and what shipped is not shaped like the
|
||||
> sketch below. This block was the design; `PublicFrame` in `src/sim/view.ts` is now the authority,
|
||||
> and `test/redaction.test.ts`'s allow-list is the enumeration of it that fails when it changes.
|
||||
> **Read those two, not this**, when building steps 2-7. The differences that matter:
|
||||
>
|
||||
> - **The shape is FLAT, not grouped.** There is no `clock`, `config`, `scoring` or `deckCounts`
|
||||
> object. Their contents sit at the top level — `day`, `stage`, `clock` (a time string), `phase`,
|
||||
> `phaseKey`, `actor`, `superintendent`, `deck`, `departments`, `departmentDepth`, `salvage`,
|
||||
> `yards`, `mode`, `days`, `optionalRules`, `houseRules`, `minCombinedRevenue`,
|
||||
> `maxCollisionsPerDay`, `maxCollisionsTotal`, `collisionsToday`, `collisionsTotal`, `status`,
|
||||
> `outcome`, `extraDays`, `extensionVotes`, `official`, `tally`, `openingRolls`, `timetable`,
|
||||
> `timetableWhat`, `trains`, `players`, `division`, `districts`.
|
||||
> - **`protocolVersion` was NOT built** and exists nowhere in the repo. Step 2 is the reconnecting
|
||||
> display stream, which is the first thing that would want one — decide there whether to add it,
|
||||
> rather than assuming it is already on the wire.
|
||||
> - **`redFlagHeld` is STRUCK OFF**, not deferred. See below.
|
||||
> - **Fields gained since this was written** that the renderer should know about: `crewTrays` and
|
||||
> `queued` (#98, the Crew Tray pool and the trains waiting for one), and on each district's cells
|
||||
> `heldAtLimits` (#99, a train stopped on the Limit Track) and `enhancementsSpent` (#101, a
|
||||
> dispatch device spent for the Day).
|
||||
>
|
||||
> **Why `redFlagHeld` is struck off rather than built.** The premise does not hold in this codebase.
|
||||
> `decks.redFlags` is written in exactly one place — `setup.ts`, from
|
||||
> `config.optionalRules.emergencyToolbox` — and never again; `redFlag.play` emits `phaseEnded` and
|
||||
> does not spend it. So every player holds one or none does, decided before the deal. A per-player
|
||||
> `redFlagHeld` would be `optionalRules.emergencyToolbox` copied N times, already public, while
|
||||
> telling every reader of the common board that it varies by player and may change mid-game. The
|
||||
> invariant is pinned by test (`test/display-gaps.test.ts`) so this does not get re-raised from the
|
||||
> plan text: if the rule ever becomes per-player, that test fails.
|
||||
|
||||
The design as originally written, kept for the reasoning:
|
||||
|
||||
```ts
|
||||
type PublicPlayerView = {
|
||||
index: PlayerIndex;
|
||||
@@ -145,6 +188,24 @@ Expose sanitized state, last transition time, and a safe error summary through t
|
||||
|
||||
## Step 1 — Secure public-state projection
|
||||
|
||||
> **BUILT — v0.7.9.2 through v0.7.9.5.** Everything in "Required changes" below shipped except the
|
||||
> Red Flag holder, which is struck off (§ Public game projection). Mapping to the work items in
|
||||
> TODO.md: the projection helpers and `currentActorOfState` are **#95**, the systematic redaction
|
||||
> net and its allow-list are **#91**, the seed and blind-draw narration leaks are **#92**, and
|
||||
> "stop passing the full game log into `frameFor()`" is **#97** — which also fixed a reconnect bug
|
||||
> the duplicate had been masking, so read that entry before touching narration in step 2.
|
||||
>
|
||||
> **The findings below are as of 2026-08-27 and are now HISTORY, not a task list.** Two are worth
|
||||
> carrying forward anyway: districts must be keyed by SEAT with ownership resolved through
|
||||
> `playerAtSeat` (Employee Rotation), and the public view must be composed UPWARD from shared
|
||||
> helpers, never by calling the player `snapshot()` once per seat. Both are load-bearing for step 3.
|
||||
>
|
||||
> One finding was struck off on measurement rather than fixed: it warns that a display reading
|
||||
> `clock.currentActor` could highlight the wrong district during a decision. Across six seeds and
|
||||
> 3,600 decision points that field and `actingPlayer` never disagreed. `currentActorOfState` exists
|
||||
> anyway as the one place to ask — and **#96** later found a real instance of the same class in a
|
||||
> state nobody had checked, the §3.3 vote, where the game is not `active` at all.
|
||||
|
||||
### Current code findings
|
||||
|
||||
`src/sim/view.ts` currently builds `Frame` for one real viewer and defaults that viewer to player zero. It combines shared table state, one district, and viewer-private fields. Calling it for a spectator would silently expose player zero’s district and private information.
|
||||
|
||||
Reference in New Issue
Block a user