fix(server): stop reverts destroying thread history; rebuild projections from events - #24
Merged
Merged
Conversation
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
Every Esc-retraction revert progressively destroyed the head of long threads.
thread.revertedretention was an allowlist keyed on checkpointed turns: any message whose turn had no checkpoint at revert time (never diff-completed, evicted, or trimmed by a previous revert) was silently dropped, and the count-based fallback could only rescueturnId === nullmessages. Production evidence: threadd1b97649…went through 11 retractions in one day and its projection shrank to 75 rows starting mid-day, while the event log holds the full 2,804-event history.Fix (3 commits)
fix(server): retain thread history during projection replay— retention inverted to a denylist of actually-reverted turns (sharedcollectRevertedTurnIds: post-baseline checkpoints + retraction target + latest/active turn unless proven retained), applied to both the in-memory projector and the SQL projection pipeline, for messages, activities, and proposed plans. Worst-case failure mode is now a stray retained message instead of destroyed history.Also makes bootstrap replay pure: per-event attachment side effects are skipped during bootstrap (a from-zero replay would prune attachment files still referenced by later events — a hazard that already existed in miniature for crash-gap recovery) and replaced with a single final-state reconciliation pass at bootstrap end, before reactors/serving start. Bootstrap also switches from projector-major to event-major replay to match live ordering, so retention sees turn/session evidence as of each historical event.
feat(server): rebuild projections from event history— migration 045 clears all event-derived projection tables and zeroesprojection_statecursors, so the next boot replays the full event log (~27k events) through the fixed projector, restoring all revert-trimmed history in every thread. Table-guarded and idempotent per fork convention.fix(web): invalidate cache after projection rebuild—StoredThreadSnapshotschemaVersion 5→6 so warm IndexedDB snapshots cold-load the rebuilt server truth (same pattern as fix(web): invalidate thread snapshot caches predating migration 044 #23/migration 044).Verification
ActivityPayloadProjectionpayload-budget tests + dependentserver.test.tscase — none of those files change in this PR)