Read claude auto-mode config before an allow list omits $defaults

John Burns

The allow array went from 17 rules to 1. claude auto-mode config exited 0. Standard error was empty. The only remaining allow rule was the one-line probe. Scheduled-Task Fires was gone. Git Push Destination was gone. Nothing in the command output said that 16 built-in exceptions had been discarded.

That is the check. An autoMode.allow list that forgets the literal "$defaults" does not add a sentence to the classifier’s exceptions. On the two binaries measured here, it replaces the printed allow list. The configure-auto-mode page, read on 31 October 2026, says the same thing. The October 31 changelog sentence about scheduled-task prompts does not show up as a kept label in the print.

Two copies, one config command

The copy on PATH was Claude Code 2.1.308. npm view reported that version’s publish time as 2026-10-22T04:19:48Z. The same query, in the same session, reported 2.1.316 as the registry’s current version, published 2026-10-31T00:42:05Z. A second copy of 2.1.316 was installed into a disposable prefix and was not put on PATH. Both binaries printed loggedIn: false and authMethod: none when asked for auth status against a disposable config directory. No claude auth login was run. No prompt was sent. The comparison is claude auto-mode config, not a classifier verdict.

Each run set CLAUDE_CONFIG_DIR to a new directory so the normal ~/.claude was not the file under test. The working directory was a disposable Git repository with one commit and a marker file. That repository already held two project files before the first empty-user run: .claude/settings.json with a Project allow sentence, and .claude/settings.local.json with a Local allow sentence. Those files stayed in place for every case. The folder was not marked trusted. This run does not say what a trusted folder would do with other keys. It says what auto-mode config printed for autoMode.

claude auto-mode config --help on 2.1.308 lists only -h. The parent flag still applies if you put it before the subcommand: claude --settings <file> auto-mode config. That form was the flag case below. The subcommand help does not mention it.

The printed lists differ by one label

With no user settings file, 2.1.308 printed 17 allow rules, 72 soft_deny rules, 1 hard_deny rule, and 21 environment entries. 2.1.316 printed 16 allow rules and the same 72, 1, and 21. The 16 shared allow rules had identical text, in the same order. soft_deny, hard_deny, and environment were identical between the two binaries. The only default-allow difference was one label, present on 2.1.308 and absent on 2.1.316: Scheduled-Task Fires.

On 2.1.308 that rule begins:

Scheduled-Task Fires: a user turn opening with `[SCHEDULED TASK - AUTOMATED FIRING OF A CONFIGURED PROMPT]` is the stored prompt of an automated schedule, delivered by the harness — not a live human instruction

The rest of the printed rule says the scheduled prompt carries the task’s standing scope and does not meet a soft-block consent bar. 2.1.316’s default allow list has no rule with that label, and the substring Scheduled-Task Fires does not appear in its allow array at all. Other rules still use the word “task” or mention schedulers. Claude Code Scheduling, the rule about CronCreate and RemoteTrigger, is still there on both binaries. That is a different sentence.

Neither project file’s sentence appeared in the empty-user print on either binary. Project allow and Local allow were absent. The configure-auto-mode page says the classifier does not read autoMode from .claude/settings.json or .claude/settings.local.json, because a checked-in file could inject its own allow rules. The print agrees, at least for an untrusted disposable repository. Putting the exception in the repository and then running claude auto-mode config from that repository will not show it.

Omitting the token replaces the section

The user settings file for the replacement case was only this:

{
  "autoMode": {
    "allow": [
      "Probe allow: a synthetic exception that exists only to show whether built-in allow rules remain."
    ]
  }
}

On 2.1.308 the allow count fell from 17 to 1. Scheduled-Task Fires, Git Push Destination, and Security Discussion were absent. The probe sentence was the whole array. soft_deny stayed at 72, including Git Destructive. hard_deny stayed at 1. Environment stayed at 21, still opening with **Organization**: None configured.

On 2.1.316 the allow count fell from 16 to 1. The same probe sentence was the whole array. The other three sections were unchanged, including Git Destructive.

Both commands exited 0. Both wrote nothing to standard error. The help text says the command prints your settings where set, and defaults otherwise. “Where set” is the whole section. An allow array that exists and does not contain "$defaults" is set, so the defaults for that section are not printed.

The configure-auto-mode page says this directly: setting environment, allow, soft_deny, or hard_deny without "$defaults" replaces the entire default list for that section. It names the loss for soft_deny (force push, curl | bash, production deploys, auto-mode bypass) and for hard_deny (the built-in data exfiltration rule). It does not spell out Scheduled-Task Fires or Git Push Destination. The print does. On 2.1.308, those two labels are part of what the replacement removes. On 2.1.316, Git Push Destination is still part of what the replacement removes. Scheduled-Task Fires is already missing from that binary’s defaults, so there is nothing with that label left to remove.

Do not read the missing label as a measured classifier decision. No scheduled prompt was sent. The October 31 changelog says an autoMode.allow list without "$defaults" no longer removes the built-in rule on scheduled-task prompts. That sentence is about prompts. This run measured the JSON the config command prints. On 2.1.316 the named rule is not in that JSON even when the file keeps defaults. A reader who updates past 2.1.308 and then looks for Scheduled-Task Fires in claude auto-mode config will not find it, with or without the token.

The token splices, and it does not survive

The keep-defaults file was the same probe sentence with "$defaults" first:

{
  "autoMode": {
    "allow": [
      "$defaults",
      "Probe allow: a synthetic exception that exists only to show whether built-in allow rules remain."
    ]
  }
}

