Where agent instructions belong: always-on files, scoped rules and on-demand skills
Every coding agent reads an instruction file, most read three kinds, and most teams put everything in the one they found first. The kinds differ in when they load and what that costs — and that, not the agent, decides where a sentence goes.
The first instruction file in a repository is a good idea. The fourth thousand-line one is a tax on every session, and the path from the first to the fourth is always the same: something went wrong once, a sentence was added to the file that was already there, and the file was the always-on one because it was the one that existed. Three surfaces exist because instructions differ in when they should load, and that is the only question worth asking about where a sentence goes.
The three surfaces
| Surface | Loads | Costs | Is for |
|---|---|---|---|
| Always-on file (AGENTS.md, CLAUDE.md, GEMINI.md, copilot-instructions.md — one per agent) | Every session, in full, before anything else | Its whole length, every turn, forever | What is true of every task in this repository: how to build, how to test, what never to do |
| Scoped rules (a rule file with a path pattern, or an instruction file nested in a directory) | When the agent touches a matching path | Only in sessions that go there | Conventions of one area: the migrations folder, the public API, the design system |
| On-demand skills (a folder with a SKILL.md, a prompt file, an agent-requested rule) | When the task matches the description; the body only then | A sentence per session, the body on match | Procedures: how to cut a release, write a migration, triage an incident, review a PR |
The always-on file is the expensive one, and it is expensive in a way nothing reports: no error, no line item, only a session that had less room to think in than the one before. That is why it should hold the smallest true set of facts and nothing that is only sometimes relevant. The other two exist to take the "sometimes" out of it.
What the always-on file is for
Facts about the project that hold for every task, stated once, tersely. How to build and run tests, because the agent will otherwise guess. The commands that are dangerous here. The conventions a reviewer would reject a change for. Where things are, when the layout is not obvious from the tree. That is the list, and it fits in a screen.
# AGENTS.md — or CLAUDE.md, GEMINI.md, copilot-instructions.md: the shape is the same whichever agent reads it
## Build and test
- `pnpm install`, then `pnpm test` (vitest). Run the single file you changed first: `pnpm vitest run <path>`.
- `pnpm typecheck` before any PR. CI fails on warnings.
## Never
- Never run migrations against `DATABASE_URL` from a shell; use `pnpm db:migrate`, which refuses production.
- Never commit `.env*`.
## Conventions
- Errors are typed (`AppError`), never strings. Handlers return them; they do not throw.
- Public API changes need a line in CHANGELOG.md under Unreleased.
## Where things are
- `src/api/` HTTP handlers · `src/lib/` pure code · `scripts/` one-off tools (not shipped).What it is not for: procedures. "How we cut a release" is twelve steps that matter in the one session a month that cuts a release and cost tokens in every other. That is a skill by definition. The tell is a heading in the always-on file that starts with "How to".
What scoped rules are for
Conventions that are true of one part of the tree and irrelevant elsewhere. The migrations folder has rules about reversibility and naming that the frontend never needs; the design system has token names the API never touches. A rule that attaches to a path loads only in sessions that go there, which is exactly the cost profile those conventions deserve.
# One shape of a scoped rule: a Markdown file with a path pattern in its front-matter
# (Cursor spells it .cursor/rules/migrations.mdc; other agents nest a file in the directory itself)
---
description: Conventions for database migrations
globs: ["db/migrations/**"]
---
- Every migration has a `down` that restores the previous schema exactly.
- Never rename a column in place: add, backfill, switch reads, drop — four migrations.
- Filenames are `<UTC timestamp>-<what-changes>.sql`; the sentence, not the ticket.Not every agent has this surface, and the ones that do spell it differently — a globbed rule file in one, a nested instruction file that applies to its own directory in another. Where it is missing, a skill whose description names the folder ("Use when editing anything under db/migrations") is the same idea loaded a different way.
What skills are for
Procedures with a beginning and an end: a task somebody asks for by name, done the same way each time, with steps, commands and a stopping rule. Release notes, a migration, an incident write-up, a dependency upgrade, a PR review against the team’s checklist. Whatever an agent calls the surface — a skill folder, a prompt file, a rule the agent picks by its description — each is a document that costs one sentence per session until the day it is asked for, and then costs its body once.
The test for "is this a skill": could a new engineer be handed this as a runbook and follow it without asking who wrote it? If yes, it is a skill. If it is a fact ("we use vitest"), it is a line in the always-on file. If it is a convention of one folder, it is a rule.
How the agents map onto the three
| Agent | Always-on | Scoped | On demand |
|---|---|---|---|
| Claude Code | CLAUDE.md (project, user, and nested per directory) | A CLAUDE.md in a subdirectory applies when work reaches it | Skill folders under .claude/skills/ |
| Codex | AGENTS.md, nested per directory | A nested AGENTS.md for its own subtree | Procedures in files the always-on one points at |
| Cursor | Rules marked always-apply | .cursor/rules/*.mdc with globs | Rules marked agent-requested, chosen by description |
| Gemini CLI | GEMINI.md, hierarchical | Nested GEMINI.md | Procedures in files the always-on one points at |
| GitHub Copilot | .github/copilot-instructions.md | Path-specific instruction files | Prompt files invoked by name |
| Anything else | Whatever file it reads first | A nested file, if it honours one | A file the always-on one points at, loaded when the task says so |
One consequence for teams on more than one agent: keep the content in one place and let each agent’s file be a pointer. Three always-on files that each say "conventions are in docs/conventions.md; procedures are in docs/runbooks/" keep three agents in agreement with one edit, and a new agent joins by adding a fourth pointer. Three copies of the content drift by the second week.
Splitting the overgrown file
- Read it once with a pen. Mark every sentence F (fact about every task), C (convention of one area) or P (procedure with steps).
- F stays. It should be a screen. If it is more, half the Fs are Cs wearing a general voice.
- Each C becomes a rule attached to its path, or a nested instruction file in that directory where the agent supports one.
- Each P becomes a skill with a description that names the ask, and the steps moved over unchanged.
- Leave a pointer. "Release procedure: the release-notes skill" — one line, so the next person does not re-add the twelve steps.
The mistake this prevents is the one nobody notices until the model seems to have got worse: a repository whose instructions cost more context than its code, every session, because every lesson ever learned was written into the one file that is always open. The lessons were right. The file was wrong.