Predict permission cascade
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: `permissions.deny: ['Bash(rm -rf:*)']`. User settings: `permissions.allow: ['Bash(*)']`. Result: rm -rf is denied. Enterprise wins.
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.