Field note · 14 October 2026 · Daniel Wright

I chose Yes, and don't ask again on a Bash prompt. The same command still asked. Official docs: deny, then ask, then allow. A local allow does not outrank a matching ask.

A large brass plate engraved ASK stands in the path on a dark wooden desk, a smaller brass plate engraved ALLOW set aside, a blank cream card, and a closed navy notebook
The brass plate in the path is the ask. The smaller plate is the allow that never gets a turn.

The annoyance

I chose Yes, and don't ask again on a Bash prompt for npm test. Claude Code wrote Bash(npm test) into .claude/settings.local.json at the git root. I asked for the same command in the next session. The prompt was back.

The project's .claude/settings.json already held Bash(npm *) under ask. The new rule was narrower, and the local file sits above the shared project file for an ordinary key. I treated that as the allow winning. npm test still stopped.

What Anthropic actually says

From the official Configure permissions page, under Manage permissions:

“Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome, and rule specificity doesn't change the order.”
“The same precedence applies between ask and allow: a matching ask rule prompts even when a more specific allow rule also matches the same call.”

Bash(npm test) is narrower than Bash(npm *). The ask matches first.

The permission system says where the click is stored, for a Bash command or a WebFetch domain:

“When you choose "Yes, and don't ask again" and the approval saves permanently, such as for a Bash command or a WebFetch domain, Claude Code saves the rule to .claude/settings.local.json at the root of the git repository, resolved through worktrees to the main checkout.”

A file edit lasts until the session ends and is not written to the file. Outside a git repository, or on Windows, the save path can differ. Mine was a Bash allow at the root. The ask stayed in the project file.

From the official settings page, under Permission rules combine differently than you expected:

“You chose "Yes, and don't ask again" on a permission prompt but still get prompted for the same tool. That choice saved an allow rule to your local file, and an allow rule there doesn't outrank an ask rule from a project or managed file; how permission rules combine explains the order.”

The CLI writes only to the local file. VS Code can save the allow into the shared project file, for everyone. A matching ask still wins, and a managed ask is changed only in managed settings.

/permissions is the list that names the file:

“You can view and manage Claude Code's tool permissions with /permissions. The dialog lists all permission rules and the settings.json file each rule comes from.”

CLAUDE.md Never Skips Permission Prompts never reaches that list. Bypass Permissions in Project Settings Does Nothing is a mode the project file cannot set. This note is an allow that did save, and still lost.

The fix

The ask that kept prompting, in .claude/settings.json or in the managed file /permissions names:

.claude/settings.json · the ask that keeps prompting
{
  "permissions": {
    "ask": [
      "Bash(npm *)"
    ]
  }
}

Yes, and don't ask again put Bash(npm test) under permissions.allow in .claude/settings.local.json. Pasting that allow beside Bash(npm *) still prompts, in the local file or in the same file. The ask matches first.

Narrow or remove the ask so npm test no longer matches. To share the exception, put the allow in the file that owns the ask and narrow that ask in the same edit. Bash(npm publish *) still asks before a publish. Once the ask no longer matches, a local allow is enough. This is the shared file:

.claude/settings.json · narrow the ask, allow in the same file
{
  "permissions": {
    "ask": [
      "Bash(npm publish *)"
    ],
    "allow": [
      "Bash(npm test)"
    ]
  }
}

Prove it

Open /permissions first. The ask row is Bash(npm *) from .claude/settings.json, or from managed settings, and the allow row is Bash(npm test) from .claude/settings.local.json. After the edit, the ask is Bash(npm publish *) or gone.

Self-check · /permissions, then npm test
# /permissions, before the edit
# ask    Bash(npm *)      .claude/settings.json
# allow  Bash(npm test)   .claude/settings.local.json
# Ask for: npm test
# The prompt is still there.

# After .claude/settings.json is the fix above
# ask    Bash(npm publish *)   .claude/settings.json
# allow  Bash(npm test)        .claude/settings.json
# Ask for: npm test
# It proceeds.
# Ask for: npm publish
# The prompt is still there.

Ask for npm test. It proceeds. Ask for npm publish. The prompt is still there.

Checklist

  1. Open /permissions and read the settings file named beside each rule.
  2. An allow in .claude/settings.local.json does not outrank an ask in the project file or in managed settings.
  3. A more specific allow still loses while the broader ask matches, including when both rules sit in the same file.
  4. Narrow or remove the ask. Put the allow in that same file when the team should share it.
  5. A managed ask changes only in managed settings.
  6. Ask for npm test again. It proceeds only when no ask still matches it. npm publish should still prompt.

Why it fails

I had ranked the local file above the project file, and the specific pattern above the broad one. The lists merge. Deny, then ask, then allow. The allow was saved. The ask matched first.

Same family as CLAUDE.md Never Skips Permission Prompts, where a sentence never grants the call, and PermissionRequest Exit 2 Does Not Deny, where the code I thought was the decision is not. This one is a local allow behind an ask that still matches.

I keep the operator notes for this series in the NavAIgate classroom at https://www.skool.com/navaigate.

Sources

Make the next AI decision concrete.

NavAIgate helps leadership teams identify high-value AI opportunities, prove them safely and turn the winners into working systems.