Four things the game counted and never said, and two it said wrong
Stays in the unshipped v0.7.9. Prompted by Jesse asking the general question after two v0.7.9 fixes turned out to be the same shape: actingPlayer existed and the Frame threw it away, and collisionsToday / collisionsTotal rode the Frame for three releases with nothing drawing them. So what else is computed, serialised and sent to nobody? THE AUDIT, done rather than guessed. Every one of Frame's 59 top-level fields grepped for a read across the seven renderers, then the same for Tally's 26 members. 55 of 59 are read. Four are not. tally.unloadsBegun was visible rather than merely unused. §9.1 makes loading and unloading the same shape — begun, then carried through — and the results screen printed "Loads still in the pipeline" for one side and nothing for the other, reporting half of a symmetric mechanism. tally.cardsDiscarded was counted by the engine and listed beside "Cards drawn" and "Cards played" without it, though Gitea#9 made throwing a Timetabled train away a deliberate move — a player CHOICE the game counted and never reported. Both are reported now. viewerSeat and overHandLimit are deferred by Jesse. The second is the fullest version of the shape: engine computes it, view.ts puts it on the Frame, session.ts declares it on the Session interface AND implements it twice, and the only caller in the repo is its own test. Four layers of plumbing, no consumer. The decision when it comes is delete-or-document, not a patch. Fixing the two turned up a third thing: resultsHtml draws tallyHtml(report?.tally ?? f.tally), and report is f.official, so a finished game reports the tally frozen at the official ending rather than the live one. The first attempt at a test overrode f.tally alone, changed nothing on screen, and failed for a reason unrelated to the fix. A SHOUTED KEYWORD IS NOT A SENTENCE. `EXTRA X18 started…` attributed to a player rendered as `Player Solitaire eXTRA X18 started…`, and the same happened to TRAIN 1 MADE UP and COLLISION. `record` folds a narration's opening word into the middle of a sentence and did it with a flat charAt(0).toLowerCase(). It now folds only a sentence-cased word — ^[A-Z][a-z], a capital followed by a lower-case letter — which also leaves X22 Pee-Dee alone, where a naive uppercase test gets it wrong because '2'.toUpperCase() is '2'. It had been filed under Play Balance, where it has no business being, which is how it survived a session that had ruled balance work out of scope. A REPLAYED SAVE NOW NARRATES WHAT THE LIVE GAME NARRATED. fromSave's loop called record(game, result.events) with no actor, so every restored save, every undo (which rebuilds through fromSave) and the replay viewer stripped the "Player X" prefix off every attributed line. submit attributes and fromMultiplayerSave attributes; this was the one path of three that did not. One argument, with actor already computed on the line above. Why it survived: nothing ever compared a fromSave-built log against a LIVE-played one. The single log-comparing test compares undo's rebuilt log against another fromSave-built log — and undo itself rebuilds through fromSave — so the gap cancelled out on both sides. The suite was green with the bug in and green with it out. The new test plays a game, saves it, restores it and asserts the two logs are identical: the missing direction, not a new requirement. All three fixes were confirmed to go RED with the fix reverted before being called done. 884 tests pass, seventeen new. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTaNBL1jVxNqgFdjHkHoo3
This commit is contained in:
co-authored by
Claude Opus 5
parent
d267f89a82
commit
e62ea54259
@@ -352,7 +352,17 @@ function tallyHtml(t: Frame['tally']): string {
|
||||
};
|
||||
push('Loads made up', t.loadsCompleted);
|
||||
push('Loads broken', t.unloadsCompleted);
|
||||
/**
|
||||
* BOTH HALVES OF THE MEN | AT | WORK PIPELINE, not just the loading one.
|
||||
*
|
||||
* §9.1 makes loading and unloading the same shape — begun, then carried through — and the Tally
|
||||
* has counted both since it was written. The screen reported only the loading side, so a player
|
||||
* with three unloads part-finished at the final whistle was told nothing about them while the
|
||||
* equivalent loads were listed. Found 2026-08-30 auditing which Frame fields nothing reads:
|
||||
* `unloadsBegun` was one of four, and the only one whose absence was visible on screen.
|
||||
*/
|
||||
push('Loads still in the pipeline', t.loadsStarted - t.loadsCompleted);
|
||||
push('Unloads still in the pipeline', t.unloadsBegun - t.unloadsCompleted);
|
||||
push('Passengers boarded', t.passengersBoarded);
|
||||
push('Passengers detrained', t.passengersDetrained);
|
||||
push('Cars coupled', t.carsCoupled);
|
||||
@@ -386,6 +396,10 @@ function tallyHtml(t: Frame['tally']): string {
|
||||
}
|
||||
push('Cards drawn', t.cardsDrawn);
|
||||
push('Cards played', t.cardsPlayed);
|
||||
// Gitea#9 made throwing a Timetabled train away a legal and deliberate move, so a discard is a
|
||||
// CHOICE the player made rather than an accident of the hand limit — and the engine has counted
|
||||
// it all along while the screen listed only draws and plays beside it.
|
||||
push('Cards discarded', t.cardsDiscarded);
|
||||
|
||||
return `<h4 class="res-h">The railroad</h4>${factTable(rows)}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user