In short. Start using two AI coding agents and you end up with two instruction files. Claude Code reads CLAUDE.md and Codex reads AGENTS.md. The moment you maintain both, they begin to drift. This post covers the arrangement that keeps one original with every other path pointing at it. It also covers how to split per agent inside that file.
Copied instructions always drift apart
The easiest fix is a copy. On the day you copy them the two files are identical. The problem is the day you edit only one of them. You usually notice the mismatch after an agent has broken a rule. By then it is not clear which side is current either.
I measured my own home directory. The instruction files at the two paths had the same md5. They were separate files, not a link. That means I had been keeping them in step by hand, and the next one-sided edit would split them. The file itself said it would "later be merged with git if the two are not identical". That sentence admits what copy-based management costs.
There is one principle. Keep one original, and have every other path point at it. The whole job is that each tool points differently.
The side with imports is the side that links
The two tools do not look for files symmetrically.
| Item | Claude Code | Codex CLI |
|---|---|---|
| File it reads | CLAUDE.md (it does not read AGENTS.md) | AGENTS.override.md → AGENTS.md → fallback |
| Global path | ~/.claude/CLAUDE.md | ~/.codex/AGENTS.md |
| Importing another file | @path supported, up to 4 hops | None (still a proposal) |
| Recognising other names | None | project_doc_fallback_filenames |
The direction is decided here. Claude Code can pull another file in from inside its own. Codex cannot, and instead extends the list of filenames it will read. So if you are starting fresh, make AGENTS.md the original. The side with imports can follow along in one line.
@AGENTS.md
## Claude Code only
- Everything from here is read by Claude alone.
Reduce CLAUDE.md to those two lines. The shared instructions then exist in one place, AGENTS.md. You can continue with Claude-only content underneath the import. That is what separates this from a symlink.
If the original in your setup is already CLAUDE.md, change the Codex side instead. One line in ~/.codex/config.toml:
project_doc_fallback_filenames = ["CLAUDE.md"]
Codex picks exactly one file per directory. It looks for AGENTS.override.md, then AGENTS.md, then the fallback, and uses the first one it finds. So in a directory that has an AGENTS.md next to it, CLAUDE.md is not read. Do not put both files in the same folder and expect both to be picked up.
An AGENTS.override.md in the same place temporarily overrides that directory's instructions. The original is not deleted. When you want to run one project under different rules for a while, the original stays untouched.
The third option is a symlink. It settles the matter in the filesystem without touching either tool's configuration.
ln -s AGENTS.md CLAUDE.md
On Windows, creating a symlink requires administrator rights or developer mode. Claude Code's own documentation recommends the @AGENTS.md import over a symlink on Windows. Git stores symlinks in mode 120000, so you can commit the link itself. It is quieter to put only the original in the repository and leave the link to each machine.
If one file faces two agents, say so in the first line
Once the arrangement is in place, one file is read by two models. That file does not tell the reader which of them is reading it. State the fact at the very top.
- This file is read as Claude Code's `CLAUDE.md` and as Codex's `AGENTS.md`.
If and only if you are Codex:
- Read any `/xxxx` skill invocation in this document as `$xxxx`,
because in Codex CLI input `/xxxx` is handled as a slash command first.
Those two lines become the premise for every conditional that follows. A model knows which tool it is, so a condition like "if and only if you are Codex" genuinely works. A difference in notation — a tool name, an invocation syntax — ends with one conditional line. There is no reason to split the document in two.
Items that differ in capability go in a shared rule plus a per-tool section
An item that differs in capability rather than notation cannot be handled by a conditional. How a subagent stops when it finds a contradiction in its instructions mid-task is one of those.
A Claude subagent has a live two-way channel with the main session, so it can ask on the spot. A Codex subagent is headless and one-shot, so it has no such channel. The action to take in the same situation is simply different. Writing the rule twice, though, splits the trigger conditions.
| Part | Where it goes | Content |
|---|---|---|
| Shared | One parent section | When to stop — the instruction contradicts the intent, a constraint makes the goal unreachable, an irreversible action has no prior agreement |
| Claude only | Child section | Message the main session while running and wait for a corrected instruction |
| Codex only | Child section | Stop immediately and return the reason with an ESCALATION: prefix at the top of the output. The main session re-runs it with a correction |
The criteria go in the shared section and only the means go in the per-tool sections. Copy the trigger list into each per-tool section and one of them will be updated alone later. That brings the duplication problem you were avoiding back inside the file.
Once the per-tool sections pass three, the item is probably a tool difference, not an instruction. At that point it is better to align how you use one tool with the other than to grow the document.
Summary
- Do not copy instruction files. Keep one original and have the rest point at it.
- The side with imports, Claude Code, is the side that links. The original is
AGENTS.mdandCLAUDE.mdis a single@AGENTS.mdline. If the original is alreadyCLAUDE.md, match it with Codex'sproject_doc_fallback_filenames. - Inside one file, handle notation differences with a conditional and capability differences with a shared rule plus per-tool sections.
If you changed the arrangement, confirm the load at the end. In Claude Code, run /context in a session and check that the file appears in the Memory files list. The link being in place while the file is not read is the most common failure.
