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
worktree.baseRef: "fresh". A WorktreeCreate hook relocates worktrees out of .claude/worktrees/ to ~/code/.worktrees/<repo>/<name>/, so the session cwd becomes that path. No branch renames involved.
Summary
Two related failures in ExitWorktree action="remove" when the session's working directory is inside the worktree being removed:
(100% reproducible) Orphaned worktree despite a success message. The first remove is refused (Could not verify worktree state … Refusing to remove without explicit confirmation); after re-invoking with discard_changes: true it returns Exited and removed worktree at …, but the worktree directory, its .git/worktrees/<name>/admin entry, and the worktree-<name>branch all remain. git worktree prune won't reap it (the dir is still present). This differs from EnterWorktree (name mode) writes core.hooksPath into the shared repo config; ExitWorktree doesn't restore it #67196's hypothesis that the leftover branch is caused by a mid-session git branch -m rename — here no rename is involved; a vanilla create → work → commit → push → exit cycle leaks.
(Intermittent, severe) Parent-repo core.bare corruption. In one occurrence the same teardown left core.bare = true in the primary repo's shared .git/config. That breaks the whole repo: git status / git worktree / commit all fail with fatal: this operation must be run in a work tree, and git's "refusing to fetch into a checked-out branch" safety rail is disabled — so a later git fetch origin main:main advanced the ref while leaving the working tree frozen, surfacing in the next session as a phantom "staged revert" of everything that had changed since. It did not recur on clean single cycles; it appears tied to the messy refuse → retry path from symptom 1.
Steps to reproduce (the reliably-reproducible orphan)
EnterWorktree (name mode) → session cwd becomes the worktree root.
Without cd-ing out, call ExitWorktree { action: "remove" } → refused: Could not verify worktree state … Refusing to remove without explicit confirmation.
ExitWorktree { action: "remove", discard_changes: true } → returns Exited and removed worktree at ….
From the primary checkout, observe the leak:
git worktree list still lists the worktree
the worktree directory still exists on disk
git branch --list 'worktree-<name>' is still present
Observed 3× in one session — every time the session cwd was inside the worktree at removal.
What I verified about the core.bare variant
Plain git worktree remove --force <wt> and rm -rf <wt> && git worktree prune run from inside a worktree do not flip core.bare, so the mutation lives in ExitWorktree's own removal path, not in git. The WorktreeCreate hook does a plain git worktree add and never touches core.bare, and there is no custom remove hook.
Expected behavior
ExitWorktree action="remove" should either fully remove the worktree (directory + .git/worktrees/<name> admin entry + branch) or report failure — never (a) claim success while leaving the worktree registered, or (b) mutate the parent repo's core.bare (or any other shared .git/config key).
Workaround
cd to the primary checkout before calling ExitWorktree.
After teardown, verify with git worktree list and git config core.bare (must be false), and clean up any orphan manually.
Environment
worktree.baseRef: "fresh". AWorktreeCreatehook relocates worktrees out of.claude/worktrees/to~/code/.worktrees/<repo>/<name>/, so the session cwd becomes that path. No branch renames involved.Summary
Two related failures in
ExitWorktree action="remove"when the session's working directory is inside the worktree being removed:(100% reproducible) Orphaned worktree despite a success message. The first
removeis refused (Could not verify worktree state … Refusing to remove without explicit confirmation); after re-invoking withdiscard_changes: trueit returnsExited and removed worktree at …, but the worktree directory, its.git/worktrees/<name>/admin entry, and theworktree-<name>branch all remain.git worktree prunewon't reap it (the dir is still present). This differs from EnterWorktree (name mode) writes core.hooksPath into the shared repo config; ExitWorktree doesn't restore it #67196's hypothesis that the leftover branch is caused by a mid-sessiongit branch -mrename — here no rename is involved; a vanilla create → work → commit → push → exit cycle leaks.(Intermittent, severe) Parent-repo
core.barecorruption. In one occurrence the same teardown leftcore.bare = truein the primary repo's shared.git/config. That breaks the whole repo:git status/git worktree/ commit all fail withfatal: this operation must be run in a work tree, and git's "refusing to fetch into a checked-out branch" safety rail is disabled — so a latergit fetch origin main:mainadvanced the ref while leaving the working tree frozen, surfacing in the next session as a phantom "staged revert" of everything that had changed since. It did not recur on clean single cycles; it appears tied to the messy refuse → retry path from symptom 1.Steps to reproduce (the reliably-reproducible orphan)
EnterWorktree(name mode) → session cwd becomes the worktree root.Without
cd-ing out, callExitWorktree { action: "remove" }→ refused:Could not verify worktree state … Refusing to remove without explicit confirmation.ExitWorktree { action: "remove", discard_changes: true }→ returnsExited and removed worktree at ….From the primary checkout, observe the leak:
git worktree liststill lists the worktreegit branch --list 'worktree-<name>'is still presentgit worktree pruneis a no-op (dir present)Cleanup requires a manual
git worktree remove --force <path>+git branch -D worktree-<name>.Observed 3× in one session — every time the session cwd was inside the worktree at removal.
What I verified about the
core.barevariantPlain
git worktree remove --force <wt>andrm -rf <wt> && git worktree prunerun from inside a worktree do not flipcore.bare, so the mutation lives inExitWorktree's own removal path, not in git. TheWorktreeCreatehook does a plaingit worktree addand never touchescore.bare, and there is no custom remove hook.Expected behavior
ExitWorktree action="remove"should either fully remove the worktree (directory +.git/worktrees/<name>admin entry + branch) or report failure — never (a) claim success while leaving the worktree registered, or (b) mutate the parent repo'score.bare(or any other shared.git/configkey).Workaround
cdto the primary checkout before callingExitWorktree.git worktree listandgit config core.bare(must befalse), and clean up any orphan manually.Related issues
core.hooksPathinto the shared repo config; ExitWorktree doesn't restore it, and notes a branch left behind after a rename). This report adds two things: the orphan happens without any rename, and the shared-config mutation can also hitcore.bare, not justcore.hooksPath.