Claude Code hooks that run on every turn feel like enforcement. Mine were not. One watched 574 turns for the exact failure it was built to stop, blocked zero of them, and I cited it to myself as proof the failure was under control.
None of the four instruments below were broken in an obvious way. Each ran, wrote a ledger, and produced a number I could point at. All four measured their own machinery rather than the thing they named. The hook system this post picks apart lives in primeline-ai/evolving-lite.
The Claude Code hooks that fired 574 times and blocked nothing
The gate is a Stop-event hook. Its job is to catch a completion claim ("fixed", "verified", "tests pass") arriving with no evidence in the same turn. I wrote it because I kept doing exactly that. See why "done" is not done for the discipline it was meant to enforce.
Three findings from the audit, in ascending order of how much they hurt.
It is not configured to block
Blocking needs two conditions: an autonomous-session lease, and an environment variable enabling enforcement. Nothing in my live setup sets that variable, and I verified rather than assumed: zero occurrences in the settings file. Across 574 recorded events, zero blocks. An observer wearing a gate's name.
The evidence it looks for never arrives
The gate scans for three markers tagging a trigger, an effect, and a downstream consumer. Those markers appeared on 0 of 574 events. Not a low rate. Zero. It has never evaluated an actual proof, because its input does not exist.

Turning enforcement on may not help
It would have flagged 0 of 35 hand-reviewed turns, which bounds the true rate under roughly 8 percent, not at "never." Those turns were written by the person who knows what the gate looks for, so the sample is biased toward zero by construction. I sent the decision to fugu --ultra and grok-build, and both called it under-determined.
Can Claude Code hooks check whether a claim is true?
No. Claude Code hooks can only check the shape of a claim, and that distinction is the one that generalizes past my setup.
When the gate does evaluate evidence it verifies two things: that a cited file path exists on disk, and that the text carries a concrete token such as a path, an exit code, a test result or a count.
Fabricated but well-formed evidence passes. A confident sentence with a real path and a plausible exit code satisfies every condition the hook imposes.
Everything I built measured whether a claim carried a token. Nothing measured whether the claim was true. In ten weeks of instrumentation I never once measured the second property. The rate at which my confident claims are actually false is still unknown.
The detector that rotted without telling me
Silence that looked like success
A separate detector watched for a wording pattern. It logged 152 matches, then went silent for 22 days while four real instances went past it. Its phrase list was written in May, and by July my vocabulary had moved on. Nothing alerted me, because a detector that stops matching produces the same output as a problem that stopped happening.
Precision failed the other way too. One match fired on a phrase that is simply "try again," which is not the behavior at all. Related: self-correcting workflows with PreToolUse hooks.
The opposite failure, same family
A monitor elsewhere fired 695 times, produced 3 findings, and was ignored every time. Both of these are Claude Code hooks that ran perfectly and taught me nothing, and both patterns are visible in primeline-ai/evolving-lite.
- -Fires on a large share of turns
- -3 real findings across its lifetime
- -You build a filter reflex within a day
- -Failure mode: you stop reading it
- +152 matches, then nothing
- +4 real instances passed through unflagged
- +Phrase list aged out, no alert
- +Failure mode: you read silence as health
This lives in primeline-ai/evolving-lite - the self-evolving Claude Code plugin. Free, MIT, no build step.
The score that could never say no
The worst one was not a hook. It was a scoring loop, and it is the failure I would most want other people to check for.
A fitness score rated delegation decisions and fed the rating into the routing threshold. The negative branch read its task type from a narrower source than the positive branch, so a bad rating was structurally unreachable.
The sibling defect was worse, because the field name lied. A modifier documented as reading a "success rate" actually read a field recording whether a task was delegated, not whether delegating worked. Delegating dropped the threshold, which made delegating more likely, which fed the counter that dropped the threshold. A positive feedback loop wearing a fitness signal's name. I deleted it rather than repairing it.
If you have any self-tuning loop driven by Claude Code hooks, the check is one line: prove the field you read records an outcome and not an action. Mine did not, and the variable name gave no hint.
Note that this one was not a hook defect. The hooks wrote their rows correctly. The consumer of those rows was wrong, which is a failure mode no amount of hook testing surfaces.
Why does zero not mean safe?
Because in three separate observe windows, zero meant the measurement had never worked. I nearly closed all three as proven safe.
One recorded 0 of 260 rows in the mode it watched, because a different safety gate blocks that mode from running at all, so no such event exists. Another had zero rows because its producer had never been invoked once. Not slow, not rare. Never run.
A third variant is easy to miss: a keyword matcher scores a perfect 1.0 on its 53 curated test cases while matching 93 percent of real prompts. The test set was not representative, so the score measured the test set rather than the matcher.
The pattern behind all four failures
The instrument carried the exact defect it was built to detect.
- The one line worth taking away
The gate against unbacked claims made an unbacked claim about itself. The scheduler built to make things fire did not fire. My tests never caught it, because they shared the code's mental model.
This is also why deleting instructions beats adding another one. Anthropic's context engineering guidance reports cutting roughly 80% of Claude Code's own system prompt with no eval loss, and it was safe because the evaluations existed first. Mine did not.
Four checks before you trust your own instrument
- Does the producer exist and has it ever run? An absent output file does not distinguish broken from never-invoked. Run it by hand before filing a bug.
- Can this thing report a negative? Trigger the code path that produces a block or a bad rating. If you cannot, the positive results mean nothing.
- Does every field name match what the field records? Read the write site, not the variable name. "Success" was a dispatch counter in mine.
- Is the checker part of the thing being checked? If yes, ask a model outside your stack where your code commits the failure it hunts. I use
fugu --ultraandgrok-buildfor this. It is the only one of the four I could not do alone, and it found two of these.
The gate in this post is public: hooks/scripts/forced-verify-stop-gate.py in primeline-ai/evolving-lite. Its own docstring says it never blocks and that autonomy is off by default, so you can check the claim rather than take my word for it. New to the event model? Anthropic's hooks guide is the reference. Related: my hook automation setup and auditing my own correction-rate measurement, the same bug class one layer up.
Honest scope
n=1 developer, my own instruments, and the counts come from my own ledgers, which is exactly the dependency this post warns about. The 574-event figure is the audited window, not the ledger's lifetime. I have not measured what fraction of my completion claims were actually false, only that nothing I built was measuring it. That gap is open and I am not reporting it as closed.

![Claude Code Code Review Needs a Non-Claude Model [2026]](/_next/image?url=%2Fblog%2Fclaude-code-code-review-hero.webp&w=3840&q=75)
![Your CLAUDE.md Rules Have an Expiry Date [2026]](/_next/image?url=%2Fblog%2Fclaude-md-expiry-date-hero.webp&w=3840&q=75)
![A Negative Result on Claude Code Agent Self-Regulation [2026]](/_next/image?url=%2Fblog%2Fcsra-negative-result-hero.webp&w=3840&q=75)