Claude Code agent harness writes broken per-worktree core.hooksPath

Resolved 💬 3 comments Opened May 19, 2026 by jeez12 Closed Jun 18, 2026

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:

  1. 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\, but hooksPath points back at D:\projects\invoiceflow-AI\frontend\.husky\_.
  2. It is written even when the shared $GIT_COMMON_DIR/config already specifies a correct relative core.hooksPath (in our case frontend/.husky/_, set by Husky's prepare script). The per-worktree value silently overrides the shared one because extensions.worktreeConfig = true is 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 install typically only runs in the worktree's own frontend/ and the main checkout's frontend/.husky/_ may not exist or be stale). Git silently runs zero hooks — core.hooksPath does 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's node_modules resolves, 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.hooksPath into the per-worktree config at all. If the shared .git/config has a relative core.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

  1. Start with any repo whose shared .git/config already has core.hooksPath set to a repo-relative path (typical of Husky 9+: frontend/.husky/_ or .husky/_).
  2. From a Claude Code session, create a worktree (e.g., via the harness's automatic worktree-on-task behavior, claude --worktree, or the EnterWorktree tool).
  3. Inspect the per-worktree config:

``
cat $GIT_COMMON_DIR/worktrees/<name>/config.worktree
``

  1. Observe:
  • core.hooksPath is set to an absolute path.
  • The absolute path is derived from the main repo's directory, not the worktree's.
  1. From the worktree, run git config core.hooksPath and git rev-parse --git-path hooks to confirm the worktree-scoped value overrides the shared one.
  2. Attempt a commit from the worktree. Husky's pre-commit does not fire (or fires from the main checkout's context, depending on whether the main frontend/.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/ with prepare script cd .. && 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

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