AGENTS.md host context drift: one file, four loaders, four different system prompts
AGENTS.md is positioned as a portable spec across coding agents. The spec itself is intentionally one paragraph long: any markdown, no required fields. Every host then fills in the gaps differently. The bytes on disk are one thing. The bytes in the model's system prompt are another.
Host context drift is what happens when the same AGENTS.md is loaded by four different agent hosts and produces four different in-context payloads. Four axes: discovery (Claude Code does not read AGENTS.md at all per Anthropic's memory docs; Cursor reads .cursorrules natively, not AGENTS.md), transformation (Claude strips block-level HTML comments, Codex preserves them), precedence (the agents.md spec says nearest file wins; Claude Code concatenates the full parent tree), and lifecycle (Claude re-injects after /compact; Cursor injects per-request; Codex differs per release). One file on disk, four payloads in context.
The file the rest of this page is about
Below is a small, realistic AGENTS.md. About 640 characters, 21 lines, one nested import, one HTML comment fence pair. We will run this exact file through four host loaders and watch what each one does with it.
Loader 1: OpenAI Codex CLI
Codex is the canonical AGENTS.md host. The agents.md spec lists it first among 20+ integrations. Codex reads AGENTS.md from the repo root with no opt-in, no import dialog, and no transformation pass that the spec documents. The HTML comment fences and the visible body all load. The @docs/style-guide.md line on the last row is treated as plain text, not as an import directive, because the spec does not define a recursive import syntax.
Loader 2: Claude Code (no CLAUDE.md, AGENTS.md alone)
A repo that ships only an AGENTS.md and no CLAUDE.md is the case where most teams discover host drift the hard way. Anthropic's memory docs are unambiguous: "Claude Code reads CLAUDE.md, not AGENTS.md." The /memory command lists exactly what loaded, and AGENTS.md is not on the list. So the same git tree that gave Codex 640 characters of guidance gives Claude zero.
This is the moment Claude starts making decisions you did not author. The agent has no idea you wanted pnpm, not npm. It has no idea you ban refactors outside scope. The advice in chat is generic, not your repo's, and you cannot find why because the rules look like they are on disk.
Loader 3: Claude Code, with @AGENTS.md pointer
Anthropic's recommended pattern for repos that already use AGENTS.md is a CLAUDE.md that contains one line: @AGENTS.md. The @-import resolver inlines the target at session start, recursive up to five hops, and then runs the transformation pass. That transformation pass strips block-level HTML comments. The BEGIN and END fences in our file are erased before Claude sees the message. Codex paid 12 tokens for those fences. Claude pays zero.
The nested @docs/style-guide.md line, which Codex read as plain text, now resolves. That is the fourth axis of drift: under Codex it was inert characters; under Claude it is another file load.
Loader 4: Cursor
Cursor's native discovery looks for .cursorrules and .cursor/rules/*.mdc. AGENTS.md is not on that list. Cursor will read it if you wire it up by hand, but by default the file is invisible. The injection model is also different: rules are added to the prompt per-request, not once at session start, so a mid-session edit to the rules file takes effect on the next message with no restart. Same file, third discovery rule, third lifecycle.
The four drift axes, named
Walk each one. The host loader is a function from bytes-on-disk to bytes-in-context, and each axis is a place where the function forks.
Axis 1: file discovery
Codex reads AGENTS.md from the repo root by default. Claude Code, per Anthropic's memory docs, reads CLAUDE.md and explicitly does not read AGENTS.md. Cursor reads .cursorrules and .cursor/rules/*.mdc. So a repo with only an AGENTS.md committed feeds Codex the full file and feeds Claude Code and Cursor exactly zero bytes from it. The first axis of drift is which hosts even see the file.
Axis 2: transformation pass
Claude Code strips block-level HTML comments before injecting CLAUDE.md (and any imported AGENTS.md) into context. Comments inside code blocks are preserved. The agents.md spec does not document any transformation pass, so Codex receives the comment bytes verbatim. A repo that uses <!-- BEGIN:section --> fences to mark managed blocks pays the fence tokens under Codex and pays zero under Claude. Same characters on disk, different characters in the system prompt.
Axis 3: precedence and scope
The agents.md spec says nested AGENTS.md files override: 'agents automatically read the nearest file in the directory tree, so the closest one takes precedence.' Claude Code's behavior is concatenation, not override: parent CLAUDE.md files (from filesystem root down to the cwd) are loaded in full and joined, with nearer files appearing later. Same monorepo with nested rules files: Codex sees only the closest one, Claude sees all of them stacked.
Axis 4: lifecycle
Claude Code re-reads root CLAUDE.md from disk after /compact and re-injects it, so imports re-resolve and a target that grew during the session produces a larger post-compact context than pre-compact. Codex compaction strategies differ per release. Cursor injects rules per-request rather than per-session, so an edit to .cursorrules takes effect on the next message without restart. Three different timing models, same file as input.
The four-host matrix
A single grid is easier to scan than four code blocks. Rows are hosts, columns are the four axes. The cells are what each host documents (or, in the case of Cursor, what its native behavior actually does).
| Host | What it reads | HTML comments | Imports | Scope and lifecycle |
|---|---|---|---|---|
| OpenAI Codex CLI | AGENTS.md (native, root) | Preserved verbatim | No documented @-import syntax | Nearest-file precedence (override) |
| Claude Code | CLAUDE.md only; AGENTS.md only via @-import | Block HTML comments stripped | @path imports, recursion depth 5, approval dialog on first run | Walks parent tree, concatenates all CLAUDE.md files |
| Cursor | .cursorrules and .cursor/rules/*.mdc; AGENTS.md not native | Preserved verbatim | No @-import equivalent; .mdc files are discovered, not imported | Per-request injection; edits apply on next message |
| Aider / generic AGENTS.md consumers | AGENTS.md (per spec) | Preserved verbatim | Not in spec; tool-specific extensions only | Per agents.md: nearest file in directory tree wins |
Codex and Claude rows sourced from agents.md and Anthropic's memory docs. Cursor row reflects its native rule discovery for .cursorrules.
Same git tree, two different system prompts
Codex reads AGENTS.md from the repo root. The HTML comment fences load verbatim. The trailing @docs/style-guide.md line is plain text. About 640 characters, roughly 160 tokens, no imports resolved.
- Discovers AGENTS.md natively
- HTML fences cost ~12 tokens
- @-import line is inert text
- ~160 token system prompt contribution
Where the analyzer sits in this picture
ccmd's analyzer (at src/lib/analyzer.ts) is a pure function: text in, score out, no filesystem access, no network. That is the contract: pasting a file into the homepage textarea runs entirely in the browser. The trade-off is that the analyzer scores the bytes you pasted, not the bytes that arrive in the model's system prompt under host X. You have to do the host-specific subtraction by hand.
The practical workflow when you target both Codex and Claude: paste AGENTS.md into the homepage analyzer, read the token count, then mentally subtract the bytes inside <!-- --> fences if you care about the Claude view. If you target Cursor, remember that the bytes are not loaded at all unless you wired up a rule reference, so the token count is a ceiling, not a floor.
A checklist for shipping AGENTS.md across hosts
- Decide which hosts your AGENTS.md targets. If Claude Code is one of them, add a CLAUDE.md (one line:
@AGENTS.md) or a symlink. Document the choice in the README so a new contributor does not silently fall into the no-CLAUDE.md case. - If you wrap managed blocks in HTML comment fences (
<!-- BEGIN:name -->), accept that the fences are free under Claude and paid under Codex. If you want fences visible to Codex but invisible to Claude, that is the default and there is nothing to change. If you want them visible to both, wrap them in a code block so Anthropic's stripper leaves them alone. - Decide the precedence shape of a monorepo. If you have nested AGENTS.md files, Codex picks the nearest one and ignores the others. Claude Code concatenates them all. If you want the same behavior under both, ship one AGENTS.md at the root and rely on Anthropic's path-scoped
.claude/rules/for Claude-specific overrides; the override pattern matches the agents.md nearest-file model more closely than tree concatenation. - Document the first-time @-import approval dialog. A new contributor who declines it once silently misses every imported rule until you change a setting. Two lines in the README is cheaper than discovering this in code review.
- Run
/memoryinside Claude Code on the repo before you ship. It prints every file actually loaded, including imports. If a file you expected is missing, your import is wrong or the approval dialog declined. Do the equivalent under Codex (read its logs or session transcript) and compare. - Paste each file in the chain into the analyzer one at a time. Write down the token count per file. Sum them. That sum is the byte cost per turn, not the size of any one file on disk.
Why this matters more after June 2026
The Agent SDK credit model that Anthropic ships in June carves the credit pool away from chat. A long-running Claude Code session pays for every byte in the system prompt on every turn. Codex, on its own pricing model, pays differently. So a repo that ships one AGENTS.md and assumes its cost is one number is wrong twice: once because the bytes are different per host, and again because the dollar conversion is different per host. Drift was already a behavior story. It is becoming a billing story.
Related reads
- When the @AGENTS.md pointer silently doubles in size covers within-host drift (pointer vs resolved payload). This page covers cross-host drift; the import-drift page is the sibling that drills into one host's loader in detail.
- CLAUDE.md vs AGENTS.md vs .cursorrules rubric drift uses the same Karpathy 12-rule rubric to grade all four formats. Rubric drift is content drift; host drift is loader drift. Different axes.
Want a host-by-host audit of your AGENTS.md?
If your repo ships one AGENTS.md across Codex, Claude Code, and Cursor, book a 20-minute review. We walk each loader with you, score the resolved payload per host, and tell you which axis of drift will bite first.
Frequently asked questions
What is AGENTS.md host context drift?
It is the gap between one AGENTS.md file on disk and the multiple in-context payloads it becomes once you run it through different coding-agent hosts. The drift has four axes: which host even reads the file, what transformation pass the host applies before injection (HTML comment stripping, code-fence preservation), how the host walks the directory tree for nested files (override vs concatenate), and when the host re-reads the file (session start only, post-compact, per-request). The agents.md spec is intentionally minimal on all four axes, so every host fills in the gaps differently. A team that ships one AGENTS.md and assumes Codex and Claude Code see the same thing is wrong.
Does Claude Code read AGENTS.md?
No, not directly. Anthropic's memory docs are explicit: 'Claude Code reads CLAUDE.md, not AGENTS.md.' The documented workflow is to create a CLAUDE.md with one line, @AGENTS.md, which inlines the AGENTS.md at session start using the @-import mechanism. A symlink also works on macOS and Linux. Without the pointer or symlink, AGENTS.md is invisible to Claude Code and a repo that ships only AGENTS.md gives Claude zero bytes of project guidance.
What does the HTML comment transformation look like in practice?
Anthropic's docs say block-level HTML comments in CLAUDE.md files are stripped before the content is injected into Claude's context. Comments inside code blocks are preserved. So an AGENTS.md with <!-- BEGIN:nextjs-rules --> and <!-- END:nextjs-rules --> fences costs about 12 to 24 tokens under Codex and costs 0 under Claude Code. The visible characters between BEGIN and END load normally on both. This means the same file produces different system-prompt sizes per host. Wrap the fences in a code block if you need them visible to the agent on both sides.
What is the difference between nearest-file precedence and tree concatenation?
The agents.md spec says nested AGENTS.md files override: 'agents automatically read the nearest file in the directory tree, so the closest one takes precedence.' In a monorepo with /AGENTS.md and /packages/api/AGENTS.md, a Codex session opened in packages/api sees only the api file. Claude Code's behavior is different: it walks the tree from the filesystem root down to the working directory and concatenates every CLAUDE.md it finds, with files closer to the cwd appearing later in the concatenation. Same monorepo, opened in the same directory: Codex sees one file, Claude sees both stacked.
How does /compact interact with host drift?
Anthropic re-reads the project-root CLAUDE.md from disk after /compact and re-injects it into the session. Imports re-resolve. So if AGENTS.md grew during the session because a teammate pushed and you pulled, the post-compact context is larger than the pre-compact context. Other hosts have their own compaction or truncation strategies. Cursor sidesteps the question by injecting rules per-request rather than per-session, so a .cursorrules edit takes effect on the next message without any restart. Drift accumulates differently depending on the host's lifecycle model.
Why does the ccmd analyzer not show host-specific results?
The analyzer at src/lib/analyzer.ts is a pure function: text in, score out, no filesystem access, no network. Pasting a file into the homepage textarea runs entirely in the browser and is the same code path regardless of which host you intend to run the file under. So the analyzer scores the bytes you pasted, not the bytes that arrive in the model's system prompt under host X. The practical workaround is to mentally subtract the HTML-comment fence tokens if you target Claude Code, or to leave them in the score if you target Codex. The paid tier handles per-host estimation by ingesting the repo and modeling the loader.
Does the @-import approval dialog matter for cross-host teams?
Yes. When a new contributor clones a repo with a CLAUDE.md that imports @AGENTS.md, Claude Code shows an approval dialog the first time, per Anthropic's docs. If they decline, the imports stay disabled and the dialog does not appear again. From that point their Claude session loads zero bytes from AGENTS.md while yours loads the full file. They get one half of the host-drift pair without knowing it. On the Codex side this contributor still sees the full AGENTS.md because Codex has no equivalent gate. Document the approval step in your README and verify with /memory which files actually loaded.
Can a symlink fix host drift?
Partially. A symlink from CLAUDE.md to AGENTS.md gives Claude Code the same content Codex sees, on macOS and Linux. On Windows it requires Administrator or Developer Mode. The symlink fixes the discovery axis: both hosts now read the same bytes off disk. It does not fix the transformation axis: Claude still strips HTML comments before injection, Codex still preserves them. It does not fix the precedence axis: Claude still concatenates parent CLAUDE.md files, Codex still picks the nearest. And it does not fix the lifecycle axis: each host still re-reads the file on its own schedule.
Comments (••)
Leave a comment to see what others are saying.Public and anonymous. No signup.