Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
140 commits
Select commit Hold shift + click to select a range
98a2e27
fix(native-chat): report a failed startup chat reconcile instead of f…
brennanb2025 Sep 30, 2026
872f31f
fix(native-chat): read restored chats without waiting on lease bookke…
brennanb2025 Sep 30, 2026
e5d1ab2
fix(native-chat): list chat tabs without opening every chat, and stop…
brennanb2025 Sep 30, 2026
d9571de
Merge remote-tracking branch 'origin/brennanb2025/records-readonly-st…
brennanb2025 Sep 30, 2026
b23eb28
fix(native-chat): keep every record-store write off the startup chat …
brennanb2025 Sep 30, 2026
f3ec1a1
fix(native-chat): start each restore pass from one lease check and st…
brennanb2025 Sep 30, 2026
25fffab
Merge remote-tracking branch 'origin/brennanb2025/records-readonly-st…
brennanb2025 Sep 30, 2026
a77e824
docs(native-chat): correct restore comments and a test name to match …
brennanb2025 Sep 30, 2026
2a030d0
fix(native-chat): keep the post-list chat restore to the chats still …
brennanb2025 Sep 30, 2026
855be2f
fix(native-chat): start the chat history restore after the tab list h…
brennanb2025 Sep 30, 2026
ba777ae
Merge remote-tracking branch 'origin/brennanb2025/records-readonly-st…
brennanb2025 Sep 30, 2026
bf3aebe
fix(native-chat): wait for the startup lease check again, and drop th…
brennanb2025 Sep 30, 2026
ebdc5b9
fix(native-chat): start the chat history restore once the tab list's …
brennanb2025 Sep 30, 2026
c3af299
test(native-chat): give the rest rig's and T3's dispatch waits a 10 s…
brennanb2025 Sep 30, 2026
0cbeb14
fix(native-chat): derive /clear replacements once per tab listing, no…
brennanb2025 Sep 30, 2026
4768d53
fix(native-chat): derive /clear replacements right before each listin…
brennanb2025 Sep 30, 2026
316d7cc
fix(native-chat): restore chat history after the tab list one chat at…
brennanb2025 Sep 30, 2026
4e5c839
Merge origin/main into brennanb2025/records-readonly-startup
brennanb2025 Sep 30, 2026
1df09d5
fix(native-chat): keep a throwing failure sink from failing the start…
brennanb2025 Sep 30, 2026
0a8bf77
test(native-chat): a send during a slow startup lease check starts it…
brennanb2025 Sep 30, 2026
c1a60fe
Merge remote-tracking branch 'origin/brennanb2025/records-readonly-st…
brennanb2025 Sep 30, 2026
e53a552
Merge remote-tracking branch 'origin/main' into brennanb2025/chat-tab…
brennanb2025 Sep 30, 2026
531c031
fix(native-chat): keep the runtime-id module within its 400-line limit
brennanb2025 Sep 30, 2026
7688715
feat(native-chat): store each chat's state with its journal; startup …
brennanb2025 Sep 30, 2026
f227685
test(native-chat): stored chat state, open settlement plan and startu…
brennanb2025 Sep 30, 2026
bf0796e
test(native-chat): one import of the journal test support in the star…
brennanb2025 Sep 30, 2026
5c03e00
Merge remote-tracking branch 'origin/main' into brennanb2025/chat-tab…
brennanb2025 Sep 30, 2026
d2cecaf
Merge remote-tracking branch 'origin/brennanb2025/chat-tabs-list-with…
brennanb2025 Sep 30, 2026
b088396
test(native-chat): settled roster uses a real terminal state; pin wha…
brennanb2025 Sep 30, 2026
8014257
Merge remote-tracking branch 'origin/main' into brennanb2025/chat-tab…
brennanb2025 Sep 30, 2026
617b30e
test(native-chat): close the chat as the user does in the restore-aft…
brennanb2025 Sep 30, 2026
a696070
Merge remote-tracking branch 'origin/main' into brennanb2025/chat-tab…
brennanb2025 Sep 30, 2026
f52521d
refactor(native-chat): store each chat's state after its journal commit
brennanb2025 Sep 30, 2026
773a05f
Merge remote-tracking branch 'origin/brennanb2025/chat-tabs-list-with…
brennanb2025 Sep 30, 2026
4a05b81
fix(native-chat): seeded rows keep a crash-cut turn's verdict; type t…
brennanb2025 Oct 1, 2026
4d4a7b0
perf(native-chat): judge each chat item once for the owed flag, not o…
brennanb2025 Oct 1, 2026
058b551
refactor(native-chat): each chat's status row, written with its journ…
brennanb2025 Oct 1, 2026
9533d1f
test(native-chat): the key guard and the repair's status write each h…
brennanb2025 Oct 1, 2026
25ee094
test(native-chat): the held-submission case resolves with a typed dis…
brennanb2025 Oct 1, 2026
7668014
refactor(native-chat): a failed append puts back what its row changed…
brennanb2025 Oct 1, 2026
7bb2d40
refactor(native-chat): a failed undo marks the fold stale instead of …
brennanb2025 Oct 1, 2026
7da72ff
perf(native-chat): judge each item once for a chat's status; status w…
brennanb2025 Oct 1, 2026
93cb6cc
fix(native-chat): chat commands wait for the startup settle before an…
brennanb2025 Oct 1, 2026
661984b
test(native-chat): read a quit's turn verdict the way the next launch…
brennanb2025 Oct 1, 2026
4ecee39
test(native-chat): the deadlock test settles chats a crash cut mid-tu…
brennanb2025 Oct 1, 2026
33fc782
fix(native-chat): type the fold undo's containers and the restore tes…
brennanb2025 Oct 1, 2026
808afc7
Merge remote-tracking branch 'origin/brennanb2025/chat-tabs-list-with…
brennanb2025 Oct 1, 2026
247d224
refactor(native-chat): one rule decides which chats this host can set…
brennanb2025 Oct 1, 2026
a7c9b3f
refactor(native-chat): expose the one closed-chat settle; it refuses …
brennanb2025 Oct 1, 2026
e4db1da
test(native-chat): type the restore test's stand-in host members by t…
brennanb2025 Oct 1, 2026
aae0428
test(native-chat): the legacy record import expects the current schem…
brennanb2025 Oct 1, 2026
02bd7e4
fix(native-chat): a failed append that removed an entry re-reads the …
brennanb2025 Oct 1, 2026
c76f31a
refactor(native-chat): one command entry waits for the startup settle…
brennanb2025 Oct 1, 2026
3ebcd7a
fix(native-chat): the startup timing line records the step whoever op…
brennanb2025 Oct 1, 2026
72def96
fix(native-chat): the startup gate closes once per launch
brennanb2025 Oct 1, 2026
4f266e8
fix(native-chat): the startup wait lets go before the AI vault's 5 s …
brennanb2025 Oct 1, 2026
9dd3852
fix(native-chat): a startup recovery that never answers leaves its le…
brennanb2025 Oct 1, 2026
50f747f
Merge remote-tracking branch 'origin/main' into brennanb2025/chat-tab…
brennanb2025 Oct 2, 2026
5735ee9
chore: put back main's orcad files the merge commit's formatter rewrote
brennanb2025 Oct 2, 2026
90324db
Merge remote-tracking branch 'origin/brennanb2025/chat-tabs-list-with…
brennanb2025 Oct 2, 2026
b122e38
test(native-chat): say why the status feed's stand-in journals are cast
brennanb2025 Oct 2, 2026
56291e6
fix(native-chat): the startup step's memo lives with the step; a stan…
brennanb2025 Oct 2, 2026
bc66892
test(native-chat): a failed Stop or Resume row leaves the queue's pau…
brennanb2025 Oct 2, 2026
df278a7
test(native-chat): an older schema's build reads a version-5 journal …
brennanb2025 Oct 2, 2026
4c6bf6e
chore: put back main's orcad files the merge's formatter rewrote
brennanb2025 Oct 2, 2026
52c6b86
fix(native-chat): a failed chat startup step reaches the diagnostics …
brennanb2025 Oct 2, 2026
a1e4852
perf(native-chat): the status feed reads the newest accepted send fro…
brennanb2025 Oct 2, 2026
913fc6f
test(native-chat): a turn a person's Stop found settles as theirs, fr…
brennanb2025 Oct 2, 2026
4d25c77
perf(native-chat): rowless listed chats get their status from their r…
brennanb2025 Oct 2, 2026
705624e
test(native-chat): a status row from a corrupt history's rows equals …
brennanb2025 Oct 2, 2026
2447f10
perf(native-chat): a status snapshot's rows reach the renderer's stor…
brennanb2025 Oct 2, 2026
79ca3cf
test(native-chat): a 247-chat snapshot reaches the store, the mobile …
brennanb2025 Oct 2, 2026
0786214
test(native-chat): listing and Activity tests follow statuses from ro…
brennanb2025 Oct 2, 2026
ebdde9d
fix(native-chat): the startup status pass gives each chat its own tas…
brennanb2025 Oct 2, 2026
8c23d92
test(native-chat): batched status rows equal rows written one at a ti…
brennanb2025 Oct 2, 2026
7bc59c1
test(native-chat): a long chat's status fold spans several tasks even…
brennanb2025 Oct 2, 2026
4d603a2
fix(native-chat): a listed chat whose startup recovery outlasted its …
brennanb2025 Oct 2, 2026
84805b6
perf(native-chat): the startup status pass writes its rows a slice at…
brennanb2025 Oct 2, 2026
92b791a
perf(native-chat): the startup status pass fills the chats on screen …
brennanb2025 Oct 2, 2026
a4b1950
fix(native-chat): the startup pass finds the chats on screen from the…
brennanb2025 Oct 2, 2026
ff1408d
fix(native-chat): the startup pass stores unfinished chats' rows unsh…
brennanb2025 Oct 2, 2026
f950b36
refactor(native-chat): the on-screen chat order lives in shared, so t…
brennanb2025 Oct 2, 2026
98eeaa2
fix(native-chat): a corrupt rowless chat gets no row from the startup…
brennanb2025 Oct 2, 2026
39ef376
refactor(native-chat): drop the single-chat status backfill; callers …
brennanb2025 Oct 2, 2026
226da57
perf(native-chat): the startup pass commits its status-row slices uns…
brennanb2025 Oct 2, 2026
f62792b
test(native-chat): after an unsynced status slice the next commit is …
brennanb2025 Oct 2, 2026
161001f
perf(native-chat): the startup status pass yields only before chats i…
brennanb2025 Oct 2, 2026
414bf53
test(native-chat): a paired client whose first tab listing times out …
brennanb2025 Oct 2, 2026
eeb2b39
test(mobile): a slow host's timed-out first tab list still ends with …
brennanb2025 Oct 2, 2026
8fc0bf3
perf(native-chat): listed chats' missing statuses are derived before …
brennanb2025 Oct 2, 2026
60037f6
refactor(native-chat): the startup settle's closed-chat open drops it…
brennanb2025 Oct 2, 2026
6168359
perf(native-chat): listed chats still in per-chat files are restored …
brennanb2025 Oct 2, 2026
144610b
fix(native-chat): startup recovers each lease once and on its own, an…
brennanb2025 Oct 2, 2026
8f2d61a
Merge main into chat-tabs-list-without-replay
brennanb2025 Oct 3, 2026
3876f77
Merge remote-tracking branch 'origin/brennanb2025/chat-tabs-list-with…
brennanb2025 Oct 3, 2026
67708e3
fix(native-chat): startup's settle recovers a lease the reconcile mov…
brennanb2025 Oct 3, 2026
334e50d
test(native-chat): a send let through mid-catch-up is answered before…
brennanb2025 Oct 3, 2026
59fd62e
refactor(native-chat): startup status comments say what each step doe…
brennanb2025 Oct 3, 2026
d1bf8ae
fix(native-chat): the runtime-id module stays within its 400-line lim…
brennanb2025 Oct 3, 2026
33e8d56
Merge remote-tracking branch 'origin/brennanb2025/chat-tabs-list-with…
brennanb2025 Oct 3, 2026
d46a936
test(native-chat): the older build in the newer-row-kind downgrade te…
brennanb2025 Oct 3, 2026
1b94e23
refactor(native-chat): the journal store stays within its length limi…
brennanb2025 Oct 3, 2026
88fff13
fix(native-chat): startup settles each closed chat after recovering i…
brennanb2025 Oct 3, 2026
a53995e
test(native-chat): the closed-chat startup settle tests check the tur…
brennanb2025 Oct 3, 2026
4550199
test(native-chat): the batch status write is checked not to rewrite a…
brennanb2025 Oct 3, 2026
2f29187
refactor(native-chat): startup checks and recovers chat leases in one…
brennanb2025 Oct 3, 2026
a2f2ddb
fix(native-chat): a stored chat status derived by other rules reads a…
brennanb2025 Oct 3, 2026
50092dc
test(native-chat): the crashed-chat helper types the leases it collects
brennanb2025 Oct 3, 2026
f125822
fix(native-chat): startup's lease answers are read from the records, …
brennanb2025 Oct 3, 2026
00cdc2c
Merge main into chat-tabs-list-without-replay
brennanb2025 Oct 3, 2026
a7d329f
Merge remote-tracking branch 'origin/brennanb2025/chat-tabs-list-with…
brennanb2025 Oct 3, 2026
df470be
test(native-chat): the host's published status carries no provider child
brennanb2025 Oct 3, 2026
54da015
test(native-chat): the older-schema downgrade baseline is the v1.4.21…
brennanb2025 Oct 3, 2026
57efd4c
Merge main into chat-status-stored-with-history
brennanb2025 Oct 3, 2026
c2e9e59
Merge origin/main (b99d28e32fd10b77352a7e0bba10b54bbd2e0f86) into bre…
brennanb2025 Oct 4, 2026
ef2f9ed
Merge origin/main (97fa6aee746) into brennanb2025/chat-status-stored-…
brennanb2025 Oct 4, 2026
61e512f
test(native-chat): type the receipt probe's transaction log without a…
brennanb2025 Oct 4, 2026
6a04a45
Merge origin/main (d17351401d7372f3f48cc3cdb423147d8c8e61c1) into bre…
brennanb2025 Oct 5, 2026
823d5a7
Merge origin/main (ca2cdc0ad9638695306da1be553aeee8b4723818) into bre…
brennanb2025 Oct 5, 2026
3005424
fix(native-chat): the stored chat status needs no schema version, so …
brennanb2025 Oct 5, 2026
943ec2e
test(native-chat): the tip test's raw status row is typed as the row …
brennanb2025 Oct 5, 2026
1ac8919
fix(native-chat): an empty epoch gets no status from the catch-up, an…
brennanb2025 Oct 5, 2026
421c354
Merge origin/main (e9ed75a5de18b2f194d65fce6ac63c5cfe4c4fef) into bre…
brennanb2025 Oct 5, 2026
98816f7
Merge origin/main (d791421568c10c41997845d8a98602f8b6bc7de4) into bre…
brennanb2025 Oct 5, 2026
fe4850e
test(native-chat): the stop-note test's journal identity uses the opa…
brennanb2025 Oct 5, 2026
0ad9d63
Merge origin/main (a47e0f56578b2f3f4f07b6401fa0d38a39d67593) into bre…
brennanb2025 Oct 6, 2026
e9cd716
test(native-chat): take main's stop-note test, which now imports the …
brennanb2025 Oct 6, 2026
14bd0a3
Merge origin/main (5cf3585b78da2dc629e77b3787f7744cb4cfdf0f) into bre…
brennanb2025 Oct 6, 2026
c3b5382
test(native-chat): the close-verdict test's quit-time reader host nam…
brennanb2025 Oct 6, 2026
05c5908
refactor(native-chat): drop the first-launch status catch-up; rowless…
brennanb2025 Oct 7, 2026
20769bf
test(native-chat): one corpus walk checks the write path and startup'…
brennanb2025 Oct 7, 2026
85858a8
test(native-chat): fold overlapping settlement and startup-gate tests…
brennanb2025 Oct 7, 2026
6170407
refactor(native-chat): the startup seed drops its closed-tab check, w…
brennanb2025 Oct 7, 2026
559dab1
test(native-chat): the tip check is pinned for a chat moved to a new …
brennanb2025 Oct 7, 2026
5ed111e
Merge origin/main (0c96550ee9a) into chat-status-stored-with-history
brennanb2025 Oct 7, 2026
fbb5c65
Merge origin/main (0bdcaf36edf) into chat-status-stored-with-history
brennanb2025 Oct 7, 2026
a2768ce
Merge origin/main (70c68766355) into chat-status-stored-with-history
brennanb2025 Oct 7, 2026
298e229
Merge origin/main (d3e1494674f) into chat-status-stored-with-history
brennanb2025 Oct 7, 2026
dce10f4
test(native-chat): run this PR's real-database startup tests in the N…
brennanb2025 Oct 7, 2026
69ff377
Merge origin/main (988f87e743e) into chat-status-stored-with-history
brennanb2025 Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config/scripts/vitest-sqlite-runtime-files.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/native-chat/agent-session-wire/structured-agent-session-live-turn-window.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-main-agent-working-agreement.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-newer-content-host.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-open-settlement.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-option-settlement.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-owed-work-release.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-persisted-tab-listing.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-pre-spawn-first-answer.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-processless-reservation.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-prompt-cancel.test.ts',
Expand All @@ -107,18 +109,22 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/native-chat/agent-session-wire/structured-agent-session-restart-offer-newer-orca.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-restart-restore.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-restart-status-publication.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-restore-after-listing.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-restore-without-import.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-rewind-at-rest.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-rewind-live-send.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-rewind.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-send-during-startup-lease-check.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-send-idempotency.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-send-open-stale-turn.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-send-preparation.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-send-restarts-failed-start.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-settled-attach-retry.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-stale-turn-verdict.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-start-failure-writer.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-startup-damaged-chat.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-startup-reconcile-failure.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-startup-state.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-child-work.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-child.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-children.test.ts',
Expand Down Expand Up @@ -183,6 +189,8 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/runtime/structured-agent-session-rollback-compatibility.test.ts',
'src/main/runtime/structured-agent-session-spawn-token-descendants.test.ts',
'src/main/runtime/structured-agent-session-startup-reconcile.test.ts',
'src/main/runtime/structured-agent-session-startup-restoration.test.ts',
'src/main/runtime/structured-agent-session-tab-listing-startup.test.ts',
'src/main/runtime/structured-claude-pending-rewind.test.ts',
'src/main/ssh-expired-lease-pane-readoption.test.ts',
'src/main/ssh-reattach-pane-cardinality.test.ts',
Expand Down
22 changes: 17 additions & 5 deletions docs/reference/agent-status-store.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,23 @@ forgot the row would strand a permanently working-looking agent.

