Fixes a regression I introduced in HAD-150: opponent moves were taking 7-10 seconds to appear.
HAD-150 wanted to avoid losing a move committed between the page's PDS read and the WebSocket opening, and did it by subscribing from a past cursor. That was the wrong mechanism. Jetstream does not seek to a cursor and then jump to the live tail; it drains the backlog at close to real time, so every later frame arrives as far behind as the cursor was old. Measured against jetstream2.us-east:
live tail (no cursor) lag median -31ms p90 -22ms max 59ms
cursor 5s in the past lag median 1840ms p90 4536ms max 5236ms
cursor 60s in the past lag median 55614ms p90 59556ms max 60296ms
The offset was worse than the nominal 5s, too: loadStartedAt is captured at the
top of loadGame, before the record read, the pairing lookup (which now pages),
the Constellation joiner query and handle resolution, so by the time the socket
opened that timestamp was already seconds stale.
Now the game and spectator sockets subscribe with no cursor, and
JetstreamConnection reads the opponent's record once on every onopen
instead. That closes the same gap without ever running behind, reuses the read
the polling fallback already does, and is safely repeatable because
applyOpponentMove ignores anything that is not strictly longer. It also runs
after a reconnect, where the same gap reappears.
Left alone deliberately: waitForOpponent still subscribes with a 60s cursor,
so join detection can lag by up to a minute. It has the same root cause but sits
on the join path rather than the move path, and I would rather not change that
on a demo day. Filed separately.