SKILL 08 / META · DEEP DIVE

AGENTS.md and Cross-Harness Portability

One repo brief for twenty agents — and when Claude Code reads it, and when it does not

2026-09-09 · BigCat · updated 2026-10-01

Your repository currently holds CLAUDE.md, AGENTS.md, .cursor/rules/, and .github/copilot-instructions.md. Four files saying the same thing, and they have already started contradicting each other.

Related → Skill 06 Reading the Official Authoring Guide (skills load on demand; this issue covers the layer that enters context every single session)
Updated 2026-10-01: when this issue first ran, Claude Code read CLAUDE.md only and not AGENTS.md, and the text was built around that claim. Since v2.1.277 it reads AGENTS.md natively (v2.1.281 extended that to Bedrock, Vertex AI, Foundry and similar sessions). The affected passages below have been rewritten against the current official docs.

// WHY THIS MATTERS

AGENTS.md is a README for agents: one plain Markdown file at the repo root, no schema, no validation, no required fields, read into context by coding agents before they start work. It is now stewarded by the Agentic AI Foundation under the Linux Foundation, and the spec repo agentsmd/agents.md shows roughly 24.2k stars and an MIT license per its GitHub page, 2026-09.

The single most useful thing to know has a version boundary: before v2.1.277 Claude Code read CLAUDE.md only, not AGENTS.md; from v2.1.277 on, it reads AGENTS.md directly when the repository has no CLAUDE.md. Mind the condition — a single CLAUDE.md in your working directory or above it (a CLAUDE.local.md counts too) and by default only CLAUDE.md is read. So "just rename the file and it works everywhere" is still only half right. This issue names what each tool actually reads, gives the exact commands that wire them together, and then answers the real question: in a repo-level brief, what belongs and what does not.

// 01

What it takes off your plate: four repo briefs that fight each other

