fix(server): successful worktree handoffs complete normally#24545

open6
main
fix/issue-24513
opened3d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 3d ago

Problem

A successful Codex worktree handoff detaches the old provider session, but the old run is then recorded as interrupted. On Linux web, a real handoff and automatic continuation succeeded while the timeline still showed a red “Run interrupted · Stopped by the provider” divider.

Change

Treat an interrupted provider turn as completed when its thread moved to another workspace, using the normal checkpoint and continuation path. Keep the raw interruption for native-child cleanup so detached children do not remain running. Check for an unpaired Stop request inside the terminal-write transaction; a concurrent Stop retains the interrupted outcome and its attribution.

Scope and approval

Fixes https://github.com/pingdotgg/t3code/issues/24513 GitCafe: https://git.cafe/pingdotgg/t3code/issues/24513

Triaged bug via the automated issue flow. Shared server lifecycle fix; no client, contract, setting, or persisted-field changes. The existing attribution fix remains untouched.

Verification

  • Red first: the handoff integration test failed on main because the run settled as interrupted. It now asserts completed, a checkpoint, the new worktree binding, and no interrupt-result item.
  • Added regressions for native-child terminal cleanup and a Stop committed during handoff finalization. The real SQLite guard test failed with the guard disabled and passes with it enabled.
  • Final merged head: 159/159 tests passed across RunExecutionService, FoundationPersistence, SelectionRestart.integration, ProviderTurnStartService, BackgroundWorkStop.integration, and ProviderEventIngestor. Test output.
  • Scoped server typecheck passed; targeted lint passed with existing warnings outside changed lines. Two independent final code reviews found no blockers.
  • Primary integrated check: real Codex GPT-6.1-Sol High handed off once, continued in the target branch with attached: true, and both runs completed with checkpoints and no false interrupted divider.

Evidence: fixture matrix, full index, narrated steps and UTC timestamps, raw bundle, runtime extracts and reproduction helpers.

Each recording starts a real native Codex child running sleep 60, immediately hands the thread off once, and waits for continuation to report its actual cwd, branch and clean status. Before: parent falsely interrupted. After: parent completed, child stopped, continuation succeeded. After recordings also open the settled parent/child details.

Before is historical main 8176b58931, not the final merge base. After is 43cd19866d; intervening main changes affect mobile Markdown, preview certificates and Cursor terminal handling. This set proves runtime behavior, not an isolated pixel comparison.

Linux Chromium 154, DPR 2, 16px root font, desktop UA at both CSS widths. Recordings retain 2880×1600 / 780×1600 pixels; desktop preview stills are downsampled to 1280×711. Stills settled and matched twice at least 500ms apart. Diff0 below is amplified ×8; threshold is explicitly 0, unmasked.

CSS viewport / themeBefore recordingAfter recording
1440×800 / lightBeforeAfter
1440×800 / darkBeforeAfter
390×800 / lightBeforeAfter
390×800 / darkBeforeAfter

What else changed in the captures: provider prose/capitalization, titles, durations, timestamps, hover controls, folding, spacing and scroll composition vary between independent real runs. The normal completed parent now folds. No claim that nothing else changed visually.

Unverified at runtime: Electron, native mobile, other providers and remote/relay/tunnel modes. The 390-wide captures are responsive web. Shared server projections are covered by the focused tests; client renderers and wire contracts are unchanged. Stop-race and event-ingestion closure are proven by deterministic tests, not live-provider race footage.

Independent proof audit: H/N handoff and native-child runtime rows supported at all four viewport/theme cells; full matrix remains incomplete. Missing explicit controls: recovery-lookup failure, unchanged/same-path workspace callbacks, and changed-workspace non-interrupted terminal variants. The auditor reproduced the settled renderer (transcript pixels matched; 0.131% total delta in header controls), but no uploaded browser replay driver exists for a fresh interaction rebuild. Historical-base and natural transcript differences also prevent an isolated pixel claim. These are pending proof gates; the observed runtime behavior did not contradict the fix.

Models and harnesses: Claude Opus 5.5 via Claude Code; GPT-6.1-Sol via Codex in T3 Code.

Checks

21 successful · 7 skipped
All checks passed
43cd198

Added 6 commits

  • fix(server): a worktree handoff no longer marks the handing-off run interrupted0b002c7
  • Merge remote-tracking branch 'origin/main' into fix/issue-2451343cd198
  • test(server): workspace handoff during a run must not interrupt ita998e6c
  • fix(server): a worktree handoff still settles the run's native subagentsb1d8a17
  • fix(server): a Stop racing a worktree handoff keeps the run interruptedca238a6
  • Merge remote-tracking branch 'origin/main' into fix/issue-24513d187673
Synced from GitHub

Approvability

Verdict: Approved at 43cd198

