From 89a1320fdb481956481fb40d4d9d235f8123c9e2 Mon Sep 17 00:00:00 2001 From: Dara Adedeji Date: Thu, 1 Oct 2026 23:00:17 -0400 Subject: [PATCH 1/2] fix(server): a Claude command you stop shows as interrupted 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 #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) --- .../server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts | 6 ++++-- .../fixtures/turn_interrupt_mid_tool/claude_output.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 48deeb4ae38f..8930b506ba9c 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -3646,7 +3646,7 @@ export function makeClaudeAdapterV2( readonly output: ClaudeNativeToolOutput; readonly status: Extract< OrchestrationV2TurnItem["status"], - "running" | "completed" | "failed" + "running" | "completed" | "failed" | "interrupted" | "cancelled" >; readonly startedAt: DateTime.Utc; readonly updatedAt: DateTime.Utc; @@ -4710,7 +4710,9 @@ export function makeClaudeAdapterV2( parentNodeId: toolCall.parentNodeId, ordinal: toolCall.ordinal, output: NO_CLAUDE_NATIVE_TOOL_OUTPUT, - status: "failed", + // A stopped turn cuts its open tools short; only a turn that + // ended on its own leaves them failed. + status: input.status === "completed" ? "failed" : input.status, startedAt: toolCall.startedAt, updatedAt: input.completedAt, presentation: toolCall.presentation, diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_mid_tool/claude_output.ts b/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_mid_tool/claude_output.ts index 72884607068b..d90531499a80 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_mid_tool/claude_output.ts +++ b/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_mid_tool/claude_output.ts @@ -81,7 +81,7 @@ export function assertTurnInterruptMidToolClaudeOutput( assert.isDefined(commandItem); assert.isDefined(interruptRequest); assert.isDefined(interruptResult); - assert.equal(commandItem.status, "failed"); + assert.equal(commandItem.status, "interrupted"); assert.include(commandItem.input, "node -e"); assert.equal(interruptRequest.status, "completed"); assert.equal(interruptResult.status, "interrupted"); From 43c83474e8f3d531040bd60fe8f0ef1f5d3642a1 Mon Sep 17 00:00:00 2001 From: Dara Adedeji Date: Thu, 1 Oct 2026 23:34:50 -0400 Subject: [PATCH 2/2] test(server): two more Claude interrupt fixtures expect interrupted commands 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) --- .../fixtures/claude_background_task_interrupt/output.ts | 4 ++-- .../testkit/fixtures/turn_interrupt_restart/claude_output.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/claude_background_task_interrupt/output.ts b/apps/server/src/orchestration-v2/testkit/fixtures/claude_background_task_interrupt/output.ts index 120f1cfc435a..c58112b37202 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/claude_background_task_interrupt/output.ts +++ b/apps/server/src/orchestration-v2/testkit/fixtures/claude_background_task_interrupt/output.ts @@ -44,7 +44,7 @@ export function assertClaudeBackgroundTaskInterruptOutput( projection.turnItems.flatMap((item) => item.type === "command_execution" ? [item.status] : [], ), - ["completed", "failed"], - "the background launch completed; the interrupted foreground command did not", + ["completed", "interrupted"], + "the background launch completed; the foreground command was interrupted", ); } diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_restart/claude_output.ts b/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_restart/claude_output.ts index 1da224a8485b..f05b470d32d8 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_restart/claude_output.ts +++ b/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_restart/claude_output.ts @@ -111,7 +111,7 @@ export function assertTurnInterruptRestartClaudeOutput( assert.equal(projection.providerThreads[0]?.status, "idle"); const commandItem = projection.turnItems.find((item) => item.type === "command_execution"); assert.isDefined(commandItem); - assert.equal(commandItem.status, "failed"); + assert.equal(commandItem.status, "interrupted"); assert.include(commandItem.input, "node -e"); const outboundFrames = transcript.entries.flatMap((entry) =>