lockstep
Updated 13 min readNaman Jain

AGENTS.md: what to put in it, and what it can't carry

A straight guide to what belongs in an AGENTS.md — commands, invariants, boundaries — what to keep out, and the coordination job no static file can do.

Open a repository that has lived through a year of agent-assisted development and count the instruction files.

There's an AGENTS.md at the root. There's a CLAUDE.md next to it, older, half-overlapping, kept alive because Claude Code doesn't read the first one. There's a .cursor/rules directory from the winter somebody tried Cursor, and a .github/copilot-instructions.md that nobody remembers writing. Four files, four eras, one repository.

Now open the AGENTS.md itself and read it the way a geologist reads a cliff face. Line 14 says the project uses styled-components; the team moved to Tailwind in March, but only the newer components know that. Line 31 says don't touch the payments module; that freeze ended two months ago, and every agent since has been carefully routing around a wall that no longer exists. Line 40 documents a test command that was renamed in April. Nothing in the file is a lie, exactly. Every line was true on the day it was written. The file just has no idea what day it is.

This is not a story about a sloppy team. This is what a static file does when you ask it to describe a moving system. And it's worth writing about properly right now, because AGENTS.md is at a strange moment: it has just won, decisively, as a standard, at exactly the moment its structural limits are starting to matter.

So this post is two things. First, a straight guide: what actually belongs in an AGENTS.md, what doesn't, and how to keep it useful. Second, the part the guides skip: the jobs this file cannot do no matter how well you write it, and what has to sit next to it.

AGENTS.md won

Two years ago, rules files were a mess. Every coding agent shipped its own convention: .cursorrules here, CLAUDE.md there, GEMINI.md, copilot-instructions, each tool insisting on its own filename for what was essentially the same document. Teams using three agents maintained three copies and watched them drift.

Then, in August 2025, a group including OpenAI, Google, Cursor, Factory, and Sourcegraph published AGENTS.md as an open standard: one markdown file at the repository root, readable by any agent, owned by no vendor. By December it had been donated to the Agentic AI Foundation under the Linux Foundation, the same body that stewards Linux and Kubernetes. More than sixty thousand open-source repositories now carry one, and the pitch is admirably clean: README.md is for humans, AGENTS.md is for agents.

Adoption is broad, though not total. Codex, Cursor, Copilot, Windsurf, Cline, Zed and a long tail of others read it natively. The big holdout is Claude Code, which still wants its own CLAUDE.md; the request for native AGENTS.md support has thousands of upvotes on Anthropic's tracker, and until it lands the standard fix is a one-line import or a symlink: ln -s AGENTS.md CLAUDE.md. Mildly annoying. Entirely workable.

Let me be unambiguous about one thing before I start complaining: this standardization is good. A boring, neutral, Linux-Foundation-governed markdown file beat five proprietary formats, and the whole ecosystem is better for it. If your repo doesn't have an AGENTS.md, you should add one this week, and the next section is my best attempt at telling you exactly what to put in it.

But notice precisely what got standardized. The container. Where the file lives, who reads it, what it's called. Nobody standardized, or even really asked, what a file like this can and cannot carry. That question is the second half of this post, and it decides whether your AGENTS.md compounds in value over the next year or rots into the sediment I described at the top.

What belongs in AGENTS.md

One test governs everything: will this line still be true in three months if nobody touches it? If yes, it's a candidate. If no, it does not belong in this file, and later I'll show you where it goes instead. AGENTS.md is a handbook. Handbooks hold constants.

Within that rule, the content that earns its place:

Commands, not descriptions. Agents execute; give them things to execute. pnpm test, not "we have a comprehensive test suite." Build, test, lint, migrate, in copy-paste form, with the non-obvious flags spelled out. Every command you write here is a command the agent doesn't have to guess, and guessed commands are how agents end up running your production seed script.

Invariants, the highest-value lines in the file. An invariant is a rule the code cannot state about itself. All money is integer paise, never floats. Migrations are generated, never hand-edited. The events table is append-only; corrections are new rows. These rules are invisible in any single diff, expensive when violated, and impossible for an agent to infer from reading the code, because the code merely obeys them without announcing them. Write the why alongside the what when you can. "Never call the inventory service in a loop, it rate-limits at 10 rps" lets an agent reason its way through cases you didn't anticipate; a bare prohibition doesn't.

The map. Ten lines of directory layout: where schemas live, where the API routes are, where generated code goes. Plus domain vocabulary. If your product says "organization" where the rest of the world says "workspace," write that down; vocabulary drift produces the most irritating class of agent bugs, the kind that compile perfectly.

