Skip to content

fix(server): settle Claude background agents when their process exits - #19464

Open
Exotic209093 wants to merge 2 commits into
pingdotgg:mainfrom
Exotic209093:fix/claude-idle-background-process-exit
Open

Exotic209093 wants to merge 2 commits into
pingdotgg:mainfrom
Exotic209093:fix/claude-idle-background-process-exit

Conversation

@Exotic209093

@Exotic209093 Exotic209093 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

A plain Claude background Agent can stay running forever when its CLI process exits after the parent turn has finished. Process-exit cleanup only settled workflow coordinators, leaving ordinary tasks without a terminal notification.

Apply the same process-owned cleanup to ordinary background agents: unexpected exits fail them, Stop cancels them, and buffered completion outcomes are preserved. Cleanup uses the exiting query's own last turn and retains the existing run and process ownership guards.

The restart recovery fixture now snapshots the legacy persisted state of a stopped provider session with an orphaned running task. It retains its original checks under both restart settings: recovery cancels the task, creates no automatic continuation, and delivers the cancellation note exactly once on the next user turn.

Validation: the idle-exit regression fails against the original production source and passes with the fix. All 255 Claude adapter tests pass, including plain agents, workflows, Stop, and buffered completion cases. The restart integration passes both cases independently. A combined focused adapter/integration run passes 260 tests. Scoped server typecheck, lint, formatting, and independent source review pass. No real provider, client, or service was launched.

Closes #19440. Related #18967 handles late notifications after replacement; this fix covers an idle process exit with no notification.

Linux CI on 3c390d7 passes all 255 Claude adapter tests and both restart integration cases. All CI checks and CodeRabbit pass on that commit.

Implemented with GPT-6.1-Sol through the Codex harness in T3 Code.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 14, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 6529453

Macroscope's review found this PR approvable — This is a focused server bug fix that makes Claude background-agent cleanup consistent with process ownership and adds regression coverage for failure, cancellation, workflow, and buffered-result cases. The change is isolated to an existing adapter path with no schema, deployment, default, or static-analysis configuration impact.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7f16b528-7e2c-485e-94cb-5caa7200a436
📥 Commits

Reviewing files that changed from the base of the PR and between 6529453 and 3c390d7.

📒 Files selected for processing: 1
  • apps/server/src/orchestration-v2/testkit/OrchestratorReplayRestartBackgroundNote.integration.test.ts

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

📝 Walkthrough

Query-exit cleanup now uses the closing query’s recorded last turn and handles its running background subagents. Tests cover cleanup after CLI exit and Stop, plus restart recovery from a snapshot taken while a background subagent is running.

Changes: Background subagent exit cleanup

Layer / File(s) Summary
Query-exit cleanup
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Cleanup uses the closing query’s recorded last turn. It handles running background subagents without requiring a workflow, while retaining process and run ownership checks.
Exit cleanup tests
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
Tests verify that CLI exit fails a running subagent, Stop cancels a running subagent, and pending background work clears.
Background subagent restart recovery
apps/server/src/orchestration-v2/testkit/OrchestratorReplayRestartBackgroundNote.integration.test.ts
The test snapshots SQLite state while the provider query remains open and the background subagent is running. Recovery and post-restart replay use the snapshot database.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 3c390

No identified issue currently blocks merging the background-subagent cleanup change after normal checks.

Security architecture risk: 🔵 Low · up to 3c390

The change improves background-task cleanup and retains ownership checks intended to protect replacement work. No introduced security issue was established. Recovery testing covers a deliberately constructed legacy state, but does not establish every repeated-restart or real-process failure transition.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated impact concerns provider-session task lifecycle, associated child tool calls and provider-facing recovery context. The inspected changes do not establish a new cross-service, credential or tool-authority path.

Trust Boundaries and Controls

  • observed — Cleanup retains query ownership checks, excludes captured tasks from earlier processes, and rechecks running status and run identity atomically before committing a terminal registry update. These controls constrain stale cleanup from settling replacement work.

Resilience and Maintainability Implications

  • observed — Stop marks the closing query as stopping and includes a timeout fallback that retires ownership before cleanup. Ordinary subagent cleanup now shares the existing cancellation and failure-containment path, including ending open child tool calls.
Pre-merge checks: Passed 4
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed The change satisfies #19440. finalizeActiveTurnAfterQueryExit now uses the closing query's lastTurn and processes every running background subagent owned by that process, including plain agents. T…
Out of Scope Changes check Passed The changes stay within #19440. The source change fixes Claude process-exit cleanup. The adapter tests verify the required lifecycle behavior. The restart fixture creates the legacy orphaned state and…
Title check Passed The title clearly and concisely describes the main change: settling Claude background agents when their process exits.
Description check Passed The description explains the problem, the implementation, scope, linked issues, verification results, limitations, and agent details. It provides sufficient information for review and matches the pull…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@otavio

otavio commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

I hit exactly this bug today. A main thread had launched several background Claude subagents, and one of them was running cargo test while the parent turn was idle. The machine ran out of memory, and the kernel killed ld inside the agent's systemd scope. Because of OOMPolicy=stop, systemd then stopped the whole scope, including the Claude CLI. The subagent's transcript ends with Exit code 137, but its card in T3 kept showing "Running" for hours and no error appeared.

I traced it to the same subagent.task.workflow === undefined filter in finalizeActiveTurnAfterQueryExit and applied the same change locally. With it, the idle-exit test settles the subagent as failed, and without it the test fails with expected 'running' to equal 'failed'. Thanks for the fix. I'd love to see this merged.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: A background Claude subagent stays running forever when the Claude process exits while its parent is idle

2 participants