On this page
I went back through 14 days of PreToolUse hook ask events. There were 159, and about 70% were false positives.
An ask is the hook stopping to ask whether to allow something. Ask too often and people go numb to it.
Want AI working in your own job, not just in demos? See the 3-month 1-on-1 program →
Where the false positives came from
Going through them one by one, the sources clustered:
- Text inside heredocs. The command itself was harmless. The heredoc body only mentioned a dangerous command, and whole-string matching caught it.
--helpand--dry-run.gogwith either flag changes nothing, but it got intercepted anyway.- Background tasks.
run_in_backgroundalso triggered the log check. env | grep. Looking up environment variables got blocked.uvxin the install gate. It was treated as an install that needed confirming.
The fix
I changed bash-pretooluse-main.sh and scratch-doc-guard.sh. The core change is one move: strip the heredoc first, then match the command position instead of the whole string.
The rest were handled one at a time. uvx came out of the install gate, gog is excluded when --help or --dry-run is present, run_in_background no longer triggers the log check, and env | grep is allowed through.
How I measured it
Counting them doesn’t require replaying any commands. Ask events are already in the transcript as PreToolUse hook_success attachments, and the stdout is what the hook reported at the time. You can count them directly.
After the fix, I ran the recorded commands through the new hook: asks dropped from 135 to 50, and all 27 cases in a control set passed.
Still not fixed: deny rules
What I fixed was ask. Deny rules still scan the raw command, so a heredoc that merely mentions git reset --hard still gets blocked outright. That’s a known limitation for now.
Even while the weekly draft was being put together, the heredoc false block happened again.
Two side effects
A Stop gate fired in the wrong directory. I ran the benchmark’s claude -p with knowledge-base as the working directory. The vault-lint Stop gate kicked in, forced it to run the archive script, and pushed automatically. The content was kept, not reverted. The guardrail is now written into my dispatch skill.
A missing cwd exclusion pointed at the wrong project. The stakeholder-context hook had no cwd exclusion. A contact with the same name inside GMAT-skills got pointed at a different client project. I added a third column for cwd exclusion, and that stopped.
What these share is what I wrote in my weekly draft: a guard hook should match the command position, not the whole string. After it ships, count the hook events in the transcript instead of replaying commands, and check before and after with a control set.
I wrote about other guard-hook design earlier, in how to write a gate that actually blocks.