Boundaries. What never gets logged. Which fields are PII. Which directories are machine-generated and must not be hand-edited. Which modules are off-limits without a human in the loop. These are cheap to write and catastrophic to omit.

Conventions no linter can enforce. Error responses follow problem+json. Tests sit next to the file they test. New files carry the license header. And the inverse rule: if a linter or formatter can enforce something, let it, and keep it out of this file. A rules file doing a linter's job does it with worse reliability and pays context for the privilege.

Then the discipline that keeps the file working:

Keep it brutally short. Every line is a tax on every session forever, and the budget is real: models reliably follow at most a couple hundred instructions, and the agent's own system prompt has already spent a chunk of that before your file loads. A focused fifty-line file outperforms a sprawling five-hundred-line one, and some tools will silently truncate an oversized file anyway, which means your most recently added rules are the ones being thrown away. If a line has never changed an agent's behavior, delete it.

Write it by hand. Auto-generated rules files are generic by construction, and generic instructions are the ones agents ignore first. Every line should exist because something went wrong once. A rules file is scar tissue, and you can't generate scar tissue.

In a monorepo, nest. The spec supports per-directory files with nearest-file precedence. Global constants at the root, package-local rules next to the package. Don't make the frontend agent carry the data pipeline's rules in its head.

Update it in the same commit as the constant it describes. If the PR that renames the test command doesn't also touch AGENTS.md, the file starts lying that day. This is the entire maintenance model, and it works precisely because constants change rarely. Which is foreshadowing.

Here's a slice of the file from Lockstep's own repo:

## Commands
- `pnpm dev` runs the API (Fastify) and web (Next.js) together
- `pnpm test` runs Vitest. Run it before every commit.
- `pnpm db:generate && pnpm db:migrate` for schema changes. Never hand-edit files in /drizzle.

## Invariants
- The decisions table is append-only. No UPDATE, no DELETE. Corrections are superseding rows.
- The MCP server exposes exactly 13 tools. Adding a tool is a product decision, not a refactor.
- All timestamps are UTC ISO-8601 strings, end to end.

