fix(server): restore worktree branch naming in the v2 orchestrator - #5309
Conversation
|
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:
Comment |
| ) { | ||
| const oldBranch = branch; | ||
| const worktreeCwd = worktreePath; | ||
| yield* generateBranchNameFor(worktreeCwd, initialMessage).pipe( |
There was a problem hiding this comment.
🟠 High orchestration-v2/ThreadLaunchService.ts:336
The background branch-rename chain can leave the thread projection permanently out of sync with the actual Git branch. git.renameBranch runs before threads.dispatch, and both are wrapped in a single Effect.catchCause that only logs. If the rename succeeds but the metadata dispatch fails (e.g. a transient store error), the worktree is on the generated branch while the thread projection keeps the temporary t3code/<hash> branch. Later Git actions use the stale branch name. Consider retrying the metadata update, rolling back the rename on dispatch failure, or otherwise reconciling the two before swallowing the error.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/orchestration-v2/ThreadLaunchService.ts around line 336:
The background branch-rename chain can leave the thread projection permanently out of sync with the actual Git branch. `git.renameBranch` runs before `threads.dispatch`, and both are wrapped in a single `Effect.catchCause` that only logs. If the rename succeeds but the metadata dispatch fails (e.g. a transient store error), the worktree is on the generated branch while the thread projection keeps the temporary `t3code/<hash>` branch. Later Git actions use the stale branch name. Consider retrying the metadata update, rolling back the rename on dispatch failure, or otherwise reconciling the two before swallowing the error.
| }), | ||
| ), | ||
| Effect.forkIn(preparationScope), | ||
| ); |
There was a problem hiding this comment.
Branch rename skips mid-flight guard
Medium Severity
Title generation re-checks canReplaceThreadTitle on the current projection before applying, so a user rename mid-generation wins. The new branch rename path captures oldBranch up front, then always runs git.renameBranch and a metadata update with no re-check that the thread branch is still temporary. A branch change during the background LLM call can be overwritten, or a git rename can run against a stale name.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit dddc16b. Configure here.
ApprovabilityVerdict: Needs human review 2 blocking correctness issues found. Multiple unresolved review comments identify race conditions and data consistency issues in the new background branch rename and title generation logic. These substantive bugs (thread projection desync, non-atomic read-then-write patterns) warrant human review before merging. You can customize Macroscope's approvability policy. Learn more. |
|
Hey! I’ve been working on the same title issue in #5176. It handles generation through the durable V2 outbox, avoids overwriting manual renames, and covers web, mobile, and already-created threads. Could we reuse that part here alongside the branch-naming changes? |
The v2 launch path only generated a branch name when no branch was passed, but the web client always passed a temporary t3code/<hash> branch — so the guard never fired and threads kept the hash as their branch, with no later rename pass (unlike v1). The server now owns worktree naming: without an explicit branch, the worktree is provisioned immediately under a server-invented temporary t3code/<hash> name, then renamed to a generated name in a background fork so name generation never delays provisioning or the provider turn. Temporary names from clients that still send them (mobile outbox) are renamed the same way, and stick if generation fails. The web client stops inventing temporary branch names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dddc16b to
7190bf2
Compare
afcb4ab
into
t3code/codex-turn-mapping
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.
Reviewed by Cursor Bugbot for commit 7190bf2. Configure here.
| let branch: string | null; | ||
| if (input.workspaceStrategy.type === "worktree" && requestedBranch === undefined) { | ||
| const uuid = yield* randomUuidV4; | ||
| branch = buildTemporaryWorktreeBranchName(() => uuid.replaceAll("-", "")); |
There was a problem hiding this comment.
Retry orphans prior worktree
Medium Severity
Branchless worktree launches now invent a fresh random t3code/<hash> on every prepareInBackground run. Launch re-schedules while a run is still preparing, and reservePreparation is only in-memory, so a crash or lost-response retry can create a second worktree and orphan the first. Web previously sent a stable client temp name that made that retry collide instead of leak.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 7190bf2. Configure here.
…5309) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>


Stacked on #2829. Complements #5176, which restored title generation — this PR covers the other half of the regression: worktree branch naming.
Problem
On the v2 orchestrator, threads stay on their temporary
t3code/<hash>branch forever.ThreadLaunchServiceonly generated a branch name whenworkspaceStrategy.branch === undefined, but the web client always passed a temporaryt3code/<hash>name — so generation never fired, and unlike v1 there was no later rename pass.Fix
The server now owns worktree naming:
t3code/<8hex>name (randomUuidV4+ sharedbuildTemporaryWorktreeBranchName).git branch -m, and dispatches a:branch-renamemetadata update. Name generation never sits on the critical path in front of worktree creation, the setup script, or the provider turn (matching v1's concurrency, where the rename raced the turn). This also removes the previous awaited-generation path, so branchless launches (scheduled tasks, MCP-driven) no longer block provisioning on an LLM call.branchstays optional in the contract: explicit user-chosen branch names are respected verbatim, and temporary names from clients that still send them (mobile's offline outbox) are renamed server-side the same way.Notes
feature/<slug>convention rather than v1'st3code/<slug>wrapper; happy to align to v1 if the namespace matters downstream.Testing
ThreadLaunchServicetests: server-invented temp names, off-critical-path rename (run reachesstartingwhile generation is gated behind a deferred), explicit-branch passthrough, generation-failure fallback, existing-worktree rename.t3and@t3tools/web.🤖 Generated with Claude Code
Note
Medium Risk
Changes thread launch provisioning and git branch lifecycle on the critical path for new worktrees; failures degrade to temp branch names rather than blocking launch.
Overview
Restores v2 thread launch behavior where worktrees stayed on client-supplied
t3code/<hash>branches and never got LLM-generated names, becauseThreadLaunchServiceused that name verbatim and did not run a rename pass like v1.Branch naming — When no explicit branch is sent, the server provisions immediately under a temporary
t3code/<8hex>name (viabuildTemporaryWorktreeBranchName/randomUuidV4), instead of awaitinggenerateBranchNameor using the removedthread-<id>fallback. After workspace metadata is written, a background fork generates a real branch name,git.renameBranch, and athread.metadata.updatewith command idbranch-rename; failures are logged and the temp name remains. Explicit user branches are unchanged; client-supplied temp names (legacy/mobile) still get renamed the same way, includingexisting_worktreelaunches.Web client —
ChatViewno longer passesbranchinprepareWorktree(removedbuildTemporaryWorktreeBranchName/randomHex); the server owns the initial ref.Tests —
ThreadLaunchService.testharness mocksrenameBranchand adds cases for server naming, off-critical-path rename, explicit branch, generation failure fallback, and existing-worktree rename.Reviewed by Cursor Bugbot for commit 7190bf2. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Restore worktree branch naming to the v2 orchestrator from the client
t3code/<hash>) from the client (ChatView.tsx) into ThreadLaunchService.ts usingbuildTemporaryWorktreeBranchNameandrandomUuidV4.git.renameBranch, then updates thread metadata; if generation or rename fails, the temporary branch is kept without affecting provisioning.thread-<suffix>fallback branch name from the old server path is removed.Macroscope summarized 7190bf2.