fix(server): follow branch drift in dedicated worktrees so PRs link to their thread - #5159
Conversation
…o their thread Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2566f5c. Configure here.
| isTemporaryWorktreeBranch(thread.branch) | ||
| ) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Temp branch blocks drift adopt
Medium Severity
followWorktreeBranchDrift returns early when the thread’s recorded branch is still a temporary placeholder, even if the checkout is already a real branch. The PR only calls for ignoring temporary checkouts, and expectedBranch already drops a stale adopt if first-turn rename wins the race. When rename fails and the agent has already branched, this leave the recorded branch stuck and the PR unlinked on the server path.
Reviewed by Cursor Bugbot for commit 2566f5c. Configure here.
ApprovabilityVerdict: Needs human review This PR adds new runtime behavior for tracking branch drift in worktrees, with an unresolved review comment identifying a potential logic bug where temporary placeholder branches may prevent drift adoption. Both the new feature scope and the open concern warrant human review. You can customize Macroscope's approvability policy. Learn more. |
## What's Changed * fix(server): follow branch drift in dedicated worktrees so PRs link to their thread by @t3dotgg in pingdotgg/t3code#5159 **Full Changelog**: pingdotgg/t3code@v0.0.32-nightly.20260801.969...v0.0.32-nightly.20260801.970 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.32-nightly.20260801.970


Problem
When an agent (or the user) runs a plain `git checkout -b` inside a thread's dedicated worktree and files a PR from the new branch, the PR never links to the thread. The thread's recorded `branch` only updates through T3 commands (creation, first-turn auto-rename), so it goes stale — and since #4460 the client only attributes PR state to a thread when the checked-out branch exactly equals the recorded one. Stale metadata silently orphans the PR, and (via #5151) also re-enables inactivity auto-settle for threads that actually have an open PR.
This happened in the wild: a thread recorded `t3code/ios-background-websocket-reconnect`, the agent branched off to `t3code/mobile-reconnect-hardening` mid-thread and filed #5154 from it, and the link never formed.
Solution
Keep #4460's strict matching and fix the stale data instead: after each turn completes, the CheckpointReactor already refreshes local git status for the session cwd. Use that fresh status to detect drift — if the checked-out branch differs from the thread's recorded branch and the worktree belongs to exactly that thread, dispatch a `thread.meta.update` adopting the checked-out branch. The command's existing `expectedBranch` compare-and-swap drops the update if the recorded branch moved concurrently.
Guards: shared cwds are never touched (that's where #4460's protection matters), detached HEAD and temporary `t3code/` placeholder checkouts are ignored, and threads with no recorded branch are left alone.
No schema changes, no client changes, no new polling — one server-side hook on an existing refresh path.
Tests
Three focused cases in `CheckpointReactor.test.ts`: adopts drift on a dedicated worktree, refuses when the worktree is shared, refuses temporary placeholder checkouts.
Diagnosed and implemented by Claude Fable 5 (claude-fable-5, 1m context) running in Claude Code.
Note
Medium Risk
Changes thread metadata on an existing turn-completion path with compare-and-swap guards; wrong adoption could mis-link PRs, but shared-worktree and temporary-branch guards limit blast radius.
Overview
When an agent or user checks out a different branch inside a thread’s dedicated worktree (outside T3’s branch commands), the thread’s stored
branchcan stay stale. That breaks PR↔thread linking and related behavior that only matches when checkout equals recorded branch.CheckpointReactor now uses the local git status already refreshed on
turn.completed. If the checked-out ref differs from the thread’s recorded branch, it dispatchesthread.meta.updatewithexpectedBranchso concurrent renames are ignored. Adoption only runs when the session cwd is that thread’s sole worktree; shared worktrees, detached HEAD, and temporaryt3code/<hex>placeholder branches are skipped.Tests extend the harness and add three cases: adopt on dedicated worktree, no adopt when worktree is shared, no adopt for temporary placeholder checkouts.
Reviewed by Cursor Bugbot for commit 2566f5c. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Follow branch drift in dedicated worktrees so PRs link to their thread
turn.completedevent,CheckpointReactornow calls a newfollowWorktreeBranchDrifthelper that compares the checked-out branch (from local git status) against the thread's recorded branch.thread.meta.updatewithexpectedBranch(CAS semantics) to adopt the drifted branch, allowing PRs on the new branch to link back to the thread.Macroscope summarized 2566f5c.