Part 1 of 2. Part 2 is about letting it run on its own.
I recently ran a hands-on workshop for an engineering team that uses Claude Code every day. To prepare, I recorded Claude working through a completely ordinary ticket in a small demo service: fix a bug, run the tests, open a pull request. Manual mode, no special setup.
It asked me for permission eight times. I said yes eight times. I read none of them.
The fix was correct. That is not the point. The point is that I would not have known if it had been wrong. Manual mode gives you prompts. It does not give you review.
This post is the do-it-yourself version of that workshop: what actually controls what Claude Code is allowed to do, and three guardrails you can install in your own repo in under an hour.
Manual Mode Lets More Through Than You Think
Most engineers believe manual mode means "it asks before doing anything." It does ask before most file edits and commands. But several things pass without a prompt:
- A built-in set of read-only commands (
ls,cat,grep, read-onlygit) runs without asking. - Every "yes, don't ask again" you clicked is saved as a permanent rule in
.claude/settings.local.json. That file stays out of git, so nobody else on your team can see what you have allowed. - Skills can pre-approve their own tools.
- Auto memory: Claude keeps its own notes from your corrections and loads them in every session.
None of this is a bug. It is what a helpful tool does when nobody has drawn a line.
Try it now: run /permissions and open .claude/settings.local.json in a repo you have used for a while. You will probably find something you do not remember deciding.
Three Doors: Advice, Rules, Walls
Everything that shapes Claude Code's behavior sits behind one of three doors:
| Door | What it is | How strong |
|---|---|---|
| Advice | CLAUDE.md, memory, skills | Usually followed |
| Rules | Permission lists: allow, ask, deny | Enforced, but they match text |
| Walls | Hooks: your own scripts that decide | Enforced, and they look at what is actually happening |
Anthropic's documentation is direct about the first door: Claude treats CLAUDE.md and memory "as context, not enforced configuration." To block something regardless of what Claude decides, you need a hook.
The second door has a subtle limit. A rule matches the words of a command, not what it does. A deny rule on rm stops rm -rf build/, but not /bin/rm -rf build/. And one case no rule can ever catch: a bare git push pushes whatever branch you are on. If you are on main, the word "main" appears nowhere in the command.
A simple way to choose: if you can write it down in advance, it is a rule. If you have to look at what is happening, it is a hook. Rules are perfect for "allow" (they silence prompts that were never real decisions) and for a short deny list you can name exactly. Hooks handle the rest.
What a Hook Is, in Plain Words
Claude is about to do something, like edit a file or run a command. Before it happens, Claude Code hands your script a short note: which tool, which file, which command. Your script answers: fine, stop, or ask a human. If it says stop, Claude reads your reason and looks for another way. (The hook that runs before an action is called PreToolUse; the one that runs after is PostToolUse.)
Two things worth knowing:
- Hooks work in every permission mode, including auto mode. A guardrail is not a mode setting.
- A crashed hook blocks nothing. A broken guardrail is an open door, so test every one by doing the forbidden thing.
You do not need to write hooks by hand. You ask Claude Code to build them.
Three Guardrails to Install This Week
Paste each prompt into Claude Code in your repo, then try to break it. If a hook does not fire, restart the session.
1. Protect the files that must never change
Include .claude/ itself, because that is where your guardrails live — and a guardrail Claude can edit is not a guardrail.
Protect some files in this repo from being edited, including by you.
These must never be edited:
[the files or folders in your repo that must never change]
.claude/
Add a PreToolUse hook that stops any edit to one of those, including shell
commands that write to them, and prints a clear reason: which file it was,
and that a human has to make this change.
Setting up this hook in .claude/ is the one exception. Do not edit any other
protected file. When done, tell me what you created.Test it: ask Claude to "add a comment at the top of" one of those files, and watch the hook block it.
2. No push to main, no force push
A force push overwrites shared history. A bare git push on main is the case rules can't see.
Add a hook that stops you from pushing to main or master, and from force pushing.
Careful: "git push" with no arguments pushes whatever branch I am on, so check
the current branch, not only the words in the command.
Block it before it runs and explain that the way forward is a feature branch
and a pull request. Prove it works by feeding the hook a sample bare "git push"
input while on main. Do not run a real push.3. Run the tests after every edit
Add a hook that runs my tests after you edit a source or test file, and shows
the last 15 lines of the result to both of us (plain output does not reach
either of us, so check). Never block on a failure, just report it. Prove it
works by breaking a test. My test command is: [your test command]Then: share them with the team
Use /permissions to add allow rules for commands that are never real decisions, like your test command. Commit .claude/settings.json and .claude/hooks/. Team guardrails belong in the shared file — not in settings.local.json, where only you have them.
The Same Ticket, With Guardrails On
I ran the same ticket again, still in manual mode, with guardrails on. The difference was not more walls. It was better questions:
- Allow rules silenced the commands that were never decisions, like running tests or committing to a branch.
- A test hook ran the tests right after Claude's edit and put a red result on screen before anyone asked.
- One more hook fired when Claude reached for the test file, with a specific question: "Editing a test. Confirm this is a real behavior change, not making a failing test pass."
Eight approvals with zero decisions became a few prompts — and the one that mattered came with its reason. That last guardrail is one sentence to Claude Code:
Add a hook that asks me before any edit under [your test folder], with the reason:
"Editing a test. Confirm this is a real behavior change, not making a failing test pass."Bottom Lines
- Manual mode is prompts, not review.
- CLAUDE.md is advice. A rule reads the words. A hook reads the situation and decides.
- A guardrail nobody has tested is a guardrail nobody trusts.
- Trust is not a mode. It is a harness you built and broke on purpose.
For managers: guardrails are what make it reasonable to let Claude do more. They turn "we don't allow that" into "we allow it, and here is what stops it from going wrong." That is also the entry ticket for Part 2: letting Claude run in a loop until a real check says the work is done.
References