Mastering Claude Code · ~7 min
Stop rubber-stamping every tool call — and stop reaching for the bypass flag. Tune autonomy so the agent runs fast where it's safe and hits an unmovable floor where it isn't.
--dangerously-skip-permissions removes the brakes entirely. The fix isn't a
third button — it's understanding which control is probabilistic (a classifier that can be argued out of
a decision) and which is a hard floor (a deny that nothing lifts), and putting your guardrails on the
right one.Auto mode buys speed by deciding each tool call with a classifier instead of you. But a classifier is an LLM, so it's the wrong place for "this must never happen." This lesson maps the layers: where speed lives, and where the unconditional floor lives.
Agentic sessions force a tradeoff. Manual approval invites rubber-stamping; the bypass flag removes safety. Auto mode decides each action with a two-stage classifier — a fast single-token filter that screens with high recall, and a chain-of-thought reasoning stage that fires only when the filter flags something. Evaluation runs in a fixed order, first match wins:
| Tier | What it covers | Result |
|---|---|---|
| User rules | explicit allow / deny | resolve immediately |
| Safe operations | reads, in-project edits | auto-approve (except protected paths) |
| Classifier | everything else | two-stage pipeline |
On real traffic the full pipeline drops the false-positive rate to 0.4% — far below Stage 1's 8.5%
alone. Start it with claude --permission-mode auto, or set "defaultMode": "auto" under
permissions in .claude/settings.json. When eligible, it appears in the Shift+Tab
cycle right after plan.
The classifier's overeager false-negative rate runs up to 17% on consent-scope misjudgment: it spots danger but assumes you intended it. Roughly one in six unsanctioned destructive actions can slip through. Manual review is still the right call for production configs or shared branches.
"Block this" is not a single thing. Claude Code stacks three deny mechanisms that fire at different stages and offer different guarantees — and the difference is the whole game:
| Mechanism | Layer | Override path |
|---|---|---|
permissions.deny | pre-classifier | none — deterministic, evaluated first |
autoMode.hard_deny | inside classifier | none — intent & allow don't apply |
autoMode.soft_deny | inside classifier | liftable by allow or explicit user intent |
permissions.deny is the deterministic, pre-classifier floor. hard_deny is the
inside-classifier floor — still read by an LLM, but unaffected by the argument that lifts a soft_deny. For
actions that must never run regardless of intent or classifier config, use permissions.deny; it
blocks before the classifier is even consulted."Explicit user intent" is narrow on purpose: it means your message named the exact action. Asking Claude to
"force-push this branch" counts; asking it to "clean up the repo" does not. That distinction is what lets a
soft_deny bend without becoming a free-for-all.
$defaults trapEach autoMode list is an array of prose rules. The literal "$defaults" splices Anthropic's
built-ins in at that position. Omit it and you replace the entire default list — silently deleting the
built-in data-exfiltration and bypass rules. Always include "$defaults" unless you've deliberately chosen
to own the whole list. Print the built-ins first with claude auto-mode defaults.
The two floors aren't interchangeable. Match the rule to its shape:
hard_deny when the rule describes intent or destination — "never send repo contents to third-party code-review APIs." LLM interpretation is acceptable; you want it in the classifier's reasoning without being argued out.permissions.deny when the rule is a tool or domain pattern — Bash(rm -rf /*), WebFetch(domain:internal.example.com) — or compliance needs deterministic enforcement that holds even when auto mode is off.Auto mode tiers its actions: file writes and edits inside the project directory run with no classifier call.
So a hard_deny like "never write production credentials to a file" never fires when the write lands in the
repo. Only what the classifier sees — shell commands, web fetches, out-of-project writes — is subject to it.
There's a third, lighter control that works by removal rather than gating. A skill (or slash command) can list
disallowed-tools in frontmatter to strip tools from the model's pool while it's active — the deny-side
complement to allowed-tools. The two are not symmetric:
| Field | Effect | Still callable? |
|---|---|---|
allowed-tools | pre-approves (suppresses the prompt) | yes — every tool stays callable |
disallowed-tools | removes from the model's pool | no — can't be seen or invoked |
allowed-tools for any overlapping tool and layers safely over existing rules. But it is
turn-scoped: the restriction clears on your next message. It's a convenience, not a durable lock — for
persistent or org-wide denial, use permission deny rules in settings.permissions.deny (deterministic, pre-classifier) → hard_deny (unconditional, inside) → soft_deny (liftable by allow/intent).permissions.deny — a classifier is an LLM and can be argued out of a call."$defaults" in any autoMode list, or you delete the built-in exfil and bypass floor.disallowed-tools to shrink a skill's blast radius for one turn — never as a session-long boundary.Retrieval practice — recall, don't peek
Question 1You need an action that must never run, regardless of user intent or classifier config. Put it in…
Question 2Omitting "$defaults" from an autoMode list does what?
Question 3The auto-mode classifier's main weakness, versus the bypass flag, is…
Question 4A skill's disallowed-tools field differs from allowed-tools because it…
Question 5 · spaced recall from an earlier lessonFrom the hooks lesson: to make a behavior deterministic rather than asking the agent nicely, you…
autoMode block for your repo? Next in Part 2: Managed Settings — how to ship these same
guardrails team-wide so no developer can quietly remove the floor.