Macroscope's review found this PR approvable — This is a bounded server lifecycle bug fix that corrects successful worktree handoffs while preserving interruption semantics for real Stop requests and native-child cleanup. Production changes are limited to the existing orchestration path and are covered by focused persistence, race, subagent, and integration tests.

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

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
CodexThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
CodexLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
CodexLive turn WebSocket decoded20.9 KiB20.9 KiB0 B (0.0%)29.3 KiB✅
CodexLive turn messages220 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.0 KiB−7 B (−0.1%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.2 KiB1.2 KiB−7 B (−0.6%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.2 KiB21.2 KiB0 B (0.0%)29.3 KiB✅
ClaudeLive turn messages220 (0.0%)8✅

Baseline: 3afb9b3 · PR result: 43cd198 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review.

External image Recent review info
External image Run configuration
External image Commits

Reviewing files that changed from the base of the PR and between 3afb9b366f271b56a3e49fd86f2594981ec11b66 and 43cd19866dc706a4e7724f3fcafb6d4d5dd4d7c2.

External image Files selected for processing: 6
  • apps/server/src/orchestration-v2/EventSink.ts
  • apps/server/src/orchestration-v2/FoundationPersistence.test.ts
  • apps/server/src/orchestration-v2/ProviderTurnStartService.ts
  • apps/server/src/orchestration-v2/RunExecutionService.test.ts
  • apps/server/src/orchestration-v2/RunExecutionService.ts
  • apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts

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

Walkthrough

The change adds a workspace-move check to root-run finalization. When an interrupted root run coincides with a workspace move, finalization attempts a completed terminal write. A pending interrupt request can block that write, causing finalization to write the interrupted terminal instead.

Changes: Workspace handoff finalization

Layer / File(s)Summary
Guard terminal writes against pending interrupts
apps/server/src/orchestration-v2/EventSink.ts, apps/server/src/orchestration-v2/RunExecutionService.ts, apps/server/src/orchestration-v2/FoundationPersistence.test.ts
writeIfRunCurrent can reject a write when an interrupt request exists without its matching result. Run finalization passes the request and result IDs and tracks whether the write committed. The persistence test covers both rejected and committed writes.
Detect workspace moves and finalize root runs
apps/server/src/orchestration-v2/ProviderTurnStartService.ts, apps/server/src/orchestration-v2/RunExecutionService.ts, apps/server/src/orchestration-v2/RunExecutionService.test.ts, apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts
The provider turn start flow passes a workspace-move check to root-run execution. For an interrupted root terminal after a move, finalization attempts a completed terminal write and falls back to the interrupted terminal if the write does not commit. Tests cover handoff behavior and a Stop-request race.

Priority: External image Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: Low

Possibly related issues

  • pingdotgg/t3code#22863 — Reports a planned Claude worktree detach that fails the run when the provider stream closes. This change also handles finalization after workspace-session detachment.
  • pingdotgg/t3code#22971 — Reports a Claude worktree handoff that fails the turn and blocks continuation. This change addresses the shared server finalization path for planned detachment.

Possibly related PRs

  • pingdotgg/t3code#24444: Addresses a queued continuation after handoff detaches its provider session; this change handles completion of the detached run.
  • pingdotgg/t3code#23971: Changes provider-session handling during active workspace moves. Both changes address workspace changes and provider-session lifecycle.
  • pingdotgg/t3code#24446: Changes worktree-related session detachment during background work. This change also handles worktree-change detachment during handoff finalization.

Suggested labels: bugfix, size:L

Suggested reviewers: maria-rcks

Merge Risk: External image Minimal · up to 43cd1

Successful worktree handoffs can settle as completed without overriding a user Stop. No actionable merge-blocking risk remains after normal checks.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Title checkPassedThe title clearly and concisely describes the main fix: successful worktree handoffs now complete normally.
Description checkPassedThe description includes all required sections. It explains the problem, implementation, scope and approval, focused verification, test results, limitations, and evidence.
Linked Issues checkPassedThe changes satisfy issue #24513. RunExecutionService detects an interrupted turn after a workspace move and first writes a completed terminal state. It retains the raw interrupted terminal for nati…
Out of Scope Changes checkPassedThe changes remain within issue #24513. The source changes are shared server lifecycle logic in RunExecutionService, EventSink, and ProviderTurnStartService, plus focused persistence and integra…
Full details: Linked Issues check

Explanation

The changes satisfy issue #24513. RunExecutionService detects an interrupted turn after a workspace move and first writes a completed terminal state. It retains the raw interrupted terminal for native-child cleanup. EventSink.writeIfRunCurrent checks for an unpaired interrupt request in the terminal-write transaction, so a concurrent Stop falls back to interrupted attribution. ProviderTurnStartService supplies the workspace-move probe and returns false when projection loading fails. Tests cover successful handoff completion, checkpoint and worktree binding, omitted interrupt results, native-child cleanup, and the Stop race.

Full details: Out of Scope Changes check

Explanation

The changes remain within issue #24513. The source changes are shared server lifecycle logic in RunExecutionService, EventSink, and ProviderTurnStartService, plus focused persistence and integration tests. These tests directly verify successful handoff state and Stop-race safety. No unrelated client, contract, setting, or persisted-field changes are shown in the reviewed change summary.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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