# Project instructions: AGENTS.md

> Write the one file a coding agent reads before it touches your project, and keep it short enough that it keeps working.

Page: https://schubergphilis.github.io/ai-training/customizing-agents/instructions/
License: CC BY-SA 4.0, https://creativecommons.org/licenses/by-sa/4.0/

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

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. Claude Code, for example, reads `CLAUDE.md`.
It reads `AGENTS.md` only when there is no `CLAUDE.md`,
`.claude/CLAUDE.md` or `CLAUDE.local.md` in the directory you start it in
or in any directory above it (How Claude remembers your project, Claude Code documentation). 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 give `CLAUDE.md` the one line
`@AGENTS.md`, which imports the file. A `CLAUDE.md` that only asks in
words for `AGENTS.md` to be read gets it read only when Claude decides to
open it (How Claude remembers your project, Claude Code documentation).

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:

```markdown
# 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

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 is close to the exercise at the end of this lesson:
add to the file when the agent repeats a mistake, or when you find
yourself giving it a correction you already gave it in an earlier session
(How Claude remembers your project, Claude Code documentation).

## 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.

#### Pitfall: The file that says everything and changes nothing

A team writes a two-page `AGENTS.md` on day one: architecture overview,
coding philosophy, a paragraph on each module. Three weeks later the agent
is still committing to `main` and still running the wrong test command,
because those two facts are in paragraph seven and paragraph eleven. The
rule: one short line per fact, commands and hard rules first, and add to
the file only when something went wrong.

#### Checkpoint: 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.

```text
# invoice-mailer

Welcome to invoice-mailer! This project is a modern, well-architected Python
service that takes care of rendering customer invoices into PDF documents and
then delivering them reliably via email. We care deeply about code quality,
maintainability and following industry best practices at all times.

Please make sure to write clean, readable, well-documented code. Always think
carefully about edge cases. Tests are very important to us, so please make
sure all the tests pass before you consider a task finished. We use Python.

The codebase is organized into several modules: the rendering module handles
PDF rendering, the mail module handles sending mail, the models module
contains the data models, and the cli module contains the command line
interface. Each of these has an __init__.py file and several submodules
which you can explore to learn more about how they work.

Be careful when making changes and try not to break anything. If you are
unsure about something, use your best judgment.
```

## Instructions in a monorepo

A repository with more than one project in it needs more than one file.
Most tools read the file at the repository root, and also a file in a
subdirectory when the agent works on files under that directory. Claude
Code, for example, reads the instruction files in the directory you start
it in and in every directory above it when the session starts. It reads a
subdirectory's file when it first reads a file in that subdirectory. In
both places a `CLAUDE.md` wins over an `AGENTS.md`. Claude Code reads an
`AGENTS.md` only under the rule above, and a subdirectory's `AGENTS.md`
only when that subdirectory also has no `CLAUDE.md`, `.claude/CLAUDE.md`
or `CLAUDE.local.md` of its own (How Claude remembers your project, Claude Code documentation).

The tools differ on a conflict between two files. The AGENTS.md format
says that the file closest to the edited file takes precedence
(AGENTS.md: a simple, open format for guiding coding agents, agents.md). Claude Code keeps every file in its context, with the
closer file after the root file, and when two rules contradict each other
it may follow either one (How Claude remembers your project, Claude Code documentation). So treat a conflict
between two files as a mistake to fix, and don't count on the closer file
to apply.

The way the tools read the files 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.

```text
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 hand
```

Resist 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/CLAUDE.md`, in every project and only for you
(How Claude remembers your project, Claude Code documentation). How you like a change explained or which shell
you use goes there. The shared file then holds only what the whole team
needs (Claude Code 101, Claude Academy).

## 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. In
Claude Code, exit code 2 is the code that blocks. A hook that exits
with 1 reports an error, and the command still runs
(Hooks reference, Claude Code documentation). "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 reference, Claude Code documentation). Hooks in the project's shared settings file,
`.claude/settings.json` in Claude Code, are committed with the code
(Hooks reference, Claude Code documentation). Everyone who clones the repository gets the same
enforcement without setting it up (Claude Code 101, Claude Academy). 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. A later lesson in this course 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

1. The agent reads `AGENTS.md` (or `CLAUDE.md`; same content, different
   tool convention) at the start of every session; it is the briefing for
   a capable contractor's first morning.
2. Put in it what the agent can't read from the code: commands,
   structure, conventions, and one line for every mistake it keeps making.
3. 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.
4. In a monorepo, the root file holds what's shared and each project's
   file holds only what differs. Keep the two from contradicting each
   other, because tools differ on which one applies.
5. 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.

## References

- How Claude remembers your project, Claude Code documentation, https://code.claude.com/docs/en/memory
- AGENTS.md: a simple, open format for guiding coding agents, agents.md, https://agents.md/
- Claude Code 101, Claude Academy, https://academy.claude.com/courses/claude-code-101
- Hooks reference, Claude Code documentation, https://code.claude.com/docs/en/hooks
