Skip to main content

AGENTS.md that works

AGENTS.md is a Markdown file in your repository that Droid loads as coding guidelines when a session starts. It is the cheapest layer of the harness and, measured by first-attempt success, usually the one that moves the number most. It's also the easiest to get wrong in a way that looks right.

This guide is the Lab 2 method without the bank. For the worked example, see Lab 2.

The test for every line​

Ask one question of every line you write: could an agent act on this?

"Write clean, maintainable code" fails. There's no action in it. "Run npm run typecheck && npm test before you say you're done" passes. So does "Percentages are basis points: use basisPoints()."

Most first drafts are written for people who already know the codebase. They describe the culture, thank contributors and ask for care. An agent can't act on care. Neither can a new hire in their first week, which is a useful check. Every line you write for Droid is also written for the next person who joins the team.

Where Droid finds it​

Droid searches from the current working directory up to the git root. At each level it checks the directory itself and the context directories .factory/, .agents/ and .agent/. Personal files live in ~/.factory/, ~/.agents/ or ~/.agent/.

  • One file at the root is the normal case. Name it AGENTS.md.
  • Nested files refine the root for one directory tree. Droid can discover apps/web/AGENTS.md when it later reads files under apps/web/, so nested guidance may load only after Droid touches that tree. Put rules that must always apply in the root file.
  • Compatible names. Droid also reads agents.md, Agents.md, CLAUDE.md and Claude.md. If your repo already has a CLAUDE.md, Droid reads it. The docs are direct about this: don't create every file. Pick one and keep it.

The docs give a mental model for precedence, not a strict merge order: the current request beats standing instructions, nested files refine the root, and project files override personal ones.

The sections that earn their place​

Factory's root template uses these headings: What this project is, Commands, Project layout, Conventions, Verification, Safety. The structure below maps onto them, with two additions that do a lot of work in a regulated repo: a never-list and a stop condition.

1. What this is, in two sentences​

