Skip to content

Commit 51aaa5d

Browse files
committed
Fix premature sequence advancement and missing archived-threads invalidation on snapshot recovery
- Add filterNewEvents() to recovery coordinator to filter events without advancing latestSequence - Defer markEventBatchApplied() in applyEventBatch() until after the snapshot-recovery check passes, preventing latestSequence from advancing when events are not actually applied - Invalidate archived-threads query in runSnapshotRecovery() so the ArchivedThreadsPanel reflects snapshot state immediately
1 parent 097c3a3 commit 51aaa5d

File tree

2 files changed

+12
-3
lines changed

2 files changed

+12
-3
lines changed

apps/web/src/orchestrationRecovery.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,14 @@ export function createOrchestrationRecoveryCoordinator() {
6262
return "apply";
6363
},
6464

65-
markEventBatchApplied<T extends SequencedEvent>(events: ReadonlyArray<T>): ReadonlyArray<T> {
66-
const nextEvents = events
65+
filterNewEvents<T extends SequencedEvent>(events: ReadonlyArray<T>): ReadonlyArray<T> {
66+
return events
6767
.filter((event) => event.sequence > state.latestSequence)
6868
.toSorted((left, right) => left.sequence - right.sequence);
69+
},
70+
71+
markEventBatchApplied<T extends SequencedEvent>(events: ReadonlyArray<T>): ReadonlyArray<T> {
72+
const nextEvents = this.filterNewEvents(events);
6973
if (nextEvents.length === 0) {
7074
return [];
7175
}

apps/web/src/routes/__root.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ function EventRouter() {
313313
const applyEventBatch = (
314314
events: ReadonlyArray<OrchestrationEvent>,
315315
): "applied" | "snapshot-recovery-needed" => {
316-
const nextEvents = recovery.markEventBatchApplied(events);
316+
const nextEvents = recovery.filterNewEvents(events);
317317
if (nextEvents.length === 0) {
318318
return "applied";
319319
}
@@ -327,6 +327,8 @@ function EventRouter() {
327327
return "snapshot-recovery-needed";
328328
}
329329

330+
recovery.markEventBatchApplied(nextEvents);
331+
330332
const batchEffects = deriveOrchestrationBatchEffects(nextEvents);
331333
const needsProjectUiSync = nextEvents.some(
332334
(event) =>
@@ -417,6 +419,9 @@ function EventRouter() {
417419
if (!disposed) {
418420
syncServerReadModel(snapshot);
419421
reconcileSnapshotDerivedState();
422+
void queryClient.invalidateQueries({
423+
queryKey: orchestrationQueryKeys.archivedThreads(),
424+
});
420425
if (recovery.completeSnapshotRecovery(snapshot.snapshotSequence)) {
421426
void recoverFromSequenceGap();
422427
}

0 commit comments

Comments
 (0)