[BUG] WorktreeCreate hook does not keep the session transcript at the launch directory (docs say it does)

Open 💬 1 comment Opened Jul 14, 2026 by wshallwshall

Summary

The worktrees docs state:

As of v2.1.198, entering or exiting a worktree also relocates the session transcript to that directory's project storage, the same way /cd does, so /desktop and --resume find the session there afterward. Worktrees created by a WorktreeCreate hook are excluded and keep the transcript at the launch directory.

That exclusion does not happen. A worktree created by a WorktreeCreate hook has its transcript relocated to the worktree's project storage exactly like a default one, leaving the launch directory with zero transcripts — even though the app itself records the worktree as "hookBased": true.

This matters because the transcript's project slug is what scopes a session list. When a session's transcript is re-filed under the worktree's slug, the session disappears from the session list of the directory it was started in. The documented WorktreeCreate exclusion is the only advertised way to opt out of that, so it is load-bearing for anyone running multiple sessions per repo.

Environment

  • Claude Code 2.1.207, native install
  • Windows 11, git 2.x, pwsh 7

Repro

mkdir repo && cd repo
git init -b main . && echo x > a.txt && git add a.txt && git commit -m init

Add a project-scoped WorktreeCreate hook in repo/.claude/settings.json that creates the worktree itself and prints its path:

{
  "hooks": {
    "WorktreeCreate": [
      { "hooks": [ { "type": "command", "command": "bash /abs/path/to/wtcreate.sh" } ] }
    ]
  }
}
# wtcreate.sh — reads {..., "name": "..."} on stdin, prints the created worktree path on stdout
payload=$(cat)
name=$(printf '%s' "$payload" | jq -r .name)
repo=$(printf '%s' "$payload" | jq -r .cwd)
wt="$repo/.claude/worktrees/$name"
git -C "$repo" worktree add -q -b "hookwt-$name" "$wt" >&2
echo "$wt"

Then:

claude -p --worktree wt-hooked "Reply with exactly: OK"

Expected

The transcript stays at the launch directory's project storage — i.e. under the slug for .../repo.

Actual

The transcript is filed under the worktree's slug. The launch directory's slug is created but holds no transcript:

projects/…-repo                                  0 transcript(s)
projects/…-repo--claude-worktrees-wt-hooked      1 transcript(s)   <-- the whole conversation

~/.claude.json shows the app knew it was hook-created:

"activeWorktreeSession": {
  "originalCwd": ".../repo",
  "worktreePath": ".../repo/.claude/worktrees/wt-hooked",
  "sessionId": "…",
  "hookBased": true
}

Variants tested — all three relocate

  1. Plain --worktree (no hook) → transcript under the worktree slug. (Expected; this is the documented behaviour.)
  2. WorktreeCreate hook returning a path inside .claude/worktrees/ → transcript under the worktree slug.
  3. WorktreeCreate hook returning a sibling path outside .claude/worktrees/ (e.g. ../repo-wt) → transcript under the worktree slug.

In all three, the launch directory ended up with zero transcripts.

Notes

ExitWorktree with action: "keep" does move the transcript back to the launch directory — so the relocation is symmetric and reversible. The loss only becomes permanent when a session ends while still inside the worktree. That is a useful escape hatch, but it isn't the documented one.

Either the exclusion is not implemented, or the docs describe an intent that didn't ship. Happy to test a fix.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