From cc7ebcaf187e9daa36fa18ef6938dffc6d76d76e Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 12 Nov 2026 11:06:25 -0800 Subject: [PATCH] fix(native-chat): retain full work projections with saved Stop Reuse the existing work-answer validity lookup when a narrow read returns. A full status projection includes the saved Stop, while narrow work leaves that overlay unrequested. Comparing only the authority work actually read preserves a valid arriving full projection and retires stale authority or context without a second cache or comparator. Extend the held-read owner scenario with a real saved Stop. Private point attribution shows unchanged full demand going from one reader point to zero, with full and work outputs equal; invalidation reuse remains one read followed by three cache hits. Owning 53 tests pass on both entries and static gates pass. Frozen721 is parent plus two W3 files only; paused SQL diagnostics work is excluded. Historical P7 benchmark costs remain small measured overhead as corrected in 12d479e6400c and report716. --- ...agent-session-status-journal-projection.ts | 56 +++++++++++-------- ...tured-agent-session-status-reentry.test.ts | 20 +++++-- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/main/native-chat/agent-session-wire/observations/structured-agent-session-status-journal-projection.ts b/src/main/native-chat/agent-session-wire/observations/structured-agent-session-status-journal-projection.ts index 4f7a72ad118..7b33df9cd30 100644 --- a/src/main/native-chat/agent-session-wire/observations/structured-agent-session-status-journal-projection.ts +++ b/src/main/native-chat/agent-session-wire/observations/structured-agent-session-status-journal-projection.ts @@ -98,6 +98,33 @@ export class StructuredAgentSessionJournalProjections { : null } + private currentWorkSql( + journal: SqlAgentSessionJournal, + captured: CapturedWork + ): JournalSqlWorkResult | undefined { + const authority = copyJournalCurrentWorkAuthority(captured.authority) + const entry = this.currentEntry(journal) + const answer: JournalSqlWorkResult | undefined = + entry?.kind === 'work-only' + ? entry.contextId === captured.contextId + ? entry.answer + : undefined + : entry?.projection.work && + entry.projection.contextId === captured.contextId && + entry.projection.authority + ? { + cursor: { epoch: entry.projection.epoch, sequence: entry.projection.sequence }, + physicalCursor: entry.physicalCursor, + authority: copyJournalCurrentWorkAuthority(entry.projection.authority), + result: { kind: 'state', state: entry.projection.work } + } + : undefined + return answer && + journalSqlWorkAnswerIsCurrent(journal, () => captured.authority, authority, answer) + ? answer + : undefined + } + /** Narrow work and full status occupy the same validated per-journal entry. */ async readWorkSql(journal: SqlAgentSessionJournal, capture: () => CapturedWork | null) { for (;;) { @@ -106,29 +133,11 @@ export class StructuredAgentSessionJournalProjections { this.byJournal.delete(journal) return null } - const authority = copyJournalCurrentWorkAuthority(captured.authority) - const entry = this.currentEntry(journal) - const answer: JournalSqlWorkResult | undefined = - entry?.kind === 'work-only' - ? entry.contextId === captured.contextId - ? entry.answer - : undefined - : entry?.projection.work && - entry.projection.contextId === captured.contextId && - entry.projection.authority - ? { - cursor: { epoch: entry.projection.epoch, sequence: entry.projection.sequence }, - physicalCursor: entry.physicalCursor, - authority: copyJournalCurrentWorkAuthority(entry.projection.authority), - result: { kind: 'state', state: entry.projection.work } - } - : undefined - if ( - answer && - journalSqlWorkAnswerIsCurrent(journal, () => captured.authority, authority, answer) - ) { + const answer = this.currentWorkSql(journal, captured) + if (answer) { return answer } + const authority = copyJournalCurrentWorkAuthority(captured.authority) const result = await journal.reads.work({ authority, operation: { kind: 'state' } }) const recaptured = capture() if (!recaptured) { @@ -141,7 +150,10 @@ export class StructuredAgentSessionJournalProjections { ) { continue } - if (!this.currentSql(journal, recaptured)) { + if ( + this.currentEntry(journal)?.kind !== 'full' || + !this.currentWorkSql(journal, recaptured) + ) { this.byJournal.set(journal, { kind: 'work-only', answer: result, diff --git a/src/main/native-chat/agent-session-wire/observations/testing/structured-agent-session-status-reentry.test.ts b/src/main/native-chat/agent-session-wire/observations/testing/structured-agent-session-status-reentry.test.ts index 08b0de1d8e7..03892dce331 100644 --- a/src/main/native-chat/agent-session-wire/observations/testing/structured-agent-session-status-reentry.test.ts +++ b/src/main/native-chat/agent-session-wire/observations/testing/structured-agent-session-status-reentry.test.ts @@ -123,6 +123,7 @@ it('discards row-free authority changes before publishing an asynchronous observ const projections = new StructuredAgentSessionJournalProjections() let revision = 0 let contextId: string | null = null + let latestStop: JournalWorkAuthority['stop'] = null let attached = true const held: string[] = [] const capture = () => { @@ -142,8 +143,8 @@ it('discards row-free authority changes before publishing an asynchronous observ openedThrough: journal.openingCursor(), reopenFloor: null, operationalRevision: revision, - stopRevision: 0, - stop: null + stopRevision: journal.stopMarks.revision(), + stop: journal.stopMarks.withCurrentSettlement(latestStop) } return { authority, contextId } } @@ -234,6 +235,13 @@ it('discards row-free authority changes before publishing an asynchronous observ ]) expect(await projections.readSql(journal, capture, () => {})).toMatchObject({ workRevision: 2 }) revision = 3 + await journal.appendStopEvent({ reason: 'user-stop' }, 1) + latestStop = await journal.stopMarks.latest() + expect(latestStop).not.toBeNull() + const captureNarrow = () => { + const captured = capture()! + return { ...captured, authority: { ...captured.authority, stop: null } } + } const workEntered = Promise.withResolvers() const workRelease = Promise.withResolvers() const workRead = journal.reads.work.bind(journal.reads) @@ -243,11 +251,15 @@ it('discards row-free authority changes before publishing an asynchronous observ await workRelease.promise return answer }) - const pendingWork = projections.readWorkSql(journal, capture) + const pendingWork = projections.readWorkSql(journal, captureNarrow) await workEntered.promise const published = await projections.readSql(journal, capture, () => {}) workRelease.resolve() await pendingWork expect(projections.currentSql(journal, capture())).toBe(published) - expect(projections.savedStop(journal, contextId)).toEqual({ value: null }) + expect(await projections.readWorkSql(journal, captureNarrow)).toMatchObject({ + result: { kind: 'state' } + }) + expect(projections.currentSql(journal, capture())).toBe(published) + expect(projections.savedStop(journal, contextId)).toEqual({ value: latestStop }) })