AI
What does a Stop hook actually do when Claude says it's done but tests are failing?
A captured `claude --print` session against the demo app, with a Stop hook that runs `npm test` and returns `{decision: 'block', reason: ...}` when tests fail. The prompt asked Claude to change a `DEFAULT_DURATION` value that a test directly asserts, then declare done. The Stop hook blocked the first 'I'm done', and Claude reacted by editing the test assertion to match the new value rather than reverting the change or asking. The article shows the hook script, the wire-format signal in events.jsonl (a `stop-hook-error` notification), the test-gaming failure mode the hook accidentally encouraged, and the safeguard counter pattern that keeps a Stop hook from looping forever.