From 7949620de703f95086f9b9360b6fd2f7b9a2a51d Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Wed, 23 Sep 2026 16:28:25 +0200 Subject: [PATCH] fix(setup): teach the verify skill's whole-user check about the dispatcher hook flavour Check 8 compared ~/.claude/git-hooks/post-checkout against git-global-post-checkout.sh only. A dispatcher-flavour install symlinks post-checkout to git-hook-dispatcher.sh, so it failed the comparison even when every file matched the framework, and the run then reported per-repo .git/hooks/* as inert although the dispatcher chains to them. #1322 fixed the same blind spot in the update skill. Generated-by: Claude Opus 5 --- docs/mode-economics.md | 6 +-- .../skills/isolated-setup-verify/SKILL.md | 2 +- .../conditional-checks.md | 51 +++++++++++++------ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/docs/mode-economics.md b/docs/mode-economics.md index 12338378d..cfcf9ed20 100644 --- a/docs/mode-economics.md +++ b/docs/mode-economics.md @@ -114,7 +114,7 @@ arithmetic, which is now tested rather than graded. -Measured on (UTC): 2026-09-22. +Measured on (UTC): 2026-09-23. Tokenizer: **tiktoken 0.14.0, `cl100k_base`**. Method: full UTF-8 file, including frontmatter and comments; line endings normalized to LF; @@ -122,7 +122,7 @@ special-token spellings counted as ordinary text. Coverage: **75 of 75 local `skills/*/SKILL.md` files**. External `source.md` redirects and harness symlinks are excluded. -Measurement manifest SHA-256: `85d0fe4f7152f2d8e57b5a92ee1b2f23bf4af93606409da954d15db39e095b40`. +Measurement manifest SHA-256: `81b7316102c485194ddcd519a69ddf6d84e150ada81cced81da97868419e0253`. | Skill file | Measured tokens | Source SHA-256 (first 16 characters) | |---|---:|---| @@ -192,7 +192,7 @@ Measurement manifest SHA-256: `85d0fe4f7152f2d8e57b5a92ee1b2f23bf4af93606409da95 | [setup-isolated-setup-doctor](../skills/setup-isolated-setup-doctor/SKILL.md) | 4,720 | `f24d416c966167f7` | | [setup-isolated-setup-install](../skills/setup-isolated-setup-install/SKILL.md) | 3,879 | `9c7d49c9e7ff947d` | | [setup-isolated-setup-update](../skills/setup-isolated-setup-update/SKILL.md) | 4,017 | `0ff14e482853f519` | -| [setup-isolated-setup-verify](../skills/setup-isolated-setup-verify/SKILL.md) | 4,556 | `5c50d8fd6d5f0df4` | +| [setup-isolated-setup-verify](../skills/setup-isolated-setup-verify/SKILL.md) | 4,562 | `b3f315456ebe2edf` | | [setup-override-upstream](../skills/setup-override-upstream/SKILL.md) | 3,681 | `968f51a19ccd4d10` | | [setup-privacy-llm](../skills/setup-privacy-llm/SKILL.md) | 2,055 | `b89f1f5e421df5cc` | | [setup-shared-config-sync](../skills/setup-shared-config-sync/SKILL.md) | 3,836 | `d8715462e3c23f38` | diff --git a/plugins/magpie-setup/skills/isolated-setup-verify/SKILL.md b/plugins/magpie-setup/skills/isolated-setup-verify/SKILL.md index d40cde19b..ca4e68e7f 100644 --- a/plugins/magpie-setup/skills/isolated-setup-verify/SKILL.md +++ b/plugins/magpie-setup/skills/isolated-setup-verify/SKILL.md @@ -145,7 +145,7 @@ Walk each in order: It reads **project-local** `settings.local.json`, not user-scope — [why](../../../../docs/setup/secure-agent-setup.md#project-root-coverage-in-the-sandbox-allowlists). - **Scope detection.** `git config --global --get core.hooksPath` equal to `$HOME/.claude/git-hooks` means **whole-user** scope, which has its own sub-checks and a reminder that per-repo `.git/hooks/*` are inert host-wide: + **Scope detection.** `git config --global --get core.hooksPath` equal to `$HOME/.claude/git-hooks` means **whole-user** scope, in its simple or dispatcher flavour, which has its own sub-checks and a reminder of whether per-repo `.git/hooks/*` still fire: [`conditional-checks.md`](conditional-checks.md#check-8--whole-user-scope-detection). Unset or pointing elsewhere means per-project scope, the default, fully covered by the two sub-checks above. diff --git a/plugins/magpie-setup/skills/isolated-setup-verify/conditional-checks.md b/plugins/magpie-setup/skills/isolated-setup-verify/conditional-checks.md index 0758bd566..4582bffaa 100644 --- a/plugins/magpie-setup/skills/isolated-setup-verify/conditional-checks.md +++ b/plugins/magpie-setup/skills/isolated-setup-verify/conditional-checks.md @@ -17,24 +17,45 @@ reading this file; read the section for a check whose condition holds. ``` If the output equals `$HOME/.claude/git-hooks` (or its tilde- - resolved form), the operator is in **whole-user** scope: - - - ✓ if `~/.claude/git-hooks/post-checkout` exists, is - executable, and matches the framework's - `tools/agent-isolation/git-global-post-checkout.sh` content. + resolved form), the operator is in **whole-user** scope. + Install Step P.0b offers two flavours of it, and the shape of + `~/.claude/git-hooks/` tells them apart: + + - **Simple flavour** — `post-checkout` is a regular file, a copy + of `tools/agent-isolation/git-global-post-checkout.sh`. + - **Dispatcher flavour** — `post-checkout` (and every other hook + name) is a symlink to `git-hook-dispatcher.sh` in the same + directory, a copy of `tools/agent-isolation/git-hook-dispatcher.sh`. + The dispatcher supersedes the standalone post-checkout script, + so do **not** compare `post-checkout` against + `git-global-post-checkout.sh` here; read the hook-name symlinks + as the installed shape, not as drift. + + Resolve `post-checkout` (`readlink -f`) and check the script it + lands on: + + - ✓ if it exists, is executable, and matches its own framework + source (`git-global-post-checkout.sh` for the simple flavour, + `git-hook-dispatcher.sh` for the dispatcher flavour). - ⚠ if the hook is missing or non-executable — the `core.hooksPath` pointer is set but the hook content is gone. Remediation: - re-run `setup-isolated-setup-install` Step P.3-whole-user, + re-run `setup-isolated-setup-install` Step P.3-whole-user + (simple) or Step P.3b-whole-user (dispatcher), or `setup-isolated-setup-update` to refresh the script copy. - - ⚠ if the hook content drifted from the framework's source-of- - truth — surface the diff, propose `setup-isolated-setup-update`. - - **Loud reminder** (every run, not a ✗): when in whole-user - scope, surface a one-line note that per-repo `.git/hooks/*` - are inert across the host (per [`docs/setup/secure-agent-setup.md` → *Per-project vs whole-user scope*](../../../../docs/setup/secure-agent-setup.md#per-project-vs-whole-user-scope)). - This is informational, not a failure — the operator chose it - deliberately during install. Surface so a future self - debugging "why didn't my pre-commit fire" recognises the - cause. + - ⚠ if the script drifted from its framework source-of-truth — + surface the diff, propose `setup-isolated-setup-update`. + - **Loud reminder** (every run, not a ✗), by flavour: + - *Simple:* surface a one-line note that per-repo + `.git/hooks/*` are inert across the host (per [`docs/setup/secure-agent-setup.md` → *Per-project vs whole-user scope*](../../../../docs/setup/secure-agent-setup.md#per-project-vs-whole-user-scope)). + This is informational, not a failure — the operator chose it + deliberately during install. Surface so a future self + debugging "why didn't my pre-commit fire" recognises the + cause. + - *Dispatcher:* surface instead that per-repo `.git/hooks/*` + still fire, because the dispatcher chains to each repo's own + hook (per [`docs/setup/secure-agent-setup.md` → *Whole-user with the per-repo dispatcher*](../../../../docs/setup/secure-agent-setup.md#whole-user-with-the-per-repo-dispatcher)). + Do not report them as inert; that is the simple flavour's + cost, which this flavour exists to remove. If `core.hooksPath` is unset (or points elsewhere), the operator is in **per-project** scope (the default). No further