Skip to content

fix(server): Stop reaches background work after a run fails before its provider starts - #15474

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/stop-background-work-after-failed-run
Closed

vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/stop-background-work-after-failed-run

Conversation

@vitalyiegorov

Copy link
Copy Markdown
Contributor

Problem

Fixes #15472. After a turn leaves background work running, a message that fails before its provider starts becomes the newest run, with no provider turn. Stop then targets that run. The client sends the newest run because the server only reaches background work through it. dispatchRunInterrupt finds no provider turn for that run's attempt and rejects it with Run … is not interruptible. The work keeps running and the thread is stuck until it ends on its own.

Change

apps/server/src/orchestration-v2/Orchestrator.ts: when the run being stopped has background work but no provider turn of its own, the interrupt uses the provider thread's latest turn.

That reuses the existing settled-turn path. ProviderTurnControlService sends requestRuntimeRestart, Claude closes the CLI process for the native thread, and the settle follow-up ends what's left. Background work on other provider threads is still reached by the existing loop.

The fallback only applies when hasBackgroundWork is true, which requires the newest run to be settled. Stop on a preparing or running run is unchanged. The client already sends the right run, so this is a server-only change.

Scope and approval

A focused fix for #15472, which I filed with the reproduction and screenshot. It waits on triage, like any bug PR. It adds a fallback inside one handler, with no contract or client changes. #15013 tracks V2 interrupt hardening and doesn't list this case.

Verification

  • BackgroundWorkStop.integration.test.ts: the stopped run is now a newer run that failed before its provider started. Stop must still reach both provider threads and interrupt all three pending items. On main the test fails with Run run:5 is not interruptible., the exact error from the report. Here it passes.
  • Ran every apps/server test file that dispatches run.interrupt or covers interrupts: BackgroundWorkStop, CodexAdapterV2, ThreadManagementService, runtimeLayer, OrchestratorMcpService, OrchestratorMcpToolkit, Orchestrator.control-reads. 220 passed and 8 were skipped (live OpenCode).
  • apps/server typecheck passes. Lint and fmt pass on the touched files; the one lint warning, an unused layerUnavailable, is already on main.
  • Not checked: Stop on a patched server in a real client.

Implemented with Claude Opus 5.5 in T3 Code (Claude Code harness).

🤖 Generated with Claude Code

…s provider starts

When the newest run failed before its provider started, it had no provider
turn, so run.interrupt rejected it as not interruptible. Stop then could
not end the background work the thread still showed. The interrupt now
falls back to the provider thread's latest turn, whose session owns that
work.

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 Oct 4, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9a5a793

Macroscope's review found this PR approvable — This is a focused server-side bug fix that corrects provider-turn selection for stopping background work after a pre-provider failure, while preserving existing behavior in other cases. It touches no schemas, defaults, infrastructure, sensitive areas, or static-analysis configuration.

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

@coderabbitai

coderabbitai Bot commented Oct 4, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (1)
docs/internals/effect-services.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ede0ebb9-b5c6-436a-8377-537797af341c
📥 Commits

Reviewing files that changed from the base of the PR and between 8fb068c and 9a5a793.

📒 Files selected for processing (2)
  • apps/server/src/orchestration-v2/BackgroundWorkStop.integration.test.ts
  • apps/server/src/orchestration-v2/Orchestrator.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

When a run fails before its provider starts, dispatchRunInterrupt now falls back to the latest turn on the provider thread if background work exists. The integration test covers stopping background work through this fallback.

Changes

Background work interruption

Layer / File(s) Summary
Provider-thread turn fallback
apps/server/src/orchestration-v2/Orchestrator.ts
dispatchRunInterrupt uses the provider thread’s latest turn when background work exists and the active attempt has no matching turn.
Failed-run integration coverage
apps/server/src/orchestration-v2/BackgroundWorkStop.integration.test.ts
The test dispatches an interrupt for a failed run with no provider-turn event and checks the provider-thread interruptions and Codex background-work results.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: t3dotgg

Merge Risk: ⚪ Minimal · up to 9a5a7

The failed-run Stop path can reach background work from an earlier turn. No issue requiring a change before merge was identified.

Architecture Summary

Architecture risk: 🟡 Medium · up to 9a5a7

The change affects 1 system.

Changed systems: apps/server

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — apps/server (service) was modified; 2 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in apps/server/src/orchestration-v2/BackgroundWorkStop.integration.test.ts: The test description now specifies that the newest run fails before its provider starts and that stopping it must reach both provider threads and end Codex background work; it previously described stopping a settled run.
  • observed — Modified behavior in apps/server/src/orchestration-v2/BackgroundWorkStop.integration.test.ts: Adds a failed-run fixture by removing its provider-turn event and changing its run-created status to failed.
  • observed — Modified behavior in apps/server/src/orchestration-v2/BackgroundWorkStop.integration.test.ts: Includes the failed-run events in the event batch alongside the earlier runs and provider-thread event.
  • observed — Modified behavior in apps/server/src/orchestration-v2/BackgroundWorkStop.integration.test.ts: Dispatches the interrupt for the failed run instead of the settled latest run, and updates the assertion comment to say the other provider thread is interrupted at its latest turn because the failed run has none. The Codex interruption remains at the subagent’s parent turn.

Reliability and maintainability

  • inferred — Risk-relevant change factors for apps/server: blast_radius_1; direct_dependents_1
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the server-side fix: Stop can reach background work after a run fails before its provider starts. It is specific and related to the main change.
Description check ✅ Passed The description covers the problem, change, scope, and focused verification results. The scope section identifies the issue and explains the narrow change, but notes that the issue awaits triage rathe…
Linked Issues check ✅ Passed Issue #15472 requires Stop to reach background work when the newest run failed before its provider started. The PR summary reports that dispatchRunInterrupt now uses the provider thread’s latest tur…
Out of Scope Changes check ✅ Passed The reported changes are limited to dispatchRunInterrupt and its focused BackgroundWorkStop.integration.test.ts coverage. The implementation and test support issue #15472. No unrelated changes are…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@vitalyiegorov
vitalyiegorov deleted the fix/stop-background-work-after-failed-run branch October 6, 2026 03:55
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.

[Bug]: Stop can't end background work after a run fails before its provider starts ("Run … is not interruptible")

1 participant