How to write AGENTS.md starts with who a fact stays true for, and whether it should change a coding agent’s next file or command. Repo-wide maps, canonical commands, and cross-module boundaries belong at the repository root. Rules that only bind backend, frontend, or a worker sit in that directory. Habits that hold across repositories stay in the user-global file. What this task must change stays in the current prompt. CLAUDE.md uses the same content boundary; Claude Code just finds it through its own files.
If the root file also stores frontend commands, backend migrations, a release workflow, and “do not touch deploy this time,” four different lifetimes enter unrelated sessions. Confirm the fact in code or build config first, then keep the long-lived rule in the smallest scope that still needs it. Workflows, task variables, and hard stops belong to other mechanisms. The same scope-and-mechanism split is already mapped in Claude Code configuration.
How to write AGENTS.md starts by filtering long-lived project facts
A line belongs in project rules only when it is true for that scope for a long time, it changes which file or command the agent should pick, and a repo fact or a team decision can check it.
“Frontend API types are generated from the contract command; do not hand-edit the generated directory” meets all three. It tells the agent which source to edit, which generator to run, and which consumer to check. “Keep the code high quality” names no object, no action, and no observable result, so it does not help at any layer.
Project rules also do not store every useful note. A one-off goal expires with the prompt. A multi-step flow that only matters for some tasks belongs in a skill. Formatting that must run after every save belongs in a hook or the existing toolchain. Tokens, cookies, and private keys stay in secret storage or environment injection. A rule can explain a boundary. It cannot replace permissions, a sandbox, or CI.
| Information to place | Where it belongs | Why |
|---|---|---|
| Reply language used in every repository | User-global rules | Personal and stable across projects |
| Repo module map and public contract boundary | Root project rules | Every directory needs it |
A migration command that is only true under backend/ | Backend directory rules | Other directories should not see it |
| This change only edits the login error copy | Current prompt | It expires with the task |
| Multi-step contract upgrade and verification | Skill | Load it only for matching work |
| Do not send requests to production hosts | Permissions, network policy, or a hook | The model following a sentence is not enough |
The source of truth is still the engineering system. If package.json, a Makefile, or CI already owns the test entry, the rule cites that entry and the directory it applies to. It does not copy a script that will rot. If OpenAPI, Protobuf, or a schema file already owns a public field, the rule says “change the source and verify direct consumers.” It does not paste the field table into markdown.
How Codex finds AGENTS.md
Codex builds an instruction chain when a run starts. OpenAI’s AGENTS.md guide(opens in a new tab) says it first checks Codex home, which defaults to ~/.codex/. A non-empty AGENTS.override.md at that layer wins; otherwise Codex reads AGENTS.md. That layer is for working habits that stay true across repositories. It is the wrong place for a project path or a business rule.
Project files start at the project root and walk down to the current working directory. In each directory Codex looks for AGENTS.override.md, then AGENTS.md, then any names in project_doc_fallback_filenames. It takes at most one file per directory. Selected files are concatenated from the root down, so closer rules appear later.
If the current directory is repo/frontend/, the chain can look like this:
~/.codex/AGENTS.md
repo/AGENTS.md
repo/frontend/AGENTS.md
repo/backend/AGENTS.md is not on that path, so it does not enter the current chain just because it lives in the same repository. If repo/frontend/AGENTS.override.md is non-empty, the ordinary AGENTS.md in that directory is not loaded with it. Override replaces the entry at that layer. It does not append a few lines.
Codex also has a merge-size limit. The same guide states that project_doc_max_bytes caps how much project instruction text is read, and the current default is 32 KiB. After the cap, later files are not added. When the second half of a file is always missing, check the actual byte size and whether local rules should move down a directory before raising the limit. Sinking a rule that is only true in a smaller scope fixes noise and capacity at the same time.
How Claude Code finds CLAUDE.md
Claude Code’s project-instruction entry is CLAUDE.md, not a bare AGENTS.md. Anthropic’s CLAUDE.md documentation(opens in a new tab) splits scope into managed policy, user, project, and local: ~/.claude/CLAUDE.md applies to every project for the current user; ./CLAUDE.md or ./.claude/CLAUDE.md is shared with the repo; ./CLAUDE.local.md holds personal project notes that should not be committed.
At launch, Claude Code loads CLAUDE.md and CLAUDE.local.md from the current working directory and every parent. Content is ordered from the filesystem root down to the working directory. In the same directory, the local file is appended after the shared file. The files are concatenated. A closer file does not delete a farther one. If two layers write opposite rules, the official docs warn that Claude may pick either sentence.
CLAUDE.md under the current working directory loads later, when Claude reads files in that subdirectory. After a session starts at the repo root, a missing frontend/CLAUDE.md in context is not automatically a fault. Read a target file under frontend/ first, then check /context for the source.
Large projects can also use .claude/rules/*.md. Rules without paths front matter load at launch. Rules with paths load when Claude works with matching files. This rule only binds frontend source in a demo project:
---
paths:
- "frontend/src/**/*.{ts,tsx,vue}"
---
# Frontend rules
- API types are generated from `contracts/`. Do not hand-edit `frontend/src/generated/`.
Path-scoped rules are for Claude Code differences that are already expressed as file patterns. If Codex also needs the same fact, keep the shared text in that directory’s AGENTS.md and import it from the directory CLAUDE.md. .claude/rules/ then only adds Claude-only path behavior. It does not copy the full body.
What belongs in global, root, and directory rules
The wider a rule applies, the more stable and technology-agnostic it should be. Global rules represent one person: language, communication style, and operating limits that hold across repositories. Putting “run this command in frontend/” in a global file sends a missing path into every unrelated repo.
Root rules represent the project team. They only keep facts every major directory needs: which modules own which responsibility, where the canonical build and test entries live, which files are generated, which direct consumers a cross-module change must check, and which external actions need extra authorization.
Directory rules represent the nearest engineering owner. backend/ can describe transactions, migrations, and service tests. frontend/ can describe routing, the request layer, and browser checks. worker/ can describe idempotency, retries, and job entrypoints. deployments/ can describe environment differences and authorization boundaries. A directory file does not repeat the root, and it does not decide how a sibling module is implemented.
Machine-local content describes this laptop. It must not pretend to be a team fact. A personal sandbox URL, a test-account alias, or an absolute tool path can live in Claude’s CLAUDE.local.md or in Codex user config. It cannot include real credentials. A runtime version the team must share belongs in a version file or build config, not in each person’s local rules.
Build a rule tree from a full-stack repo
The demonstration repository has a server, an admin UI, a user UI, async jobs, deploy config, and a shared contract. The names mark responsibility. They are not a rename requirement:
repo/
├── backend/ # API, database, domain services
├── admin/ # operator UI
├── frontend/ # user-facing pages
├── worker/ # async jobs and scheduling
├── contracts/ # schema source consumed by more than one client
└── deployments/ # environment and delivery config
Assume the repo’s existing build files already confirm this: the public contract lives in contracts/openapi.yaml, make contracts-check verifies generation, backend tests run go test ./... from backend/, and the user UI runs pnpm --dir frontend test from the repo root while generating frontend/src/generated/ from the contract. Those commands belong to this demonstration repository. A real project uses its own build files.
The original root file stored all of that together. After the split, scope and trigger decide the owner:
| Original root content | Owner after the split | What the root still keeps |
|---|---|---|
| Backend migration commands and transaction limits | backend/ directory rules | Data changes that touch the public contract must check direct consumers |
| Frontend test command and generated directory | frontend/ directory rules | Generated files change through the source and the generator |
| Full release judgment and operating flow | Project-level skill | Which class of task must call that flow |
| Do not change deploy config this time | Current prompt | Deploy boundaries that need extra authorization |
| Syntax check after saving a contract | Hook or the existing toolchain | The producer/consumer relationship for the contract |
“A contract change must check producers and direct consumers” is true for more than one directory, so the root owns it. “After a backend API change, run go test ./...” is only true under backend/. “Do not hand-edit frontend/src/generated/” is only true under frontend/. The root does not import every subdirectory file. It names directory responsibility and the cross-module boundary.
When a task starts in frontend/, the agent gets the repo-wide contract boundary and the frontend local commands. It does not need database migration detail. When a task starts in backend/, it gets the same public boundary and the backend test entry. A contract-spanning task has to read both sides. The root then requires tracking producers and consumers; each directory rule supplies implementation and verification detail.
What the root rule keeps
The root file does not need a fixed chapter count. For the demonstration repository, a repo map, canonical command entries, cross-module boundaries, and result checks already answer where to edit, what to run, and where to confirm the result.
# Project rules
## Repo map
- `backend/` owns API and database changes.
- `frontend/` owns user-facing pages.
- `contracts/` owns API schema sources and client generation input.
- Before changing a module, read the nearest directory rules.
## Canonical commands
- After changing `contracts/openapi.yaml`, run `make contracts-check`.
- Each stack’s test entry lives in that directory’s rules.
## Cross-module boundaries
- Generated files change only through their source and generator.
- A public contract change must track producers and direct consumers.
- Prompts, rules, command output, and version control must not carry secrets.
## Result checks
- Check both the repository command and the affected API, worker, or browser result.
- If a required environment is unavailable, say so. Do not infer a pass.
This is not a template to copy into every repository. If the project has no generated contract, delete those three lines. If the project is one application, do not invent module boundaries. Each rule should answer a concrete choice: where to edit, what to run, what not to edit directly, and where to observe the result.
README still helps humans understand the project, install software, and contribute. A root rule can point at a stable original in the README or an architecture doc and quote only the boundary that changes agent action. Copying the whole file creates two project descriptions to keep in sync, and it puts human-only background into every session.
How directory rules cover the nearest module
A directory file only adds local differences the root does not already state. In the demonstration repository, backend/AGENTS.md can say:
# Backend rules
- Run backend commands from this directory.
- After an API behavior change, run `go test ./...`.
- Change database migrations together with the code that consumes that shape.
- When an API response changes, verify the contract source and the direct clients.
frontend/AGENTS.md owns a different set of choices:
# Frontend rules
- From the repo root, run `pnpm --dir frontend test`.
- Do not hand-edit `frontend/src/generated/`. Change the contract source instead.
- After a page-behavior change, check the user-visible state in a browser.
Neither directory repeats “secrets stay out of git” or “a public contract must be checked on both sides,” because the root already covers those. Backend rules do not pick a frontend state library. Frontend rules do not describe database transactions. If a requirement is only true for a deeper package, sink it again. The split follows the responsibility boundary, not a line budget.
When a directory is renamed, a command moves, or a generated path changes, update the matching rule in the same engineering change. A rule only the docs maintainer knows has already failed: the agent will keep running the old command, and code review will struggle to see why it picked wrong.
Keep one source of truth for AGENTS.md and CLAUDE.md
Which file is canonical depends on the clients the repo actually uses and the files already present. It does not depend on which name looks more standard.
| How the repo is used today | Shared original | How the other client joins |
|---|---|---|
| Codex only | AGENTS.md | Do not add CLAUDE.md |
| Claude Code only | CLAUDE.md | Do not add AGENTS.md |
Both, and AGENTS.md already exists | AGENTS.md | CLAUDE.md imports @AGENTS.md |
Both, and CLAUDE.md already exists | CLAUDE.md | Team-wide Codex fallback; if that cannot be unified, move to the row above |
When both clients are in use, shared body text can have only one original. CLAUDE.md and a Codex fallback only help each client find that original. They do not copy the project facts.
If the repo already uses AGENTS.md, Claude Code’s docs support importing it from the root CLAUDE.md:
@AGENTS.md
Claude-only behavior can continue after that import line. It must not repeat text already in AGENTS.md. The same pattern applies in a directory that has local shared rules: that directory’s CLAUDE.md imports the sibling AGENTS.md. Import paths resolve relative to the CLAUDE.md that contains them. To mention an @ path without importing it, put the path in code formatting. Import only shares text. It does not convert Claude Code and Codex settings, permissions, or hook formats.
If the existing original is CLAUDE.md, Codex can add a fallback name in ~/.codex/config.toml:
project_doc_fallback_filenames = ["CLAUDE.md"]
Every Codex environment on the team has to carry the same fallback. Codex still takes at most one file per directory, in the order AGENTS.override.md, AGENTS.md, then fallback names. Once AGENTS.md exists in that directory, fallback CLAUDE.md is not added. If the fallback cannot be made consistent, move the shared facts to AGENTS.md and let Claude Code import them. Do not let some teammates silently miss the file.
Official docs also allow CLAUDE.md to symlink to AGENTS.md when there is no Claude-only content. Symlinks are affected by Windows permissions, container mounts, packaging, and how a tool resolves paths. Cross-platform teams can review an import more easily. Either way, verify loading in both clients. Identical file bytes do not prove identical runtime behavior.
When rules conflict, fix the source of truth first
Precedence only decides the order a client sees text. It does not tell the team which copy to maintain. If the root says “run every test from the repo root” and the backend directory says “run from backend/,” and the build files only support the latter, fix the root’s wrong scope. Do not rely on the closer file to contradict it forever.
Conflicts usually come from four places: the same command was copied into several layers and only one copy was updated; a local rule was lifted to the root; each client keeps a near-duplicate body; a one-off task or a temporary state stayed in a long-lived file. Repair starts in package.json, the Makefile, the contract, the registration map, or an architecture decision. Cite that fact in the smallest rule layer that needs it, and delete the other copies.
AGENTS.override.md and CLAUDE.local.md are also the wrong place to hide a broken shared rule forever. The first replaces Codex’s ordinary file in that directory. The second loads after Claude Code’s shared file in that directory. One laptop can look fine while everyone else still hits the original collision. If a shared fact is wrong, change the shared file. Local files only keep real personal differences.
When a file gets longer, fix ownership before compressing sentences. Anthropic’s CLAUDE.md documentation(opens in a new tab) targets under 200 lines per file. Claude Code still loads a CLAUDE.md up to 4 MiB in full and skips a larger file, so the 200-line target is about adherence, not a hard cap. Codex uses project_doc_max_bytes to cap merged reads. Neither number is a quota to fill, and neither is a best length for every AGENTS.md. Sink directory-specific content. Move flows that are only needed sometimes into a skill. Leave checks a program can run to a hook or CI. Put background explanation back in ordinary engineering docs.
A 2026 study of configuration smells in AGENTS.md and CLAUDE.md(opens in a new tab) across 100 public repositories listed context bloat, conflicting instructions, lint-rule leakage, and skill-content leakage. That sample cannot give every project a length. It does show that rule count and rule quality are not the same metric. After a delete or a sink, still run a representative task and confirm the agent can find the right file, command, and boundary.
Prove the rules actually took effect
Verification watches loaded sources, the agent’s choice, and a software result, in that order. A file on disk only proves it was written. An agent quoting a rule only proves the sentence entered context.
From the repo root, Codex can run the official read-only smoke command:
codex --ask-for-approval never "Summarize the current instructions."
Then point --cd at the real subdirectory and compare the closer source:
codex --cd frontend --ask-for-approval never "Show which instruction files are active."
When those files exist, the second result should include the user layer, the repo root, and the target directory. It should not include backend/AGENTS.md if that path is off the chain. If the same directory has an override, confirm the ordinary file did not load with it. After a config edit, start a new command or a new session so an old instruction chain is not mistaken for the current one.
For Claude Code, start a new session at the repo root and in the target subdirectory, run /context, and check Memory files for CLAUDE.md, imports, and local sources. For a rule below the current directory, read a matching file first, then inspect context. When .claude/rules/ uses paths, also verify with one matching file and one non-matching file.
| Observable symptom | Check first | Where to fix it |
|---|---|---|
| No project rules at all | Launch directory, project root, filename, Codex home, or Claude Code --setting-sources | The discovery entry, not a rewritten prompt |
| Root rules load, directory rules do not | Codex working directory; whether Claude already read a target file; whether paths match | Working directory or the nearest directory rule |
| Unexpected content loaded | Same-directory override or local file, import chain, fallback, parent files | The source that actually added the extra text |
| The second half never reaches Codex | Whether merged content hit project_doc_max_bytes | Sink local rules; raise the cap only if the text still belongs to one scope |
| Sources are correct, choices still drift | Vague wording, contradictory rules, a hard action left to the model | The rule’s source of truth, or permissions, a hook, or CI |
After the load is correct, ask the same no-write question in both places: “After a code change in this directory, which smallest verify entry should run, and where does that instruction come from?” A root answer should name a shared entry or say to keep reading module rules. A frontend/ answer should point at the frontend command and must not drag in backend migrations. If the answer still cites a path that does not exist, inspect duplicate rules and stale originals. Do not keep pasting corrections into the prompt.
Then run one small representative task. In the demonstration repository, a frontend generated type should follow a contract change: the agent edits contracts/openapi.yaml first, runs generation and make contracts-check, then runs the frontend tests. The diff should not treat frontend/src/generated/ as a hand-edited source. Project rules have joined delivery only when the file choice, the command result, and the direct consumer all match the rule.
If the load is correct and the result is still unstable, the rule may be too abstract, it may conflict, or it may leave a hard requirement to the model. Change “do not edit generated files” into a source, a generator, and a result path. Hand actions that must be blocked to permissions, a hook, or CI. AGENTS.md and CLAUDE.md supply long-lived project context. They are not the last control a system cannot afford to miss.
When loaded sources and the software result agree, the repository already has a working answer to how to write AGENTS.md. A new task prompt only states the state this change must reach, the known facts, the range, and the result. It does not copy the directory map and every command. That prompt is the next job.