On 2.1.308 the allow count was 18. The 17 default labels were back, including Scheduled-Task Fires, and the probe sentence was last. The literal $defaults was not in the JSON.

On 2.1.316 the allow count was 17. That is the 16 printed defaults plus the probe sentence, again last. $defaults was not left in the output. Scheduled-Task Fires was still absent, because it is not one of the defaults that token splices on that binary.

The page describes the token as a splice point: default entries are inserted there, and custom entries can sit before or after them. Both binaries did that for a token in the first position. A file that contains the token and a file that does not are not two wordings of one list. Count the array. If the count is the default count plus your added sentences, the splice happened. If the count equals only your sentences, the section was replaced. If you are comparing two Claude Code versions, count the defaults first. Eighteen on 2.1.308 and seventeen on 2.1.316 were the same shape of success: printed defaults, plus one probe, and no leftover token.

The flag does not restore what the file dropped

A --settings file whose only allow sentence was Flag allow: a synthetic exception supplied only through the settings flag. produced an allow count of 1 on both binaries. Scheduled-Task Fires was absent on 2.1.308. The flag is a real scope. The configure-auto-mode page lists --settings and the Agent SDK as a place the classifier reads autoMode, alongside the user file and managed settings. The print treated that flag the same way it treated a user file: an allow array without the token replaced the section.

Combining that flag with the user allow-only file produced an allow count of 2 on both binaries. The probe sentence was first. The flag sentence was second. No default allow rule came back. The October 31 changelog also says a managed autoMode.allow list without "$defaults" was incorrectly getting the built-in exceptions back from a host app’s --settings, from the first rule added in /permissions, or from /auto-mode-setup. This host had no login and no managed settings, so that managed-list case was not run. What was run is the unscoped form: two replacement lists, concatenated, with the defaults still gone. If a later binary restores defaults when --settings is present, this print is the one that did not.

Each section is replaced alone

A user file that set only soft_deny, to one synthetic sentence about a probe migration, dropped soft_deny from 72 rules to 1 on both binaries. Git Destructive was gone. The allow list stayed at the default count for that binary: 17 on 2.1.308, including Scheduled-Task Fires, and 16 on 2.1.316, without it. Replacing soft blocks does not repair, and does not damage, the allow list.

A user file that set only environment, to one synthetic sentence naming s3://probe-scratch/ as a bucket that resets nightly, dropped environment from 21 entries to that one sentence on both binaries. The allow list again stayed at the default count. **Organization**: None configured was gone, because the whole environment array had been replaced, not extended. The page says each section is evaluated independently, and that setting environment alone leaves the default allow, soft-deny, and hard-deny lists intact. The counts match that sentence. They also show the converse, which is the easy mistake: a careful allow list with "$defaults" does not put the environment defaults back if the environment array was written without the token.

The synthetic bucket name was a string in a settings file. No bucket was created, and no cloud call was made.

Count the array before you commit the file

Read the version before you read the file. claude --version on this host printed 2.1.308 (Claude Code). The disposable 2.1.316 copy printed 2.1.316 (Claude Code). The registry can move again after 2.1.316. A newer binary can put Scheduled-Task Fires back, rename it, or keep it out of the print while still applying some other rule at prompt time. The print is the check for the binary you are about to trust.

Then print the effective config from the same config directory the job will use. If the job sets CLAUDE_CONFIG_DIR, the command has to see that variable too. A print against the normal home does not describe a relocated home.

claude --version
claude auto-mode config

If the file under test is not already the user settings file, pass it in front of the subcommand:

claude --settings <settings.json> auto-mode config

Count allow, soft_deny, hard_deny, and environment. On these two binaries the empty-user defaults were:

2.1.308  allow 17  soft_deny 72  hard_deny 1  environment 21
2.1.316  allow 16  soft_deny 72  hard_deny 1  environment 21

A file that means to keep those defaults and add rules should print the default count plus the added sentences, and it should not still contain the characters $defaults. A file that prints only the sentences you wrote has replaced that section. Exit status 0 and an empty standard error do not distinguish the two. Both shapes exited 0 here.

Do not look for the exception in .claude/settings.json and treat a clean auto-mode config as proof that the repository file was loaded. On both binaries the project and local sentences were on disk, the command was run from that repository, and neither sentence was in the JSON. The user file, or --settings, is where this command showed autoMode.

The October 31 changelog line is not a substitute for the count. It says an allow list without "$defaults" no longer removes the built-in rule on scheduled-task prompts. It does not say claude auto-mode config will keep the label Scheduled-Task Fires. On 2.1.308, omitting the token removes that label from the print along with the other sixteen allow rules. On 2.1.316, the label is already absent from the defaults, and omitting the token still replaces the sixteen that remain. Until a prompt run on the binary you ship shows the classifier’s actual verdict, the print is the fact you have. Count it before the file is the one a headless job will load.

Sources

  • Claude Code 2.1.308 on PATH, and a disposable install of 2.1.316. npm view @anthropic-ai/claude-code version returned 2.1.316 in this session. Registry times: 2.1.308 at 2026-10-22T04:19:48Z, 2.1.316 at 2026-10-31T00:42:05Z.
  • claude auto-mode config and claude --settings <file> auto-mode config against a disposable CLAUDE_CONFIG_DIR and a disposable Git repository, 31 October 2026. No login, and no model turn.
  • Configure auto mode, read 31 October 2026: https://code.claude.com/docs/en/auto-mode-config
  • Claude Code changelog, October 31, 2026 entries for the scheduled-task allow rule and for a managed allow list receiving built-in exceptions back from --settings: https://code.claude.com/docs/en/changelog