Skip to content

[Bug]: Sidebar branch and copy-branch go stale when the agent renames the worktree branch #28463

Open

Description

@TonybynMp4

Area

apps/server

Steps to reproduce

  1. Start a worktree thread. The sidebar row shows the branch the thread was created on, e.g. fix/task-tools-broken.
  2. Have the agent rename the worktree's branch during a turn, e.g. to feat/thread-details-agent-tasks.
  3. 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.

Image Image

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.

Impact

Minor bug or occasional failure

Version or commit

main @ 575579d

Environment

Linux, T3 Code dev build, Claude (Opus 5.5)

Screenshots

Two focused crops follow in a comment.

Activity

  1. TMeerhof commented on Nov 2, 2026

    @TMeerhof

    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:

    1. 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.
    2. 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions