Skip to content

[Fix] Cursor stale active turn#3748

Open
TimotheFCN wants to merge 3 commits into
pingdotgg:mainfrom
TimotheFCN:fix/cursor-stale-active-turn
Open

[Fix] Cursor stale active turn#3748
TimotheFCN wants to merge 3 commits into
pingdotgg:mainfrom
TimotheFCN:fix/cursor-stale-active-turn

Conversation

@TimotheFCN

@TimotheFCN TimotheFCN commented Jul 6, 2026

Copy link
Copy Markdown

Clear stale active turns after interruption

What Changed

  • Keep the projected session running when an interrupt is requested, so follow-up work still sees the active provider turn until the provider confirms it stopped.
  • Clear the active turn only when provider lifecycle ingestion emits a confirming thread.session-set with a non-running session state.
  • Prevent recovered provider lifecycle events from restoring an interrupted Cursor turn as running.
  • Add regression coverage for both projection state and the Cursor recovery sequence:
    session.startedsession.state.changedthread.started.

Why

Interrupted Cursor threads could remain stuck on “Working” after an app or host restart. In a real incident, the turn was marked interrupted, but the session still retained that turn as active. Recovery then restored the session as running from stale state.

The fix keeps interrupt requests from optimistically marking a live provider session idle, while still allowing a provider-confirmed ready/terminal lifecycle event to clear stale active turn state during recovery.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • Added regression coverage

Tests

  • Ran pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts.
  • Ran pnpm exec vp check.
  • Ran pnpm exec vp run typecheck.

Note

Fix stale activeTurnId on Cursor session after turn interrupt

  • When a Cursor provider emits session.state.changed to ready after an interrupted turn, activeTurnId was not being cleared, leaving the session with a stale active turn reference.
  • Fixes this by setting nextActiveTurnId to null in ProviderRuntimeIngestion.ts when a session.state.changed event maps to a status that is not starting or running.
  • Adds a regression test in ProviderRuntimeIngestion.test.ts covering the full Cursor interrupt-then-recover flow.
  • Behavioral Change: session.state.changed events that resolve to ready or error now clear activeTurnId; previously they preserved the prior value.

Macroscope summarized d21bb79.


Note

Medium Risk
Changes thread session/turn lifecycle mapping during provider recovery, which affects UI “working” state; scope is narrow with targeted tests but touches core orchestration ingestion.

Overview
Fixes interrupted Cursor threads staying on Working when recovery replays provider lifecycle events while the session still pointed at a stale activeTurnId.

Provider runtime ingestion (ProviderRuntimeIngestion.ts) now derives session status before activeTurnId. On session.state.changed, when the mapped status is neither starting nor running, nextActiveTurnId is cleared instead of keeping the previous active turn. That stops the Cursor recovery sequence (session.startedsession.state.changedthread.started) from re-attaching an already-interrupted turn as running.

Regression tests add an integration case for interrupt-then-Cursor-recovery and a projection test that thread.turn-interrupt-requested marks the turn interrupted while the session row stays running with the same activeTurnId until a later thread.session-set moves to ready with activeTurnId: null. The ingestion test harness also exposes a shared readEvents helper.

ProjectionPipeline.ts only has a trivial guard-style formatting change to applyThreadSessionsProjection; session clearing on interrupt is not implemented there in this diff.

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

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

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: f503c962-33c6-4086-83b5-71c385ac08f1

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.

@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 Jul 6, 2026
Comment thread apps/server/src/orchestration/Layers/ProjectionPipeline.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved

Straightforward bug fix that clears the active turn reference when the provider session state changes to a non-running state. The change is limited to one conditional expression with comprehensive test coverage validating the expected behavior.

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

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