Repository navigation
fix: guard replay reads against mid-hydration prefixes across backend respawns - #301
Merged
Merged
Conversation
… respawns Three aligned guards against the wake-reconnect truncated-history report (first entry after phone wake replayed a conversation that ended in the middle; re-entry was fine once hydration finished): - resetBackendGeneration() now also voids backendLoadedSessions on every spawn: a stamp that survived a respawn kept isBackendSessionLive true for a process that never loaded the session, so session/load skipped the resume RPC and replayed empty or mid-re-hydration prefix. - fetchMessagesForReplay settle-polls the first replay read of a backend generation (no hydrationWatermark entry); raiseWatermark materializes the entry at 0 and create/fork seed it - a fresh store is complete by construction. The turn-path baseline opts out (guardFirstRead:false). - The turn finally re-stamps backend-loaded only while the subscribe-proven instance is still the live backend - a failed backend-lost recovery must not vouch for a session the respawned backend never loaded (ghost stamp, found in review).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Phone wake-reconnect could enter a conversation whose replay ended in the middle; re-entering the same conversation replayed it whole. Root cause (diagnosed across bridge + client, source-verified against upstream): the backend's
session/messagesreturns a PREFIX while a resumed session's store is still hydrating, and three bridge-side gaps let an unguarded plain read ship that prefix (or an empty read) to clients:backendLoadedSessionsstamps survived backend respawns (ensureBackendcleared hydration bookkeeping but not the stamps) —isBackendSessionLivestayed true for a process that never loaded the session, sosession/load's alreadyLive check skipped the resume RPC.hydrationUnsettled— later plain reads had no protection at all.runOneTurn's finally stamped backend-loaded unconditionally — a backend lost mid-turn with a FAILED recovery left a fresh stamp vouching for a session the respawned backend never loaded (ghost stamp, 5-min TTL of empty replays). Found in adversarial review.Fix
resetBackendGeneration()(extracted from ensureBackend's spawn branch) now also voidsbackendLoadedSessions.fetchMessagesForReplaysettle-polls the first replay read of a backend generation (nohydrationWatermarkentry — presence, not value, is the observed-settled marker);raiseWatermarkmaterializes the entry at 0; create/fork seed 0 (a fresh store is complete by construction). The turn-path baseline opts out (guardFirstRead:false) — no-watermark is unreachable there once stamps are void, and the settle's 2×300ms gaps must not sit on the prompt path.residentBackend) is still the live backend.Residual, accepted: a read landing above the watermark while a false-stable hydration still grows can still ship a prefix — the client-side last-message-id reconciliation is that defense (separate repo).
Verification