Part 2 · Determinism & Control

Mastering Claude Code · ~7 min

Permission Modes

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.

Why this, for you: you've felt both failure modes. Manual approval turns you into a click farm that approves everything by reflex; --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.

1 Auto mode: a classifier between you and rubber-stamping

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:

TierWhat it coversResult
User rulesexplicit allow / denyresolve immediately
Safe operationsreads, in-project editsauto-approve (except protected paths)
Classifiereverything elsetwo-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.

It's safer than bypass — not infallible

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.

2 Three deny layers, not one

"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:

MechanismLayerOverride path
permissions.denypre-classifiernone — deterministic, evaluated first
autoMode.hard_denyinside classifiernone — intent & allow don't apply
autoMode.soft_denyinside classifierliftable 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.

The $defaults trap

Each 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.

3 Classifier-shaped vs tool-shaped — pick the right floor

The two floors aren't interchangeable. Match the rule to its shape:

In-project writes skip the classifier entirely

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.

4 Subtracting tools at the skill layer

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:

FieldEffectStill callable?
allowed-toolspre-approves (suppresses the prompt)yes — every tool stays callable
disallowed-toolsremoves from the model's poolno — can't be seen or invoked
# A read-only audit skill: the smallest blast radius name: code-audit disallowed-tools: Bash, Write, Edit, MultiEdit allowed-tools: Read, Grep, Glob # Write/shell tools are absent — an injected file # instruction has no Write to call.
Removal can only subtract — it shrinks the pool and never widens it, so it dominates 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.

↪ Your win: speed where it's safe, an unmovable floor where it isn't

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…

Ask me anything. Want help deciding whether a specific guardrail is classifier-shaped or tool-shaped, or a starter 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.
✎ Feedback