Skip to content

fix(native-chat): offer resume after restart for chats whose only work was in the background - #27167

Merged
brennanb2025 merged 3 commits into
mainfrom
brennanb2025/fix-resume-after-update
Oct 11, 2026
Merged

brennanb2025 merged 3 commits into
mainfrom
brennanb2025/fix-resume-after-update

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​267 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​265
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​11 $\color{#cf222e}{\Huge{\mathbf{−}}}$​5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​6

ELI5

When Orca quits or installs an update, it notes which chats were still working so it can offer to pick them up after the restart. A chat whose agent had finished replying but still had a background command or a subagent running was missing from that list. Orca was throwing away its record of that background work a moment before checking it.

What Changed

Before: say a chat's agent finished its reply but left a background command running, like a long wait for another agent's report, or a subagent still working. The sidebar shows it as working. Then Orca restarts for an update. After the restart, the "Resume" dialog doesn't list that chat. The background command is gone, and the chat just shows a red "Claude stopped while this response was in progress" row. Nothing carries on unless the user notices and types something.

After: that chat shows up in the Resume dialog alongside the others, with its background work named. Resuming asks the agent to carry on, as it already did for chats that were mid-reply.

Mechanism: before Orca stops each chat's agent at quit, it takes a snapshot to decide whether to offer that chat for resume. A chat whose own reply had finished counts only if its subagents or background commands were still running. Orca keeps those in the agent-status store inside the agent hook server. The quit sequence (main-process-quit.ts) starts the chat teardown without waiting for it, then immediately calls agentHookServer.stop(), which empties that store. The chat teardown waits on other work before it takes any snapshot, so every snapshot read an empty list and judged these chats finished.

agentHookServer.stop() now takes a quit-only { keepStatus: true }. It still stops the hook listener and saves terminal status exactly as before, but leaves the in-memory store for the chat teardown to read. The process exit frees that memory.

Why

This is a regression from #22614 (merged 2026-09-30). Resume offers for chats whose only remaining work was a subagent or background command came in with #22560 (09-24). At that point the quit snapshot read each chat's background work from the agent adapter's own task tracker, which lives in the chat host and survives the quit. #22614 moved every reader of that work, including this snapshot, onto the hook server's agent-status store. It did not account for the quit stopping and clearing that store before the snapshot runs. The tests that cover this case run the chat host without the real quit sequence, so they kept passing. From #22614 on, Claude and Codex chats with only background work at quit were never offered.

Seen on a real update: one restart dropped 4 chats this way. Each had finished its reply while a background command was still running, and the agent's own journal shows the command stopped and the turn cut by the quit. Every chat that was offered had a reply in progress, which the snapshot can see without the store.

Alternatives considered:

  • Stop the hook server after the chat teardown finishes. Rejected: the stop has to run before killAllPty(). Otherwise terminal panes exiting during quit would clear agent statuses that are meant to survive the restart.
  • Read the chat's own background-task rows when the store is empty. Rejected: the store is meant to be the one place every reader gets agent status from, and a second source could disagree with what the sidebar showed.

Keeping the store alive through quit fixes the ordering itself, so every chat-status reader is covered, not just this one.

Linked Issue

N/A (maintainer). Found from a user report after an Orca update.

Visual Proof

N/A. The visible change is that one more row appears in the existing Resume dialog. Hitting this case live needs a real update restart with a chat in exactly this state. The proof is the journal evidence above and the tests below.

Testing

  • New hook server test: subagent and background-task records stay readable after stop({ keepStatus: true }), and a plain stop() still clears them. It fails without the fix.

  • New quit-sequence test: the quit stops the hook server with keepStatus. It fails without the fix.

  • src/main/agent-hooks and src/main/startup test suites pass. orca-ci-checks passes (24/24 locally, typecheck included).

  • Platforms: macOS locally. The quit path is shared by all platforms.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • SSH / remote: not covered here. The remote server (orcad) doesn't appear to shut chats down through this path at all, so remote chats may never get a resume offer. Not verified; separate follow-up.
  • Known, not fixed here: a chat cut this way still shows a red "Claude stopped while this response was in progress" row. The cut turn was one the agent opened in response to being stopped, and the quit only labels a turn that was already running when it started. Separate follow-up.
  • No wire, storage or user-data format changes; nothing persists differently.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

PR author: @BrennanKB5

…background

The quit started the chat teardown and then stopped the hook server, which
emptied the status store holding each chat's subagents and background
commands. The teardown reads that store to decide which chats were still
working, so a chat whose turn had finished but whose background command or
subagent was still running was never offered to resume after a restart or
update. The quit now stops hook ingress without clearing the store.
@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a9effac2-9db9-47ee-ad5b-a0cee10c6af1

📥 Commits

Reviewing files that changed from the base of the PR and between 74d0332 and ea70667.


📒 Files selected for processing (4)
  • src/main/runtime/structured-child-records-switch.test.ts
  • src/main/runtime/structured-claude-scripted-runtime-test-support.ts
  • src/main/runtime/structured-codex-child-work-runtime.test.ts
  • src/main/startup/main-process-quit-pending-writer.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.



📝 Walkthrough

Walkthrough

The hook server stop method now accepts an optional keepStatus flag. When enabled, it retains canonical status and skips status-change notifications. Quit teardown passes this option. Tests cover child-work visibility after a retaining stop, clearing after a full stop, and the option passed during quit. Runtime tests also cover background-work shutdown and recovery data for Claude and Codex.


Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to ea706

Quit teardown can retain the status needed to offer eligible chats for resume. No identified issue prevents merging after normal checks.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 7 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: offering resume after restart for chats with only background work.
Description check Passed The description covers the required sections, explains the user impact and mechanism, documents testing, notes platform scope and limitations, and completes the checklist. The maintainer-specific Link…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@brennanb2025

brennanb2025 commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor Author

When Orca quit or restarted after an update, a native chat could finish its main reply while a background command or subagent was still running. Hook shutdown cleared the status records before chat teardown read that work, so the chat disappeared from the next launch’s Resume dialog.

This change preserves the existing records during quit until each chat captures its unfinished work before stopping the agent. Chats interrupted with background work are offered in the existing Resume dialog; work that already finished stays absent.

Two review rounds retained the production approach and added ten permanent regression cases to the existing Claude and Codex runtime tests. They cover commands and subagents, quit and update, saved recovery information, the Resume list after restart, and finished-command controls. Independent architecture, correctness, regression, readiness and final merge reviews found no proven meaningful code findings. No code findings were deferred.

At ea706676b6de8a94770944a823cda43f41edf7dc, 126 targeted tests and all 24 pre-push static checks passed. Main was synced once after review. The single CI watch finished with 17 passing and 13 expected skipped checks, with no failures or pending checks (checks).

Remote Electron QA on M4Air verified a real native Codex chat with a completed reply and a live background command. After the production restart path, the saved recovery candidate matched that chat and command; the rendered Resume dialog offered it, and clicking Resume 1 chat restored the conversation with the normal continuation prompt and an agent reply. The development server stopped during automatic relaunch, so the dialog was checked on a fresh dev launch of the same saved profile after the production restart.

Rendered QA did not exercise Claude, subagents, finished-work exclusion, dismissal, the actual update installer, remote-server restart, live SSH/WSL or other platforms. The provider, command/subagent and finished-work cases above have automated coverage. The PR remains unmerged.

Before restart: the native Codex chat starts its command. The later recorded session history confirms that the reply finished while the command stayed active.

Native Codex chat starting its command before restart

After restart and a fresh launch of the saved profile: the existing Resume dialog offers the interrupted chat.

Existing Resume dialog offers the interrupted Codex chat

After clicking Resume 1 chat: the conversation contains the continuation prompt and reply.

Restored conversation after clicking Resume 1 chat

@brennanb2025
brennanb2025 merged commit 2201653 into main Oct 11, 2026
30 checks passed
brennanb2025 added a commit that referenced this pull request Oct 11, 2026
…k was in the background (#27167)

* fix(native-chat): offer restart resume for chats whose only work was background

The quit started the chat teardown and then stopped the hook server, which
emptied the status store holding each chat's subagents and background
commands. The teardown reads that store to decide which chats were still
working, so a chat whose turn had finished but whose background command or
subagent was still running was never offered to resume after a restart or
update. The quit now stops hook ingress without clearing the store.

* test: preserve background-only chat resume across shutdown

(cherry picked from commit 2201653)
brennanb2025 added a commit that referenced this pull request Oct 15, 2026
…k was in the background (#27167)

* fix(native-chat): offer restart resume for chats whose only work was background

The quit started the chat teardown and then stopped the hook server, which
emptied the status store holding each chat's subagents and background
commands. The teardown reads that store to decide which chats were still
working, so a chat whose turn had finished but whose background command or
subagent was still running was never offered to resume after a restart or
update. The quit now stops hook ingress without clearing the store.

* test: preserve background-only chat resume across shutdown

(cherry picked from commit 2201653)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant