Project instructions: AGENTS.md
In your first session you gave the agent context by hand, from the files that mattered to the test command and the branch to leave alone. In this lesson we write that context down once, in a file the agent reads at the start of every session. We also look at what belongs in that file and what doesn’t, and at what to do when a written rule isn’t enough.
You don’t run anything in this lesson. The examples are file contents, and the
fixture is a small fictional project called invoice-mailer: a Python
service that renders invoices to PDF and sends them over SMTP. You can
reproduce every step in an empty directory with a text editor.
One file, read before anything else
Section titled “One file, read before anything else”Most coding agents look for a Markdown file at the root of the repository
and load it into their context before the first user message. The
convention differs by tool: some look for AGENTS.md, some for
CLAUDE.md, some for both. The content is the same either way, so the
common pattern is to write AGENTS.md and make CLAUDE.md a symlink to
it, or to have one contain one line pointing at the other.
The file is the briefing you would give a capable contractor on their first morning. They can read code. They can’t know which command your team runs before pushing, which directory is generated, or which obvious-looking fix has already been tried and reverted twice. That’s the gap the file fills.
Here is the smallest useful version for our fixture:
# invoice-mailer
Renders invoices to PDF and sends them over SMTP. Python 3.13, uv.
## Commands
- Test: `uv run pytest`- Lint and format: `uv run ruff check . && uv run ruff format .`An agent that reads these few lines runs the right test
command on the first try instead of guessing between pytest,
python -m pytest and make test. That alone pays for the file.
What belongs in it
Section titled “What belongs in it”Everything in the file should be something the agent can’t work out from the code, or could work out but gets wrong often enough that you are tired of correcting it. In practice that’s four kinds of content.
Commands. How to install, test, lint, build, and run. One line each, with the exact invocation. If the project has a task runner, name it and say that the runner is the source of truth.
Structure. Where things live, at the level of directories, not files. Which directories are generated and must not be edited by hand. Where new code of each kind goes.
Conventions. The rules the code follows that a reader would not infer from one file: the commit message format, the branch naming scheme, the test naming scheme, and which style choices are deliberate.
Rules that remove a recurring mistake. This is the most valuable category and the hardest to write in advance. Every time the agent does the same wrong thing twice, add one line that stops it. “Don’t add retries around SMTP calls. The queue already retries” is a good rule. It is specific, it says what to do instead, and it exists because of a real incident.
Build one now. Tick what’s true for a project you know, fill in the short fields, and read the result as if you were the agent seeing it for the first time.
Notice how short the output is even with every box ticked. That’s the target. A real project’s file grows over time, but it grows one line per lesson learned, not one paragraph per feature.
Claude Code can write a first draft of the file with its /init command,
from what it finds in the repository. Treat that draft as a list of
candidates and delete every line the agent could have found itself. The
vendor’s own advice matches the exercise at the end of this lesson: start
a project without the file and add one line each time you have to correct
the agent [1].
What doesn’t belong
Section titled “What doesn’t belong”The failure mode of instruction files isn’t that they’re missing. It is that they’re long. Every line costs context on every turn of every session, and an agent given two pages of prose follows some of it and silently drops the rest.
Leave these out:
Anything the agent can read from the code. The list of dependencies
is in the lockfile. The function signatures are in the source. The API
routes are in the router. Pointing at the file (“routes are registered in
app/routes.py”) is fine; copying its contents isn’t, because the copy
goes stale and the agent then has two conflicting sources.
Long prose. Architecture essays, project history, the reasoning behind every decision. If the reasoning matters, put it in a design document and link to it. The agent reads it when the task calls for it.
Restating the obvious. “Write clean code.” “Follow best practices.” “Make sure tests pass.” A capable agent already does these, and the lines teach nothing about your project. They also dilute the lines that do.
Fix the instructions
Section titled “Fix the instructions”This is the current AGENTS.md of invoice-mailer. The agent keeps running
python -m pytest (wrong) and committing to main. It also wraps mail
calls in retry loops. Rewrite the file so that each of those stops, and so that
nothing in it restates what the agent could read from the repository.
# invoice-mailer Renders invoices to PDF and sends them over SMTP. Python 3.13, uv. ## Commands - Test: `uv run pytest` - Lint and format: `uv run ruff check . && uv run ruff format .` - Run locally: `uv run invoice-mailer --dry-run` (never sends mail) ## Structure - `src/invoice_mailer/`: the package. `rendering/`, `mail/`, `models/`, `cli/`. - `templates/`: Jinja templates for the PDF. Generated `build/` is not committed. - `tests/`: mirrors the package layout; one test file per module. ## Rules - Never commit to `main`; branch as `feat/...` or `fix/...`. - Do not edit `uv.lock` by hand; use `uv add` / `uv remove`. - Do not add retries around SMTP calls; the queue already retries. - Every change to a template needs a matching golden-file test in `tests/rendering/`.
Which lines here would change what the agent does on its next task? Which lines could it have read from the repository itself?
Instructions in a monorepo
Section titled “Instructions in a monorepo”A repository with more than one project in it needs more than one file. The tools handle this the same way: the agent always reads the file at the repository root, and it also reads a file in a subdirectory when it works on files under that directory. Closer wins on conflict.
That gives a natural split. The root file holds what’s true everywhere: the task runner, the commit convention, the branch rule. Each project’s file holds only what differs: its own test command, its own language, its own recurring mistakes.
repo/ AGENTS.md # task runner, commit format, branch rule services/ invoice-mailer/ AGENTS.md # uv run pytest; no SMTP retries web/ AGENTS.md # bun test; never edit generated/ by handResist the urge to copy the root rules into each child file so that it reads standalone. The agent sees both; the copy only adds a second place to keep in sync.
Your personal preferences belong in neither file. Claude Code also reads
a user-level CLAUDE.md from your home configuration directory, in every
project and only for you. How you like a change explained or which shell
you use goes there. The shared file then holds only what the whole team
needs [1].
When instructions aren’t enough
Section titled “When instructions aren’t enough”An instruction is a request. The agent reads it, usually follows it, and
sometimes doesn’t, particularly late in a long session when the file has
scrolled far back in its context. If a rule is one you can’t afford to
have skipped even once, don’t rely on prose for it. A hook runs a
command of yours at a fixed point in the agent’s loop, such as before a
tool call or after an edit. When it runs before a tool call, it can
block the action outright. For example, a hook that runs before every
shell command, inspects the command the agent is about to run, and
exits with code 2 when it matches git push to main means the push
never happens, however far back the instruction file has scrolled
[2]. “Never push to main” in AGENTS.md is advice.
The hook enforces it. A hook that runs the formatter after every edit
works the same way for a procedure the agent often forgets. The message
a blocking hook prints goes back to the agent, so it learns why the
action was refused and can change course instead of retrying. Hooks in
the project’s shared settings file are committed with the code.
Everyone who clones the repository gets the same enforcement without
setting it up [1]. And when a rule isn’t a
prohibition but a procedure, a multi-step thing the agent should do the
same way every time, it is better packaged as a skill: a named,
self-contained set of instructions the agent loads only when the task
calls for it, so it doesn’t sit in every session’s context. The next
lesson is about writing one.
Exercise
In an empty directory, create a throwaway project: any language, a test
command that works, one source file. Run a coding agent in it and give it
three small tasks without any instructions file. Note every time it
guesses wrong (test command, file location, style, branch). Then write an
AGENTS.md of at most fifteen lines that would have prevented each wrong
guess. Delete the project’s git history and repeat the same three tasks.
A good result: the second run makes none of the wrong guesses from the first, and every line in your file traces back to one of them. If a line doesn’t, it is a candidate to delete.
Stretch: Now add one more rule that is enforced rather than advised: an agent hook that blocks the action, or a CI check that fails when the rule is broken, and remove the equivalent line from AGENTS.md.
Recap
- The agent reads
AGENTS.md(orCLAUDE.md; same content, different tool convention) at the start of every session; it is the briefing for a capable contractor’s first morning. - Put in it what the agent can’t read from the code: commands, structure, conventions, and one line for every mistake it keeps making.
- Leave out what the code already says, long prose, and rules that apply to every project on earth. Short files get followed; long ones get skimmed.
- In a monorepo, the root file holds what’s shared and each project’s file holds only what differs, and on conflict the closer file applies.
- A rule you can’t afford to have skipped belongs in a hook rather than in prose, and a procedure the agent should repeat belongs in a skill.
You can now
- Writes instructions that remove a recurring agent mistake
References
Section titled “References”- Anthropic. Claude Code 101. Claude Academy. Course.
Academy claude-code-101 - Anthropic. Hooks reference. Claude Code documentation. Reference.
Claude Code hooks