Skip to content

fix(subagents): report manually quit child sessions#53

Open
w-winter wants to merge 1 commit into
HazAT:mainfrom
w-winter:fix/manual-subagent-close
Open

fix(subagents): report manually quit child sessions#53
w-winter wants to merge 1 commit into
HazAT:mainfrom
w-winter:fix/manual-subagent-close

Conversation

@w-winter
Copy link
Copy Markdown
Contributor

@w-winter w-winter commented May 13, 2026

Fixes a lifecycle gap where a Pi-backed child subagent could be manually quit while the parent kept treating it as still running. Graceful child shutdown now uses the existing .exit sidecar protocol, so the watcher receives a terminal exit record and can clear parent-side state instead of relying on shell/screen sentinel behavior.

I believe the issue originates in the initial .exit sidecar design: subagent_done and caller_ping wrote terminal sidecars, but quitting the child session directly didn't. b4b0287 made that direct quit observable via session_shutdown("quit"); this PR wires that event into the same terminal-exit contract.

The implementation logic: child sessions classify their own exits as done, ping, error, or quit, and the parent renders manual quit as "closed by user" rather than task completion or failure. The child-side sidecar claim is process-local and uses exclusive create semantics, so the first successful writer wins and later shutdown bookkeeping can't reclassify an already-claimed done / ping / error exit as a stale quit after the parent consumes the sidecar.

Summary of changes:

  • child extension: add an idempotent .exit sidecar writer with atomic first-writer-wins semantics
  • child extension: write done for normal auto-exit and preserve error, ping, and explicit subagent_done sidecars
  • child extension: write quit on unclaimed session_shutdown("quit") so manual child exits notify the parent
  • parent watcher: decode { type: "quit" } as a terminal quit reason
  • parent result flow: thread exitReason through subagent completion, resume handling, result details, and rendering
  • UI: present manual child shutdown as "closed by user" instead of completed or failed
  • tests: cover sidecar writes, non-overwrite behavior, stale-quit race protection, quit decoding, resume fallback, and result rendering

Verification:

  • unit: npm test / node --test test/test.ts → 138/138 passing (with a local-only patch for a confirmed-preexisting planner fixture mismatch)
  • integration: full cmux integration suite passed with PI_TEST_MODEL=openai-codex/gpt-5.4-mini (I'm without Claude in Pi currently) → 15/15 passing
  • manual: spawned an auto-exit: false worker, quit the child with /quit, and confirmed the parent emitted Sub-agent "ManualQuitCheck" closed by user, not completed/failed
  • manual: resumed a previously completed subagent session after its sidecar had been consumed; confirmed it accepted the follow-up and did not immediately emit a stale closed by user result

Screenshots:

  • Manually exiting subagent sessions without interrupting subagent turns:

exited manually without subagent turn interruption


exited manually without subagent turn interruption

  • Manually exiting a subagent session and, in the process of doing so, interrupting a subagent turn:

exited manually with subagent turn interruption

@w-winter w-winter force-pushed the fix/manual-subagent-close branch from 23fa69a to e56bdf3 Compare May 14, 2026 01:00
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