Skip to content

Repo Instruction Files: What Actually Belongs in Them

4 min read · updated August 3, 2026

It is not documentation and it is not configuration. It is a prompt prefix that gets prepended to every request a coding tool makes, and once you see it that way the editorial decisions become obvious.

What these files are

AGENTS.md is the cross-tool convention that emerged in 2025 for a repository-root file that coding agents read. CLAUDE.md, .cursor/rules/, .github/copilot-instructions.md and .windsurfrules are the same idea with different filenames. Whichever your tools read, the mechanism is identical: the file’s contents are inserted into the model’s context, usually near the front, on every request.

Practical consequences of that mechanism, in order of how often they are missed. It is paid per request, not per session. Its position near the front means it is prime real estate for caching and a poor place for anything that changes. It competes for attention with the actual task. And it is advisory — nothing enforces it, so a rule that contradicts the code loses, because the code is evidence and the file is an assertion.

Most tools also stack per-directory files: a root file plus one in packages/api/ applies both when working there. That is the right shape — global rules global, local rules local — and it is also how these files quietly triple in size without anyone deciding to.

The multiplier nobody computes

A 3,000-token instruction file, in an agent session of 25 turns, is 75,000 input tokens. Four sessions a day is 300,000 tokens a day from the instruction file alone, before any code is read. Grow it to 12,000 tokens — which happens naturally, one reasonable addition at a time — and it is 1.2 million.

At an assumed $3 per million that is a few dollars a day per developer, which is not the argument. The argument is attention: those tokens sit at the front of every request, competing with the task, and material in a long context is used less reliably the further it is from the instruction — the position effect applies to your own file too. A 12,000-token instruction file does not instruct twelve thousand tokens’ worth.

So the test for a line is not “is this true”. It is: would a competent stranger get this wrong from reading the code, and does getting it wrong cost more than the tokens? Aim for something a person would read in two minutes — call it 100 lines. Every line beyond that is competing with the lines that matter.

What earns its place

  • The commands. How to run the tests, one test file, the linter, the type checker, the dev server, the build. This is the highest-value content in the file by a distance, because an agent that cannot run your tests cannot verify anything, and it will guess npm test in a repository where the answer is pnpm -F api test.
  • Constraints that are invisible in the code. “This package must stay dependency-free.” “Migrations are generated, never hand-written.” “Anything under generated/ is overwritten by the build.” “Handlers must be idempotent; the queue is at-least-once.” Each of these prevents a whole afternoon.
  • Conventions the codebase is inconsistent about. If every file agreed, the model would infer it. The value is exactly at the points of inconsistency: “new code uses the Result type, not exceptions — you will see both.” Naming the legacy pattern as legacy is what stops it being copied.
  • A five-line layout map. Where things live, one line per top-level area. Cheap orientation that saves exploration turns.
  • The definition of done. Types clean, tests pass, changelog entry, no new dependency without asking. An agent with no completion criterion invents one.
  • Security prohibitions with the local alternative. “Never interpolate SQL; use db.scoped(orgId).” See the classes this addresses.

A workable skeleton, and note how much of it is commands:

# AGENTS.md

## Commands
- install:    pnpm install --frozen-lockfile
- test all:   pnpm -r test
- test one:   pnpm -F @acme/billing test -- invoice.test.ts
- types:      pnpm -r typecheck        # must be clean before you finish
- lint:       pnpm lint --fix          # do not hand-fix formatting

## Layout
packages/api      HTTP handlers, thin; no business logic
packages/billing  money, tax, invoicing. Pure; no I/O.
packages/db       schema + generated client. Do not edit generated/.

## Constraints
- Money is integer cents (type Cents). Never floats, never Number for ids.
- Every DB query goes through db.scoped(orgId). No bare .query().
- Migrations: pnpm db:make <name>. Never hand-edit a migration.
- Handlers are retried; make them idempotent.

## Done means
types clean, pnpm -r test green, a test that fails without your change,
and a changelog entry under .changeset/.

What to delete today

  • Anything a formatter enforces. Indentation, quote style, semicolons, import order, line length. Prettier and Ruff decide these deterministically and for free. Every such line is tokens spent on a settled question.
  • Anything the code says. The framework, the language version, the directory names, the fact that you use React. The model can see all of it.
  • Aspirational architecture. “We are moving towards hexagonal architecture” describes an intention. The model will find both patterns and cannot tell which file is which. Say instead: “new handlers go in packages/api/v2/; v1/ is legacy, do not extend it” — a checkable rule.
  • Long prose and onboarding material. The human welcome, the history, the team norms, the links. Real and belonging in CONTRIBUTING.md, which nothing prepends to every request.
  • An API reference. It goes stale, it is enormous, and the types are already in the repository.
  • Politeness and emphasis. “IMPORTANT: please always remember to be very careful.” If everything is in capitals, nothing is.

Making the file fail the build

The worst failure mode is not a bloated file, it is a lying one. A test command that changed six months ago sends every agent session down a wrong path, and nothing reports it, because nobody runs the file.

Fix it structurally: make CI execute the commands the file claims.

# ci/verify-agents-md.sh — the file is executable documentation or it is a lie
set -euo pipefail
grep -oP '(?<=^- test one:\s{4}).*' AGENTS.md | while read -r cmd; do
  echo "verifying: $cmd"
  eval "$cmd" >/dev/null
done
# also assert the paths it names still exist
grep -oE '(packages|apps)/[a-z0-9/_-]+' AGENTS.md | sort -u | while read -r p; do
  test -e "$p" || { echo "AGENTS.md references missing path: $p"; exit 1; }
done

The other question worth asking about a file this size is whether any given line is doing anything. You can find out: delete a rule, run a task that should trip over it, and see whether the model gets it wrong. Rules that survive that test are load-bearing; rules that make no difference are tokens. It is a crude experiment and it is a great deal better than the usual policy, which is that every line ever added stays forever because nobody can prove it is unnecessary. Do it for the five longest entries and you will usually delete two of them.

Two more habits worth having. Review the file whenever an agent session goes badly — a wasted session is usually a missing constraint, and adding it is the highest-value edit available. And review it for deletions once a quarter, because these files only ever grow: every bad session adds a line, and nothing ever removes one.

Repo Instruction Files: What Actually Belongs in Them · Multigrid