Skip to content

Why a hook is stronger than a rule or a permission

You’ve now governed the agent three ways across this course, and the ledger gate you just built sits apart from the other two in a way worth making explicit — because reaching for the wrong one gives you a guarantee weaker than you think you have. Lay them side by side:

  • A rule (project memory, an AGENTS.md line) is context. It tells the model what’s true and what you want, and the model factors it into its decisions. It lives in the context window, which means it can be compacted out, drowned by a long session, or simply not weighed heavily enough on a given turn.
  • A permission (an allow or deny rule) is model-mediated enforcement. It’s stronger than a rule — a deny rule is a hard wall the agent genuinely cannot cross. But it governs the tools the agent may invoke. It answers “may the agent call this?” — not “is the content of this specific commit acceptable?”
  • A hook is deterministic, model-independent code. It doesn’t advise the model and doesn’t sit in the context window at all. It runs on the rail at a lifecycle event, inspects the actual payload, and decides in plain shell. The model’s reasoning, its mood, its remaining context budget — none of it is in the loop.

The sharpest way to feel the difference: a rule and a permission both ultimately route through the model’s judgment or a tool-name match. A hook routes through neither. That’s why the ledger guarantee had to be a hook. “No ledger change without a test” isn’t a fact to remember (a rule) and it isn’t a tool to forbid (a permission) — git commit is a tool you obviously do allow. It’s a condition on the content of a specific action, checked deterministically at the moment that action is attempted. Only a hook can express that.

So which do you reach for? The test is about what kind of guarantee the situation demands:

  • State a preference or a fact the agent should weigh — “we use pnpm,” “the auth module is fragile, be careful.” That’s a rule. You want the model informed, and you accept that it’s the model deciding.
  • Forbid a whole class of action — never read secrets/, never touch prod config. That’s a permission deny. You want a wall around a tool or a path, no judgment involved.
  • Enforce a condition that no instruction can be trusted to hold, checked against the real action — no ledger commit without a test, no push to main that skips CI, format every file on write. That’s a hook. You want a guarantee that survives a forgetful agent and a 2am session.

Put plainly: rules and permissions shape what the agent may do; a hook enforces what must be true regardless of what the agent does. The first two trust the agent inside the lines you draw. The hook doesn’t need to trust it at all.

That last property — runs regardless, with nobody in the loop — is exactly why hooks become load-bearing the moment you stop watching the agent work. We’ve built the gate while sitting at the terminal; its real value shows up when you’re not there. Hold that thought.

For now, you’ve assembled the whole kit: the agent has reach (MCP into the database and the board), and it has a gate (the ledger hook that can’t be talked past). But all of it lives in your .claude/ directory, on your machine. Your teammates have none of it. The last move in this chapter is turning this hand-built setup into something you can hand over as a single unit.