Allow npm test, Gate Destructive Git: Claude Code Permissions

July 24, 2026 · Claude Certified Architect (CCA) Prep (part 30)

▶ Watch on YouTube & subscribe to The Stack Underflow

You are ten minutes into a session with Claude Code, and it stops to ask permission before every single npm test call. You approve it. It asks again two minutes later. You approve it again. By the twentieth prompt you are tempted to just turn permissions off entirely — except you also want it to keep asking before it runs git push --force or rm -rf. Those two goals feel like they’re in tension. They aren’t, and the reason they aren’t is the entire answer to this question.

The one-sentence version: Permissions in Claude Code are not a single on/off switch — they are scoped rules per tool pattern, so you can allow the safe, repetitive command and still gate the dangerous, irreversible one.

The question

You want npm test to run without prompting, but destructive git to still ask. Best config?

  • A) Turn off all permissions
  • B) Scoped permission rules — allow Bash(npm test:*), keep git behind ask/deny
  • C) Approve everything once
  • D) bypassPermissions mode

Pause here and pick an answer before you scroll — the mechanism matters more than the letter.

The answer

B. Scoped permission rules — allow Bash(npm test:*), keep git behind ask/deny.

Claude Code’s permission system is not a global toggle. It is a set of rules, each scoped to a specific tool-and-pattern combination, evaluated per tool call. Every time the agent wants to invoke a tool, that specific invocation gets checked against the rule set and resolves to exactly one of three outcomes: allow (run silently), ask (prompt you), or deny (refuse outright). That means “permissive for tests, cautious for destructive git” isn’t a workaround — it’s the system working exactly as designed. You write one rule that matches the npm test invocation pattern and puts it in the allow list, and you leave (or explicitly place) destructive git patterns in ask or deny. The two rules don’t conflict because they match different things.

Claude Code permission evaluation — per tool call

  Claude wants to run:  Bash(npm test)


  ┌─────────────────────────────────────────────┐
  │ permissions.allow:  ["Bash(npm test:*)"]  ✓  │ ← pattern matches → ALLOW
  │ permissions.ask:    ["Bash(git push:*)"]     │   (runs silently, no prompt)
  │ permissions.deny:   ["Bash(rm -rf *)"]       │
  └─────────────────────────────────────────────┘

  Claude wants to run:  Bash(git push --force)


  ┌─────────────────────────────────────────────┐
  │ permissions.allow:  ["Bash(npm test:*)"]     │
  │ permissions.ask:    ["Bash(git push:*)"]  ✓  │ ← pattern matches → ASK
  │ permissions.deny:   ["Bash(rm -rf *)"]       │   (you get prompted)
  └─────────────────────────────────────────────┘

  Claude wants to run:  Bash(rm -rf node_modules)


  ┌─────────────────────────────────────────────┐
  │ permissions.allow:  ["Bash(npm test:*)"]     │
  │ permissions.ask:    ["Bash(git push:*)"]     │
  │ permissions.deny:   ["Bash(rm -rf *)"]    ✓  │ ← pattern matches → DENY
  └─────────────────────────────────────────────┘                    (refused, no execution)

In settings.json, this is a permissions object with allow, ask, and deny arrays of tool-pattern strings — the pattern is the tool name plus an argument matcher, for example Bash(npm test:*) matches any npm test invocation, and Bash(git push:*) matches any git push invocation regardless of flags. Wildcards let you scope narrowly (Bash(npm test:*) only) or broadly (Bash(git *) catches every git subcommand). The safe, repetitive, side-effect-free command gets an allow rule so it never interrupts your flow. The irreversible command — force-push, hard reset, rm -rf — stays behind ask (or gets moved to deny if you never want it run at all, even with confirmation). Exact flag names and file paths shift release to release, so treat the strings above as illustrative and confirm current syntax in Anthropic’s official Claude Code docs before you write your own config.

Why the other options fail

  • A) Turn off all permissions. This removes the gate for everything, not just npm test. Destructive git now runs silently too — you’ve solved the annoyance and reintroduced the exact risk the question is asking you to avoid.
  • C) Approve everything once. A one-time approval is not a durable rule. It either applies to a single invocation (so the prompting comes right back next time) or, if it’s a broad “always allow this session” click, it isn’t scoped to npm test specifically — it quietly covers whatever else gets approved alongside it, including things you didn’t mean to allow.
  • D) bypassPermissions mode. This is a global mode that skips confirmation for every tool call across the board. It’s the same failure as option A wearing a different name: you get silence on npm test, but you also get silence on the git command that deletes a branch nobody meant to delete. A global bypass cannot express “allow this, ask for that” — only scoped rules can.

The concept behind it

The pattern here generalizes well past this one question, and it’s worth internalizing because Domain 3 leans on it repeatedly: Claude Code’s permission model is rule-based and additive, not a single switch. Every tool invocation — a bash command, a file write, a web fetch — gets matched against your configured rules and resolves to allow, ask, or deny. There is no fourth state and no implicit default beyond “ask” for anything not otherwise matched.

That gives you a design axis you can use deliberately: put commands on a spectrum from safe and repetitive to rare and irreversible, and write rules that match that spectrum.

Command patternRisk profileTypical rule
Bash(npm test:*)Repeatable, no side effects outside the sandboxallow
Bash(npm run lint:*)Repeatable, read-onlyallow
Bash(git commit:*)Reversible, low blast radiusallow or ask
Bash(git push:*)Affects shared state, hard to fully undoask
Bash(git push --force:*)Can destroy others’ historyask or deny
Bash(rm -rf *)Irreversible, destructivedeny

A few things worth knowing about how the rules actually behave:

  • Patterns are specific, not vague categories. Bash(npm test:*) matches an npm test invocation and its arguments — it does not implicitly cover npm run build or npm install. If you want a broader allow, you write a broader pattern; the system won’t guess your intent for you.
  • The three-way split (allow/ask/deny) exists for every tool, not just Bash. File writes, MCP tool calls, and web fetches go through the same evaluation. The npm test example is the clearest teaching case because everyone recognizes the annoyance, but the underlying mechanism is tool-agnostic.
  • Scoped rules compose with organizational policy. Your local allow/ask/deny rules sit inside a larger precedence hierarchy — managed enterprise policy can override what you set locally, which is a different (and equally testable) mechanism covered in the permission-precedence tutorial linked below.
  • Deny beats ask, and ask beats silence. When a command could match more than one rule, the more restrictive outcome should win — this is why deny is the right home for genuinely irreversible actions rather than relying on ask and hoping you’re paying attention every time.

The exam-relevant takeaway is not “memorize the exact bracket syntax,” because that syntax is exactly the kind of live-product detail Anthropic revises between releases. The durable fact is the shape of the system: permissions are evaluated per tool call against scoped patterns, with three possible outcomes, and you author rules to match your own risk tolerance per command category rather than accepting one blanket setting for the whole session.

FAQ

Does allowing npm test also allow other npm commands? No. A rule like Bash(npm test:*) matches that specific command pattern. npm install, npm run build, and other subcommands are unaffected unless you write separate rules for them (or a broader pattern that intentionally covers more).

Where do these permission rules actually live? In Claude Code’s settings files, as a permissions object with allow, ask, and deny arrays of tool-pattern strings. Which settings file you put them in — shared project settings versus your own local, gitignored settings — determines whether the rule applies to your whole team or just to you; that split is covered in the personal-vs-team settings tutorial below. Confirm the exact current file paths and key names in Anthropic’s docs, since this is one of the fastest-moving corners of Claude Code.

Is bypassPermissions mode ever the right call? Sometimes, deliberately — for example, a fully sandboxed CI job where nothing in the environment matters if it gets destroyed. But it is a global override, not a scoping tool, so it’s the wrong answer any time you want different behavior for different commands, which is most day-to-day development work.

What happens if a command doesn’t match any rule? It falls back to a prompt — you get asked. Scoped allow rules exist specifically to remove that prompt for the commands you’ve decided are safe to run unattended.

Where this fits in the CCA series

This question sits in Domain 3 — Claude Code Configuration & Workflows (20% of the exam), the domain that covers how you actually configure and govern day-to-day Claude Code usage. Permission scoping is one mechanism among several in this domain worth knowing cold:

For the full picture of how all five exam domains fit together, start with the Claude Certified Architect (CCA) Exam Guide.

Not affiliated with, authorized, or endorsed by Anthropic. “Claude” and “Claude Certified Architect” are trademarks of Anthropic. These are original practice questions for study, not real exam content — verify current exam details on Anthropic’s official pages.

Exam logistics as of mid-2026: Pearson VUE, $125, retakes available, 60 questions in 120 minutes, passing score 720/1000, five domains — verify current details on Anthropic’s official pages before you register. Browse all tutorials for the rest of the series.

Found this useful? The deep version lives on YouTube — new breakdowns of how AI dev tools actually work, weekly.

Subscribe on YouTube →