From 8fa8e00086b586dafdbd64bd383e2a20ab2b7824 Mon Sep 17 00:00:00 2001 From: ProtocolWarden <32967198+ProtocolWarden@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:39:25 -0400 Subject: [PATCH] fix(hooks): resolve workspace root from the main clone, not the worktree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `git rev-parse --show-toplevel` returns the WORKTREE root. In a `git worktree` checkout that is /.claude/worktrees/, so /.. was the worktrees dir — not the workspace holding the sibling repos. Two things broke as a result, both only from a worktree: * Boundary-artifact discovery globbed a directory containing nothing but other worktrees, so every worktree push failed closed on "missing REPOGRAPH_BOUNDARY_ARTIFACT_FILE" before the audit ever ran. * The custodian-multi fallbacks all missed: a worktree has no .venv of its own, and $workspace_root/Custodian/.venv/ pointed inside .claude/worktrees/. With no PATH entry the hook died on "custodian-multi not found" — the state of the WSL fleet box, where /.venv/bin/custodian-multi is the only custodian present. --git-common-dir always resolves to the ORIGINAL clone's .git: relative to cwd for a plain checkout (".git" at the top level, "../.git" from a subdirectory), absolute from a worktree. dirname + cd normalizes all three, since cd interprets a relative path against cwd — which is exactly what git means by it. Avoids --path-format=absolute so the hook keeps working below git 2.31. Added $main_repo_root/{.venv,.warehouse-venv}/bin/custodian-multi to the binary candidates; for a plain checkout main_repo_root == repo_root and they are harmless duplicates. Left the `command -v` PATH-first ordering alone — it changes nothing in either environment today (Windows has no repo venv, so PATH is the only candidate; WSL has no PATH entry, so the venv already wins). The audit target stays "$repo_root": we audit the content being pushed, which is the worktree. Verified from all three repo shapes — worktree, plain clone at top level (how git invokes hooks), and plain clone from a subdirectory — each resolving to the same main_repo_root/workspace_root and finding the artifact. workspace_root for a plain clone is byte-identical to before, so non-worktree pushes are unaffected. End-to-end run of the hook from a worktree with REPOGRAPH_BOUNDARY_ARTIFACT_FILE unset now reaches the audit instead of failing closed. Co-Authored-By: Claude Opus 5 --- .hooks/pre-push | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.hooks/pre-push b/.hooks/pre-push index 2b21a167a..5ae361d78 100755 --- a/.hooks/pre-push +++ b/.hooks/pre-push @@ -3,7 +3,20 @@ set -e repo_root="$(git rev-parse --show-toplevel)" -workspace_root="$(cd "$repo_root/.." && pwd)" + +# --show-toplevel is the WORKTREE root. In a `git worktree` checkout that is +# /.claude/worktrees/, so /.. is the worktrees dir — NOT +# the workspace holding the sibling repos. Deriving workspace_root from it made +# artifact discovery glob an empty directory, so every worktree push failed +# closed on "missing REPOGRAPH_BOUNDARY_ARTIFACT_FILE", and the Custodian venv +# fallback below looked under .claude/worktrees/Custodian/. +# +# --git-common-dir always resolves to the ORIGINAL clone's .git: relative to cwd +# for a plain checkout (".git" at the top level, "../.git" from a subdirectory), +# absolute from a worktree. dirname + cd normalizes all three — cd interprets a +# relative path against cwd, which is exactly what git means by it. +main_repo_root="$(cd "$(dirname "$(git rev-parse --git-common-dir)")" && pwd)" +workspace_root="$(cd "$main_repo_root/.." && pwd)" if [ -z "${REPOGRAPH_BOUNDARY_ARTIFACT_FILE:-}" ]; then shopt -s nullglob @@ -30,10 +43,15 @@ custodian_multi="" if command -v custodian-multi >/dev/null 2>&1; then custodian_multi="$(command -v custodian-multi)" fi +# A worktree has no venv of its own — the interpreter lives in the original +# clone, so the main_repo_root candidates are what actually resolve there. For a +# plain checkout main_repo_root == repo_root and they are harmless duplicates. for candidate in \ "$custodian_multi" \ "$repo_root/.venv/bin/custodian-multi" \ "$repo_root/.warehouse-venv/bin/custodian-multi" \ + "$main_repo_root/.venv/bin/custodian-multi" \ + "$main_repo_root/.warehouse-venv/bin/custodian-multi" \ "$workspace_root/Custodian/.venv/bin/custodian-multi" do if [ -x "$candidate" ]; then