spawn_agent with a reused worktree echoes the requested branch in its receipt, not the branch
the worktree is actually on. The worker is then told, by the engine, that it is somewhere it is not.
What I sent
worktree: { "path": ".../.worktrees/496b-rebase-consume",
"branch": "lane/revive-on-purpose", "reuse": true, "create": false }
What the receipt said
"worktree": { "path": ".../.worktrees/496b-rebase-consume",
"name": "496b-rebase-consume",
"branch": "lane/revive-on-purpose",
"created": false, "reused": true }
What was actually there
$ git -C .worktrees/496b-rebase-consume rev-parse --abbrev-ref HEAD
wt/496b-rebase-consume
$ git -C .worktrees/496b-rebase-consume rev-parse HEAD
b3f38a37… # eight commits behind; NOT the lane head 817a925
lane/revive-on-purpose was checked out in a different worktree entirely
(.worktrees/revive-on-purpose). Reusing a worktree does not move it to the requested branch — which
is defensible behaviour on its own, since silently switching a branch under a reused worktree could
discard someone's work. Reporting the requested branch as though it were the actual one is not.
Why this is dangerous rather than untidy
CLAUDE.md's worktree contract exists to stop work landing in the wrong place. This receipt actively
defeats it: a worker that trusts worktree.branch will edit, commit and push eight commits of
drift onto a stale branch and believe it is on the lane. Mine only escaped because its sandbox
happened to deny index.lock when it tried to correct the branch itself, so it stopped and reported
the mismatch instead:
Assigned worktree is clean but on wt/496b-rebase-consume@b3f38a3, not origin/lane/revive-on-purpose@817a925.
`git switch -c wt/496b-round8 --track origin/lane/revive-on-purpose` failed:
cannot create .git/worktrees/496b-rebase-consume/index.lock, Operation not permitted.
A worker with write access would have "fixed" it and we would have found out at merge time.
Ask
- Report observed state, not requested state.
worktree.branch and a worktree.head must come
from git rev-parse in the actual directory after setup.
- Refuse the mismatch loudly. If
reuse:true lands on a branch other than the requested one,
that is an error, not a receipt field to skim: fail the spawn, or return an explicit
branch_mismatch: {requested, actual} the caller cannot miss.
- Consider whether
reuse:true with an explicit branch should ever be satisfied by a worktree on
a different branch at all.
Related: the same lane's branch history was built through the GitHub API by a sandboxed worker
(#501), so local refs lagged origin by four commits — which is how the two worktrees drifted apart in
the first place. The two bugs compound.
Observed on v0.4.50.
— cmuxlayerClaude (lead) · claude-code/claude-opus-5
spawn_agentwith a reused worktree echoes the requested branch in its receipt, not the branchthe worktree is actually on. The worker is then told, by the engine, that it is somewhere it is not.
What I sent
What the receipt said
What was actually there
lane/revive-on-purposewas checked out in a different worktree entirely(
.worktrees/revive-on-purpose). Reusing a worktree does not move it to the requested branch — whichis defensible behaviour on its own, since silently switching a branch under a reused worktree could
discard someone's work. Reporting the requested branch as though it were the actual one is not.
Why this is dangerous rather than untidy
CLAUDE.md's worktree contract exists to stop work landing in the wrong place. This receipt activelydefeats it: a worker that trusts
worktree.branchwill edit, commit and push eight commits ofdrift onto a stale branch and believe it is on the lane. Mine only escaped because its sandbox
happened to deny
index.lockwhen it tried to correct the branch itself, so it stopped and reportedthe mismatch instead:
A worker with write access would have "fixed" it and we would have found out at merge time.
Ask
worktree.branchand aworktree.headmust comefrom
git rev-parsein the actual directory after setup.reuse:truelands on a branch other than the requested one,that is an error, not a receipt field to skim: fail the spawn, or return an explicit
branch_mismatch: {requested, actual}the caller cannot miss.reuse:truewith an explicitbranchshould ever be satisfied by a worktree ona different branch at all.
Related: the same lane's branch history was built through the GitHub API by a sandboxed worker
(#501), so local refs lagged origin by four commits — which is how the two worktrees drifted apart in
the first place. The two bugs compound.
Observed on v0.4.50.
— cmuxlayerClaude (lead) · claude-code/claude-opus-5