diff --git a/packages/app/src/timeline/viewed-timeline-sync.test.ts b/packages/app/src/timeline/viewed-timeline-sync.test.ts index 49ee020c8d..6550350829 100644 --- a/packages/app/src/timeline/viewed-timeline-sync.test.ts +++ b/packages/app/src/timeline/viewed-timeline-sync.test.ts @@ -497,6 +497,40 @@ test("a chat without coverage that recovers a gap loads the latest tail when his world.sync.dispose(); }); +test("a recovery's follow-up page starts after rows another fetch applied meanwhile", async () => { + const world = new TimelineWorld(); + world.sync.setConnected(true); + world.sync.replaceVisibleAgentIds("workspace", ["agent-a"]); + (await world.nextMembership()).succeed(); + (await world.nextFetch("agent-a")).respond({ hasNewer: false, seq: 0 }); + await vi.waitFor(() => expect(world.sync.getAgentTimelineStatus("agent-a")).toBe("ready")); + + world.sync.recoverGap("agent-a", { epoch: "epoch-agent-a", endSeq: 0 }); + const firstPage = await world.nextFetch("agent-a"); + world.cursors.set("agent-a", { epoch: "epoch-agent-a", endSeq: 40 }); + firstPage.respond({ hasNewer: true, seq: 40 }); + const latestTail = await world.nextFetch("agent-a"); + expect(latestTail.request.direction).toBe("tail"); + + world.sync.recoverGap("agent-a", { epoch: "epoch-agent-a", endSeq: 40 }); + const recovery = await world.nextFetch("agent-a"); + world.cursors.set("agent-a", { epoch: "epoch-agent-a", endSeq: 300 }); + latestTail.respond({ hasNewer: false, seq: 300 }); + recovery.respond({ hasNewer: true, seq: 80 }); + + const followUp = await world.nextFetch("agent-a"); + expect(followUp.request).toEqual({ + direction: "after", + cursor: { epoch: "epoch-agent-a", seq: 300 }, + limit: 40, + projection: "projected", + wholeTurns: true, + }); + followUp.respond({ hasNewer: false, seq: 300 }); + await vi.waitFor(() => expect(world.sync.getAgentTimelineStatus("agent-a")).toBe("ready")); + world.sync.dispose(); +}); + test("reports a chat current only once its parked follow-up page completes", async () => { const world = new TimelineWorld(); world.sync.setConnected(true); diff --git a/packages/app/src/timeline/viewed-timeline-sync.ts b/packages/app/src/timeline/viewed-timeline-sync.ts index 59abc5cadd..c9a8045791 100644 --- a/packages/app/src/timeline/viewed-timeline-sync.ts +++ b/packages/app/src/timeline/viewed-timeline-sync.ts @@ -742,7 +742,10 @@ export function createViewedTimelineSync(ports: ViewedTimelineSyncPorts): Viewed await fetchUntilCurrent( agentId, generation, - planTimelineCatchUpAfter(page.endCursor), + planTimelineCatchUpPastApplied( + planTimelineCatchUpAfter(page.endCursor), + ports.readCursor(agentId), + ), false, ); return;