Claim: the real cost of multiple instruction files is not duplicated typing, it is drift — you update one of them and the other three keep teaching your agent a build command you abandoned six months ago. The fix is to pick one canonical file and point the rest at it with a symlink or an import.
WHO READS WHAT (per each vendor's own docs) ────────────────────────────────────────────────────────────── Claude Code CLAUDE.md / .claude/CLAUDE.md / CLAUDE.local.md .claude/rules/*.md AGENTS.md <- v2.1.277+, only when there is no CLAUDE.md OpenAI Codex AGENTS.md (git root -> cwd) + ~/.codex/AGENTS.md GitHub Copilot AGENTS.md (nearest wins) · CLAUDE.md · GEMINI.md .github/copilot-instructions.md .github/instructions/**.instructions.md (applyTo) Gemini CLI GEMINI.md; context.fileName can be ["AGENTS.md", ...] Cursor .cursor/rules/*.mdc · .cursorrules ────────────────────────────────────────────────────────────── AGENTS.md is the greatest common denominator, not the whole set.

The first thing you can run right now

# Make AGENTS.md the canonical file. Claude Code v2.1.277+ with no CLAUDE.md in the repo: do nothing
# Verify: a new session prints  no CLAUDE.md found; AGENTS.md loaded: <path>
# To load both files: /config -> Project instructions -> claude-md-and-agents-md

# On older versions, or to append Claude-only text, the two earlier recipes still work (content is read once) --
# (1) symlink: simplest when you need no Claude-specific text; prints nothing on success
ln -s AGENTS.md CLAUDE.md
# On Windows a symlink needs Administrator or Developer Mode; use (2) instead

# (2) import: leaves you a place to append Claude-only instructions
# contents of CLAUDE.md:
@AGENTS.md

## Claude Code
Use plan mode for changes under src/billing/.

# Verify: open a new session, run /context, confirm CLAUDE.md is under Memory files

# Gemini CLI takes one setting to accept AGENTS.md (.gemini/settings.json)
{ "context": { "fileName": ["AGENTS.md", "GEMINI.md"] } }
# Inspect what got concatenated: /memory show      Rescan: /memory reload

# Already have Cursor / Copilot rules and want them inside Claude Code
/import                # v2.1.213+, also carries over MCP, commands, subagents, skills

The difference between /import and /init is worth memorizing. /init reads your old rules and regenerates a CLAUDE.md — by default from .cursor/rules/, .cursorrules and .github/copilot-instructions.md; with CLAUDE_CODE_NEW_INIT=1 it also reads AGENTS.md, .devin/rules/, .windsurf/rules/ and .clinerules. /import appends a one-time copy and brings MCP servers, commands, subagents and skills along with it. The first one merges; the second one moves house.

Failure mode: copying is more tempting than linking because it works immediately. Drift starts at that moment — three months later nobody can say which of the two files is current. The official wording for /import is literally "a one-time copy": it is a migration tool, not a sync tool.
// 02

The mechanism: it works because it specifies almost nothing

Claim: AGENTS.md got adopted by twenty-odd tools precisely because it is not a protocol. No schema, no validation, no required fields — the cost of supporting it is one more filename to look for. What actually differs between tools is not the format but the loading semantics.

Three loading semantics; do not conflate them

A property that gets overlooked

The Claude Code docs are explicit: CLAUDE.md content is delivered as a user message after the system prompt. It is context, not enforced configuration. So "I wrote it down and it still ignores me" is not a bug, it is the nature of this layer. To pin an action down — lint must run before every commit, say — the documented answer is a hook, which fires as a shell command at a fixed lifecycle event regardless of what the model decides. The same holds for AGENTS.md, only more so: across harnesses you do not even know whether the other tool has hooks at all.

// 03

Real usage and limits: what belongs in the file and what does not

Claim: there is now an official answer to this, and it is subtractive. Claude Code's /doctor (v2.1.206+) proposes trims for a checked-in CLAUDE.md on one criterion: cut what the model can derive from the codebase, keep what it cannot.

The official trim criteria, verbatim enough to copy

/doctor CUTS /doctor KEEPS ────────────────────────────────────────────────────────────── directory layouts pitfalls dependency lists rationale architecture overviews conventions that differ from tool defaults ^ the model cannot derive these ────────────────────────────────────────────────────────────── HARD NUMBERS (Claude Code docs) target under 200 lines per file; a file over 4 MiB is skipped "Shorter files produce better adherence" -- their words, not folklore

Turned around: to decide whether a line belongs in AGENTS.md, ask three questions. (1) Would a new teammate need to be told this? Yes → keep it. (2) Could the agent find it with one grep? Yes → delete it. (3) Is it true only inside one subdirectory? Yes → move it to a path-scoped rule (.claude/rules/ with paths:) instead of spending context on it every session.

Three boundaries

Nesting may not load the way you assume. Codex walks "git root to current directory", not "every subdirectory". Issue #13288 in openai/codex (opened 2026-03-02, now closed) reports exactly this: launched at the repo root and editing code under go/, the go/AGENTS.md is never merged in. Claude Code's subdirectory CLAUDE.md files, by contrast, are loaded on demand — they enter context when Claude reads a file in that directory. The same nested layout behaves differently under the two harnesses.

Monorepos get polluted by other teams' briefs. Claude Code offers claudeMdExcludes, matched as globs against absolute paths. Put it in .claude/settings.local.json and it stays on your machine:

{ "claudeMdExcludes": ["**/monorepo/CLAUDE.md"] }

Personal preferences do not belong in the canonical file. CLAUDE.local.md (gitignore it) is for your sandbox URLs and test data. Across git worktrees it only exists in the one where you created it, so the docs suggest importing from your home directory instead: @~/.claude/my-project-instructions.md. A new trap: adding a CLAUDE.local.md to a repo that relies on AGENTS.md stops Claude Code from reading AGENTS.md by default (it counts as "having a CLAUDE.md"); to read both, set Project instructions to claude-md-and-agents-md.

Failure mode: when two rules contradict each other, the official line is that Claude "may pick one arbitrarily". Across harnesses this gets worse — AGENTS.md says pnpm, an unmaintained .cursorrules says npm, and you never learn which one won today. When you absorb old rule files, deleting the source matters more than the import itself.
// 04

The cost and the alternatives: one file for many agents means the lowest common denominator

Claim: AGENTS.md can carry exactly one thing — a block of plain text that enters context every session. MCP servers, hooks, subagents, skills, path-scoped rules: none of what actually makes an agent strong travels with it. Portability is bought by giving capability up.

Three routes, an order of magnitude apart in cost

What you can do without installing anything

Make AGENTS.md canonical: Claude Code (v2.1.277+), Copilot and Codex read it natively, and Gemini CLI needs one context.fileName change; on an older Claude Code add one ln -s. Those few steps cover almost everyone's needs at zero cost. A generator only starts paying for itself once "each tool also needs its own paragraph" becomes the norm — at that point you are no longer maintaining one file but a template plus N diffs.

The honest cost: AGENTS.md enters context on every session. It is a standing tax, not on-demand loading. A skill is read in only when it is judged relevant; a rules file is not. So every paragraph you add has to answer one question: is this worth appearing in every single conversation you have for the rest of the year? Most "best practices" are not.

// ONE-PAGE BUYING CARD

Do these three first: (1) make AGENTS.md canonical. On Claude Code v2.1.277+ with no CLAUDE.md in the repo there is nothing to do — a new session showing the AGENTS.md loaded line is your confirmation; on older versions, or in a repo that already has a CLAUDE.md, use ln -s AGENTS.md CLAUDE.md or an @AGENTS.md import and check that it is listed under /memory; (2) run /doctor and apply its criteria — delete the directory tree, the dependency list and the architecture overview, keep pitfalls, rationale and anti-default conventions, and get under 200 lines; (3) move anything that is only true inside one subdirectory into .claude/rules/ with a paths: frontmatter, so it stops taxing every session.

Pick by situation: one or two agents → a symlink is enough, skip the generator; distributing to a dozen tools with per-tool differences → npx @intellectronica/ruler apply with .ruler/AGENTS.md as the source; a pile of Cursor .mdc files you just want converted once → rulesync convert --from cursor --to copilot,claudecode, then uninstall; moving a whole setup of MCP servers, commands, subagents and skills into Claude Code → /import (v2.1.213+); polluted by other teams in a monorepo → claudeMdExcludes.

Numbers to keep handy: target under 200 lines for CLAUDE.md · over 4 MiB is skipped · @path imports nest at most four hops · Gemini CLI takes an array in context.fileName · Copilot is nearest-wins while Codex and Claude Code concatenate · CLAUDE_CODE_NEW_INIT=1 makes /init read AGENTS.md as well · Claude Code v2.1.277+ reads AGENTS.md when there is no CLAUDE.md (a CLAUDE.local.md counts as one).

The criterion in one line: if one grep would tell the model, delete it; if a new teammate would need to be told, keep it; if it is true in only one subdirectory, move it into a path-scoped rule.

// GLOSSARY

harness
The shell that runs the model: Claude Code, Codex, Cursor and Copilot are each a harness. Same model, different harness, and both the files it reads and the permission machinery change.
context file
An instruction file read into context at session start. AGENTS.md, CLAUDE.md and GEMINI.md are all of this kind; only the filename and the loading semantics differ.
concatenation vs nearest-wins
The two ways multiple matching files get handled: stack them all (Claude Code, Codex), or use only the closest one in the directory tree (Copilot's stated behavior for AGENTS.md).
path-scoped rule
A rule loaded only when the agent touches a matching file. .claude/rules/ uses paths:, Copilot uses applyTo. The main lever for saving context.
drift
Several copies of one convention evolving separately until they contradict each other. The number-one disease of multi-instruction-file repos.
lowest common denominator
To be readable by every harness, the file can hold only plain text — no MCP, no hooks, no skills. This is what portability costs.

// FURTHER READING