Chess on the ATmosphere checkmate.blue

Recover games where the opponent joined while we were offline #48

Closed checkmate.blue
targeting main
Closed opened by and assigned to checkmate.blue
targeting main

HAD-149 and HAD-150. Stacked on fix/verify-opponent-results; target that branch, not main.

Create an open game, close the tab, a friend joins an hour later. You come back to "Waiting for opponent" forever, because waitForOpponent replays only the last 60 seconds of Jetstream and the join is long past. The joiner is stuck too: their isMyTurn needs status === 'active', which only ever arrives from your record. Bricked for both, with no recovery.

  • The joiner's child record is the only durable evidence of the join, so findJoiner locates it through Constellation's backlink index: /links?target=<gameUri>&collection=blue.checkmate.game&path=.parentGameUri. I verified that query against the live service before building on it, since the ticket flagged it as unconfirmed. New findLinkingRecords in microcosm.ts.
  • Each candidate is validated: parentGameUri must match, exactly one slot must name the repo owner, and the other must be us. Earliest record wins.
  • Discovery runs early in loadGame and patches the in-memory record's empty slot and status, so init, handle resolution, reconciliation and Jetstream all behave as if the record had been current. The alternative was duplicating that logic inside the discovery path.
  • The joiner side is simpler: if our own child record says active while the owner's still says waiting, we set active.
  • The pairing write in waitForOpponent no longer swallows its error.

HAD-150: loadStartedAt is captured before any PDS read and both the participant and spectator sockets rewind to it, so a move made between the read and the socket opening is replayed instead of lost.

This pull request has no versions submitted yet.