Module 03 · 20 min

Rules

You can predict which instruction files load, in what order, and why none of them override each other.

Surface
CLAUDE.md · .claude/rules/

Four locations hold instruction files, and Claude Code reads all four. In load order, broadest first:

A managed policy file, placed by an administrator. On macOS that is /Library/Application Support/ClaudeCode/CLAUDE.md, on Linux and WSL /etc/claude-code/CLAUDE.md, on Windows C:\Program Files\ClaudeCode\CLAUDE.md.

Your user file, ~/.claude/CLAUDE.md. It applies to every project you open.

The project file, ./CLAUDE.md or ./.claude/CLAUDE.md.

And the local file, ./CLAUDE.local.md, which you gitignore because it holds the instructions that are yours rather than the team’s.

Alongside those sit rules: .claude/rules/*.md for the project and ~/.claude/rules/*.md for you. Both are found recursively, which means Claude Code looks inside those folders, then inside every folder within them, all the way down.

Nothing overrides anything

The docs put it plainly: all discovered files are concatenated into context rather than overriding each other. Concatenated means stuck end to end, in order, like pages in one long document. No file replaces another and no file cancels another.

The order comes from a walk down the folders. Claude Code starts at the filesystem root, the very top of the drive, and works down to the folder you are working in, collecting what it finds. Within each folder, CLAUDE.local.md is added after CLAUDE.md. User-level rules load before project rules, which the docs describe as giving project rules higher priority.

That priority comes from position and nothing else. Everything collected lands in the context window, the single block of text the model can read at one time, and the project rule lands later in that block. Later text tends to weigh more. The user rule is still sitting there in full.

Look at the top of .claude/rules/api.md in the tree below and you will see a short block fenced by two lines of three dashes. That block is called frontmatter, and it holds settings for the file rather than instructions for Claude. A rule whose frontmatter has no paths list loads at launch with the same priority as .claude/CLAUDE.md. A CLAUDE.md in a subdirectory loads on demand, when Claude reads a file in that directory.

What paths does is narrow a rule to part of the repository. The set of files something applies to is its scope, and a rule with a paths list is scoped to those files. You will meet the word on every surface in this course.

A layout you can read top to bottom

my-project/
├─ .claude/
│ └─ rules/
├─ src/
│ └─ api/
CLAUDE.md

The project file. No frontmatter exists for CLAUDE.md; it is plain markdown.

# my-project

Node 22, pnpm, TypeScript strict.
Run tests with `pnpm test` before proposing a commit.
Keep functions under 40 lines where the logic allows.
Read more

Target under 200 lines. Claude Code loads a CLAUDE.md up to 4 MiB in full and skips anything larger, so an oversized file is not truncated, it is absent. Block-level HTML comments are stripped before injection, which gives you a free place for maintainer notes.

Four files, all of them loaded, none of them overriding the others.

Open a session in this project and touch a file under src/api/. All four files are in context: root CLAUDE.md, then CLAUDE.local.md, then the unconditional and matching rules, then the nested CLAUDE.md when the read happens. You can see the list yourself. /context prints a Memory files section, /memory opens them, and the InstructionsLoaded hook fires when they load.

Build your own

Write a CLAUDE.md at the root of a project you actually work in. Keep it under 200 lines. Put in it the things you find yourself repeating: the package manager, the test command, the one architectural constraint a newcomer always breaks.

Then add one rule scoped to a single directory. Pick a directory with a convention the rest of the repo does not share, and write .claude/rules/<something>.md with a paths: list and three or four bullets. Nothing else. paths is the only field.

If your instructions are getting long, split them and pull them back in with an import. Writing @path/to/file on a line of a memory file, which is what Claude Code calls the CLAUDE.md family listed at the top of this page, pulls that file in where the line sits. A relative path is read from the folder of the file doing the importing, and an imported file can import another, up to four levels deep. Imports inside code spans and fenced blocks are skipped, so a literal `@README` in a code span stays literal. Imports do not save you context: imported files load at launch, same as everything else.

Ships to your plugin

Nothing. This is the first of two modules that produce no plugin artifact. A plugin cannot carry a CLAUDE.md, because instruction files belong to a repository and a person rather than to a distributable package. Your rules stay in the repo you wrote them for.

The mistake most people make first

You have a root CLAUDE.md that says:

Prefer named exports throughout the codebase.

Then you start a legacy directory that uses default exports everywhere, and you write a rule to carve out the exception:

---
paths:
  - "src/legacy/**/*.ts"
---

Use default exports in this directory.

You expect the narrower rule to win inside src/legacy/. That is how CSS works. It is how settings files work, where a fixed order decides which file wins and the losing value is discarded; that fixed order is called precedence, and module 6 is built on it. It is how nearly every configuration system you have used works. It is not how this works.

What actually happens: both files load. The context window now contains “prefer named exports throughout the codebase” and “use default exports in this directory,” in that order, with no relationship between them declared anywhere. There is no override, no shadowing, no error. Claude reads two instructions that point in opposite directions and resolves the conflict however it resolves any ambiguity in a prompt, which is to say inconsistently. Some sessions you get default exports. Some you get named ones and a note about the convention.

The fix is not a precedence mechanism, because there isn’t one. Write the exception into the broader file so the two sentences are one instruction:

Prefer named exports, except under `src/legacy/`, which uses default exports.

Or, when the exception is large enough to deserve its own file, make the root file explicitly hand off: “Directory-specific conventions live in .claude/rules/; follow the narrower file where one applies.” You are supplying the precedence yourself, in prose, because concatenation will not supply it for you.

The tell

Whenever you catch yourself writing “this overrides” in an instruction file, you are describing behaviour the system does not have. Rewrite it as a single statement that covers both cases.

Does this move to another harness?

The content ports everywhere; only the filename changes. Rename your file AGENTS.md at the repo root and most other harnesses read it, which is the closest thing to a shared convention here. The traffic is one-way, though: Claude Code reads CLAUDE.md, not AGENTS.md. To close the loop, put @AGENTS.md as an import at the top of a CLAUDE.md, or symlink one to the other. On Windows, use the import, since symlinks need Administrator or Developer Mode.

Check yourself

  1. Your user CLAUDE.md asks for comments in English. The project CLAUDE.md asks for French. You delete neither. What decides which language you get?
  2. A rule’s paths list has an unclosed bracket in its glob, so the rule never fires and nothing is printed. Where do you look to find out whether it loaded?
  3. You move half of your root CLAUDE.md into a second file and pull it back in with @docs/conventions.md. How much room in the context window does that save you?