v0.7.9.2 — two things the table could hear that only one seat should

Both leaks were found while planning the common board (Gitea#20 step 1),
and both are live multiplayer bugs with or without that display, so they
are fixed now rather than with 0.8.0.

`game.log` is one shared list and `linesSince(seat)` slices it with no
per-seat filter, so every line reaches every player. It carried the SEED
in the opening line of each multiplayer game — the whole future of the
deal — and the NAME OF A CARD DRAWN BLIND from the face-down Home Office
deck. Solitaire deliberately keeps both: a one-seat table has nobody to
leak to, the seed is what a bug report quotes, and a player's own history
naming their own draw is the record. A Department slot is face up and
stays named. The drawer still learns their card through `justDrawn`,
which already goes to that seat alone.

Neither was found by a test. Every test in `redaction.test.ts` passes an
empty log, so the whole of narration has sat outside the redaction net
since the net was built. Both now have tests there; TODO #91 carries what
is still owed and supersedes #78, which described a gap that had already
been closed and never mentioned this one.

`docs/rules/` had no current description of the game, and `content.ts`
named `card-reference.md` as the file that carries what the cards say —
a file whose own banner says not to use its numbers, describing the
v0.4.5 deck where 3/4 is a Mail-Express with three coaches. Every file in
that directory is a deliberate historical record, so none of them is
rewritten. `as-built.md` is new and GENERATED from the same catalogues
the engine instantiates from, with a test that re-runs the generator and
fails when the checked-in file disagrees. A hand-written replacement
would have drifted the same way, for the same reason.

TODO.md: #32 closed — the playtest migration note did its job and the
jump is made; the durable fact it carried is kept. #78 retired in favour
of #91. The "play it at a table" section now records that 0.7.4-0.7.9
were test-run without change requests, and that more testing comes at the
end of the 0.7.9 series.

897 tests pass, up from 891.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3Qk7uresKCHksdZajXCLg
This commit is contained in:
Jesse.Markowitz
2026-09-07 12:09:25 -04:00
co-authored by Claude Opus 5
parent 7ade60e21f
commit 819996faa2
10 changed files with 717 additions and 41 deletions
+65
View File
@@ -19,6 +19,71 @@ page as `v0.1.0 · <sha> · <date>`, so what is deployed can always be identifie
---
## 0.7.9.2 — 2026-09-07
Two multiplayer information leaks, and the documentation problem that let a wrong table sit in
`docs/rules/` for several releases with the code pointing at it.
### The shared narration log was telling every seat things only one seat should know
Both found while planning the public common board (Gitea#20 step 1), and **both are live multiplayer
bugs with or without that display** — which is why they are fixed here rather than waiting for
0.8.0.
`game.log` is ONE list. `linesSince(seat)` (`server/session.ts:181`) slices it with no per-seat
filter at all, so every line written there reaches every player. Two things were being written into
it that should never have left the seat that caused them:
**The seed, in the opening line of every multiplayer game.** `competitive · 3 players · seed 4242`
handed each player the entire future of the deal — every card order, every die roll.
**The name of a card drawn blind from the Home Office deck.** `Player Cy drew Red Flags from the
Home Office deck`, to the whole table, from a face-down deck.
**Solitaire deliberately keeps both, and that is the rule rather than an exception.** A one-seat
table has nobody to leak to; the seed in the log is what a bug report quotes — both of the issues
fixed in 0.7.9.1 opened by naming it — and a solo player's own history naming their own draw is the
record, not a leak. The rule is *do not tell the OTHER seats*, not *write less down*. A Department
slot stays named for the same reason: those piles are face up, a discard goes onto one precisely so
a rival can take it, so the card was public before it was drawn.
The drawing seat still learns what it got. `justDrawn` is the owner-only channel and `session.ts`
already sends it to that seat alone, so hiding the name from the shared log costs the drawer nothing.
The seed remains in `game.seed`, in every save and in the lobby record, so nothing administrative or
replayable loses it.
**These were not found by a test. They were found by reading a plan.** Every test in
`redaction.test.ts` passes `[]` for the narration log, so the whole of it has sat outside the
redaction net since the net was built. Tests for both now live there, but two strings are not a net —
TODO #91 carries what is still owed, and supersedes #78, which described a gap that had already been
closed and never mentioned this one.
### `docs/rules/` had no current description of the game, and `content.ts` pointed at a superseded one
`content.ts` named `docs/rules/card-reference.md` as "the place that now carries what the cards say".
That file opens with its own banner — **"⚠ SUPERSEDED… Do not use its numbers"** — and describes the
v0.4.5 deck: twelve numbered trains, `3 / 4 | Mail-Express | 3 coaches, no caboose`, against a
`content.ts` whose train 3 is the Express, two freight cars, `oneFreightPerLocation`. The code was
sending readers to a table it had itself replaced.
Every file in `docs/rules/` turns out to be a historical record and says so: `rules-v0.1.md` is a
faithful transcription of the prototype PDFs, `open-questions.md` is the gap tracker, `rules-v0.2.md`
and `card-reference.md` both carry superseded banners. **So the fix is not to rewrite one of them** —
the record is worth more intact than patched, and there was simply no current reference at all.
`docs/rules/as-built.md` is new and is **generated** — trains, Mainline cards, Offices, freight
facilities, modifiers and track, emitted from the same exported catalogues the engine instantiates
from, by `scripts/build-card-reference.ts` (`npm run build:cards`).
`test/card-reference.test.ts` re-runs the generator and asserts the checked-in file matches, so
changing a card face without regenerating turns the suite red.
**A hand-written replacement would have drifted exactly the same way**, and for the same reason:
nothing fails when a table falls behind a constant. That is the whole lesson of the file it replaces.
897 tests pass, up from 891.
---
## 0.7.9.1 — 2026-09-07
Two playtest bugs from one session (seed 550943578). Both were reported as the game getting a rule