Repository navigation
Conversation
7867445 to
f22d187
Compare
…ait forever When a run's provider event stream is lost mid tool call, for example when t3_worktree_handoff detaches the session running it, the run was marked failed but its open tool call stayed running. Later runs inherited it as background work, so the thread showed "Waiting on background task" after every later run until a server restart. The lost-stream fallback now cancels the run's open command and dynamic tool items with the run, as restart recovery does for a dead process. Provider-reported terminals are unchanged: their process is alive, so open items still pass to the next run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…calls A failure to store an event also ends a run's ingestion, but the provider can still be running the tool. Cancelling it then would hide live work from Stop and the next run. The cleanup now applies only when the provider's own stream fails, as when its session is released. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r updates The cancellation written for a lost stream's open tool calls came from the adapter's raw item, so it skipped the image stripping the ingestor applies to every stored update. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3cd7adf to
2f44ec9
Compare
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a scoped server bug fix that cancels only run-owned tool and command items when their provider stream is actually lost, while preserving existing background-work behavior for live streams. The production change is localized and supported by targeted unit and integration coverage, with no schema, default, deployment, or static-analysis configuration impact. You can add or adjust custom eligibility rules. Learn more. |
|
Note Written by Thanks for tracing the tool calls left running after their provider stream is lost. Right now we are only taking focused bug fixes from outside contributors, and our limited review capacity means we are declining unvouched submissions over 500 changed lines. This PR changes 555 lines across run finalization and integration test restructuring. Please keep the reproduction documented in issue #15993 and request reconsideration with a smaller fix or an agreed maintainer takeover. |
Problem
When a run's provider event stream is lost mid tool call, the run is marked failed but its open tool call stays
running.t3_worktree_handoffdoes this every time, because it detaches the session that is running it. Because the run failed, later runs on the same provider thread inherit the item as background work, so the thread shows "Waiting on background task …" after every later run until a server restart. Because the item counts as pending work, the sidebar keeps the thread at Waiting and auto-settle skips it. Fixes #15993.Change
RunExecutionServicealready tracks the ids of open background items to decide when to stop ingesting. It now keeps each item's latest update as well. When the provider's own stream fails, as when its session is released, the lost-stream fallback cancels the run's own opencommand_executionanddynamic_toolitems in the same write that records the run as failed. This matches what restart recovery does when a provider process is gone. The cancellation is built from the adapter's item, so it gets the same unserved image stripping the ingestor applies to stored updates. A failure to store an event goes through the same fallback, but there the provider may still be running the tool, so its items are left as before. Subagent rows already end through the existing cascade.Terminals reported by the provider are unchanged. Their process is still alive, so open items still pass to the next run through
selectInheritedBackgroundTurnItems.The cleanup is in run finalization rather than in a change to session shutdown order, because a crashed provider can't emit its own cancellations.
The fake adapter that was inline in
BackgroundWorkStop.integration.test.tsmoved into ascriptedAdapterhelper, so the new integration test reuses it instead of copying it.Scope and approval
interruptedterminal, which bypasses this fallback. If both land, that detached terminal should pass the sameorphanedTurnItems.Verification
BackgroundWorkStop.integration.test.ts› "cancels a tool call cut off when a workspace change detaches its session". It uses the real orchestrator, session manager, and run execution with a scripted adapter that has Claude's single-thread session capabilities. A dynamic tool is running whenProviderSessionManager.detachreleases the session, as a workspace change does.running(expected 'running' to equal 'cancelled'). This matches the persisted state of the affected thread: the handoff call from a failed run was stillrunningafter 63 later runs.cancelled, andderivePendingBackgroundWorkreturns nothing.RunExecutionService.test.ts:interruptedterminal still leaves an open command for the next run.vp test runonRunExecutionService,BackgroundWorkStop.integration,ProviderSessionManager,ProviderTurnStartService,ProviderFailure,SelectionRestart.integration,SteeringCompletion.integration, andtestkit/OrchestratorReplayFixtures.integration: 290 passed.tsc --noEmitforapps/server: no errors.vp lintandvp fmt --checkon the changed files: clean.Fixed by Claude Opus 5.5 in Claude Code, running inside T3 Code.
🤖 Generated with Claude Code