Check ID
HKS-01
What went wrong
False negative — check failed but the artifact is actually there
Repository / minimal reproduction
mkdir -p repro/.claude && cd repro
printf '# Repro\n' > CLAUDE.md
cat > .claude/settings.json <<'EOF'
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "echo gate" }] }
],
"PostToolUse": [
{ "matcher": "Edit", "hooks": [{ "type": "command", "command": "echo feedback" }] }
]
}
}
EOF
npx harness-score . # Hooks & Guardrails: 14/14
npx harness-score .claude # Hooks & Guardrails: 0/14
Expected vs. actual
Same settings.json, two scan roots, opposite results.
| Scan root |
Hooks & Guardrails |
repro/ |
14/14 |
repro/.claude/ |
0/14 |
Scanning the .claude directory as the root fails HKS-01 through HKS-05. The config is valid, has PreToolUse (gate) and PostToolUse (feedback), and passes every check when scanned one level up.
Cause is in packages/cli/src/harness/registry.ts:88:
{ toolId: 'claude-code', kind: 'hooks', pathRegex: /(^|\/)\.claude\/settings\.json$/ }
The pattern requires a literal .claude/ segment. When .claude is the scan root, paths are relative to it and the file is just settings.json, which never matches:
NOMATCH settings.json
MATCH .claude/settings.json
MATCH some/nested/path/.claude/settings.json
There is a second-order effect worth noting. readNormalizedHooks (harness/hooks.ts:153) picks whichever collected config declares the most events. With the root file excluded from collection, any unrelated nested .claude/settings.json (a vendored repo, a cached job artifact, a test fixture) becomes the only candidate and wins by default. In my case the reported evidence pointed at a scratch directory buried several levels down while the real config sat at the root, so the failure message named the wrong file rather than reporting nothing found.
A repo-root settings.json would fix the primary case, though it may be worth deciding whether a nested .claude/ inside vendored or cached content should be a scoring candidate at all.
harness-score version
1.5.2 (built from main at clone time)
Check ID
HKS-01
What went wrong
False negative — check failed but the artifact is actually there
Repository / minimal reproduction
Expected vs. actual
Same
settings.json, two scan roots, opposite results.repro/repro/.claude/Scanning the
.claudedirectory as the root fails HKS-01 through HKS-05. The config is valid, hasPreToolUse(gate) andPostToolUse(feedback), and passes every check when scanned one level up.Cause is in
packages/cli/src/harness/registry.ts:88:The pattern requires a literal
.claude/segment. When.claudeis the scan root, paths are relative to it and the file is justsettings.json, which never matches:There is a second-order effect worth noting.
readNormalizedHooks(harness/hooks.ts:153) picks whichever collected config declares the most events. With the root file excluded from collection, any unrelated nested.claude/settings.json(a vendored repo, a cached job artifact, a test fixture) becomes the only candidate and wins by default. In my case the reported evidence pointed at a scratch directory buried several levels down while the real config sat at the root, so the failure message named the wrong file rather than reporting nothing found.A repo-root
settings.jsonwould fix the primary case, though it may be worth deciding whether a nested.claude/inside vendored or cached content should be a scoring candidate at all.harness-score version
1.5.2 (built from
mainat clone time)