Hook Architecture
In one line: Hooks convert "remember to lint" into "linting happens automatically" — structural enforcement that does not depend on anyone's memory.
What: Claude Code hooks are automated actions triggered at specific points in the development flow. They execute without intervention and make quality gates automatic rather than voluntary.
Why: Without hooks, quality checks depend on remembering to run them — the discipline-based model Section 2.4 identified as fragile. A hook cannot be forgotten because it does not depend on memory.
Hook Types:
Claude Code supports three hook trigger points:
PreToolUse — fires before a tool is invoked. Use for validation, permission checks, or context injection before an action occurs.
Example: A PreToolUse hook on the Write tool could verify that the target file is not a protected configuration file before allowing the write.
PostToolUse — fires after a tool completes. Use for validation, formatting, or quality checks after an action has occurred.
Example: A PostToolUse hook on file edit tools runs the linter on the edited file, providing immediate feedback on any issues introduced by the edit. This is how Layer 1 (post-edit linting) is implemented.
Stop — fires when the AI is about to conclude its response. Use for completion verification — ensuring that claims of completion are backed by evidence.
The shipped Stop hook is a diff-aware, advisory, command-type hook (templates/hooks/verify-before-stop.sh). It checks the git working tree, and only when code files actually changed does it print a reminder to verify with the same commands CI runs (full ruff check, ruff format --check, pyright, and tsc --noEmit && eslint if the frontend changed). It always exits 0.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/verify-before-stop.sh"
}
]
}
]
}
}
Retired pattern (do not reintroduce): an earlier draft used a blocking
"type": "prompt"Stop hook that asked the AI to self-attest verification. That variant trapped sessions in completion loops and relied on the AI's self-report rather than command output. v3 replaced it with the diff-aware advisory command hook above. The disposition is recorded indocs/rule-inventory.md(the prompt-type Stop hook is marked superseded).
Hook implementation patterns:
Hooks can be implemented as:
- Command hooks (
"type": "command") — execute a shell command at the trigger point. The command's output is injected into the conversation. This is the mechanism used by both the post-edit linting hooks and the shipped (diff-aware, advisory) Stop hook. - Prompt hooks (
"type": "prompt") — inject a prompt into the conversation at the trigger point. The AI processes the prompt and responds to it. Use sparingly: a blocking prompt-type Stop hook that asks the AI to self-attest verification was retired in v3 because it trapped sessions in completion loops (see the Stop-hook note above).
Hooks are configured in .claude/settings.json (project-level) or ~/.claude/settings.json (global-level). Project-level hooks take precedence when both exist for the same trigger point.
Evidence: The Stop hook in .claude/settings.json fires on every task completion but only surfaces when the diff shows changed code files — so it does not nag on doc-only or no-op turns. Its specificity is what makes it work: rather than asking "did you verify?" (which the AI answers "yes" without checking), it names the exact CI-equivalent commands to run, with full output. Reproducible: finish a task with changed code and an unverified claim, and watch the hook print the verification checklist before concluding. The earlier blocking prompt-type variant was retired (it looped sessions); see the Stop-hook note above and appendix-e-hooks.md.