Skip to content

fix(orchestration): clear stale active turn after interrupt#2360

Open
joshmeads wants to merge 2 commits intopingdotgg:mainfrom
joshmeads:bug/provider-turn-state-interrupt
Open

fix(orchestration): clear stale active turn after interrupt#2360
joshmeads wants to merge 2 commits intopingdotgg:mainfrom
joshmeads:bug/provider-turn-state-interrupt

Conversation

@joshmeads
Copy link
Copy Markdown
Contributor

@joshmeads joshmeads commented Apr 27, 2026

What Changed

Fixes stale projected provider turn state after a successful interrupt.

After the provider accepts an interrupt, the shared orchestration layer now clears the projected active turn and marks the session ready. Runtime ingestion also allows the provider-current turn to replace stale projected active-turn state while still rejecting unrelated stale lifecycle events.

Why

A provider could already be idle while T3 Code still projected an older turn as active. In that state, pressing Stop could call the provider interrupt successfully, but the UI stayed stuck as “Working” because no later provider event cleared the stale active turn.

This is shared orchestration logic, so the fix applies across providers. OpenCode exposed the issue, but the stale turn projection bug was not OpenCode-specific.

Diagnosed from persisted projection/event state during a live repro: the provider had completed later turns, while projection_thread_sessions.active_turn_id still pointed at an older running turn. Stop/interrupt succeeded, but no later provider event cleared the stale projected turn.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Constraint: Lifecycle guard must still reject unrelated stale runtime events.
Confidence: high
Scope-risk: moderate
Tested: bun run test src/orchestration/Layers/ProviderCommandReactor.test.ts src/orchestration/Layers/ProviderRuntimeIngestion.test.ts
Tested: bun fmt
Tested: bun lint
Tested: bun typecheck
Tested: bun run build:desktop

Screenshots

  1. turn was complete but it remained on "working" and stop button became non functional.
Screenshot 2026-04-26 at 17 10 31

Note

Medium Risk
Touches shared orchestration session/turn lifecycle projection logic; mistakes could incorrectly clear or accept turn state and affect UI/session consistency across providers.

Overview
Fixes cases where a successful Stop/interrupt still leaves a thread projected as running.

ProviderCommandReactor now clears projected session state after thread.turn.interrupt (sets session to ready, clears activeTurnId, and resets lastError). ProviderRuntimeIngestion now (1) treats turn.aborted like turn.completed for closing lifecycle state and finalizing buffered assistant/proposed-plan output, and (2) allows a conflicting turn.started to replace a stale projected activeTurnId when it matches the provider’s currently active turn (while continuing to reject unrelated stale lifecycle events).

Reviewed by Cursor Bugbot for commit 1a9b981. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Clear stale active turn and session state after thread interrupt

  • After a thread.turn.interrupt event, ProviderCommandReactor now sets the thread session status to ready, clears activeTurnId, and clears lastError.
  • ProviderRuntimeIngestion now treats turn.aborted as a lifecycle-closing event: clears the active turn, sets session to ready, and finalizes buffered assistant messages and proposed plan content.
  • A turn.started event that conflicts with the projected active turn is now accepted when the provider's expected active turn matches the incoming event, allowing the provider's current turn to replace stale projected state.
  • Behavioral Change: sessions that previously remained in a non-ready state after an interrupt or abort will now transition to ready with no active turn.

Macroscope summarized 1a9b981.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d1afdf24-16e2-4a9e-b4ca-b1d57238669e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Apr 27, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit dcd2fef. Configure here.

Comment thread apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
Comment thread apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 27, 2026

Approvability

Verdict: Approved

Straightforward bug fix that clears stale session state (activeTurnId, lastError) after turn interrupts/aborts. Changes are self-contained within orchestration state management and include comprehensive test coverage.

You can customize Macroscope's approvability policy. Learn more.

@joshmeads joshmeads force-pushed the bug/provider-turn-state-interrupt branch from dcd2fef to acceb5f Compare April 27, 2026 01:12
A provider can already be idle while the projected session still points at an older active turn. Interrupt now clears projected session state after the provider accepts the interrupt, and runtime ingestion lets a provider-current turn replace stale projected active-turn state.

Constraint: Lifecycle guard must still reject unrelated stale runtime events.
Rejected: Make this OpenCode-specific | the stale projection logic is shared across providers.
Confidence: high
Scope-risk: moderate
Tested: bun run test src/orchestration/Layers/ProviderCommandReactor.test.ts src/orchestration/Layers/ProviderRuntimeIngestion.test.ts
Tested: bun fmt
Tested: bun lint
Tested: bun typecheck
Tested: bun run build:desktop
@joshmeads joshmeads force-pushed the bug/provider-turn-state-interrupt branch from acceb5f to edd9310 Compare April 27, 2026 01:23
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:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant