CCLearn

Auto mode: what the classifier reviews and where it hands back

verified against Claude Code 2.1.261 · 2026-09-0913 min

By the end you can

  • Describe what the auto-mode classifier sees and what is withheld from it
  • State the decision order that runs before the classifier, and which of your rules auto mode suspends
  • Predict whether a given action is blocked or allowed by default
  • Choose between a spoken boundary and an ask or deny rule
  • Recognise the conditions under which auto mode hands control back

You turned auto mode on because the prompts were unbearable, and the prompts stopped. What replaced them is a second model that reads part of your session and decides, action by action, whether the thing about to run is the thing you asked for. Everything useful about auto mode follows from knowing precisely what that model can see, and what has been withheld from it.

Lesson 10.1 placed auto among the six modes. This is what happens underneath it.

What auto mode replaces

Auto mode lets Claude execute without routine permission prompts. A separate classifier model reviews actions before they run, blocking anything that escalates beyond your request, targets unrecognized infrastructure, or appears driven by hostile content Claude read. Explicit ask rules still force a prompt. — Permission modes

Three things follow. The classifier is a model, so its verdicts are judgements. It runs before the action, adding a round trip that reads and working-directory edits outside protected paths skip entirely. And it is a per-action control:

The classifier is a per-action control, not an isolation boundary — Sandbox environments

It decides whether each call runs. It does not limit what a call can reach once it does — that is what an isolation boundary is for. Lesson 10.4 takes the distinction properly; carry this much of it: nothing about the classifier constrains a command it approved. That is the ceiling Lesson 10.1 quoted — auto mode reduces prompts, it does not guarantee safety.

Rules run before the classifier

Auto mode does not replace your permission rules. It sits behind them.

Deny and explicit ask rules are evaluated before the classifier and still block or prompt. — Configure auto mode

Each action goes through a fixed decision order, first match wins:

StepResolves there
1Actions matching your allow, ask or deny rules
2Read-only actions and file edits in your working directory — auto-approved, except writes to protected paths and the first read outside the working directories, which prompt you
3Everything else — the classifier
4If the classifier blocks, Claude gets the reason and tries an alternative — from v2.1.208 that reason is the fixed text Blocked by classifier in most sessions, not an explanation

That step-2 read prompt fires once, the first time Claude uses Read, Grep or Glob on a path outside the working directories. Keep allowing runs the read and stops asking; Block from now on refuses it and writes permissions.blockReadsOutsideWorkingDirectories into your user settings, which makes the file tools refuse such reads in every later session and every mode — that setting requires v2.1.257. The prompt does not appear in non-interactive -p runs or background sessions; reads there run as before.

Step 1 has exceptions, and they are the cases where a rule you wrote does not end the matter. Protected-path writes route to the classifier even when an allow rule matches. So do rm and rmdir removals targeting a critical path — but that routing is v2.1.218 and later; what holds in every version is the narrower rule that no allow rule and no PreToolUse "allow" approves such a removal at all. MCP tools marked requiresUserInteraction, and connector tools your organisation set to ask, prompt you directly and never reach the classifier. And an ask rule matching on a command's content, such as Bash(git push *), falls back to a permission prompt.

Entering auto mode also drops the broad allow rules that grant arbitrary code execution — blanket Bash(*) or PowerShell(*), wildcarded interpreters like Bash(python*), package-manager run commands, and Agent rules — and restores them when you leave. Monitor rules join that list only in v2.1.236 and later; before that a Monitor allow rule matching the whole tool approved Monitor commands, which run through the shell, without classifier review.

Narrow rules like Bash(npm test) stay in effect, which is exactly how a destructive argument the rule's prefix did not anticipate can still slip past unreviewed. Setting autoMode.classifyAllShell to true suspends every Bash and PowerShell allow rule while auto mode is active — but the key requires v2.1.193 or later, and earlier versions ignore it and carry narrow shell allow rules into auto mode regardless.

What the classifier can see

This is the sentence to memorise:

The classifier sees user messages, tool calls other than read-only lookups such as file reads and searches, and your CLAUDE.md content. Tool results are stripped, so hostile content in a file or web page can't manipulate it directly. — Permission modes

Read both halves. Your CLAUDE.md is classifier input: "never force push" in a project's memory file steers Claude and the classifier at once. And tool results are not, deliberately — it is why injected text in a fetched page cannot directly argue its way past the check. Level 9 showed you a tool result issuing instructions; this is the architectural answer, and lesson 10.6 takes the subject.

