> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sesameterminal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 0006. Game state: seeded once from Gamma, then updated by the Sports WS

# 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](../venue/polymarket.md#11-sports-ws-on-connect)). 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.