Claude Code agent harness writes broken per-worktree core.hooksPath
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet. Related but distinct: #27474 (writes to shared
$GIT_COMMON_DIR/config) and #30251 (duplicate of #27474, closed). This report covers a different file and a different failure mode — see "Relationship to #27474" below. - [x] This is a single bug report.
- [x] I am using the latest version of Claude Code.
What's Wrong?
When Claude Code creates a worktree, the harness writes the following into the per-worktree config file ($GIT_COMMON_DIR/worktrees/<name>/config.worktree):
[core]
longpaths = true
hooksPath = D:\\projects\\invoiceflow-AI\\frontend\\.husky\\_
Two issues with the hooksPath value:
- It is an absolute path pointing at the main repo's hooks directory, not the worktree's. The worktree lives at
D:\projects\invoiceflow-AI\.claude\worktrees\awesome-thompson-7c0a94\, buthooksPathpoints back atD:\projects\invoiceflow-AI\frontend\.husky\_. - It is written even when the shared
$GIT_COMMON_DIR/configalready specifies a correct relativecore.hooksPath(in our casefrontend/.husky/_, set by Husky'spreparescript). The per-worktree value silently overrides the shared one becauseextensions.worktreeConfig = trueis enabled by the same harness, and worktree-scoped config wins.
Net effect for any project using Husky (or any tool that installs a repo-relative core.hooksPath): hooks become dormant inside Claude-created worktrees. Git follows the absolute path, and one of two things happens:
- The absolute path doesn't exist in the worktree (most common, since
npm installtypically only runs in the worktree's ownfrontend/and the main checkout'sfrontend/.husky/_may not exist or be stale). Git silently runs zero hooks —core.hooksPathdoes not fall back to anything when its target is missing. - The absolute path does exist in the main checkout. Git runs hooks from the main repo's directory while operating on the worktree's index. Binary resolution (
npx,lint-staged, project-local Node version) drifts to wherever the main checkout'snode_modulesresolves, not the worktree's. Hooks "fire" but against the wrong context.
Either way, the protection chain the project author installed is bypassed for every PR generated from a Claude-created worktree, with no warning surfaced to the user.
What Should Happen?
One of the following:
- Preferred: Do not write
core.hooksPathinto the per-worktree config at all. If the shared.git/confighas a relativecore.hooksPath(as Husky and most repo-hook tools install it), git already resolves it correctly per-worktree by interpreting the path relative to the worktree root — no override is needed. - Acceptable: If the harness must write a path (e.g., to handle bare-checkout edge cases), write a worktree-relative value or an absolute path resolved against the actual worktree root, not the main repo's root.
Error Messages/Logs
None. The failure is silent.
Steps to Reproduce
- Start with any repo whose shared
.git/configalready hascore.hooksPathset to a repo-relative path (typical of Husky 9+:frontend/.husky/_or.husky/_). - From a Claude Code session, create a worktree (e.g., via the harness's automatic worktree-on-task behavior,
claude --worktree, or theEnterWorktreetool). - Inspect the per-worktree config:
````
cat $GIT_COMMON_DIR/worktrees/<name>/config.worktree
- Observe:
core.hooksPathis set to an absolute path.- The absolute path is derived from the main repo's directory, not the worktree's.
- From the worktree, run
git config core.hooksPathandgit rev-parse --git-path hooksto confirm the worktree-scoped value overrides the shared one. - Attempt a commit from the worktree. Husky's
pre-commitdoes not fire (or fires from the main checkout's context, depending on whether the mainfrontend/.husky/_directory happens to exist on disk).
Evidence
Run from inside a Claude-created worktree at D:\projects\invoiceflow-AI\.claude\worktrees\awesome-thompson-7c0a94\:
$ git config --worktree --list
core.longpaths=true
core.hookspath=D:\projects\invoiceflow-AI\frontend\.husky\_
$ git rev-parse --git-path hooks
D:\projects\invoiceflow-AI\frontend\.husky\_
$ test -d D:/projects/invoiceflow-AI/.claude/worktrees/awesome-thompson-7c0a94/frontend/.husky/_ && echo EXISTS || echo MISSING
MISSING
$ test -d D:/projects/invoiceflow-AI/frontend/.husky/_ && echo EXISTS || echo MISSING
EXISTS
The hooks resolve into the main checkout's husky directory while the worktree's own frontend/.husky/_ does not exist — the dormancy/cross-context behavior.
The bug is systematic across worktrees on the same machine:
$ for w in affectionate-cray-18805d awesome-thompson-7c0a94 cool-solomon-082b06 cranky-mirzakhani-663c1b distracted-hamilton-3bbfec; do
echo "--- $w ---"
cat $GIT_COMMON_DIR/worktrees/$w/config.worktree
done
--- affectionate-cray-18805d ---
[core]
longpaths = true
hooksPath = D:\\projects\\invoiceflow-AI\\frontend\\.husky\\_
--- awesome-thompson-7c0a94 ---
[core]
longpaths = true
hooksPath = D:\\projects\\invoiceflow-AI\\frontend\\.husky\\_
--- cool-solomon-082b06 ---
[core]
longpaths = true
hooksPath = D:\\projects\\invoiceflow-AI\\frontend\\.husky\\_
--- cranky-mirzakhani-663c1b ---
[core]
longpaths = true
hooksPath = D:\\projects\\invoiceflow-AI\\frontend\\.husky\\_
--- distracted-hamilton-3bbfec ---
[core]
longpaths = true
hooksPath = D:\\projects\\invoiceflow-AI\\frontend\\.husky\\_
Shared .git/config (untouched, correct):
[core]
repositoryformatversion = 0
...
hooksPath = frontend/.husky/_
[extensions]
worktreeConfig = true
Relationship to #27474 / #30251
These earlier reports flagged that the harness wrote core.hooksPath into the shared $GIT_COMMON_DIR/config, which corrupted the main checkout. The harness now correctly scopes the write to per-worktree config.worktree — addressing the #27474 concern — but the value it writes is still derived from the main repo's directory rather than the worktree's. The end-user impact has moved from "main checkout broken" to "every Claude worktree silently has dormant or cross-context hooks." It is the same root cause (the path is computed from the wrong base directory) surfacing in a new file.
Impact
For any project using Husky, lefthook, pre-commit, or any repo-hook tool that installs a repo-relative core.hooksPath:
pre-commit,commit-msg,pre-push,post-merge, etc. do not run inside Claude-created worktrees.- Whatever those hooks were meant to enforce (typecheck, lint, secret scanning, schema-drift checks, build gates, conventional-commits, signing) is silently bypassed for every commit Claude makes.
- No warning is surfaced to the user. The first signal is typically CI failing on something local hooks would have caught — or in worse cases, a bug landing because the main checkout's hooks pass against stale code in a different directory.
Suggested Fix
The simplest and least-invasive fix:
-# Currently in $GIT_COMMON_DIR/worktrees/<name>/config.worktree
-[core]
- longpaths = true
- hooksPath = <absolute path to main repo's hooks dir>
+# Proposed:
+[core]
+ longpaths = true
+# (omit core.hooksPath entirely — let it resolve from the shared config)
If the harness has a use case that genuinely requires per-worktree core.hooksPath, derive the absolute path from the worktree's own root (e.g., the path passed to git worktree add), not the main checkout. The longpaths = true setting is independently fine and can remain.
Claude Model
Not sure / Multiple models (harness bug, model-independent).
Is this a regression?
No, this never worked. The failure mode shifted from #27474 (writes corrupted the shared .git/config) to its current shape (writes a bad value into per-worktree config.worktree), but hooks have never resolved correctly inside Claude-created worktrees for repos using a repo-relative core.hooksPath.
Last Working Version
N/A — never worked.
Claude Code Version
2.1.131 (Claude Code)
Platform
Anthropic API
Operating System
Windows (build 10.0.26200, Windows 11 Pro).
Terminal/Shell
PowerShell (PS 5.1; Claude Code's Bash tool also reproduces from /usr/bin/bash via git-bash).
Additional Information
Reproduction repo layout (for context):
- Main checkout:
D:\projects\invoiceflow-AI\ - Husky installed at:
frontend/.husky/withpreparescriptcd .. && husky frontend/.husky(per Husky 9's subdirectory pattern: https://typicode.github.io/husky/how-to.html) - Shared
core.hooksPath(correct):frontend/.husky/_ - Worktrees:
D:\projects\invoiceflow-AI\.claude\worktrees\<name>\ - Git version: 2.51.0.windows.1
- Node version: v22.19.0
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