Desktop app: worktree session is cut from a cached origin/main (~10 min refreshSourceRef window), not re-resolved at creation
What's Wrong?
Starting a worktree session from the Desktop app cut the worktree from the local main ref, which was 2 commits behind origin/main. origin/HEAD was present and resolvable and origin/main was already at the correct commit — the worktree was nevertheless based on the stale local branch.
The app then fetched and fast-forwarded local main ~10 seconds after creating the worktree, which is too late for that worktree. So the first worktree created after origin/main advances starts on stale code; the next one is correct.
This is the same symptom as #93703, but that report scopes it to sessions rooted inside a git submodule and states the app is correct when rooted at the superproject. That generalisation does not hold: this repo has no submodules and the session is rooted at the superproject, and the base was still stale. The mechanism also differs — here the fetch does run, but after the worktree is cut, and the creation-time fetch is skipped as throttled.
Expected
The worktree branches from the repository's default branch on the remote (origin/main), per the documented fresh default:
"fresh"(default): branch from the repository's default branch on the remote, usuallymain, so the worktree starts from a clean tree matching the remote. — https://code.claude.com/docs/en/worktrees.md#choose-the-base-branch
Timeline (all times +0800)
09:57:32 PR merged on GitHub → origin/main should be be86ca9
09:58:00 fetch → refs/remotes/origin/main = be86ca9 ✅ fresh
10:00:28 Created worktree "redesign-..." ← CUT HERE
local main is still 849d8ce ❌ stale
10:00:33 [createWorktree] fetching origin (full, ...)
10:00:36 full origin fetch completed in 2982ms
10:00:38 [createWorktree] fast-forwarded local main 849d8ce0e2..be86ca98a4 ← 10s too late
10:28:11 Created worktree "peaceful-..." → based on be86ca9 ✅ correct now
Error Messages/Logs
1. The app log (%LOCALAPPDATA%\Claude-3p\logs\main.log)
10:00:28 [info] Created worktree "redesign-task-creation-page-1f6983" at F:\GitHub\Doitsh_Android\.claude\worktrees\redesign-task-creation-page-1f6983 (prep=539ms fetch=0ms/throttled add=194ms stage1=308ms git_spawns=11)
10:00:33 [info] [createWorktree] fetching origin (full, shaped by the fetch posture) { baseRepo: 'F:\\GitHub\\Doitsh_Android' }
10:00:36 [info] [createWorktree] full origin fetch completed in 2982ms { baseRepo: 'F:\\GitHub\\Doitsh_Android' }
10:00:38 [info] [createWorktree] fast-forwarded local main 849d8ce0e2..be86ca98a4 { baseRepo: 'F:\\GitHub\\Doitsh_Android', worktreePath: '' }
Creation (10:00:28) happens before the fetch (10:00:33) and the fast-forward (10:00:38). The fetch at creation time was skipped as throttled (fetch=0ms/throttled).
2. The worktree's HEAD reflog proves the base
.git/worktrees/redesign-task-creation-page-1f6983/logs/HEAD, first entry:
849d8ce0e2b550c9b50da7dd1138f5e8e78ff71e be86ca98a41ecc9fdc83c9814037a074f0145126 ...
Parent commit 849d8ce = the local main at creation time, not origin/main (be86ca9).
3. origin/HEAD was resolvable — so this is not the documented fallback
$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/main
$ git rev-parse refs/remotes/origin/HEAD
be86ca98a41ecc9fdc83c9814037a074f0145126
The docs say a worktree falls back to local HEAD only "if no remote is configured, or origin/HEAD isn't cached locally and can't be fetched". Neither applies: the remote is configured and origin/HEAD resolves to the correct commit. Yet the base was still the stale local branch.
4. The next worktree was correct
A worktree created at 10:28:11 has be86ca9 as its base — because the 10:00:38 fast-forward had repaired local main by then. This confirms the base follows the local ref.
5. The app had already resolved the correct remote — and still used the local ref
The Desktop app's own worktree registry (%LOCALAPPDATA%\Claude-3p\git-worktrees.json) records, for this very worktree:
"redesign-task-creation-page-1f6983": {
"baseRepo": "F:\\GitHub\\Doitsh_Android",
"sourceBranch": "main",
"originTopLevel": "F:\\GitHub\\Doitsh_Android",
"originRemote": {
"host": "github.com",
"repo": "CakewthinSUm/Doitsh_Android",
"ident": "github.com/CakewthinSUm/Doitsh_Android.git"
}
}
Remote host, repo, and the superproject root are all identified correctly — so this is not a failure to find the remote. The base was still taken from the local branch despite the remote being known.
Root cause (as observed)
Desktop resolves the worktree base to the local default branch, and fast-forwards that local branch after creating the worktree. When origin/main has advanced since the last local fast-forward, the next worktree starts on stale code.
Two contributing factors:
- Base is the local ref, not the remote ref. Even with a fresh, resolvable
origin/HEAD, the cut used localmain. - Fetch is throttled at creation time. The log shows
fetch=0ms/throttled; a later line states the window explicitly:
````
10:28:16 [info] [createWorktree] skipping full origin fetch — Desktop already fetched this repository within the last 30 min this app session
Documentation discrepancy
The documented fresh behaviour does not match what the Desktop app does:
| Documented (worktrees.md) | Observed in the Desktop app |
|---|---|
| Fetch when not fetched in the last 24 hours | 30-minute throttle window, per the log |
| Fetch capped at five seconds | 2982ms full fetch, run after creation |
| Fall back to local HEAD only if origin/HEAD is unresolvable | Local ref used even though origin/HEAD resolves correctly |
worktree.baseRef is documented as "Any file" scope with default "fresh", but nothing states which creation paths read it. Setting it to "fresh" cannot change anything (it is already the default), and there is no documented key that governs the Desktop path's base. https://code.claude.com/docs/en/desktop.md says nothing about the base branch or about fetching at session start.
Steps to Reproduce
- Have a plain git repo (no submodules) with
originon github.com, checked out onmain. - Merge a commit to
origin/mainso it advances beyond localmain. git fetch(soorigin/mainandorigin/HEADare current) — leave localmainuntouched.- Start a worktree session from the Desktop app.
- In the new worktree:
git reflog show HEAD | tail -1— the parent commit is the stale localmain, notorigin/main. - Check the log for the ordering:
Created worktreeappears beforefast-forwarded local main.
Note the fetch is throttled to a 30-minute window, so this reproduces most readily when the repo was fetched recently in the same app session.
Impact
Silent. The session reports sourceBranch: "main" and looks normal, but starts on old code. If the missing commits are schema/migration or API changes, the session's work can be built against an outdated base without any warning. Repos where main moves frequently hit this on the first session after each merge.
Suggested fix
For the fresh base, resolve to origin/<default-branch> (via origin/HEAD) rather than the local branch, and run the fetch before cutting the worktree. At minimum, warn when the chosen base is behind origin/HEAD — the silent selection of a stale base is the part that makes this hard to notice.
Claude Code Version
Bundled Claude Code CLI 2.1.295 (Desktop Claude-3p build; the app's own version string is not exposed in this build). Newer than the 2.1.260 in #93703, so this is not an already-fixed build.
Is this a regression?
Unknown — first noticed on this build. The related #93703 reports a working superproject case on 2.1.260, which this contradicts.
Operating System
Windows 11 Pro for Workstations (10.0.26300), git version 2.55.0.windows.1
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