Skip to content

fix(server): Stop ends a Claude thread's background work after the turn settles - #13792

Merged
juliusmarminge merged 3 commits into
t3code/codex-turn-mappingfrom
fix/v2-claude-stop-settled-background
Sep 27, 2026
Merged

juliusmarminge merged 3 commits into
t3code/codex-turn-mappingfrom
fix/v2-claude-stop-settled-background

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

On V2, a Claude thread whose turn has finished but still shows Waiting on background task cannot be stopped from that strip. Clicking Stop does nothing: the background shell keeps running and the strip stays up. The server log shows the provider-turn.interrupt effect failing and retrying:

ProviderAdapterProtocolError: Claude provider turn provider-turn:…:ordinal%253A5%3Aattempt%3A1 is not the active turn.

I reproduced it live. I started node -e "setTimeout(…, 120000)" with run_in_background, let the turn settle, and clicked Stop. The provider thread's pendingBackgroundTasks still listed the task 30s later.

Cause

39c08e1 ("stop retained background work after a turn settles") let a settled run's Stop through. ProviderTurnControlService.interrupt now calls interruptTurn with requestRuntimeRestart: true when the session still reports background work. Codex handles that case by interrupting its settled turn contexts. Claude's interruptTurn still required the provider turn to be the active one and returned a protocol error otherwise.

Fix

When no Claude turn is active, requestRuntimeRestart is set, and the live query belongs to the thread's native session, interruptTurn does three things:

  1. It closes that query. The background shells belong to the CLI process, so they stop with it.
  2. It clears the thread's wake state.
  3. It resets its background roster. This is the same process-reset path used when a query is replaced, so it emits an idle provider_thread.updated with an empty roster.

The next message resumes the session in a new process. Stopping an active turn is unchanged.

Verification

  • New test ClaudeAdapterV2 background wake turns › stops background work after the turn settled. It uses the wake harness with the recorded local_bash task_started shape: start a background task, settle the turn, then call interruptTurn({ requestRuntimeRestart: true }) for the settled turn. The test asserts that the query closes, the roster empties, hasPendingBackgroundWork is false, and no continuation is offered.
    • Without the fix it fails with Claude provider turn … is not the active turn., the same error the live server logged.
    • With the fix it passes.
  • vp test run src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts: 119/119 pass.
  • OrchestratorReplayFixtures, ClaudeReplayFixtures, and ProviderTurnControlService tests filtered to claude/interrupt/stop: 37 pass.
  • vp exec tsc --noEmit -p . in apps/server: no error TS or warning TS.
  • vp lint on the touched files: no new findings. The existing no-unused-vars warning on layer is on the base.
  • knip --workspace apps/server --exports: clean.
  • Live re-check on a dev server built from this branch (Claude Sonnet 5): I started a 150s run_in_background node timer, let the turn settle, and clicked Stop on the Waiting strip. The node process exited, the provider thread went to idle with pendingBackgroundTasks: [], the strip and the sidebar Waiting pill cleared, and no is not the active turn errors were logged. The next message ran normally (AFTER_STOP_OK) on a resumed session.
  • Not run: the full server suite.

Model: Claude Opus 5.5 (Claude Code)

🤖 Generated with Claude Code


Devin Review

…rn settles

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Sep 26, 2026
Comment thread apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 4.9 KiB — 6.8 KiB ✅
Codex Thread snapshot wire — 3.7 KiB — 4.9 KiB ✅
Codex Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Codex Live turn WebSocket decoded — 20.4 KiB — 29.3 KiB ✅
Codex Live turn messages — 2 — 8 ✅
Claude Total thread wire — 4.9 KiB — 6.8 KiB ✅
Claude Thread snapshot wire — 3.7 KiB — 4.9 KiB ✅
Claude Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Claude Live turn WebSocket decoded — 20.8 KiB — 29.3 KiB ✅
Claude Live turn messages — 2 — 8 ✅

Baseline: unavailable · PR result: 4b0d26a · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 106.1 KiB
  • Claude decoded thread snapshot: 106.4 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@macroscopeapp

macroscopeapp Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 4b0d26a

Macroscope's review found this PR approvable — The change narrowly fixes Stop handling for settled Claude turns with background work, reusing existing query and roster cleanup paths while preserving active-turn interruption behavior. Targeted tests cover both normal cleanup and a concurrent replacement-process scenario.

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

juliusmarminge and others added 2 commits September 26, 2026 11:36
…te alone

Stop after the turn settled waits up to 10s for the old CLI to exit. A turn
started during that wait can open a replacement query for the same native
thread, and the cleanup then cleared that new process's Waiting roster and
wake state and marked the thread idle. Only clear it while the stopped query
is still the live one (or none is).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 069058c into t3code/codex-turn-mapping Sep 27, 2026
23 checks passed
@juliusmarminge
juliusmarminge deleted the fix/v2-claude-stop-settled-background branch September 27, 2026 00:52
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:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant