refactor(frontend): centralize prompt-sync reconciliation invariant - #2665
Merged
Conversation
The "a pull must never clear the dirty flag; only a successful push does" rule was implemented ad-hoc at three local↔backend merge sites and got it wrong in two — which is how a promoted interrupt showed as live in the UI while the backend kept interrupt=false (the merge re-confirmed the entry "synced" on a poll, so the push skipped it). Extract a single exported `reconcileEntry(local, backend, pushed)` that encodes the invariant once: - backend-owned fields (status/retry/error) always reflect the backend - syncedToBackend cleared (true) only when pushed=true (a real push-ack); a pull (pushed=false) preserves a pending local edit (false stays false) All three sites — mergeWithBackend, the syncToBackend push-ack, and the status poll — now route through it, so the invariant can't diverge again. The push-ack site is also now more correct: it clears the dirty flag only for the entries it actually pushed (toSync), not every entry in the response. Adds usePromptHistory.test.ts covering the invariant (pull-preserves-dirty, push-clears-dirty, backend status reflected, frontend-owned fields not clobbered) — these would have caught the original bug. Follow-up to #2664. See design/2026-06-19-incident-interrupt-during-boot-context-loss.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Follow-up to #2664. The dirty-flag bug we just fixed existed because one rule — "a pull must never clear the dirty flag; only a successful push does" — was implemented ad-hoc at three local↔backend merge sites and got it wrong in two. That's how a promoted interrupt showed live in the UI while the backend kept
interrupt=false: a status poll re-confirmed the entry "synced", so the push skipped it.Change
Extract a single exported
reconcileEntry(local, backend, pushed)that encodes the invariant once:status/retry/error) always reflect the backendsyncedToBackendis cleared (true) only whenpushed=true(a real push-ack); a pull (pushed=false) preserves a pending local edit (falsestaysfalse)All three sites —
mergeWithBackend, thesyncToBackendpush-ack, and the status poll — route through it, so the rule can't diverge again. The push-ack site is also now stricter: it clears the dirty flag only for the entries it actually pushed (toSync), not every entry in the response.Tests
usePromptHistory.test.tscovers the invariant directly — pull-preserves-dirty (the bug), push-clears-dirty, backend status reflected even on a dirty entry, frontend-owned fields (interrupt/content) not clobbered. These would have caught the original bug. Locally: 12/12 prompt tests green,yarn buildclean.See
design/2026-06-19-incident-interrupt-during-boot-context-loss.md.🤖 Generated with Claude Code