Guardrails vs Instructions — Requests vs Enforcement
After this lesson you can tell a request from a wall, and convert the rules
Learning Objectives
- Tell a request (context) from an enforcement (mechanism)
- Convert a must-never rule into a hook or a permission rule
- Explain why instructions can be overridden and guardrails cannot
- Place each guardrail where it actually fires
The Problem
This repo's own brief says: one agent, one branch; never force-push; commit before you exit. An agent that had read the file still force-pushed. The prose was in its context window — it just weighed the instruction against a task that said "clean up the branch" and lost.
The instruction had no wall behind it. The mechanism that would have blocked the command — a hook — was never installed.
The Concept
There are two layers, and they are not the same kind of thing.
Instructions are natural-language text in the context window: CLAUDE.md, AGENTS.md, the system prompt, the task. They steer the model. They are cheap, flexible, and carry no guarantee. The model may comply, and under a competing instruction it may not.
Guardrails are mechanisms outside the model: a hook (Lesson 55), a permission rule (Lesson 33), a sandbox, a git protection. The model can still propose the action, but the mechanism decides whether it runs.
flowchart LR
M["model proposes an action"] --> G{"guardrail check<br/>(hook / permission)"}
G -- allow --> T["tool executes"]
G -- block --> B["action denied,<br/>reason returned"]A hook's exit code carries the veto: from Lesson 58, one code blocks the call and returns the reason, another only reports an error without blocking. That distinction is what makes a hook a wall rather than a complaint.
The reframe: You cannot instruct a wall into existence — enforcement is a
mechanism the model cannot talk its way through.
The practical consequence: keep both. The instruction keeps the model from even proposing the action, which is cheaper and clearer. The guardrail is the guarantee for the day the instruction loses.
Which rules become walls? The ones where the cost of a single failure is unacceptable: force-push, writing to main, deleting without a commit, sending to production.
Build It
- List the "never" rules you currently rely on.
- For each, ask: if the model ignored this, what would stop it? If the answer is "nothing," it is a request.
- Pick the most dangerous one and convert it into enforcement — a
PreToolUsehook that matches the command and exits with the blocking code from Lesson 58, or a permission deny rule. - Test it: ask the agent to do the forbidden thing. The guardrail must fire and the action must not happen.
- Keep the instruction as well, and note in writing that the hook, not the sentence, is the guarantee.
Step 4 is the only proof that matters.
Use It
| Command / concept | When you reach for it |
|---|---|
| Instruction / request | When you want to steer behaviour cheaply |
| Guardrail / enforcement | When a single failure is unacceptable |
PreToolUse hook | When a specific action must be blocked before it runs |
| Permission deny rule | When a whole tool or path should never be reachable |
| Exit-code veto (Lesson 58) | When you need the hook to actually stop the call, not just warn |
Exercises
- Classify five of your rules as request or enforcement, with a reason. ✅ You pass when each rule is labelled and the enforcement ones name the mechanism that fires.
- Convert one request into a guardrail and prove it blocks. ✅ You pass when the forbidden action is attempted and does not happen.
- State, in one sentence each, what the instruction adds that the guardrail does not. ✅ You pass when the sentence names steering (the model avoids proposing the action) rather than enforcement.
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| instruction | "a rule the agent follows" | natural-language text in the window that the model may or may not obey |
| guardrail | "a rule the agent follows" | a mechanism outside the model that allows or blocks the action |
| hook | "a script that watches the agent" | code that runs on a loop event and can veto a tool call (Phase 08) |
| deny rule | "a permission setting" | configuration that makes a tool or path unreachable |
| exit-code veto | "the hook returns an error" | the specific exit code that blocks the call rather than only reporting one (Lesson 58) |
What this lesson skipped
The full hook event list is Phase 08, Lesson 56. Permission modes are Phase 04, Lesson 33. Sandboxing is Phase 17, Lesson 114. Hook behaviour and exact exit-code semantics are version-gated — verify them against https://code.claude.com/docs/llms.txt and date any claim you repeat.
Check yourself
4 questions. Answer before you reveal — recognising the right option is not the same as being able to produce it.
-
CLAUDE.md says 'never edit files under /vendor.' An agent edits them anyway. What do you add?
Show answer
B. A PreToolUse hook or permission deny rule for that path
The instruction already failed, so adding more instruction is the same layer failing again. A mechanism outside the model stops the edit regardless of what the model decides. 'A stronger sentence' is tempting because it feels more forceful, but emphasis does not change the fact that it is still a request.
-
Which of these is a guardrail rather than an instruction?
Show answer
C. A PreToolUse hook that blocks
git push --forceOnly the hook runs outside the model and can veto the action. The other three are text the model weighs and may override. The tempting answer is the system prompt, because it sounds authoritative — authority in the window is still just a request.
-
You already have a hook that blocks force-push. Why keep the instruction too?
Show answer
B. Because the instruction keeps the model from proposing the action at all, which is cheaper and clearer — the hook is still the guarantee
Instructions and guardrails do different jobs: the instruction steers, the guardrail enforces. Keeping both avoids needless blocked calls while preserving the guarantee. 'The hook will not fire without it' is tempting but false — a hook fires on the event, not on the prose.
-
A hook is meant to block a dangerous command but the command runs anyway. Which detail should you check first?
Show answer
B. Whether the hook exits with the code that blocks, rather than the code that only reports an error
From Lesson 58, only one exit code blocks the call; the other reports an error without stopping it — the classic exit-1-vs-2 trap. The tempting answer reaches for model size, but a hook that returns the wrong code is a wall with no door.