You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Sidebar branch and copy-branch go stale when the agent renames the worktree branch #28463
Start a worktree thread. The sidebar row shows the branch the thread was created on, e.g. fix/task-tools-broken.
Have the agent rename the worktree's branch during a turn, e.g. to feat/thread-details-agent-tasks.
Let the turn finish.
Expected behavior
The sidebar row, its tooltip, and "Copy branch" use the branch the worktree is on now, the same one the composer footer shows.
Actual behavior
The composer footer shows the new branch (read from live git status). The sidebar row and "Copy branch" keep the old name, because they read the stored thread.branch, which is never updated.
Cause
followWorktreeBranchDrift in CheckpointReactor used to adopt the checked-out branch at turn end, skipping detached HEAD, temporary t3/<hex> branches and shared worktrees. The orchestrator rewrite (#2829, de34391427) removed it. RunFinalizationService's observer refresh now only refreshes git and PR status, and it returns early when thread.branch !== local.refName.
Related: #8126 (client-side attempt, closed as redundant with the server reconciliation that has since been removed) and #4926.
Proposed fix
Restore the reconciliation on the server: after the post-turn refresh, dispatch thread.metadata.update with the live branch, with the same guards as before. That fixes the sidebar, copy-branch, search and every other client in one place.
Another entry point into the same stored-thread.branch problem, which may be worth covering in the same fix.
t3_thread_launch with workspaceStrategy: { type: "existing_worktree", worktreePath } and no branch: the server does branch = requestedBranch ?? null and never reads the checkout, so the thread stores null while the worktree sits on a real branch. The sidebar shows no branch, and t3_worktree_status returns attached: true, worktreePath: <path>, branch: null. Verified on 0.1.6 by binding a thread to an existing worktree on branch X: the agent's own git rev-parse --abbrev-ref HEAD returns X, the thread record says null.
Two notes on the proposed reconciliation:
The old followWorktreeBranchDrift skipped shared worktrees. Threads bound to a long-lived, reused worktree are the main consumers of existing_worktree — in our setup a pool of pre-warmed slots claimed per branch — so if that guard still skips them, this entry point stays broken after the fix.
Turn-end reconciliation leaves launch → first-turn-end unlabeled. Resolving the branch at bind time, a rev-parse --abbrev-ref HEAD in the existing_worktree path, closes that window and is independent of drift-following.
Activity
TMeerhof commentedon Nov 2, 2026
Another entry point into the same stored-
thread.branchproblem, which may be worth covering in the same fix.t3_thread_launchwithworkspaceStrategy: { type: "existing_worktree", worktreePath }and nobranch: the server doesbranch = requestedBranch ?? nulland never reads the checkout, so the thread stores null while the worktree sits on a real branch. The sidebar shows no branch, andt3_worktree_statusreturnsattached: true, worktreePath: <path>, branch: null. Verified on 0.1.6 by binding a thread to an existing worktree on branchX: the agent's owngit rev-parse --abbrev-ref HEADreturnsX, the thread record says null.Two notes on the proposed reconciliation:
followWorktreeBranchDriftskipped shared worktrees. Threads bound to a long-lived, reused worktree are the main consumers ofexisting_worktree— in our setup a pool of pre-warmed slots claimed per branch — so if that guard still skips them, this entry point stays broken after the fix.rev-parse --abbrev-ref HEADin theexisting_worktreepath, closes that window and is independent of drift-following.