Two rules the ingest must keep:

- **Never persist a structured row.** The journal is the durable truth for a
structured session and the host republishes on restore. A structured row in
`last-status.json` would hydrate as `restoredUnconfirmed` and then fight the
live republish. The serializer skips rows carrying `structuredHost`, and
hydrate drops any such row found on disk. Applying one therefore also skips
- **Never persist a structured row in the store.** The chat journal database
is the durable truth for a structured session. Each chat's status is stored
there beside its journal (`journal_session_state`), written in the same
transaction as the journal rows it describes, so it is always current. A
chat last written before that table existed, by an older build, or whose row
was derived by other rules, has no current row: it gets one when it is
opened, which also settles it and publishes its status. A listed one is
opened by the background restore after the tab listing answers, as is a
listed chat whose history is still in an older build's per-chat file. A
corrupt chat gets no row until its open rebuilds it.
At host startup, before the tab listing answers, the host republishes the
row of each settled chat that is still listed and not already open, without
opening it; after that it settles every chat a gone process left with work.
A structured row in `last-status.json` would
hydrate as `restoredUnconfirmed` and fight that republish, so the serializer
still skips rows carrying `structuredHost`, and hydrate still drops any found
on disk. Applying one therefore also skips
the persist schedule: the walk and stringify could only reproduce the file
that is already on disk, once per debounce window for every streaming chat.
- **Never let it fight a hook row.** A structured session has no PTY, so no
Expand Down
49 changes: 32 additions & 17 deletions src/main/agent-hooks/first-work-branch-rename.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { GlobalSettings } from '../../shared/global-settings-types'
import type { Repo } from '../../shared/repo-types'
import type { AgentJournalRenderItem } from '../../shared/agent-session-journal-types'
import type { AgentSessionJournal } from '../native-chat/agent-session-journal/journal-store'
import { projectStructuredAgentSessionStatusState } from '../../shared/structured-agent-session-projection'
import { StructuredAgentSessionStatusFeed } from '../native-chat/agent-session-wire/structured-agent-session-status-feed'
import { maybeAutoRenameWorkspaceOnFirstStructuredTurn } from './first-work-structured-session-rename'
import { WORKTREE_ID_SEPARATOR } from '../../shared/worktree/id'
Expand Down Expand Up @@ -104,12 +105,15 @@ describe('maybeAutoRenameBranchOnFirstWork', () => {
// A real journal's sequence only ever advances, so the feed's projection
// cache must miss on every publish here: this test is about the rename.
let sequence = 0
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the status feed reads only cursor(), lastActivityAt(), stopMarks and snapshot() of a journal.
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: a stand-in journal offering only what the status feed reads (submissions, cursor, Stop marks, its projection, activity).
const journal = {
snapshot: () => ({ items, submissions: [] }),
submissions: () => [],
stopMarks: { latest: () => null, revision: () => 0 },
lastActivityAt: () => 1,
cursor: () => ({ epoch: 1, sequence: (sequence += 1) })
cursor: () => ({ epoch: 1, sequence: (sequence += 1) }),
sessionStatus: {
at: (fence?: number) => projectStructuredAgentSessionStatusState(items, [], fence)
}
} as unknown as AgentSessionJournal
const pending: Promise<void>[] = []
const observe = vi.fn((summary, options) => {
Expand Down Expand Up @@ -193,24 +197,35 @@ describe('maybeAutoRenameBranchOnFirstWork', () => {
const { deps, setDisplayName, setRenameError } = makeDeps({
getRepo: () => ({ id: REPO_ID, kind: 'folder', path: '/workspace/platform' }) as Repo
})
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the status feed reads only cursor(), lastActivityAt(), stopMarks and snapshot() of a journal.
const items: AgentJournalRenderItem[] = [
{
itemId: 'user-1',
sequence: 1,
revision: 1,
observedAt: 1,
body: { kind: 'message', role: 'user', blocks: [{ type: 'text', text: 'Fix auth' }] }
},
{
itemId: 'turn-1',
sequence: 2,
revision: 1,
observedAt: 1,
body: {
kind: 'status',
text: 'Working',
turnLifecycle: { turnId: 'turn-1', state: 'running' }
}
}
]
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: a stand-in journal offering only what the status feed reads (submissions, cursor, Stop marks, its projection, activity).
const journal = {
lastActivityAt: () => 1,
stopMarks: { latest: () => null, revision: () => 0 },
cursor: () => ({ epoch: 1, sequence: 1 }),
snapshot: () => ({
items: [
{ body: { kind: 'message', role: 'user', blocks: [{ type: 'text', text: 'Fix auth' }] } },
{
body: {
kind: 'status',
text: 'Working',
turnLifecycle: { turnId: 'turn-1', state: 'running' }
}
}
],
submissions: []
})
sessionStatus: {
at: (fence?: number) => projectStructuredAgentSessionStatusState(items, [], fence)
},
submissions: () => []
} as unknown as AgentSessionJournal
const location = {
executionHostId: 'local' as const,
Expand Down
25 changes: 16 additions & 9 deletions src/main/claude/claude-background-wake-retention.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { AgentJournalSubmission } from '../../shared/agent-session-journal-types'
import { structuredRunningChildWork } from '../../shared/agent-child-work-listing'
import { projectStructuredAgentSessionStatusState } from '../../shared/structured-agent-session-projection'
import type { AgentHookServer } from '../agent-hooks/server'
import type { StructuredAgentSessionJournalProjections } from '../native-chat/agent-session-wire/structured-agent-session-status-journal-projection'
import { StructuredAgentSessionStatusFeed } from '../native-chat/agent-session-wire/structured-agent-session-status-feed'
Expand All @@ -29,31 +30,37 @@ async function wiredSession() {
const run = await producer(host)
const submissions: AgentJournalSubmission[] = []
let sequence = 0
const snapshot = () => ({
items: [...run.journalItems.values()]
.sort((a, b) => a.sequence - b.sequence)
.map((item) => ({ ...item, revision: 1, observedAt: item.sequence })),
submissions: submissions.map((submission) => ({ ...submission }))
})
const journal = {
cursor: () => ({ epoch: 'epoch-1', sequence: ++sequence }),
lastActivityAt: () => 1,
submissions: (): ReturnType<Journal['submissions']> =>
submissions.map((submission) => ({ ...submission })),
submissions: (): ReturnType<Journal['submissions']> => snapshot().submissions,
item: (itemId: string): ReturnType<Journal['item']> => {
const item = run.journalItems.get(itemId)
return item ? { ...item, revision: 1, observedAt: run.now() } : null
},
// No Stop was ever pressed here.
stopMarks: { latest: () => null, revision: () => 0 },
snapshot: () => ({
items: [...run.journalItems.values()]
.sort((a, b) => a.sequence - b.sequence)
.map((item) => ({ ...item, revision: 1, observedAt: run.now() })),
submissions: submissions.map((submission) => ({ ...submission }))
})
// The journal's own projection, which the feed shares with the status stored beside it.
sessionStatus: {
at: (fence?: number) => {
const { items, submissions: sent } = snapshot()
return projectStructuredAgentSessionStatusState(items, sent, fence)
}
}
}
const feed = new StructuredAgentSessionStatusFeed({
logger: createStructuredAgentSessionLogger(),
sessions: new Map([
[
parent.sessionId,
{
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the fixture supplies the feed's cursor, clock, Stop marks, snapshot, submissions and item reads.
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the fixture supplies the feed's cursor, clock, Stop marks, submissions, item reads and projection.
journal: journal as unknown as Journal,
params: {
location: {
Expand Down
13 changes: 13 additions & 0 deletions src/main/claude/claude-subagent-resume-after-restart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import {
import type { AgentSessionJournal } from '../native-chat/agent-session-journal/journal-store'
import { createTrackedJournalOpener } from '../native-chat/agent-session-journal/journal-host-database-test-support'
import { createDeferredStructuredAgentSessionEventSink } from '../native-chat/agent-session-wire/structured-agent-session-event-sink'
import {
appendOpenSettlement,
planOpenSettlement
} from '../native-chat/agent-session-wire/structured-agent-session-open-settlement'
import { createClaudeJournalTranslator } from './claude-structured-journal-translation'
import { claudeSubagentGroupBody, claudeSubagentGroupIdentity } from './claude-subagent-group-row'
import { testEventSinkLogging } from '../native-chat/agent-session-wire/structured-agent-session-logger-test-support'
Expand Down Expand Up @@ -455,6 +459,15 @@ describe('a Claude subagent resumed after its provider restarted', () => {
// contact was lost, not when.
await crashed.close()
const journal = await openJournal()
// What the conversation's open appends before an acquisition reads the journal.
await appendOpenSettlement(
journal,
planOpenSettlement(journal, null, { acquisition: true }),
0,
(error) => {
throw error
}
)
const [lost] = rowsListing(journal, 'agent-a')[0]?.agents ?? []
expect(lost).toMatchObject({ id: 'agent-a', state: 'unverifiable' })
expect(lost?.settledAt).toBeUndefined()
Expand Down
Loading
Loading