Stripping results has two consequences that look like bugs until you know why:

  • Command output is not evidence. A repository is assumed private unless its remote host and name say otherwise, or evidence in what the classifier reads shows it is public — your own message naming it, say. The output of a gh repo view does not reach the classifier and settles nothing. The transcript-evidence half of that check requires v2.1.200 or later. The one documented exception is arranged by Claude Code rather than by Claude: before a command that would discard uncommitted work, it runs git status itself and shows the classifier whether staged, modified or untracked work is present — reporting untracked files even where the repo sets status.showUntrackedFiles=no.
  • A delete whose target came from output gets blocked. From v2.1.205, a recursive forced delete such as rm -rf "$VAR", where the variable is assigned nowhere in the conversation the classifier sees, is blocked by default: the value came only from earlier output, so the target cannot be checked against the deletion rules. Naming the exact path, or re-running with the literal path, clears it.

One channel lets annotated context through: a PostToolUse hook's classifierContext field, which requires v2.1.236 or later. Read what the docs say about its weight — for hooks configured in Claude Code the classifier treats the note as unverified, application-provided context that never establishes user intent, and checks any claim that you approved something against your own messages. The reason to still handle it carefully is the documented warning against copying untrusted tool output or third-party text into it: doing so hands back exactly what stripping was protecting.

A second, separate layer: a server-side probe scans incoming tool results and flags suspicious content before Claude reads it. That is not the only place hostile content is considered — the classifier itself blocks actions that appear driven by hostile content Claude read — but it is the only pre-read scan the documentation describes.

Blocked by default, allowed by default

The classifier trusts your working directory and the remotes configured for it when the session started. From v2.1.200, a remote added or repointed mid-session with git remote add or git remote set-url is not trusted — before that, such remotes were trusted too. Everything else is external until you configure it.

Blocked by default includes curl | bash-style download-and-execute, sending sensitive data to external endpoints, production deploys and migrations, mass deletion on cloud storage, granting IAM or repo permissions, irreversibly destroying files that existed before the session, force push, git reset --hard and its siblings, and terraform destroy. Later releases added, among much else — this one from v2.1.203:

Content from a sensitive local store, or from a file whose name, path, or type marks it as sensitive, entering a commit, a push, PR or issue text, a gist or paste, or a package publish, unless you named both the source and the destination. — Permission modes

Now the half people guess wrong. Allowed by default covers local file operations in your working directory, installing dependencies declared in your lock files or manifests, read-only HTTP requests — and this:

Reading .env and sending credentials to their matching API — Permission modes

Auto mode is not a secrets boundary. If .env must stay unread, that is a deny rule, and it is lesson 10.5. Pushes are the other surprise:

Pushing to any branch of the repository you're working in and creating a pull request that matches your request run without a prompt, unless the push or pull request falls under the blocked list, such as secrets or sensitive data leaving the repository, or a pull request that targets a different repository or organization. — Permission modes

This is the claim most exposed to your version. Any branch, the default branch included, has been allowed by default only since v2.1.211; from v2.1.203 to v2.1.211 the allowance covered the branch you started on, branches Claude created and routine default-branch pushes; before v2.1.203 a direct push to the default branch was blocked outright. Check your own binary before you assume a colleague's session would have let the same push through.

Three qualifiers come attached to that allowance. A non-default branch whose name marks it as a deploy or publication target, such as production, release or gh-pages, is not covered by it: the classifier judges a push there on its own terms, including as a production deploy. The push's content is still checked against the other rules. And a permissions.deny rule blocks pushes to named branches outright in every mode, as does the remote's own branch protection.

The trap: a boundary you said out loud

Telling Claude "don't push until I review" does work. The classifier treats a boundary stated in conversation as a block signal, it holds even where the default rules would allow the action, and Claude's own judgement that the condition was met does not lift it. Only a later message from you does. And then:

Boundaries are not stored as rules. The classifier re-reads them from the transcript on each check, so a boundary can be lost if context compaction removes the message that stated it. For a hard guarantee, add a deny rule instead. — Permission modes

Lesson 3.2 drew the line that decides this: what lives on disk comes back after compaction, what lived only in the conversation is summarised with loss. A spoken boundary is the second kind, and losing it costs more than losing an explanation. The docs describe no warning when it happens: the session keeps running, the classifier keeps checking, and the constraint is simply no longer among the things checked against.

