Repository navigation
Native chat: recover interrupted messages before opening and retry cleanup in the background - #28122
brennanb2025 wants to merge 69 commits into
Conversation
Each journal item row, subagent roster entry and background-task entry now records ownerFence: the lease fence of the agent run whose output produced it. Optional, persisted, no row version bump and no migration. The journal's itemFence() returns it, so the turn-verdict rules and per-run provider activity read the producing run rather than the writer's fence.
… record death first The host decides what work is current in one place (structured-agent-session-current-work.ts) from the lease, the exit this host saw, the runtimes it replaced, its live child and each item's provenance. Every host reader (queue, Stop, prompt answers, commands, status feed, idle sweep, teardown snapshot, history pages and frames, orchestration mailbox) reads it, so a dead agent's leftovers stop reading Working or holding the queue even when their cleanup write failed. The journal's activeTurnId()/liveTurnScope() become the raw runningTurn(). On exit the host records the child's end first and signals generationEnded, which republishes frames, the status feed and settlement waits and wakes the queue drain with no journal row. Pages and frames carry optional working and actionablePromptIds fields.
…wers to a stopped agent plainly Desktop and mobile prefer the host's working and actionablePromptIds fields when present (merged across batches like latestTurn) and fall back to today's row derivation against an older host. A prompt an ended agent raised stops looking active; answering it is refused before any provider sees it, with "<Agent> has stopped, so your answer was not sent. Send a message to continue." Cancelling it only dismisses it in the journal: it no longer triggers the chat's Stop, pauses the queue or withdraws queued cards.
… run left An exit now settles, in one lifecycle batch planned at its own turn in the write queue, everything its run left (turn, calls, prompts, reasoning, unanswered sends) plus leftovers of earlier ended runs, and only the subagent roster entries and background tasks those runs observed, by each entry's own provenance. Main's stale settle moves to stale-state-settlement.ts with fence scoping; dead-generation-settlement.ts becomes a planner; leftover-settlement.ts combines them. Resume eligibility widens in one case: this host saw the root exit at the lease's fence and the release write failed; the acquisition replaces the owner only once a probe proves it gone.
…ator; heal at startup; opening a chat only reads Background bookkeeping (an exit's settlement and release, the queue's automatic send) runs with SQLite's busy timeout at 0 through typed background handles, so another program holding the chat database no longer freezes the app for 5 s per write. One retry coordinator per database connection keeps the set of chats that still owe work, wakes on any commit or a 1 s then 2 s timer, and gives up after about 3 minutes, keeping the owed set. Every lease write that ends a generation signals it (onGenerationEnded). Startup re-derives what an earlier process left (settlement, unsent sends kept as cards, the reopen mark, rewind recovery) in the background, scoped so it never stops a surviving agent on a hidden chat. Opening a chat writes nothing but a brand-new chat's first epoch: the open-time and start-time settles, the roster sweep at open and the delivery loop's restart hold are removed.
…l until a caller needs it Nothing outside the projection calls it in this part; Part 5 exports it for the start-failure row.
…wner checks into the main case Drops the epoch-replacement case (Part 2 rewind test reads the same values through the same rebuild) and the corrupt-value case (no version writes one).
…nd the still-live rewind case The flow cases keep every outcome: dead leftovers read as nothing, Stop has nothing to stop, a waiting card drains, a live owner publishes as before, and a rewind never promotes dead work.
…plit-3 # Conflicts: # src/main/native-chat/agent-session-wire/structured-agent-session-current-work.test.ts
…ack each Keeps desktop and phone Working from the host (with the older-host fallback), the reducer merge contract, the ended question Cancel that only dismisses, the agent-named refusal and the resolution receipt. Drops per-helper prompt tests, the transcript-placement test, the stop-note client reduction, the Steer cards that match main, and per-reason refusal rows.
…ws and the per-entry scoping flow Drops the no-added-delay timing check, the frame-once check, two stamping unit cases and the Codex combined case (the roster flow test covers its outcome).
… the unstamped-entry case to Part 1
…s as its row provenance Moved here from Part 4 so it sits beside stampJournalEntryProvenance.
…plit-5 # Conflicts: # src/main/codex/codex-subagent-roster-retention.test.ts # src/main/native-chat/agent-session-wire/structured-agent-session-current-work.test.ts # src/main/native-chat/agent-session-wire/structured-agent-session-exit-delivery.test.ts
…epoch holds no rows; fewer, flow-level tests The reconciliation load skips a chat whose saved epoch is named but empty, as the earlier founding check did: opening it would write a fresh epoch with a person's 5 s lock wait, outside the chat's lane. The open-path comments now name that case as the second exception to "only reads". Tests: one startup suite on the scan fixture (heal reaches unopened chats, the empty epoch, an unloadable journal, a busy lane, the replay/latch/read-only scopes, a hidden survivor never stopped); one lock suite (dying agent, relaunch, long lock, rewind, drain, dead-turn frames, refused hold); one leftover suite (retry settles in one commit while a send goes out and a stale answer is refused, reads write nothing, a live owner is untouched). Unit suites of the retry's wake, budget and generation-end signal, and per-branch cases already covered by a flow, are dropped.
Restores the ten-chats-one-lock budget test (a bug that happened in review, J-4). To stay under the production lines, drops the unloadable-journal startup case (a review-round finding), the live-owner settle case (the replaced-server recovering-owner test asserts the same nothing-settled outcome), and a duplicated surviving-owner helper.
…plit-1 # Conflicts: # src/main/acp/acp-timeline-grok-subagent-retention.test.ts # src/main/acp/acp-timeline-grok-subagents.test.ts
…n removed its body helper Main #27947 writes one row per subagent and deleted journal-subagent-group-body; a roster row from an older build is still what this case reads.
…amping Main's #27947 writes one saved row per subagent, and each background task already has its own row, so an item's own ownerFence names the run of every entry a producer still writes. Per-entry stamps inside a row mattered only for the shared roster rows saved before #27947, and no run writes into those any more, so the row's own provenance settles them correctly. Removes the entry stamping, the optional ownerFence on roster entries and background tasks, and their tests.
…plit-2 Brings in main 6741f4f through part 1. Main's history page now reads with a HistoryReader, so the published current work moves to agent-session-published-work.ts and lays over each finished page. Main's new Stop path (chat-stop target, acceptance, unconfirmed note, targeted cancel) and the per-subagent row writer read what runs through the host's current-work projection, as the rest of this part does.
…tup open, settled on first open
…eservation clears it A chat attached directly (a send from the phone or the CLI) reserves its lease before any reader opens it, and the reservation clears the death evidence the release kept. The store now announces that proof with the reservation, as it does for a reservation over a proven-dead owner, so a settlement that failed before quit still ends the turn at the known exit time. The open no longer captures it.
main moved journal-atomic-terminal-settlement.test.ts into a registered .spec.ts (#29163); this branch's edit (the observed-exit test settler) is carried there. Registrations take main's createAcpAdapter shape with this branch's producer argument.
main moved four journal suites into registered .spec.ts files (#29163); this branch's edits to them are carried into those files unchanged. main's thread-goal reader put journal-store.ts one line over its limit; holdReopenFromOpen becomes a one-line arrow.
… reconcile) Part 4 landed as squash 7a78dbd, so this merge is resolved against that squash as the base for its changes: Part 4's own lines come in once, and only what main changed after it meets Part 5. #26555 composed with Part 5's retry: one reconciler with an optional chat id. A chat's own action (start, attach, send, a restored read) reconciles only that chat and never joins a background run; a background run joins a person's run for its target; the every-chat background pass (startup, the retry's store-wide step) lets chat checks in flight finish first and holds no chat's lane. The restore keeps #26555's one every-chat check per pass. After the squash, main kept the acquisition-time settle and open option that Part 5 removes; those stay removed. A failed start records only this process's sends (Part 5) with main's new start-failure causes.
main's #28966 lets a journal transaction commit streamed rows without the per-commit fsync; this PR's background writes ride the same transaction. The two are one options object (`{ background?, streamed? }`), so a background streamed write is both.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Merge Risk: 🔵 Low · up to
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟠 Major · Pass the transaction options as an object. · journal-streamed-text-revision.test.ts:43
src/main/native-chat/agent-session-journal/journal-streamed-text-revision.test.ts:43
🎯 Functional Correctness | 🟠 Major | ⚡ Quick winPass the transaction options as an object.
JournalHostDatabase.transactionexpectsJournalWriteOptions & { streamed?: boolean }. This call still passes the primitivetrue, so it cannot type-check.🐛 Suggested fix
- }, true) + }, { streamed: true })Source: Pipeline failures
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
7d00008a-244d-40f5-aae8-993bf6b79472
📒 Files selected for processing: 124
config/scripts/vitest-sqlite-runtime-files.mjssrc/main/acp/acp-structured-host-restore-failed.test.tssrc/main/native-chat/agent-session-journal/journal-context-controller.tssrc/main/native-chat/agent-session-journal/journal-context-rewind.tssrc/main/native-chat/agent-session-journal/journal-database.tssrc/main/native-chat/agent-session-journal/journal-epoch-controller.tssrc/main/native-chat/agent-session-journal/journal-epoch-replacement.tssrc/main/native-chat/agent-session-journal/journal-host-database.tssrc/main/native-chat/agent-session-journal/journal-lifecycle-batch-appender.tssrc/main/native-chat/agent-session-journal/journal-open-failure.tssrc/main/native-chat/agent-session-journal/journal-pending-submission-recovery.tssrc/main/native-chat/agent-session-journal/journal-queued-messages.tssrc/main/native-chat/agent-session-journal/journal-row-writer.tssrc/main/native-chat/agent-session-journal/journal-store-collaborators.tssrc/main/native-chat/agent-session-journal/journal-store-contracts.tssrc/main/native-chat/agent-session-journal/journal-store-open.tssrc/main/native-chat/agent-session-journal/journal-store-restore.tssrc/main/native-chat/agent-session-journal/journal-store.tssrc/main/native-chat/agent-session-journal/journal-streamed-text-revision.test.tssrc/main/native-chat/agent-session-journal/journal-subagent-item.spec.tssrc/main/native-chat/agent-session-journal/journal-subagent-liveness.spec.tssrc/main/native-chat/agent-session-journal/journal-subagent-liveness.tssrc/main/native-chat/agent-session-journal/journal-submission-writer.tssrc/main/native-chat/agent-session-journal/journal-unsent-send-hold.tssrc/main/native-chat/agent-session-journal/queued-message-bookkeeping-failure.spec.tssrc/main/native-chat/agent-session-journal/queued-message-holds.tssrc/main/native-chat/agent-session-journal/queued-message-reopen-floor.tssrc/main/native-chat/agent-session-journal/queued-message-store.spec.tssrc/main/native-chat/agent-session-wire/structured-agent-session-accept-then-deliver.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-acquire-isolation.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-attach-context.tssrc/main/native-chat/agent-session-wire/structured-agent-session-attach-orchestration.tssrc/main/native-chat/agent-session-wire/structured-agent-session-attach-reconciliation.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-attach-test-conversation.tssrc/main/native-chat/agent-session-wire/structured-agent-session-background-lock.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-background-writes.tssrc/main/native-chat/agent-session-wire/structured-agent-session-child-close.tssrc/main/native-chat/agent-session-wire/structured-agent-session-child-exit.tssrc/main/native-chat/agent-session-wire/structured-agent-session-claude-stop-exit-ends-record.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-client-delivery.tssrc/main/native-chat/agent-session-wire/structured-agent-session-conversation-close.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-conversation-open.tssrc/main/native-chat/agent-session-wire/structured-agent-session-conversations.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-conversations.tssrc/main/native-chat/agent-session-wire/structured-agent-session-crash-turn-end.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-current-work.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-current-work.tssrc/main/native-chat/agent-session-wire/structured-agent-session-delivery-loop.tssrc/main/native-chat/agent-session-wire/structured-agent-session-exit-delivery.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host-delivery.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host-lifetime.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host-mutations.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host-runtime-state.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host-tabs.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host.tssrc/main/native-chat/agent-session-wire/structured-agent-session-idle-sweep.tssrc/main/native-chat/agent-session-wire/structured-agent-session-lease-release.tssrc/main/native-chat/agent-session-wire/structured-agent-session-leftover-settlement-triggers.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-leftover-settlement.test-fixture.tssrc/main/native-chat/agent-session-wire/structured-agent-session-leftover-settlement.tssrc/main/native-chat/agent-session-wire/structured-agent-session-mutation-context.tssrc/main/native-chat/agent-session-wire/structured-agent-session-observed-exit.test-fixture.tssrc/main/native-chat/agent-session-wire/structured-agent-session-provider-child-record.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-provider-child.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-abandonment.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-card-holds.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-compact.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-message-rig.test-fixture.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-messages.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-newer-orca.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-pause-lift.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-pause.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-publication.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-stop-row.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-wiring.tssrc/main/native-chat/agent-session-wire/structured-agent-session-readable-restorer.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reconciliation-backoff.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reconciliation-load.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reconciliation-pass.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reconciliation-retry.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reconciliation-visit.tssrc/main/native-chat/agent-session-wire/structured-agent-session-recovery-resolution.tssrc/main/native-chat/agent-session-wire/structured-agent-session-replaced-server-turn.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-resend-ledger-refusal.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-rest-test-rig.tssrc/main/native-chat/agent-session-wire/structured-agent-session-restart-context-clear.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-restart-interruption-test-harness.tssrc/main/native-chat/agent-session-wire/structured-agent-session-restart-provider-output.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-restart-reconcile.tssrc/main/native-chat/agent-session-wire/structured-agent-session-restart-restore.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-restart-restore.tssrc/main/native-chat/agent-session-wire/structured-agent-session-restart-stream-duration.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-retry-budget.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-retry.test-fixture.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reveal.tssrc/main/native-chat/agent-session-wire/structured-agent-session-send-open-stale-turn.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-send-options.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-stale-turn-open.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-start-failure-row.tssrc/main/native-chat/agent-session-wire/structured-agent-session-startup-candidates.tssrc/main/native-chat/agent-session-wire/structured-agent-session-startup-scan.test-fixture.tssrc/main/native-chat/agent-session-wire/structured-agent-session-startup-scan.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-stop-event-binding.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-stop-event-restart.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-stopping.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-task-queue.tssrc/main/native-chat/agent-session-wire/structured-agent-session-unadjudicated-owner-queue.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-unexpected-exit.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-unrun-send-hold.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-unsent-send-hold.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-wedged-profile-migration.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-write-lock.test-fixture.tssrc/main/native-chat/agent-session-wire/structured-rewind-recovery.tssrc/main/runtime/agent-session-death-evidence-persistence.test.tssrc/main/runtime/agent-session-generation-end.tssrc/main/runtime/agent-session-record-store.tssrc/main/runtime/agent-session-store-transactions.tssrc/main/runtime/orca-runtime-tests/repository-project-operations-part-02.spec.tssrc/main/runtime/structured-agent-session-mobile-restore.test.tssrc/main/runtime/structured-agent-session-startup-tab-restore.test.tssrc/main/runtime/structured-agent-session-tab-index-seed.tssrc/main/sqlite/sqlite-read-failure.tssrc/renderer/src/components/native-chat/NativeChatSubagentRun.tsxsrc/shared/agent-session-lease-adjudication.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.
|
After an agent stopped unexpectedly, cleanup could delay opening chats, and deferring recovery exposed older messages as current work. Stop could then discard the recovery card still owed for an unsent message. A failed automatic send could lose its hold when a different card changed, and an unobserved helper could remain Working after a failed reconnection. The source fixes recover earlier messages before a chat is published, keep Stop scoped to this process's sends, retire only the changed card's hold, and show unobserved helper work through the existing uncertain state. Other cleanup stays in one bounded background retry path. Foreground recovery uses the ordinary journal writer and opens the chat even if recovery cannot save; background ownership reconciliation can still run afterward. A failed reconnection uses the existing full refresh, which can return loaded older history to the latest messages; older messages remain available. At least 17 accumulated review passes remain recorded. The selected recovery-before-publication design and source were independently checked, followed by narrow integration reviews. Main’s formatting fix #30137 is now included, and the corrective merge at 69e277f was accepted in source review. Its CI run found that the existing failed-reconnection refresh call was missing its optional argument in the shared callback type. Current pushed head d0b282f fixes that type declaration only; the normal commit hook passed and its single CI watch is pending. Source acceptance does not establish runtime readiness. M4Air fixture validation: an earlier corrected five-file packet passed 67 of 68 tests; its sole failure was an invalid off-page history fixture. After correcting that fixture, the current catalog/create packet passed all 17 tests. These runs are not a full-suite or final merged-head pass. The earlier QA driver probes used an incomplete setup and do not establish that a driver is unavailable. The recorded working isolated-driver setup will be used inside the admitted QA checkout after final-head CI passes. The failed Windows low-spec dispatch was released without a process action, and its QA ticket is closed. No real Electron run or screenshots exist yet. Main-versus-branch Electron QA with real signed-in agents, screenshots, exact-head CI and the final readiness checker remain required. The PR is not ready to merge. For Brennan's review Main-versus-branch screenshots are pending. The main judgment points are recovery before publication, helper state after failed reconnection, and the intended history refresh that may return a reader to the latest messages. Exact final-head links and screenshots will be added after validation. |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
6184f0f9-136f-4126-95da-e352672ed352
📒 Files selected for processing: 77
src/main/ai-vault/session-scanner-discovery.tssrc/main/antigravity/antigravity-acp-runtime-probe.tssrc/main/grok/grok-transcript-import.tssrc/main/ipc/pty/runtime/spawn-preflight.tssrc/main/ipc/pty/spawn-pane-env.tssrc/main/native-chat/agent-session-journal/adopted-transcript-source.tssrc/main/native-chat/agent-session-journal/journal-legacy-import.tssrc/main/native-chat/agent-session-journal/journal-stop-acceptance.tssrc/main/native-chat/agent-session-journal/journal-store.tssrc/main/native-chat/agent-session-journal/journal-streamed-text-revision.test.tssrc/main/native-chat/agent-session-wire/agent-session-bounded-frames.test.tssrc/main/native-chat/agent-session-wire/agent-session-history-page.tssrc/main/native-chat/agent-session-wire/agent-session-published-work.tssrc/main/native-chat/agent-session-wire/agent-session-subscriber-catch-up.tssrc/main/native-chat/agent-session-wire/structured-agent-session-accept-then-deliver.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-attach-context.tssrc/main/native-chat/agent-session-wire/structured-agent-session-attach-failure.tssrc/main/native-chat/agent-session-wire/structured-agent-session-background-lock.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-chat-stop-target.tssrc/main/native-chat/agent-session-wire/structured-agent-session-client-delivery.tssrc/main/native-chat/agent-session-wire/structured-agent-session-conversation-open.tssrc/main/native-chat/agent-session-wire/structured-agent-session-create.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-create.tssrc/main/native-chat/agent-session-wire/structured-agent-session-current-work.tssrc/main/native-chat/agent-session-wire/structured-agent-session-delivery-loop.tssrc/main/native-chat/agent-session-wire/structured-agent-session-earlier-sends.tssrc/main/native-chat/agent-session-wire/structured-agent-session-history-result.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host-delivery.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host-runtime-state.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host-status-feed.tssrc/main/native-chat/agent-session-wire/structured-agent-session-host.tssrc/main/native-chat/agent-session-wire/structured-agent-session-idle-sweep.tssrc/main/native-chat/agent-session-wire/structured-agent-session-launch-env.tssrc/main/native-chat/agent-session-wire/structured-agent-session-leftover-settlement.tssrc/main/native-chat/agent-session-wire/structured-agent-session-mutation-context.tssrc/main/native-chat/agent-session-wire/structured-agent-session-open-child-work.tssrc/main/native-chat/agent-session-wire/structured-agent-session-open-recovery.tssrc/main/native-chat/agent-session-wire/structured-agent-session-provider-started-catalog.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-abandonment.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-compact.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-queued-messages.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reconciliation-pass.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reconciliation-retry.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reconciliation-visit.tssrc/main/native-chat/agent-session-wire/structured-agent-session-restart-context-clear.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-resumed-child-owner.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-reveal.tssrc/main/native-chat/agent-session-wire/structured-agent-session-stale-state-settlement.tssrc/main/native-chat/agent-session-wire/structured-agent-session-startup-attempt.tssrc/main/native-chat/agent-session-wire/structured-agent-session-startup-scan.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-status-feed.tssrc/main/native-chat/agent-session-wire/structured-agent-session-status-journal-projection.tssrc/main/native-chat/agent-session-wire/structured-agent-session-status-ownership.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-status-ownership.tssrc/main/native-chat/agent-session-wire/structured-agent-session-stop-acceptance.tssrc/main/native-chat/agent-session-wire/structured-agent-session-subscribers.tssrc/main/native-chat/agent-session-wire/structured-agent-session-turns.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-unsent-send-hold.test.tssrc/main/native-chat/agent-session-wire/structured-conversation-command-admission.test.tssrc/main/native-chat/codex-adoption-preparation.tssrc/main/native-chat/session-file-resolver.tssrc/main/native-chat/structured-agent-cli-conversations.tssrc/main/native-chat/structured-agent-cli-source.tssrc/main/native-chat/structured-agent-session-source-admission.test.tssrc/main/native-chat/transcript-opencode-sqlite-query.test.tssrc/main/native-chat/transcript-opencode-sqlite-query.tssrc/main/omp/omp-session-cwd-lookup.test.tssrc/main/runtime/orca-runtime-structured-agent-session-create-intent.test.tssrc/main/runtime/rpc/methods/structured-agent-session-adoption-replay.test.tssrc/main/runtime/rpc/methods/structured-agent-session-create.tssrc/main/runtime/session-tabs/structured-session-create-support.tssrc/main/runtime/spawned-pty-env-fixtures.tssrc/main/runtime/structured-agent-session-codex-held-sends.test.tssrc/main/runtime/structured-agent-session-create-adoption.tssrc/renderer/src/store/slices/floating-editor-file-id-layout-owner.test.tssrc/shared/structured-agent-session-create.test.tssrc/shared/structured-agent-session-mutation.ts
💤 Files with no reviewable changes: 1
- src/main/native-chat/agent-session-wire/structured-agent-session-status-ownership.test.ts
🚧 Files skipped from review as they are similar to previous changes: 1
- src/main/native-chat/agent-session-wire/structured-agent-session-conversation-open.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.
ELI5
After an agent stopped unexpectedly, a native chat could keep showing work in progress or leave queued messages stranded. Cleanup also waited on database locks, which could freeze Orca while it tried to save housekeeping changes.
This change gives failed background cleanup one bounded retry path. Startup opens only chats whose saved records show recovery is owed. Opening a chat first recovers the earlier process's messages, so Stop preserves unsent text as queued cards and older connected clients receive the same message state. If saving fails, the chat still opens and background recovery retries.
What Changed
Intentional differences: recovery is selective and lazy per chat rather than a host-wide replay; bookkeeping fails open; background writes do not wait on locks. A failed reconnection now uses the existing full history refresh used for unexpected agent exits. This can return a loaded older history window to the latest messages; older messages remain available. Foreground History preparation adds no owner probe, stop or eviction; the existing background ownership reconciliation can still run. The common pattern gives old work its canonical recovery meaning before dependent use; this change follows that ordering with Orca's existing journal operations. No temporary difference, new saved field, new wire operation or minimum connected-client version is introduced.
Why
Publishing raw earlier-process messages before recovery let existing actions and historical clients interpret them as current work. Recovering those messages through the journal removes that ordering gap without adding a second actionable queue in readers. All other cleanup stays off the foreground path, with one retry coordinator and obligations re-derived from existing state.
A chat's own start, attach or send still checks only that chat. The background every-chat ownership pass is separate, so an unrelated chat's slow check cannot hold a person's action. Losing contact with an SSH host remains uncertainty, never proof that its process exited. Folder workspaces use the same journal path without requiring Git.
Linked Issue
Part 5 of the work tracked in #26867; parts #28092, #28103, #28104 and #28111 are merged. This PR remains the final part on main.
Visual Proof
Real main-versus-branch Electron QA is pending. No completion claim is made from fixture tests or source review.
Testing
Review
Full review path: this changes recovery, saved journals, ordering and mixed-version behavior. At least 17 inherited review passes are recorded; a three-model design ruling selected recovery before publication. The source verification accepted recovery before publication; narrow final merge verification continues without restarting that round count.
Author: @BrennanKB5.
Agent skill upstream boundary
Checklist