← Back to blog

    Writing a Good AGENTS.md

    If you have ever opened an AGENTS.md and found a pile of old commands, half-remembered preferences, and one rule that obviously contradicts the repo, you already know the problem. The file is supposed to be the durable instruction layer for Codex. In practice it often becomes the place people paste every frustration they had with an agent last week. That is not memory. That is sediment.

    AGENTS.md matters because Codex reads it before doing work. OpenAI's Codex docs describe it as custom instructions and context for the project, discovered from global and project scopes, then merged in precedence order. That makes it parallel to CLAUDE.md in Claude Code: not the whole harness, but one of the first pieces of harness state the agent sees. If it is sharp, the session starts with good constraints. If it is vague or stale, the agent starts by paying attention to the wrong map.

    This piece is about writing an AGENTS.md that works under pressure: what it is for, the three properties that matter, the five sections every good file needs, the failure modes I keep seeing in audits, and a before/after example you can steal.

    What AGENTS.md is for

    AGENTS.md is not a README. A README explains the project to a human who can ask questions, skim selectively, and ignore obsolete sections. AGENTS.md explains the operating environment to an agent that is about to run commands, edit files, and make irreversible decisions if the harness allows it.

    That distinction changes the writing style. A good README can be narrative. A good AGENTS.md is closer to a boot sequence. It tells the agent what world it is in, what commands are real, what rules are hard, what assumptions are dangerous, and where deeper references live.

    The harness framing is useful here. Mitchell Hashimoto's harness-engineering argument, OpenAI's Codex writing on repository knowledge, and LangChain's recent agent-harness work all point at the same thing: the model is not the product by itself. The system around the model matters. Tools, rules, memory, feedback loops, CI, docs, and permissions shape the work. AGENTS.md is the always-loaded prose layer inside that system.

    That does not mean AGENTS.md should contain everything. OpenAI's own harness-engineering lesson is that a giant instruction file fails in predictable ways: it crowds out the task, it makes every rule feel equally important, it rots, and it is hard to verify. The better pattern is a short AGENTS.md that acts as a map: enough to orient the agent, strict enough to prevent common mistakes, and explicit about where deeper source-of-truth documents live.

    What "good" looks like

    A good AGENTS.md has three properties, in this order: it is correct, it is readable in one session, and it is enforced by layers outside the prose.

    Correctness comes first because one false rule poisons the rest. If AGENTS.md says "use pnpm" but package-lock.json and CI prove the project uses npm, the file is wrong. If it says "run make test" but there is no Makefile, the file is wrong. The agent will eventually discover the mismatch, and once it does, every other rule in the file becomes a little less credible. Stale guidance is not neutral. It teaches the agent that the instructions are advisory.

    Readability is second. Codex has to fit the instructions into the working context with the user's task, the code, command output, and any tool results. A 900-line AGENTS.md is usually an admission that nobody decided what matters. A useful target is under 200 lines for a repo root file, with nested AGENTS.md files only where a subdirectory genuinely has different rules. If the project needs 40 pages of knowledge, put that knowledge in docs/ and link to it from a short map.

    Enforcement is third. AGENTS.md can say "no direct pushes to main." It cannot stop git push origin main. AGENTS.md can say "run typecheck before merge." It cannot make typecheck pass. The rule becomes real when CI, hooks, permissions, or review gates enforce it. The prose tells the agent what to do. The harness proves whether it happened.

    The five sections every AGENTS.md needs

    Across the files I see that hold up, five sections appear again and again. Names vary. The underlying jobs do not.

    The first section is project facts. One short paragraph. What the project does, what stack it uses, what kind of product it is, and where the main code lives. This should not be a pitch. It should let the agent distinguish a marketing site from a CLI, a monorepo from a single app, a library from an internal service.

    The second section is commands. The commands that actually work today: install, build, lint, unit test, integration test, dev server, code generation. Include package-manager boundaries. If CI uses npm, say npm. If the repo uses uv, say uv. Do not list commands you wish existed. Do not list every possible script. List the ones the agent is expected to run.

    The third section is working rules. Branching, commits, PR expectations, review policy, dependency policy, generated files, lockfiles, secrets. These are the rules that shape every edit. They should be falsifiable. "Keep changes small" is weak. "One logical change per commit; if the commit message needs 'and', split it" is stronger.

    The fourth section is architecture and boundaries. What not to rewrite. What layer owns what. Which directories are generated. Which APIs are stable. Which migrations are intentionally one-way. This section prevents the expensive category of agent mistake: plausible refactors that violate local architecture.

    The fifth section is deeper references. Point to the real docs instead of copying them. docs/auth.md for auth invariants. docs/release.md for release procedure. .github/workflows/ci.yml for CI truth. package.json for script truth. This keeps AGENTS.md short and gives the agent a route to more detail when the task needs it.

    What goes wrong

    The first common failure is vagueness. "Write clean code" is not a rule. "Follow best practices" is not a rule. "Be careful with migrations" is not a rule. The agent cannot verify any of them. Replace them with rules that have visible pass/fail conditions:

    - Database migrations must be reversible unless the PR description says why not.
    - Public API changes require updates to docs/api.md and at least one integration test.
    - New React components must live under src/components/ and use existing UI primitives before adding new styling primitives.
    

    The second failure is contradiction. This often happens when global and project instructions collide. A global AGENTS.md says "use pnpm." The repo AGENTS.md says "use npm." That can be fine if the project rule intentionally overrides the global rule, but the file needs to say so. "Use npm in this repo; overrides global pnpm preference because CI and lockfile are npm-based." Now the contradiction is not a bug. It is a declared override.

    The third failure is stale references. Paths move. Scripts get renamed. CI jobs get replaced. A stale command in AGENTS.md is worse than no command because the agent wastes time running it, then starts improvising. Every AGENTS.md edit should include a quick check that paths and commands still exist.

    The fourth failure is unbounded sections. "Guidelines" starts with five good bullets. Three months later it has forty. Some are preferences. Some are rules. Some are explanations. Some repeat each other. Once a section becomes unbounded, nobody curates it. Put a budget on it. If "Principles" has more than seven bullets, delete or move.

    The fifth failure is language drift. A file starts in English, accumulates Chinese chat excerpts, then adds copied CI logs. Or it mixes human-facing commentary with agent-facing commands. The agent may still parse it, but the drift is a maintenance smell. Pick the language for the file. Keep code, commands, and file names literal. Keep explanations short.

    A worked example

    Here is a before example based on a real audit pattern:

    # AGENTS.md
    
    ## Notes
    - This is a React app.
    - Use good TypeScript.
    - Tests are important.
    - Do not break production.
    - Use the right package manager.
    - Ask before big changes.
    - There are docs in the docs folder.
    - Follow the existing style.
    

    The intent is fine. The file is nearly useless. "Good TypeScript" has no edge. "Tests are important" does not say which tests. "Right package manager" makes the agent infer from the filesystem. "Big changes" means whatever the model thinks it means at that moment. "Docs in the docs folder" points everywhere and nowhere.

    Here is the same file rewritten:

    # AGENTS.md
    
    ## Project facts
    This is a Vite + React + TypeScript marketing site for a developer tool.
    The app code lives in src/. Static content lives in src/content/.
    
    ## Commands
    - Install: npm ci
    - Local build: npm run build
    - Unit tests: npm test
    - Lint: npm run lint
    
    ## Working rules
    - Use npm only. Do not add pnpm/yarn/bun lockfiles.
    - Keep blog content in English unless the file is under src/content/blog/zh/.
    - Do not edit generated dist/ output by hand.
    - No direct pushes to main. Open a PR for all changes.
    
    ## Architecture and boundaries
    - Route metadata is centralized in src/lib/seo.ts and scripts/generate-sitemap.ts.
    - Blog slugs are derived from markdown filenames after stripping numeric prefixes.
    - If you add a blog post, update related posts, sitemap routes, and OG metadata in the same change.
    
    ## References
    - package.json is the source of truth for commands.
    - scripts/generate-sitemap.ts is the source of truth for static routes.
    - src/content/blog/ contains editorial style examples.
    

    This is still short. But now every important rule is testable. You can check the lockfile. You can run the commands. You can inspect the route script. You can verify whether a blog change updated the adjacent metadata. The agent has fewer words to read and more real constraints to follow.

    Why a linter belongs in the loop

    Even a good AGENTS.md drifts. The repo changes, the package manager changes, the global file changes, and a nested override gets added under services/payments/. The right maintenance habit is not "write it perfectly once." The right habit is to check the file against the repo every time it matters.

    AgentLint catches the failure modes that make AGENTS.md silently unreliable: vague rules, contradictions, stale paths, stale commands, missing enforcement, unbounded sections, and language drift. The point is not to make the file pretty. The point is to make sure the instruction layer Codex reads before work is still true.

    Related posts