Volume IV · Chapter 6
Hooks, Gates & Checkpoints
Deterministic lifecycle control points and checkpoint/rollback mechanics — every team convention living in a senior engineer's head can be encoded as enforced harness logic.2026-07-12 · 8 min read
A senior engineer who has spent a year on a team carries a checklist nobody wrote down anywhere. Run the linter before committing. Never let a stray .env file or an API key end up in a diff. Run the test suite before marking a ticket done. Tag a snapshot before touching anything three other services depend on. None of it lives in the onboarding document, because writing it down never felt necessary — it lives in the engineer’s reflexes, assembled the slow way, one incident at a time.
Hand the same codebase to an AI coding session and the checklist does not transfer with it. A team can write the same conventions into a CLAUDE.md or the intent files an earlier volume in this Library describes — a real improvement over saying nothing — and a session that reads them carefully that turn will follow them. A session that doesn’t, because the instruction lost a competition for attention against everything else in a long prompt, won’t, and nothing in the harness notices. A session under no structural obligation to remember can commit a secret, skip the test run, or overwrite a file another session is mid-edit on, and the miss surfaces later, in review or in production, rather than at the moment it happened.
What a hook actually is
A hook, in the sense this chapter uses the word, is a deterministic piece of code that fires at a specific, named point in an agent’s lifecycle and can inspect, block, modify, or log what is happening at that point before the model’s own — probabilistic, sometimes wrong — judgment gets the final say. Claude Code’s implementation is the concrete version this chapter works from: user-defined shell commands, HTTP calls, or model-graded checks, wired to events like a tool call about to run, a tool call that just finished, a session starting or ending, a context compaction about to happen, or a notification firing. Anthropic’s own framing of the mechanism is direct about what it is for: hooks “provide deterministic control over Claude Code’s behavior, ensuring certain actions always happen rather than relying on the LLM to choose to run them.”
The mechanics are simple by design. A hook script receives structured JSON on stdin describing the event — which tool, which file, which command — and answers through stdout, stderr, and an exit code. Exit 2 blocks the action and sends the script’s stderr back to the model as feedback it can act on, the same principle Anthropic’s guidance for tool design applies to ordinary tool errors: a rejection is more useful when it says what to do instead of just refusing. Exit 0 lets the action proceed. Structured JSON on top of that gives finer control for the events that support it — deny with a reason, escalate to the user, or auto-approve. None of this is conceptually new; git itself has shipped hooks, scripts triggered at points like a commit or a push, for two decades. What changed is having a comparably rich set of lifecycle points inside a single AI session, not just around version control.
A wall, not a suggestion
The previous chapter in this volume covered permission architecture as a way of gating individual risky actions — auto-approve reads, prompt before writes, block dangerous commands outright. Hooks generalize that same mechanical-enforcement idea from individual actions to any lifecycle moment a team cares about. A pre-tool-use hook that checks a file path against `.env`, `package-lock.json`, and anything under `.git/` before an edit is allowed to proceed is not a reminder the model can talk its way past by sounding confident; it is a wall. The distinction is not rhetorical. In Claude Code, a hook that denies a tool call blocks it even when the session is running in a mode that skips interactive permission prompts entirely — one of the few places in the harness explicitly designed so that a user changing their own settings cannot quietly disable a rule the team relies on.
The same primitive covers conventions that have nothing to do with security. A post-tool-use hook that runs a formatter after every edit keeps a house style enforced without a formatting argument ever reaching code review. A session-start hook scoped to the compaction path can re-inject the two or three facts a team cannot afford to lose across a compaction boundary — the sharpest edge of what an earlier volume in this Library names Context Debt. A notification hook can alert a person the moment a session is genuinely waiting, instead of the person polling a terminal that might be idle for five seconds or twenty minutes and looks the same either way.
The events teams actually wire
Claude Code alone exposes dozens of named lifecycle events, from a tool call about to run to a worktree about to be created. Most teams do not use most of them. A small, recognizable set accounts for nearly everything a team actually wires up in practice, which is itself informative: the conventions worth enforcing mechanically turn out to be a short list, even though the list of places a hook could fire is long.
| Event | Fires when | What teams typically wire to it |
|---|---|---|
| SessionStart | A session begins, resumes, or a compaction just finished | Re-inject facts a team cannot afford to lose across compaction; load per-directory environment variables |
| PreToolUse | Before a tool call executes | Block writes to protected paths (.env, package-lock.json, .git/); validate a shell command against a deny pattern |
| PostToolUse | After a tool call succeeds | Auto-format the file just edited; log every command that ran, for audit |
| Stop | The agent finishes responding | Verify the test suite actually passes before letting the session consider itself done |
| Notification | The agent is waiting on input or a permission prompt | Desktop or chat alert, so a human is not polling an idle terminal |
| SessionEnd | A session terminates | Clean up scratch files; close out session bookkeeping and hand off state |
| PreCompact / PostCompact | Before and after context compaction | Preserve critical facts going in; confirm they survived coming out |
| ConfigChange | A settings or skills file changes mid-session | Audit, or block, unauthorized changes to the hook and policy configuration itself |
Checkpoints: pausing on purpose
Hooks operate at the grain of a single action. Checkpoints operate one level up: rather than gating every edit, a checkpoint pauses an agent’s progress at a meaningful milestone — between steps of an already-approved plan, or before a batch of changes is treated as finished — takes a snapshot a human can actually review, and supports rolling back to any earlier checkpoint if a later step turns out to be wrong. The snapshot itself is usually cheap: a git tag, a diff summary, sometimes both. What makes the mechanism valuable is not the snapshot, which is unremarkable, but the discipline of stopping on purpose at a point small enough to review in minutes — rather than discovering the problem after eleven files have changed and some of them happen to be right.
Anthropic’s own guidance for long-running agents leans on a related but looser version of the same idea: agents are pointed at git to revert bad changes and recover a working state, and are asked to leave the codebase in what the guidance calls a clean state — no major bugs, well organized, ready for the next session to pick up — documented through commit messages and a running progress file rather than through a formal approval gate. Several coding tools have since made the gate explicit rather than advisory. Kilo Code, for one, snapshots the workspace automatically at the start and end of every model turn using git-based tracking, with a limitation worth naming honestly: rollback there is granular to the whole user message, not to an individual step or file, so a good edit and a bad edit inside the same turn revert together.
The same discipline, a smaller grain
This is not a new discipline so much as a familiar one applied at a different point. Plan-mode thinking, as Volume III describes it, is itself a checkpoint: a pause before any code exists, reviewable in the time it takes to read a paragraph rather than a diff. What this chapter adds is the same decide-then-do pattern applied inside a plan that has already been approved — pausing again at step three of five, not only before step one, because a plan that looked right at the start can still go wrong in execution. The cost of catching that at step three, with a checkpoint gate in place, is a git tag and a five-minute review. The cost of catching it without one is an archaeology project through eleven changed files, sorting the incidental fixes from the mistake they got tangled up with.
Reading the log
Wiring hooks and checkpoints into a harness produces something a prose convention never does on its own: a record of how often the rule actually mattered.
None of this replaces judgment. A hook still has to be written by someone who knows what the team’s real conventions are, and a checkpoint still needs a human willing to read the diff rather than rubber-stamp it — the ceremony failure mode Volume III names for plan-mode review applies here without modification; a checkpoint nobody reads closely is a delay with a rubber stamp at the end of it, not a safety mechanism. What changes is where the convention lives. Move it out of one engineer’s head and into a script or a gate that runs whether or not the model ever understood the rule existed, and the convention survives that engineer’s vacation, the fresh session that has never seen the codebase before, and the confident wrong turn that no amount of prose in an intent file fully prevents. That is a real step past intent files, not a replacement for them: intent files still carry the why, the context a hook has no room to express, while hooks and checkpoints carry the part that cannot be allowed to depend on a session reading carefully.
For Discussion
- Write down, honestly, the checklist a senior engineer on your team runs before trusting a change. How much of it currently exists only as a prompt instruction an AI session might or might not follow this time?
- Of the last ten AI-generated changes your team had to revert, how many would a pre-tool-use hook or a plan-step checkpoint have caught before the change was ever committed, versus after?
- If a hook silently stopped firing tomorrow — a misconfigured matcher, a script exiting non-zero for the wrong reason — how long would it take your team to notice?
References
- establishedHooks “provide deterministic control over Claude Code’s behavior, ensuring certain actions always happen rather than relying on the LLM to choose to run them”Anthropic — Claude Code docs, "Automate actions with hooks" · 2026-07
- establishedFull hook lifecycle event schema and decision semantics; a PreToolUse deny overrides bypass-permissions modeAnthropic — Claude Code docs, "Hooks reference" · 2026-07
- establishedGit-based reversion, a running progress file, and a "clean state" target for long-running coding agentsAnthropic engineering — "Effective harnesses for long-running agents" · 2025-11-26
- establishedTool error responses should be specific and actionable so an agent can adjust its next actionAnthropic engineering — "Writing effective tools for agents" · 2025-09-11
- establishedBranch protection: required status checks and required reviews enforced mechanically before a merge, regardless of who is mergingGitHub Docs — "About protected branches" · 2026-07
- establishedGithooks: scripts triggered automatically at points in git’s execution (pre-commit, pre-push, and others) — the naming lineage hooks in AI harnesses generalizegit-scm.com — githooks documentation · 2026-07
- emergingAutomatic git-based workspace snapshotting per model turn, with rollback granularity limited to the user-message boundaryKilo Code — "Checkpoints" documentation · 2026-07