How Claude Code Rules Actually Work
The phrase "Claude Code rules" gets used loosely. Sometimes it means CLAUDE.md. Sometimes it means slash command definitions. Sometimes it means hook configurations. Sometimes it means MCP server permissions or tool restrictions. The pieces interact in non-obvious ways, and a rule that works in one place can be silently overridden by a rule somewhere else. This piece traces the actual loading order, what each layer can and cannot do, and where the most common confusions come from.
What "rules" means inside Claude Code
Claude Code, as of early 2026, has at least six places where rules can live. Each layer affects the agent differently, has its own loading semantics, and interacts with the others in specific ways.
The first is CLAUDE.md, both the user-global one (~/.claude/CLAUDE.md or ~/CLAUDE.md) and the project-local one. These are markdown prose, loaded into the agent's context at session start. They establish the operating context and behavioral norms.
The second is settings.json, again both user-global and project-local. This is where hooks, permissions, environment variables, and plugin configuration live. Settings.json is config, not prose — the agent does not read it; the harness does, and the harness enforces what it says.
The third is hooks — shell scripts triggered by lifecycle events (SessionStart, PreToolUse, PostToolUse, PreCompact, Stop). Hooks are configured in settings.json and executed by the Claude Code runtime. They can inject text into the session, block tool calls, run validations, or send notifications.
The fourth is slash commands — markdown files in ~/.claude/skills/ or .claude/skills/ whose names are invocation triggers. When you type /handoff or /review, you are invoking a skill. Skills can prescribe a workflow, restrict tool access, or require specific outputs.
The fifth is MCP server permissions — entries in settings.json that allow or deny specific tools from MCP servers. These are the finest-grained access controls.
The sixth is environment-level constraints — environment variables, permission modes (auto, plan, bypassPermissions), and runtime flags. These are usually set by the launching shell or by the Claude Code CLI itself.
The loading order
When a session starts, the harness loads in this order. Understanding the order is the difference between a rule that works and a rule that is silently overridden.
Step one: environment-level constraints load first. The permission mode is set, environment variables are bound, the working directory is established. These cannot be changed by anything inside the session.
Step two: settings.json is loaded. Both user-global and project-local. Project-local settings layer on top of user-global. Hooks are registered, MCP servers are connected, plugin lists are resolved.
Step three: SessionStart hooks fire. Each registered SessionStart hook runs as a shell command. Their stdout is injected into the session as system-reminder messages. This is the layer that can dynamically inject context — it is also the layer that introduces the most surprise, because hook output is invisible until the agent actually receives it.
Step four: CLAUDE.md is loaded. User-global first, then project-local. The contents are added to the session context. CLAUDE.md cannot override settings.json or hooks; it can only describe behavioral norms within the constraints those layers set.
Step five: the agent's first turn begins. The user's first message arrives. The agent's response is shaped by everything loaded above.
When something seems "wrong" — a rule isn't being followed, a tool isn't accessible, a hook isn't firing — the debug protocol is to walk back up the loading order and check each layer in turn.
What CLAUDE.md can and cannot do
CLAUDE.md is prose. It can describe norms, encode decisions, and explain reasoning. It cannot enforce.
It can say "All changes go through PR." It cannot prevent the agent from invoking git push directly to main. The push will happen if the harness allows it; only a PreToolUse hook on git push can actually block it.
It can say "Run lint before commit." It cannot run lint. Only a pre-commit hook (Husky or similar) can run lint.
It can say "Use TypeScript strict mode." It cannot make tsc strict. Only tsconfig.json can.
This is the single biggest confusion. Authors write a CLAUDE.md rule, see the agent follow it for a week, and conclude the rule is enforced. Then the agent has a different session — different context, different attention pressure, different temperature — and the rule is silently violated. The lesson is: every rule that matters needs a layer below CLAUDE.md doing the actual enforcement.
What hooks can do that CLAUDE.md cannot
Hooks are where the actual enforcement lives. Three patterns are common.
Block tool invocations. A PreToolUse hook on Bash can examine the command being run and exit non-zero to block it. The push-gate hook in many setups blocks git push origin main from a non-main branch unless a review step has run.
Inject context dynamically. A SessionStart hook can run a script that fetches the current branch, recent commits, open PRs, and inject a one-line summary into the session. This is how some teams give the agent a fresh briefing every session without bloating CLAUDE.md.
Validate post-action. A PostToolUse hook on Edit or Write can run a linter or typecheck on the changed file and inject the result. The agent sees the validation output as a system-reminder and can react in the same turn.
The catch with hooks is that they run on the user's machine, in the user's shell, with the user's environment. A hook script that assumes a tool is on PATH, that a directory exists, or that an environment variable is set will silently fail under different conditions. The robust pattern is to guard hook scripts with explicit checks and fallbacks.
Slash commands as scoped rule sets
Slash commands (skills) are where you scope rules to specific workflows. A skill is a markdown file with frontmatter and a body. The frontmatter declares triggers and constraints. The body is read into the agent's context when the skill is invoked.
This is how you say "for handoff workflows specifically, the file format must follow Schema A or B" without bloating CLAUDE.md with handoff-specific rules. The skill activates only when the workflow does. The rest of the time, the rules in the skill are dormant and do not consume context budget.
The 2026 trend is to move workflow-specific rules out of CLAUDE.md and into skills. CLAUDE.md becomes the always-loaded, project-wide rule set; skills become the workflow-loaded, scoped rule sets. This keeps CLAUDE.md short and lets specific workflows have richer rules without paying for them in every session.
Where the confusion comes from
Three patterns produce most of the "rules don't work" complaints.
Confusing CLAUDE.md with enforcement. Already covered above. CLAUDE.md describes; the harness enforces. If you don't have a hook or CI check below the rule, the rule is advisory.
Layered overrides without clarity. A rule in user-global CLAUDE.md is overridden by a contradicting rule in project-local CLAUDE.md, but the override is not always intended. The agent reads both files; if they contradict, the agent may pick either depending on phrasing. The fix is to make project-local rules say "overrides ~/CLAUDE.md § X" explicitly when overriding.
Hook scripts that fail silently. A SessionStart hook that errors does not crash the session — it just doesn't inject what it was supposed to inject. The session proceeds without the missing context. This produces "the agent didn't know X" bugs that are very hard to diagnose because nothing visible failed. The fix is to log hook failures somewhere persistent and check the log when behavior is surprising.
Putting it together
The mental model that keeps the layers straight: CLAUDE.md is the agent's reading material; settings.json + hooks are the agent's environment; skills are scoped briefings; environment constraints are the agent's hard limits. Rules live in the layer that has the right teeth. Process rules with hard consequences live in hooks. Workflow-specific rules live in skills. Behavioral norms live in CLAUDE.md. Hard access controls live in MCP permissions. Trying to enforce a rule from the wrong layer produces the silent failure mode that frustrates so many teams new to Claude Code.
If you are setting up a new project's rules, the order is: write ~/CLAUDE.md for global norms, write project CLAUDE.md for project-specific decisions, write hooks for enforcement of process rules, write skills for workflow-scoped rule sets, set MCP permissions for tool access. Run AgentLint to keep the CLAUDE.md files honest. The system feels intricate but the layering is consistent — and once the layering clicks, "Claude Code rules" stops being a vague phrase and becomes a specific architecture you can debug.