## Boundaries
- Never log request bodies on ingest routes.
- packages/generated/** is machine-written. Do not edit.

Nothing in that excerpt will be stale in three months. That's the whole trick, and it's also the tell for what comes next.

What doesn't belong in AGENTS.md

Everything with a shelf life. The moment you type "currently," "for now," "as of," "this sprint," or a date, stop. You are writing news into a handbook.

The deprecation you shipped this morning. The endpoint rename. "Payments is frozen until the audit clears." The migration that's halfway done, so half the tables follow the new convention and half the old. These are, frustratingly, the most important things your agents need to know today, and this file is the wrong place for every single one of them.

Not because the information is wrong. Because of what happens after you write it. News expires, and a file has no way of knowing that it has. A markdown line has no status: it can't be marked superseded the way a decision record can. It can only be deleted, which erases the history, or left in place, which manufactures the sediment from the top of this post, one true-at-the-time line at a time.

And every stale line does double damage. It misleads the agent, and it spends one slot of a strictly limited instruction budget doing so. Stuff enough news into the handbook and the agent gets worse at following the constants too. The news crowds out the rules.

The same shelf-life logic evicts two other tenants people keep moving in: task instructions ("today, refactor the auth module") belong in the prompt, not in a persistent file every future session must read; and full API documentation belongs at a link the agent can pull when needed, not inlined where it taxes every session whether relevant or not.

The handbook and the standup

Here's the everyday version of this distinction. No software required.

Every company has an employee handbook. Values, expense policy, security rules, who to call for what. It's genuinely useful, and it's read exactly once, on day one. And no company on earth announces a fire drill by editing the handbook and hoping everyone rereads it. News travels through different machinery entirely: the standup, the announcements channel, a manager saying it out loud. Two channels, two jobs. Constants go in the handbook. State moves through the standup. No one confuses them, because with humans the confusion is impossible; you can feel that editing a PDF is not communication.

With agents, we have somehow talked ourselves into exactly that confusion. AGENTS.md is a handbook, and for most teams it's also the only channel that exists, so the news gets stuffed into it, and the sediment builds. If you want it in restaurant terms: the menu is laminated for a reason, and "we're out of the sea bass" travels by mouth. Nobody reprints the menu mid-service.

The reasons a file can't run the standup are structural. Better writing doesn't fix them. A file is read at session start, so the decision made at 2:00pm never reaches the agent session that began at 1:55 and will run for three more hours. A file is pull, not push: it informs whoever happens to read it and notifies no one. A file broadcasts: every reader gets everything, with no way to say "this concerns exactly the two services that consume this endpoint." A file collects no acknowledgments: you cannot know whether the mobile agent saw the deprecation before it spent the afternoon building against the old route. And a file is trapped inside its repository, while the decisions that hurt most are the ones that span three of them.

I made the long version of this argument about ADRs, and the physics is identical. A static record fails at exactly the moment a fresh decision needs to travel.

"Just have the agent update it"

The obvious fix, and I hear it every time: if decisions keep changing, have the agent append them to AGENTS.md and commit. The file stays current, git distributes it, problem solved.

Walk through what actually happens. The update reaches nobody. Your teammate's agent learns about it at its next session start, if a pull happened first; the three sessions currently mid-flight never learn at all. The single file becomes a write hotspot, and five agents appending decisions produce merge conflicts on the coordination file itself, which is a small poem about the whole situation. The context tax compounds, because appended decisions accumulate and every future session pays for every past decision forever; markdown has no supersede semantics, so the file grows until a human prunes it by hand and destroys the very history the appending was supposed to preserve. There is still no acknowledgment, no notion of a decision being binding on anyone, no targeting. And it all dies at the repo boundary anyway.

Follow the fix far enough and you notice what you're doing: rebuilding a coordination system inside a markdown file. Statuses, notifications, ordering, acknowledgments, scopes. Markdown has none of those primitives, and no amount of convention will add them. Files hold state. They don't move it.

Where Lockstep fits

So the division of labor I'm arguing for is simple. Keep AGENTS.md as the handbook, written by hand, brutally short, all constants. And give the news its own machinery. That machinery is what I'm building with Lockstep.

The demand for it is already filed, literally. Native AGENTS.md support isn't the only thing being asked of Anthropic's tracker: an engineering manager running two teams (fourteen engineers) requested shared team memory across Claude Code sessions, calling the gap "the single biggest efficiency bottleneck for teams adopting Claude Code seriously." Context dies at the boundary between one person's sessions and another's. That's a standup problem. No handbook fixes it.

In Lockstep, a decision is a record with a lifecycle, not a line in a file: proposed, acknowledged by the people and agents it affects, binding, eventually superseded. A dependency graph knows who consumes which surface, so a decision is pushed to the agents it concerns at the moment it's made, not discovered at the next session start. Every session opens with a briefing of what it missed, ordered by blast radius, which is to say: the standup. And at merge time a gate checks changed shared surfaces against the ledger, so a contract change with no binding decision behind it doesn't ship.

The two layers also meet inside the file itself. Lockstep maintains a generated section of your AGENTS.md containing the currently binding decisions, rewritten as they change and removed as they're superseded. Humans own the constants above the line; the ledger keeps the news below it current. The file stops rotting because no human has to remember to update it, and no agent has to hope it was updated.

It speaks MCP, so it doesn't care whether your agent reads AGENTS.md, CLAUDE.md, or something proprietary. And only decisions and metadata flow through it. Your code never leaves your machine.

Go back to the sediment file from the top. Every line true on the day it was written; the file with no idea what day it is. That was never a maintenance failure. It was a category error: a handbook asked to run the standup.

The industry spent a year standardizing the handbook, and it was a year well spent. I wrote a while ago that value migrates from the record to the feed that reads across it. AGENTS.md is the record, finally standardized, deservedly won. The feed is still missing.

Write the handbook. Keep it short. Stop asking it to run the standup.

Frequently asked questions

What is AGENTS.md?
AGENTS.md is an open-standard markdown file at the repository root that gives AI coding agents project instructions. The pitch: README.md is for humans, AGENTS.md is for agents. It's readable by any agent and owned by no vendor.
What should go in AGENTS.md?
Constants — things still true in three months if nobody touches them: copy-paste commands, invariants the code can't state about itself, a short map of the codebase, boundaries (PII, generated directories), and conventions no linter can enforce. Keep it brutally short.
What should not go in AGENTS.md?
Anything with a shelf life — deprecations, freezes, half-done migrations, task instructions, and full API docs. News expires and a static file can't mark a line superseded; it just becomes misleading sediment.
Does Claude Code read AGENTS.md?
Not natively yet — Claude Code still wants its own CLAUDE.md. The standard workaround is a one-line import or a symlink: ln -s AGENTS.md CLAUDE.md.

Keep reading