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:
Jesse.Markowitz
2026-09-09 15:31:46 -04:00
co-authored by Claude Opus 5
parent 312e0301e0
commit 02289e94b8
25 changed files with 2669 additions and 145 deletions
+2 -1
View File
@@ -24,12 +24,13 @@ import { impediments, narrate } from '../src/sim/narrate.ts';
import { readFileSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
import { actionMenu } from '../src/web/game.ts';
import { newCollector } from '../src/sim/display-step.ts';
import type { Game } from '../src/web/game.ts';
/** The thin wrapper `actionMenu` expects, built directly around an already-created multi-player state
* — `newGame` (game.ts) hardcodes one player, so it cannot construct this for a multi-seat game. */
const wrap = (s: GameState): Game =>
({ state: s, seed: s.seed, history: [], log: [], cues: [], scheduled: null, justDrawn: null, announced: null });
({ state: s, seed: s.seed, history: [], log: [], cues: [], scheduled: null, justDrawn: null, announced: null, display: newCollector() });
const competitive: GameConfig = {
mode: 'competitive',