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
findJoinerlocates 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. NewfindLinkingRecordsinmicrocosm.ts. - Each candidate is validated:
parentGameUrimust match, exactly one slot must name the repo owner, and the other must be us. Earliest record wins. - Discovery runs early in
loadGameand 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
waitForOpponentno 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.