The first time I added AGENTS.md to a repo, I treated it as decoration. A list of preferences. Vibes for the model. The harness would read it, the model would nod sagely, and a tool call later the agent would do exactly what AGENTS.md told it not to.
The mistake was thinking of it as advice. It isn't advice. It's the repo telling the harness what counts as good work here, in the language the harness can act on. Once I started treating it like a tool — load-bearing, executable steering — the file started doing its job.
The tools post made the case that the loop is the agent. The loop still needs to know what's allowed at this repo. That's what AGENTS.md is.

The repo as instruction surface
tiny-ledger from the prior posts already has a minimal AGENTS.md. Tighten it:
# tiny-ledger agent rules
- Inspect files before editing.
- Parser changes need a fixture and a test.
- Prefer small functions over regex piles.
- Do not replace the parser with a dependency.
- Run `npm test` before claiming done.
- If `npm test` fails, report the failing command and the next smallest fix.
That's not a vibe. Every line is a constraint the harness can check. "Inspect before editing" maps to a tool-call pattern. "Parser changes need a fixture and a test" maps to a hook (more on that in the hooks post). "Do not replace the parser with a dependency" is a load-bearing one — if you skip it, the model will eventually decide your homegrown parser should be peggy or nearley, and you get to discover that taste lives in repos, not in model weights.
The win over generic prompting: this file lives next to the code, gets committed with the code, and survives the next session. The model doesn't have to remember it. The harness reads it on startup.
Reference passing beats paste walls
The other thing project context unlocks: passing the model references instead of forcing it to regurgitate prose.
pi @README.md "Summarize the project contract and the verification command."
The @ prefix tells Pi to load the file as context. Same idea for directories, log files, fixtures. The pattern that scales is point, not paste:
pi @AGENTS.md @src/parseReceipt.js @fixtures/cafe-ugly.txt "Add support for this receipt format. Keep the parser small."
Three references, one instruction. The model gets the rules, the current parser, and the new input shape without anyone copy-pasting code into the chat. The harness handles file loading; the model handles the thinking.
This sounds trivial until you watch the alternative — a session where the user is pasting parseReceipt.js into the chat, then pasting the test, then pasting the failure, then pasting the diff back into the file, then pasting the next failure. The human becomes the file system. Every paste is an opportunity for a transmission error. Reference passing kills that loop.
Why repo instructions beat system prompts
The temptation is to put all the project rules in a global system prompt — one big "you are a careful code assistant" preamble loaded for every conversation. It works until you have more than one repo.
The two repos have different rules. The global prompt either bloats to cover both, or contradicts itself, or gets stripped to be so generic that it stops doing real work. Meanwhile AGENTS.md sits in the repo, gets versioned with the code, and changes when the project changes. The day the repo migrates from Jest to node --test, you update one file and every harness reading from it picks up the change at the next session start.
A few patterns that have earned their place in my AGENTS.md files:
- "Run
npm testbefore claiming done." Stops the agent from declaring victory based on prose alone. - "Prefer small functions over regex piles." Keeps the agent from solving the next bug by making the regex three lines longer.
- "If
npm testfails, report the failing command and the next smallest fix." Turns failure into a structured handoff instead of "I attempted to add support for X but encountered an issue. Let me know how you'd like to proceed." - "Do not edit generated files." Cheap. Saves the recurring bug where the agent edits
dist/and wonders why the change didn't survive a rebuild. - "Inspect before editing." The single biggest correlate with whether the run produces a real fix.
These read like preferences. They're really guardrails the harness enforces, which is a different thing.
Treat repo docs as executable steering
The framing shift that did the most for me here: stop reading AGENTS.md as documentation. Read it as a contract the harness loads at startup. The model is part of the system that has to honor that contract; so are the hooks, the tools, and the verification gates later in the series.
Write AGENTS.md the way you write a contract. Specific. Verifiable. Each line should be something a human reviewer or a hook could check. "Use clean code" is a vibe. "Parser changes need a fixture and a test" is a contract — and the next post wires that contract into actual file-handling discipline.
Exercise
- Run a Pi session in
tiny-ledgerwithoutAGENTS.md. Ask it to fix the next parser bug. Note how many of the project rules it figures out on its own. (Spoiler: some, not all.) - Add the tightened
AGENTS.md. Re-run the same session. Note which rules survived without being repeated in the prompt. - Add one rule you don't think the model will honor (e.g. "always start commit messages with the affected module"). See whether the harness alone is enough, or whether you need a hook to enforce it.
The third experiment is where the next post starts.
Next primitive
The next post goes deeper on reference passing — why the filesystem is a better state store than the chat transcript, and what to do when your context budget is the constraint.