Appearance
date: 2026-06-03 tags: [hooks, claude-code, false-positive] status: active graduated_to:
A workflow-gate hook can't tell a real invocation from a string that looks like one
Symptom — the workflow-gate hook (then suggest-gates.sh, since renamed suggest-skills.sh) fired its "PR raised → find-learnings" and "ci:check passed" nudges during a Bash test command whose JSON fixture merely contained gh pr create / cd repo && gh pr create as data.
Root cause — PostToolUse hooks receive the tool command as a string and match on it. A phrase embedded as an argument (an echo, a grep, a quoted heredoc, a test fixture) is indistinguishable from a real invocation — especially once it sits at a command boundary inside quotes.
Fix — anchor matching to command boundaries: at_boundary() greps (^|[;&|])[[:space:]]*<phrase> in .claude/hooks/suggest-skills.sh, so echo '… gh pr create …' no longer matches. Residual remains (a fixture with a literal && gh pr create still matches), and that's acceptable.
Guard — the at_boundary() anchoring + the validation cases run against the hook. Standing rule: workflow-gate hooks are skippable nudges, never blocking gates — wording them as reminders Claude judges (not exit-code-2 blocks) makes the occasional false fire harmless.