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:
Jesse.Markowitz
2026-09-08 03:41:39 -04:00
co-authored by Claude Opus 5
parent 88a42ae9e2
commit 312e0301e0
8 changed files with 243 additions and 8 deletions
+47
View File
@@ -388,3 +388,50 @@ describe('a dispatch device says whether it is still available today (#101)', ()
);
});
});
/**
* THE RED FLAG HOLDER IS NOT A PER-PLAYER FACT, so it does not belong on the public player view.
*
* `docs/plans/jitsi-common-board.md` step 1 asks for one: "Add the Red Flag holder to the public
* player projection. It is public game state but is currently absent from `Frame`", and its
* `PublicPlayerView` carries `redFlagHeld: boolean`. Every other step-1 item shipped across v0.7.9.2
* to v0.7.9.5; this one is STRUCK OFF instead, because 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. There is no `.set` anywhere else, and
* `redFlag.play` (the intent gated on holding one) emits a `phaseEnded` event and does not spend it.
* So every player holds one or none of them does, decided before the first card is dealt.
*
* A `redFlagHeld` on each player would therefore be `optionalRules.emergencyToolbox` copied N times
* — already on the public projection — while implying to every reader of the common board that it
* varies by player and might change during a game. That is worse than the absence.
*
* This test exists so the plan item is not re-raised from the plan text: if the rule ever DOES
* become per-player, this fails and the projection is the right place to look.
*/
describe('the Red Flag holding is the Emergency Toolbox option, not a per-player fact', () => {
const dealt = (emergencyToolbox: boolean): GameState =>
createGame({
id: 'g',
seed: 99,
config: { ...config, optionalRules: { ...config.optionalRules, emergencyToolbox } },
playerNames: ['Ann', 'Bob', 'Cy'],
});
for (const toolbox of [true, false]) {
it(`gives every player the same answer with the toolbox ${toolbox ? 'on' : 'off'}`, () => {
const s = dealt(toolbox);
const held = s.players.map((p) => s.decks.redFlags.get(p.index) === true);
assert.deepEqual(held, [toolbox, toolbox, toolbox], 'the Red Flag has become a per-player fact');
});
}
it('is already public, through the option it comes from', () => {
const pub = publicSnapshot(dealt(true));
assert.equal(
pub.optionalRules.emergencyToolbox,
true,
'a spectator cannot tell whether the hand limit is three or four',
);
});
});