Skip to content

A Stop Hook Gates the Turn, Up to a Cap

2026-07-30

The session reports that the experiment code is done. The smoke test is red. A Stop hook turns that gap into something the session cannot talk past, within a limit you should know before you rely on it.

Per the Claude Code hooks reference, a Stop hook runs when Claude finishes responding. If it exits with code 2 or returns decision: "block", Claude Code prevents the stop, and Claude receives your reason as feedback. The workflow guidance lists this as the deterministic gate among its ways to verify work.

The limit is in the same reference. Consecutive blocks are capped: the default is 3 per session, and the stopHookBlockCap setting changes it, with 0 removing the cap. After the cap, the next block is passed to Claude as additional context and no longer forces a retry. A Stop hook is a firm gate with a ceiling, not a promise that the session runs until your test passes.

The check. Write a hook script that runs uv run pytest -q tests/smoke and exits 2 on failure. Break the test on purpose with a one-line assertion you can remove later. Start a session and ask for a small unrelated change. Expected, per the documentation: three blocked stops with the test output as the reason, then a stop that goes through with the failure delivered as context. Write down what you actually observe and the output of claude --version, since hook behavior has changed across versions.

Two habits follow for research work. Keep the task's state outside the conversation, in a file or an issue, so a reached cap or a timeout leaves the unfinished work visible to the next person. And confirm that the gated test fails on the error you care about. A hook that reliably runs an irrelevant test still lets the wrong result through. A whole day: reliability, honest reports and readiness is about reporting what a check did and did not establish.

Hooks reference.