fix: preserve history coverage during tail refresh
Keeps an existing complete cursor/coverage after refreshing the latest messages Prevents tail refreshes from reintroducing an incorrect "load older" state Adds a regression test for complete history coverage across refreshes
This commit is contained in:
@@ -89,6 +89,28 @@ describe("SessionMessageLoader", () => {
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("preserves complete history coverage across a tail refresh", async () => {
|
||||
let calls = 0
|
||||
const { childStores, loader } = createLoader(async ({ sessionID }) => {
|
||||
calls += 1
|
||||
return calls === 1
|
||||
? response([createRecord(sessionID, "msg_1")])
|
||||
: response([createRecord(sessionID, "msg_2")], "stale-tail-cursor")
|
||||
})
|
||||
const target = { directory: "/repo", sessionID: "session-a" }
|
||||
|
||||
await loader.ensure(target)
|
||||
expect(loader.getSnapshot(target).complete).toBe(true)
|
||||
expect(loader.getSnapshot(target).cursor).toBe(undefined)
|
||||
|
||||
await loader.refreshTail(target, 2)
|
||||
|
||||
expect(loader.getSnapshot(target).complete).toBe(true)
|
||||
expect(loader.getSnapshot(target).cursor).toBe(undefined)
|
||||
loader.dispose()
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("does not deduplicate identical session IDs across directories", async () => {
|
||||
const calls: string[] = []
|
||||
const { childStores, loader } = createLoader(async ({ directory, sessionID }) => {
|
||||
|
||||
@@ -265,18 +265,25 @@ export class SessionMessageLoader {
|
||||
const store = this.childStores.ensureChild(normalized.directory, { bootstrap: false })
|
||||
this.bumpGeneration(entry)
|
||||
return this.startLoad(normalized, entry, store, "refresh", async (isCurrent) => {
|
||||
const previousCoverage = entry.snapshot.resolved
|
||||
? { cursor: entry.snapshot.cursor, complete: entry.snapshot.complete }
|
||||
: null
|
||||
const page = await this.fetchPage(normalized, Math.max(1, limit))
|
||||
if (!isCurrent()) return
|
||||
const committed = this.commitPage(normalized, entry, store, page, "merge", isCurrent)
|
||||
if (!committed || !isCurrent()) return
|
||||
const coverage = previousCoverage ?? page
|
||||
this.patchEntry(entry, {
|
||||
status: "ready",
|
||||
loadingKind: null,
|
||||
error: null,
|
||||
resolved: true,
|
||||
limit: Math.max(entry.snapshot.limit, committed.messages.length),
|
||||
cursor: page.cursor,
|
||||
complete: page.complete,
|
||||
// A tail refresh uses a deliberately small window. Its cursor only
|
||||
// describes that window, so it must not replace the established
|
||||
// history coverage and spuriously expose "load older".
|
||||
cursor: coverage.cursor,
|
||||
complete: coverage.complete,
|
||||
updatedAt: Date.now(),
|
||||
})
|
||||
this.persistCoverage(normalized, entry.snapshot)
|
||||
|
||||
Reference in New Issue
Block a user