0006. Game state: seeded once from Gamma, then updated by the Sports WS
- Status: Accepted
- Date: 2026-09-30
Context
The board shows each game’s score, clock, period and status. Polymarket has two places that hold this state: the Sports WebSocket (wss://sports-api.polymarket.com/ws) and the
Gamma event for the game (score, period, elapsed, live, ended).
Phase 0 verification showed that the Sports WS sends changes only. On connect there is
no snapshot: in two runs of 65 s and 150 s with many games live, frames arrived one game
at a time, and a live game appeared only when its state changed
(polymarket.md §11). A game in a quiet
spell would show no score for minutes if the app listened only to the feed.
CLAUDE.md allows one source per signal and no fallbacks. A snapshot followed by deltas
from the same feed counts as one source.
Decision
engine/gamesowns the state of every tracked game. It is the only producer of thegame:<game_id>topic and of the game state inGET /api/sports/board.- Each game is seeded once from its Gamma event when it enters the board, that is,
when
engine/gamesstarts tracking it. - After the Sports WS reconnects, every tracked game is seeded again, once, because the feed does not replay changes missed while it was down.
- Apart from those seeds, only Sports WS frames change a game.
- Gamma is never polled for scores. No timer, request or page load re-reads game state from Gamma.
- The seed plus the feed’s changes is one snapshot-and-deltas source, as recorded in the PLAN §3.7 signal table.
- While the Sports WS is disconnected, game state is shown as stale through the
feedstopic (pm.sports). It is not refreshed from Gamma in the meantime. - A feed change that arrives while a seed request is in flight must not be overwritten by the older seed. How the actor orders the two is defined by implementation.
Consequences
- A game shows its state as soon as it is on the board, without waiting for a change.
- A seed is only as fresh as Gamma. If Gamma lags, the game shows the older value until the feed sends the next change.
- Changes missed during a Sports WS outage are recovered at the re-seed after the reconnect, not before.
- Gamma requests for game state scale with games entering the board and with Sports WS reconnects, not with time or with the number of browser tabs.
- The board REST response and the
game:<game_id>snapshot read the same actor state, so they cannot disagree. - The feed sends team names and a single
scorestring whose format varies by sport (2-1,7-6(7-2), 0-0,000-000|1-1|Bo3). Mapping those ontoGame(home_score,away_score, team ids) is venue adapter work, defined by implementation.
Alternatives considered
- Sports WS only, no seed. Rejected. A game shows nothing until it next changes, which can take minutes.
- Poll Gamma for scores on a timer, alongside the feed. Rejected. It is a second source for the same signal: when the feed dies, the polled scores keep the board looking healthy and hide the outage.
- Gamma only. Rejected. Polling is slower than the feed and spends Gamma rate limit on every game.
- Re-seed on a timer to correct drift. Rejected. A periodic re-seed is polling under another name.