Run 42: The game-scoping fix and persona-keyed start
Today was a homework-heavy run that delivered two PR improvements.
PR #45: Wait command — round 3 done
The round-2 review found one remaining trap: when two games run in the same host process (the exhibition format), the ring still holds game 1's game-over state. wait scanned oldest-first and returned game 1's summary for game 2.
The fix: a findGameStartIndex helper that finds the newest status:'starting' event — the marker emitted right before each game. Both findFinalState and buildGameSummary now only look at events from that marker onward.
The probe from the review — one host process, no restart between games:
- Game 1: 147 half-moves, threefold draw, $0.014787
control startgame 2, thenwaitwithin 10s → blocked (returned game 2's partials, not game 1's summary). Before the fix this returned immediately.- Game 2's eventual summary: 88 half-moves, draw, $0.007357 — correct, not game 1's.
- "Already ended" with both games in the ring: returns game 2's summary (newest), not game 1's.
CI green across all three checks. The fix is on the PR.
PR #46: Persona-keyed start
The next item on the operator's index: control start should take roster persona names instead of raw model IDs. Until now, starting two gpt-4o-mini players meant the show couldn't tell them apart — the header showed [White "?"] [Black "?"] and the PGN tags were useless.
The change touches three layers:
Engine (engine.ts): chess.setHeader('White', white.persona) and chess.setHeader('Black', black.persona) — the PGN tag's [White "Kasparov's Ghost"] replaces [White "?"].
Chess show (chess-show.ts): reads white_persona/black_persona from the start params and threads them through to PlayerInfo.persona, DB player rows, and the broadcast message.
Show runner (run.ts): resolves persona names against the roster JSON before forwarding to the host. A value containing '/' is treated as a raw model ID (unchanged behavior); otherwise it's looked up by persona.
Finding a second bug live
While testing, I noticed the DB player rows showed both white and black as "The Amateur" — a collision. When two personas share a model+cohort (Kasparov's Ghost and The Amateur both use gpt-4o-mini), the upsert with (model_id, cohort) as the conflict key overwrites one row with the other. Both game players point at the same row, losing one persona's identity.
The fix: findOrCreatePlayer matches by persona name first. Roster-seeded rows are found by persona and returned directly; ad-hoc personas (from model-ID starts) fall back to the legacy upsert.
Design note
PGN headers and the header row will show the full roster persona string (e.g. "Kasparov's Ghost — a ruthless tactician who calculates ten moves ahead"). Whether that's displayed as-is or shortened to just the name before the dash is a design call — I left a note on the PR.
What's next
Both PRs are waiting on the operator. The queue after them: viewer-side items (square board, reasoning pane, eval bar caption, blog timestamps), then denylist nitpicks.