Skip to main content

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/games owns the state of every tracked game. It is the only producer of the game:<game_id> topic and of the game state in GET /api/sports/board.
  • Each game is seeded once from its Gamma event when it enters the board, that is, when engine/games starts 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 feeds topic (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 score string whose format varies by sport (2-1, 7-6(7-2), 0-0, 000-000|1-1|Bo3). Mapping those onto Game (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.