Making a checkpoint stick

BoundaryMechanismBehaviour in auto mode
Prompt before the actionpermissions.askAlways prompts for content-scoped rules; the classifier cannot auto-approve a matching action
Never run the actionpermissions.denyBlocks before the classifier is consulted; neither the classifier nor user intent can override it
One-off, this sessionSay it in conversationBlocks matching actions, but can be lost to compaction

The ask recipe is the one most people want and never write. It keeps auto mode on for everything else and puts a human in front of the two actions that usually deserve one:

json
{
  "permissions": {
    "ask": [
      "Bash(git push *)",
      "Bash(gh pr create *)"
    ]
  }
}

Lesson 10.2's precedence is doing the work here: ask and deny are evaluated first, so neither the classifier nor a persuasive-sounding intent gets a vote.

If your problem is the opposite — routine internal work getting blocked — the fix is autoMode.environment, prose entries naming the repos, buckets, domains and services inside your boundary. Note where that block is read from: user settings, managed settings, or --settings, and deliberately not the two files that live in the repo, so a checked-in project or a build step cannot inject its own trust. That exclusion only became complete in v2.1.207: before it the classifier also read .claude/settings.local.json, a file a repository can commit and a local tool can write. The docs still tell you to move any autoMode block out of that file and into ~/.claude/settings.json — no longer because it is dangerous there, but because it is inert there, and a trusted-infrastructure list nothing reads looks exactly like one the classifier disagrees with. Deploying that across a team is Level 12's subject.

When auto mode hands back

SituationWhat happens
A blocked actionA notification, plus an entry under Recently denied in /permissions, where r retries it with a manual approval
Repeated blocksAuto mode pauses and prompting resumes
A session that cannot promptA -p run without --permission-prompt-tool has nothing to fall back to, so when repeated blocks reach a threshold the action does not run and Claude keeps working. Claude Code does not stop the run
A mode switch mid-checkA verdict the new mode would not have requested is discarded; you are prompted instead, or auto-denied in dontAsk

The second row has exact numbers, and they are not yours to tune:

if the classifier blocks an action 3 times in a row or 20 times total, auto mode pauses and Claude Code resumes prompting. Approving the prompted action resumes auto mode. These thresholds are not configurable. — Permission modes

Any allowed action resets the consecutive counter. The total counter persists for the session and resets only when its own limit triggers a fallback. Repeated blocks usually mean the classifier is missing context about your infrastructure, not that Claude is misbehaving.

A fifth case is not a block at all: the classifier failed to produce a verdict. The docs separate four such failures, and they do not behave alike.

FailureWhat you see, and what it costs you
The classifier model is unavailableA message naming a model and saying auto mode "cannot determine the safety" of the tool. Transient for the (rate-limited), (overloaded) and (server error) categories; retry after a few seconds. On Bedrock it repeats until your account can invoke that model
The classifier's response did not parseDenied for safety, with no verdict, and Claude Code records no Recently denied entry. Retrying usually succeeds
A safety check separate from auto mode refused the classifier's own request, because of earlier conversation contentDenied with no verdict and no Recently denied entry. Retrying will not help — the same content trips the same filter. Switch permission mode and approve the action, or start a fresh conversation
The conversation outgrew the classifier's context windowNot a denial: in an interactive session auto mode falls back to an ordinary permission prompt for that action, and /compact brings later actions back inside the window. In a -p run with no --permission-prompt-tool, the action does not run and the run continues

The first row used to hide a second cause, and that one was never transient. A routine token expiry does not surface as this message any more, because Claude Code "refreshes the token and retries the request once" — the docs then date that fix: "Before v2.1.216, an expired or rotated token failed each classifier request, and auto mode denied every checked action with this message until the token was refreshed" (Errors and warnings). The reading habit that gap taught outlives it, because the message itself still says nothing about scale. One command failing under a (rate-limited), (overloaded) or (server error) category is the transient case the row describes, and a retry a few seconds later clears it. Every checked action failing is not that, and the docs name one standing case of it: on Bedrock the message repeats until your account can invoke the classifier model. Retrying is the wrong move there — as it was on a binary below v2.1.216, where the same wall meant an expired token.

