Skip to content

fix(web): skip history replacements whose target seq is absent#9

Open
Antisophy wants to merge 1 commit into
CyberShadow:masterfrom
Antisophy:fix/history-replacement-skip
Open

fix(web): skip history replacements whose target seq is absent#9
Antisophy wants to merge 1 commit into
CyberShadow:masterfrom
Antisophy:fix/history-replacement-skip

Conversation

@Antisophy

Copy link
Copy Markdown
Contributor

replaceHistoryBoundary throws when a replace-style boundary event references a message seq the client's reduced state does not contain. Two ways to get there in practice:

  • a live boundary broadcast racing a still-streaming history load (the flush path's pendingHistoryBoundaries defer only guards the separately-routed task_history_boundary_replaced kind; boundary-carrying events flowing through handleTaskMessage reach the reducer unguarded), and
  • a boundary persisted under an older seq numbering, replayed against a session whose seqs no longer line up. Observed on a large compacted session (7400+ events) after updating across the recent history refactors: every load threw History replacement matched 0 messages at seq 7433, which permanently wedged the session view at "Loading session..." with no recovery short of code changes.

Since the throw converts a cosmetic inconsistency into an unviewable session, this makes the matched-0 case fail soft: drop the replacement with a console diagnostic and return the state unchanged. The cost is one message rendering without its replacement until seqs realign. The structural invariants keep throwing (multiple matches, canonical-identity mismatch), and the replay flush path's defer behavior is unchanged; this only aligns the reducer's own behavior for the paths that have no defer queue.

The existing "rejects invalid replacement targets" test pinned the throw; it now asserts the skip returns the same state instance. Full nix flake check suite is green on the branch.

A replace-style boundary event can reference a message the client does
not have: a live boundary broadcast racing a still-streaming history
load, or a boundary persisted under an older seq numbering (observed
after the 2026-07-20 upstream rebase on a large compacted session:
'History replacement matched 0 messages at seq 7433'). The reducer
threw for that case, which wedged the whole session view on every load,
permanently sticking the task at 'Loading session...'.

Drop the unmatched replacement with a console diagnostic instead: the
cost is one message rendering without its replacement, against a
permanently unviewable session. Structural violations (multiple
matches, identity mismatch) still throw. The history-replay flush path
already treats a missing target as 'defer'; this aligns the reducer's
own behavior for the paths without a defer queue.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant