B5.2Bloom · AnNot started

Predict permission cascade

Reading depth

What you'll learn

Settings cascade in order: Enterprise > CLI > project-local (.local.json) > project-shared (.json) > user. Higher always wins. Local-project beats shared-project.

  • Precedence: Enterprise > CLI > project local > project shared > user.
  • Higher layers can lock fields lower layers cannot override.
  • Local project (`.local.json`) wins over shared project (`.json`). Inverse of common guess.

Claude Code's permission system is *cascading* — settings come from multiple layers, and the layers stack with a defined precedence order. Understanding the order is the difference between 'I added Allow * and it doesn't take effect' (because a higher layer Denies) and 'why did this command get blocked, I never set that' (because of an Enterprise-managed rule).

Precedence order, highest → lowest: **Enterprise-managed settings** (set by org IT, often via MDM) → **CLI flags** for the current invocation → **`.claude/settings.local.json`** (per-project, gitignored, your local override) → **`.claude/settings.json`** (per-project, checked in, shared with the team) → **`~/.claude/settings.json`** (per-user, your default across all projects). Higher layers can lock fields lower layers cannot override.

The trap to internalise: *local project settings override shared project settings*. Your `.claude/settings.local.json` (gitignored) wins over `.claude/settings.json` (committed) in your project. This is the inverse of what people often guess under exam pressure ('the team setting feels more authoritative'). The local-overrides-shared semantics is intentional — it lets individuals customise without forcing the team config to change.

How a single rule resolves: walk top-down. If Enterprise sets `permissions.deny: rm -rf` then nothing below can override it. If no Enterprise rule applies, look at CLI flags. Then settings.local.json. Then settings.json. Then user. The first explicit decision wins; absence of a rule means 'pass through.' For *deny* outcomes specifically: a deny anywhere in the chain is sticky if higher layers don't explicitly re-allow.

Key points

  • Precedence: Enterprise > CLI > project local > project shared > user.
  • Higher layers can lock fields lower layers cannot override.
  • Local project (`.local.json`) wins over shared project (`.json`). Inverse of common guess.
  • First explicit decision in the cascade wins; absence = pass-through.
  • Enterprise deny is sticky and intentional — IT's veto.

Examples

Enterprise deny + user allow

Enterprise: `permissions.deny: ['Bash(rm -rf:*)']`. User settings: `permissions.allow: ['Bash(*)']`. Result: rm -rf is denied. Enterprise wins.

Local override of shared

Project shared (`.claude/settings.json`): `permissions.deny: ['Bash(npm publish:*)']`. Local (`.claude/settings.local.json`): `permissions.allow: ['Bash(npm publish:*)']`. Result on this machine: allowed. The local file overrides the shared one for this user.

Pitfalls

  • Inverting local vs shared. Under exam pressure, people often guess 'shared/team wins' — wrong. Local wins.
  • Forgetting that CLI flags sit *between* Enterprise and per-project. They're scoped to the current invocation.
  • Thinking absence of a rule means 'deny.' Absence means 'pass through to the next layer'; default is allow unless something denies.
  • Treating Enterprise rules as suggestions. They're locks; nothing below can override.

Source notes: 00-academy-basics/notes/03-claude-code-101.md

Ask Claude

Build a prompt with this lesson + your question, copy it, and open the Claude Project in a new tab.