One threshold note, and it is the one that bites on an older binary: the third row is exempt from the pause counters, but only from v2.1.225. Before that, Claude Code counted those refusals like any other block and returned the same rejection message as a genuine classifier block. Do not assume the other rows are exempt either. In all four, reads, searches and edits inside your working directory skip the classifier and keep working.

What this mode actually is

A judgement layer with a known blind spot and a known escape hatch. Choose it deliberately, then write down the one or two boundaries that must survive a compaction. Everything else can stay a conversation.

Checkpoint

  1. You tell Claude "don't deploy to staging until I've looked at the diff", work for two hours, and the session compacts. Is the boundary still enforced? What should you have written instead, and where?

    Show answer

    Probably not, and nothing tells you. Boundaries are not stored as rules — the classifier re-reads them from the transcript on each check, so a boundary can be lost if compaction removes the message that stated it. The session keeps running and the classifier keeps checking; the constraint is simply no longer among the things checked against. Until then it was doing real work: a stated boundary blocks even where the default rules would allow the action, and Claude's own judgement that the condition was met does not lift it — only a later message from you does.

    For a hard guarantee, write it into a settings file's permissions block instead: ask for the thing you actually wanted, a prompt before the deploy, or deny if it must not run at all. Both are evaluated before the classifier, so neither the classifier nor a persuasive-sounding intent gets a vote — and unlike a sentence in the transcript, neither can be summarised away.

  2. A teammate says auto mode "protects the classifier from prompt injection because it can't see the page". State what it actually cannot see, and name the one field that lets annotated content reach it anyway.

    Show answer

    What is withheld is tool results — all of them, not just fetched pages. The classifier sees your user messages, tool calls other than read-only lookups such as file reads and searches, and your CLAUDE.md content; results are stripped so hostile content in a file or web page can't manipulate it directly. Two consequences fall straight out of that: your CLAUDE.md is classifier input, and command output is not — a gh repo view showing a repository is public settles nothing, because the verdict is made without it.

    The one channel through is classifierContext on a PostToolUse hook, which requires v2.1.236 or later. It is context, not evidence: for hooks configured in Claude Code the classifier treats the note as unverified, application-provided context that never establishes user intent, and checks any claim that you approved something against your own messages. The docs' warning is the operative part — don't copy untrusted tool output or third-party text into it, because that hands back exactly what stripping was protecting. Keep the scope straight too: stripping protects the classifier, not you. Claude still reads the page.

  3. Your CI job runs claude -p in auto mode and the classifier blocks the same aws s3 sync three times. Describe what happens next in that run — and what would have happened in your terminal instead.

    Show answer

    Three blocks in a row hits the documented threshold — 3 consecutive or 20 total, and not configurable — so auto mode pauses and prompting resumes. But a -p run without --permission-prompt-tool has nothing to fall back to: the action does not run and Claude keeps working. Claude Code does not stop the run, so the job can finish looking healthy with the sync never performed.

    In your terminal you get the prompt instead, and approving the prompted action resumes auto mode; any allowed action resets the consecutive counter. Each block also raises a notification and leaves a Recently denied entry in /permissions, where r retries it with a manual approval. Repeated blocks usually mean the classifier is missing context about your infrastructure rather than that Claude is misbehaving — the fix is autoMode.environment naming that bucket and those services, read from user settings, managed settings or --settings, and deliberately not from the two settings files that live in the repo.

  4. Which of these stops a git push in auto mode without ending the mode: an allow rule, an ask rule, a sentence in chat, a deny rule? For each, say how firm it is.

    Show answer

    Three of the four do, and none of the three ends auto mode. An allow rule stops nothing — it does the opposite. (Broad rules granting arbitrary code execution are dropped on entering auto mode and restored on leaving; narrow ones stay in effect, which is a different problem, not a brake.)

    An ask rule such as Bash(git push *) always prompts for content-scoped rules, and the classifier cannot auto-approve a matching action; it resolves at step 1, before the classifier, so it is firm. A deny rule is firmest: it blocks before the classifier is consulted, and neither the classifier nor user intent can override it. A sentence in chat genuinely works — the classifier treats a stated boundary as a block signal, it holds where the default rules would allow the push, and only a later message from you lifts it — but it is the least firm of the three, because it is re-read from the transcript on every check and vanishes silently if compaction drops the message. Without one of these, the push simply runs: pushing to any branch of the repository you are working in is allowed by default.

Sources