Ian Provencher
Listen to the podcast
Operating Stack

The hooks · Claude Code

A convention nobody has to remember.

A hook is a shell command the tool fires on an event — a prompt arriving, a tool call about to run, a session opening or closing. There are 32 of them across 6 events, and the reason there are that many is one idea: a rule that lives only on a checklist gets missed, and the more carefully the checklist is written the more it feels like a control. So each rule worth keeping is moved into the command that already runs.

Grouped here by event, because that is the useful axis: what runs before the model reads a message, what refuses an action outright, what inspects the result, and what has to happen at the boundaries of a session so nothing is left stranded on one machine.

One session, start to end — the hooks firing

The five injectors

UserPromptSubmit
What it does
Before the model reads my message, five hooks add what the turn needs: which delegation lane fits, what has already been taught so it is not taught twice, the intent gate for a build, and the closing structure the answer has to end in.
Why
A rule stated once at the top of a long session is a rule the middle of that session has forgotten. Re-stating it every turn costs a few hundred tokens and removes the whole class of drift.

Six guards, before the action

PreToolUse
What it does
Refuses the action rather than reporting it afterwards: a plan that has not settled its intent, a question that hides jargon in an option label, a file about to land outside the folder this session started in, a command about to touch a remote box.
Why
A guard that fires AFTER the write has only told you what happened. These sit in front of the tool call, so the bad state is never reached — which is the difference between a warning and a control.

Four checks, after it lands

PostToolUse
What it does
Reads what the action actually did — the house conventions for a generated tool, the writing rules for anything shipped, and the estate impact of a change that touches shared machinery.
Why
Some faults only exist once the output does. Checking the intent before a write cannot see them, so this is the half that has to run afterwards.

Twelve, and most are watching the watchers

SessionStart
What it does
Pulls the doctrine and skills, loads the memory for this folder, then reports on the machinery itself: whether the gates are still fast enough to be run, whether delegation is drifting from its own table, whether a scheduled job has gone quiet, and whether the current model matches what the setup assumes.
Why
Every one of those was a failure that had already happened once and shown nothing at the time. A silent gate, a stalled job and a stale mirror all look identical to a healthy estate until something reports on them.

Three, so nothing is stranded

SessionEnd
What it does
Scrubs anything secret-shaped from what was written, then commits and pushes the config and the skills so the next session on any machine starts from the same place.
Why
Work that only exists on one disk is work that ends with that disk. The scrub runs FIRST, because a push is the point of no return.

Two, pricing the delegation

SubagentStop
What it does
Reads each finished sub-agent’s own transcript for what it actually cost and whether its lane held, and releases the plan lock it was holding.
Why
Delegation is easy to feel good about and hard to price. Reading the real transcript is the only way to know whether a fan-out paid for itself or just felt fast.

The doctrine and skills live in private repos, referred to here by role. Counts are read from the real configuration rather than typed — an earlier version of this page said “five”, then “six”, while the true number was 32, and the drift checker reported it in sync the whole time because it compares event names and never reads what a hook does.