Skip to content

fix(server): Claude's wake turn after a background task no longer dies to a session release - #37253

Closed
mvanhorn wants to merge 1 commit into
pingdotgg:mainfrom
mvanhorn:fix/claude-wake-killed-by-session-release
Closed

mvanhorn wants to merge 1 commit into
pingdotgg:mainfrom
mvanhorn:fix/claude-wake-killed-by-session-release

Conversation

@mvanhorn

Copy link
Copy Markdown
Contributor

Problem

When a background Bash task finishes after the root turn has ended, the Claude CLI emits task_notification and background_tasks_changed [], then opens its own wake turn. Until that turn's first frame arrives, hasPendingBackgroundWork reports no work: the internal roster is already empty, and the wake buffer holds only the notification, which the probe does not count. Any session release in that window (idle release, a linked-environment change on open, or a detach) closes the CLI mid-wake. The wake is lost, and the thread stays silent until the user sends another message.

Seen in the field: the notification arrived at 02:12:27.305Z, query.close went out at 02:12:27.316Z, and the CLI's wake request was still in flight. The thread then sat silent for 13 hours. Details are in #37252.

Change

hasPendingBackgroundWork also counts a buffered task_notification whose task recorded a wake report, until the wake run takes the buffer. Only wake-eligible notifications get a report. Nested subagent notifications, which never wake the root, still don't count.

Model and policy switches read hasPendingBackgroundWorkForThread / liveProcessRunsBackgroundWork, which are unchanged, so a switch during that window is still allowed and still carries the buffered notification into the new process.

Scope

A reliability bug fix in the Claude adapter. Fixes #37252.

Verification

  • Extended existing tests in ClaudeAdapterV2.test.ts:
    • The "keeps an exited background command on the roster until its wake run starts" cases (awaited local_bash task, root settled, notification plus an empty background_tasks_changed, in each order) now assert that the session-wide probe holds right after the notification, and that it clears once the wake run finishes.
    • "buffers wake output and requests a single continuation run" now expects the hold after the notification.
    • The model/policy replacement test asserts that the per-thread probe a switch reads stays false, and that the session-wide hold is still owed after the switch.
  • On main these fail: 6 tests in that file. With the fix: 449/449 in ClaudeAdapterV2.test.ts, and 71/71 in ProviderSessionManager.test.ts.
  • vp run check:changed passes: format, lint, knip, typecheck, and the tests beside the change.
  • Not checked: which release path fired in the field incident. The server traces had rotated, but the provider event log shows the close 11 ms after the notification.

Runtime transcript from the incident (provider event log):

02:12:24.649Z incoming result success end_turn
02:12:27.305Z incoming system task_notification completed
02:12:27.305Z incoming system background_tasks_changed []
02:12:27.316Z outgoing query.close
15:08:34.801Z outgoing query.open   (next user message)

Model: Claude Opus 5.5, harness: Claude Code.

🤖 Generated with Claude Code

…s to a session release

hasPendingBackgroundWork reported no work between a background task's
task_notification and the first frame of the wake turn Claude opens for it,
so an idle, linked-environment, or detach release could close the CLI
mid-wake and lose the wake. A buffered notification that recorded a wake
report now counts as pending until the wake run takes it.

Fixes pingdotgg#37252
@github-actions github-actions Bot added the vouch:unvouched Author is not yet trusted in the VOUCHED list. label Nov 15, 2026
@github-actions github-actions Bot added the size:S 10-29 changed lines (additions + deletions). label Nov 15, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 15, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 418b3b4

Macroscope's review found this PR approvable — Claude sessions now remain alive when a buffered task notification matches a recorded wake, preventing premature release before the wake turn starts. The change is narrowly scoped and retains existing idle-pin limits.

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

@juliusmarminge

Copy link
Copy Markdown
Member

Thanks for this. #36029 makes the same change and is the one being kept.

#36029 addresses the same session-release race using the adapter's existing owed-wake tracking. It also protects the interval after the roster empties but before the completion notification arrives, which makes it the more complete fix to keep.

Your incident logs establish the impact of this race, and the notification-ordering and model-switch cases provide useful evidence for validating that fix.

Closing as a duplicate.

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

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched Author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Claude's wake turn after a background task is killed when the session is released before its first frame

2 participants