Repository navigation
fix(server): a Claude command you stop shows as interrupted - #14708
SunkenInTime wants to merge 2 commits into
Conversation
ApprovabilityVerdict: Would Approve Macroscope's review found this PR approvable — This focused fix changes only Claude tool-finalization status for interrupted turns and updates targeted interruption tests; completed-turn behavior remains unchanged. A separate unresolved High-severity orchestration-contract finding remains an independent blocking risk outside this diff. Not approved because:
Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more. |
Dismissing prior approval to re-evaluate dd4d8f3
Dismissing prior approval to re-evaluate dd4d8f3
Dismissing prior approval to re-evaluate dd4d8f3
When a Claude turn ended with tools still open, finalizeActiveTurn closed every one as failed, so stopping a turn mid-command recorded the command as failed. Codex, Pi and (since pingdotgg#13806) Cursor record it as interrupted. Close open tools with the turn's own status when the turn was stopped or cancelled; a turn that ends on its own still leaves them failed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ommands claude_background_task_interrupt and turn_interrupt_restart asserted the old failed status for the command a stop cut short. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| }), | ||
| /** Provider acceptance of a mailbox delivery; distinct from the agent reading its result. */ | ||
| Schema.Struct({ | ||
| type: Schema.Literal("notification.delivery.accept"), |
There was a problem hiding this comment.
🟠 High src/orchestrationV2.ts:2674
Clients can submit server-only lifecycle and delivery commands through dispatchCommand, allowing them to acknowledge or dispose delegated results, release or fail preparing runs, and forge thread.created.record history. This can permanently suppress delegated-task delivery, start unprepared workspaces, terminate another launch, or corrupt agent-visible history. Keep notification.delivery.accept, prepared-run.release, thread.created.record, delegated_task.completion-delivery.acknowledge, delegated_task.completion-delivery.dispose, and prepared-run.fail out of OrchestrationV2Command and include them only in the internal/server-only union, or require an unforgeable worker capability.
🤖 Copy this AI Prompt to have your agent fix this:
In file @packages/contracts/src/orchestrationV2.ts around line 2674:
Clients can submit server-only lifecycle and delivery commands through `dispatchCommand`, allowing them to acknowledge or dispose delegated results, release or fail preparing runs, and forge `thread.created.record` history. This can permanently suppress delegated-task delivery, start unprepared workspaces, terminate another launch, or corrupt agent-visible history. Keep `notification.delivery.accept`, `prepared-run.release`, `thread.created.record`, `delegated_task.completion-delivery.acknowledge`, `delegated_task.completion-delivery.dispose`, and `prepared-run.fail` out of `OrchestrationV2Command` and include them only in the internal/server-only union, or require an unforgeable worker capability.
dd4d8f3 to
43c8347
Compare
|
Warning Review limit reachedOnly developers with an assigned seat can use this organization's usage-based review budget, and seats here are assigned manually. Ask an admin to assign a seat, or change the review continuation mode in Billing. Next included review available in 42 minutes. View limit detailsLimit details: You’ve used all 10 included reviews currently available. Review configuration: ⚙️ Run configurationConfiguration used: Repository: pingdotgg/t3code/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (4)
Comment |
Problem
When you stop a Claude turn while a command is running, the command is saved as
failed. The run and the provider turn are correctlyinterrupted. Codex, Pi and (since #13806) Cursor record a stopped command asinterrupted.What it looks like
The visible difference is small: the stopped command's terminal icon is red ("failed") before this change and grey ("interrupted") after. That matches how Codex, Cursor and Pi show a stopped command. Agents reading the thread with
t3_thread_readsee the same status, so before this change they were told the command failed.Full screenshots and the Stop interaction
Same prompt on a dev build of this branch, using Claude Haiku 4.5 on Windows. The thread runs a 60-second
nodecommand through the Bash tool, and I press Stop about 25 s in. For the "before" run I swapped inClaudeAdapterV2.tsfrom the current base, which is slightly newer than this PR's base. The only change that matters here is thefinalizeActiveTurnstatus.The projection rows for those two threads have
command_executionwith statusfailedbefore andinterruptedafter.Evidence
Seen on my V2 install (Windows 11, T3 Code Alpha 0.0.43-preview.20260925.2234). Two Claude runs I stopped mid-command,
cb27a173…run 1 and25318ad4…run 1, show their in-flight tool items (toolu_01Dsw5XiGCxQMvQetAYPoZJJ,toolu_015FxSiZtU3XTHWstWE8Tr56) asfailed. A Codex run stopped the same way on the same day (bbd82247…,exec-ca9958dc) showsinterrupted.The recorded
turn_interrupt_mid_toolClaude fixture asserted this behavior:commandItem.status === "failed".Change
finalizeActiveTurninClaudeAdapterV2closes every tool that never got a result. It always closed them asfailed. Now aninterruptedorcancelledturn closes them with its own status. Tools on a turn that ends on its own stayfailed, as before.Scope and approval
Very small, focused fix for an obvious status bug: one line in the Claude adapter plus the three fixture assertions that encoded the old status, mirroring #13806 for Cursor. Julius asked for last small V2 bug fixes before the nightly merge.
Verification
turn_interrupt_mid_tool/claude_output.tsnow assertscommandItem.status === "interrupted".vp test run src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts -t turn_interrupt_mid_tool:expected 'failed' to equal 'interrupted'(1 failed, 3 passed).claude_background_task_interrupt/output.tsandturn_interrupt_restart/claude_output.tsasserted the same oldfailedstatus for a stopped command and now expectinterrupted. The fullOrchestratorReplayFixtures.integration.test.ts: 121 passed.vp test run src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts: 124 passed, 1 failed. The failure, "names an expired Claude login instead of the terminal API error" (expected 'Claude could not authenticate…' to include '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/synthetic/project'), fails identically on the base without this change.vp exec tsc --noEmit -p .inapps/server: noerror TS.vp linton the touched files reports only the existing unusedlayerwarning at the end ofClaudeAdapterV2.ts.Diagnosed and implemented by Claude Opus 5.5 in Claude Code, running inside T3 Code.
🤖 Generated with Claude Code