What the service does and why it matters. If a mistake here has consequences outside engineering (a regulator, a customer's money, a patient record), say so. It changes how the rest of the file is read.

2. Commands, exactly​

Install, test everything, test one file, typecheck, lint, build. The exact strings, copyable. Note any command that is slow, expensive or dangerous, and what to do about it.

AGENTS.md
## Commands

- Install: `npm ci`
- Test everything: `npm test`
- Test one file: `npm test -- path/to/file.test.ts`
- Types: `npm run typecheck`
- Lint: `npm run lint` (never run `--fix` over src/billing/)

An agent that has to guess the test command will guess, and a wrong guess costs a turn or worse.

3. Layout, with the dangerous parts marked​

One line per important directory. Mark the ones that are protected, frozen or generated, and say what to do instead of editing them.

- `src/billing/` rating and invoicing. **Protected.**
- `migrations/` SQL migrations. **Protected: needs a change ticket.**
- `vendor/parser/` third-party code under contract. **Frozen: wrap it, never edit it.**
- `src/generated/` built by `npm run codegen`. **Never edit by hand.**

4. Invariants that name the helper​

Number them. Each states the rule, the reason in a few words, and the function to use. A rule without a named alternative leaves the agent to invent one.

BeforeAfter
Be careful with money.Amounts are integer minor units: use Money (cents). No floating point on amounts. Percentages are basis points: use basisPoints().
Respect user privacy in logs.Customer IDs never appear unmasked in logs, errors, fixtures or commits. Use maskCustomerId().
Make sure tests are reliable.Tests never read the real clock. Use freezeClock() from test/helpers/clock.ts.

5. The never-list​

Short, blunt and specific. Each item is something that has happened, or would be very expensive if it did. Pair each "never" with what to do instead.

## Never

- Never edit src/billing/, migrations/ or vendor/ without a human decision.
If the task seems to need it, stop and say so.
- Never change a test's expected value to make it pass. Find out why it differs.
- Never add retries or skips to a flaky test. Fix the cause.
- Never connect to a database or call a production host.

The second and third items come from a real failure pattern. Asked to make a failing test pass, an agent can satisfy the request by changing the test or the code under test. Both go green. Only one is right, and the file has to say which.

6. When to stop​

Say explicitly that stopping is a good outcome. Agents are trained to finish tasks. Without permission to stop, a blocked agent looks for another way through.

## When you are unsure

Stop and explain what you found and what decision you need.
A clear question is a good outcome here, not a failure.

7. Verification, before "done"​

A short checklist the agent runs before it reports success, including a check on what changed.

## Verification (before you say done)

1. `npm run typecheck && npm test`
2. `git diff --name-only` shows nothing under protected paths unless a human approved it.
3. Summarize what changed and why in two or three sentences, naming any invariant you relied on.

The third item is for the reviewer. A two-sentence summary that names the invariant is what lets a tired reviewer spot the one line that matters.

What to leave out​

  • Culture and values. Put them in the README or the handbook.
  • Anything a linter or formatter enforces. The tool already enforces it. Mention the command instead.
  • Long explanations of architecture. Link to them. Droid can read the linked file when it needs to.
  • Procedures for occasional tasks. "How to add a new fee" belongs in a skill, which loads only when the task matches. See Lab 6.
  • Secrets, hostnames, credentials. Obviously. The file is committed.

How long​

Factory's docs set limits: 80,000 characters for the initial AGENTS-style load and 40,000 for guidance discovered later along a read path. They are caps, not targets. The docs themselves say smaller files are usually better. A good AGENTS.md for a single service is often 60 to 120 lines. If yours is growing past a few hundred, move procedures into skills and area-specific rules into nested files.

Test it, don't admire it​

A file that reads well and a file that works are different things. You find out which one you have by measuring.

  1. Pick five to eight real tasks with automatic checks, including one where the right answer is to stop and ask.
  2. Run them headless before you write the file, three times each: droid exec --auto medium -f task.md. Record passes.
  3. Write the file. Run the same tasks again.
  4. Read the failures. Open each failed session with droid --resume <session-id> and find the turn where it went wrong. Usually the file didn't say something, or said it vaguely, or said it in a place Droid wasn't looking.
  5. Look for rules Droid read and then broke. Those aren't AGENTS.md problems. They are candidates for a hook or a permission rule. See Lab 5.

For a quick check before you run anything, paste your file into the AGENTS.md scorer. It looks for what an agent can act on: commands, named helpers, protected paths, a never-list and a stop condition. It is a heuristic, not a measurement, but it catches the file that is all adjectives.

What AGENTS.md cannot do​

AGENTS.md is a request. A good one moves first-attempt success a long way, and that matters: an agent that knows the invariants rarely runs into a control. But on the run where the model weighs your rule against "make the test pass" and decides the rule doesn't apply, nothing in AGENTS.md stops it.

Write the file for the ninety-something percent of runs. Enforce the few rules that must hold on every run with permission rules and hooks. The regulated guardrails guide shows how the layers fit together.

A starting template​

AGENTS.md
# <Service name>

<What it does, in one sentence.> <Why a mistake here matters, in one sentence.>

## Commands

- Install: `<command>`
- Test everything: `<command>`
- Test one file: `<command> <path>`
- Types: `<command>`
- Lint: `<command>`

## Project layout

- `<dir>/` <what lives here>
- `<dir>/` <what lives here>. **Protected.**
- `<dir>/` <what lives here>. **Frozen: never edit. Wrap it.**

## Invariants (never traded for a passing test)

1. <Rule>. Use `<helper>()`.
2. <Rule>. Use `<helper>()`.

## Never

- Never edit <protected paths> without a human decision. If the task seems to need it, stop and say so.
- Never change a test's expected value to make it pass.
- Never <the thing that went wrong last time>.

## When you are unsure

Stop and explain what you found and what decision you need.

## Verification (before you say done)

1. `<typecheck and test commands>`
2. `git diff --name-only` shows nothing under <protected paths>.
3. Summarize what changed and why, naming any invariant you relied on.