Agent rules are config, not prose
Every agent instruction file starts life the same way: a Markdown file, a dozen bullet points, one machine. Then reality arrives. A second laptop wants the same rules minus one. A colleague wants the team rules without your personal ones. The company wants a security section on every employee's machine. With a prose file there is exactly one tool for all of these: copy the file and edit the copy. Now you have forks, and forks drift, silently, in the one file whose job is to be trusted.
Configuration solved this decades ago. Nobody copies sshd_config per host and hand-edits the copies; config is data, overridden per key, layered by scope. Agent instructions deserve the same treatment, because that's what they are: behavioral configuration for a fleet of very literal-minded users.
Rules as data
The agents role renders ~/AGENTS.md from a structured variable instead of shipping a wall of Markdown. Sections and rules are keyed maps, ordered by weight:
agents_agentsmd_sections:
general:
title: General
weight: 10
items:
concise: "Be concise. Prefer short, direct responses over verbose explanations."
tooling: "Always read `~/.agents/TOOLING.md` for the CLI tools installed on this machine."
code:
title: Code
weight: 20
items:
no_comments: "Match the surrounding code's comment, docstring, and type-annotation density; never add them to code you did not otherwise change."
tests_lint: "Before reporting a task complete, run the project's tests and linter and confirm they pass; if the project has neither, say so."Because every rule has a stable key and the overrides deep-merge, the three operations that force a fork in a prose file become one-liners in a group_vars or host_vars file:
agents_agentsmd_sections:
code:
items:
tests_lint: "Run `make check` before finishing." # reword one rule, in place
no_comments: false # drop one rule
no_secrets_log: "Never log secrets to stdout." # add one rule
security: # add a whole section
title: Security
weight: 15 # lands between General (10) and Code (20)
items:
no_prod: "Never touch production without confirmation."No fork, no drift: every machine renders from the same shipped defaults plus its own delta, and improving a default rule improves it everywhere that didn't explicitly override it. Prose that the structure can't express keeps an escape hatch (point the role at your own template), but in practice rules are bullets, and bullets are data.
What the rules should say
Structure is half the story; the content has real research behind it now. GitHub's study of 2,500+ instruction files, Anthropic's guidance, and the AGENTS.md ecosystem converge on a handful of findings worth encoding:
- Pair every "don't" with a "do". A wall of prohibitions makes agents cautious and exploratory; a paired alternative tells them what to do instead. "Never add comments" underperforms "match the surrounding code's comment density".
- Give an escape hatch. "When a requirement is ambiguous or you are stuck, ask instead of guessing" prevents the large speculative change that costs more to review than it saved.
- Make the approval boundary explicit. The strongest structural pattern in the GitHub study is the three-tier boundary; the tier that matters is ask first: committing, adding dependencies, destructive or irreversible actions.
- Verify against current docs. Agents assume APIs, flags, and versions from training data that has an expiry date; a rule to check the codebase and current docs before using them counters the single most common class of confident error.
- Delete zero-signal rules. "Follow best practices" and "write clean code" carry nothing an agent doesn't already do. The fastest improvement to most instruction files is removal, and a keyed structure makes every line earn its place conspicuously.
- Grow from observed failures. The best files weren't planned; they accreted one rule at a time, each one preventing a mistake that actually happened.
Where rules live: one razor
The layering question ("does this rule go in my global file or the repo?") collapses to a single test:
If a teammate clones this repository, should their agent behave the same way?
Yes: the rule is team configuration and belongs in the repo's committed AGENTS.md, reviewed like code, because changing it changes how every contributor's agent behaves. No: it's about how agents work with you (communication style, your worklog, your resource preferences), and it belongs in the personal global file that travels with your machine and is invisible to everyone else. The consensus across the ecosystem is exactly this split, with a warning attached to each direction: team rules in your global file silently apply only to you, and personal taste in the repo file imposes itself on the team.
The layers compose rather than compete: global loads first, repo root next, subdirectory files nearest-last, and the most specific file wins a conflict.
The company tier
Between "mine" and "this repo's" sits the tier the standard doesn't define: rules for a group of projects, a client, a company. There's no official org level in AGENTS.md, so it has to be composed, and this is where treating rules as config pays off twice.
A company that already provisions its fleet sets the company rules as group_vars in its collection, and the same deep-merge that customizes one machine now standardizes hundreds:
# company collection, group_vars/all.yaml
agents_agentsmd_sections:
acme:
title: ACME
weight: 15
items:
no_prod: "Never touch production without a change ticket."
internal_registry: "Install packages from registry.acme.internal, never from public mirrors directly."Every provisioned machine renders the company section into its global file; the employee's own overrides still merge on top; nothing is forked. For rules that must travel with a repository to outside contributors, the committed project file (stamped from a scaffolding template) stays the vehicle, and for rules that must be enforced rather than suggested, instruction files are the wrong tool entirely: that's what managed settings, permission deny-lists, and hooks are for.
The tool-specific layer
One last boundary keeps the shared file honest. AGENTS.md is read by every agent, so a rule referencing a Claude Code slash command would be noise to Codex or Cursor. Tool-specific rules live one layer up, in the tool's own file, which imports the baseline. The claude role ships exactly that:
# CLAUDE.md
@~/AGENTS.md
## Claude Code
- When suggesting a model or effort change, name the concrete command, e.g. `/model haiku` or `/effort low`.
- When fanning out subagents for mechanical work (search, bulk reads, formatting), run them on a cheaper model like `haiku`; keep review, design, and debugging subagents on the session model.Claude Code reads both; every other agent reads only the baseline. Generic rule in the shared file, concrete command in the tool file.
The next rule you write, add it as a key, not a paragraph: every machine, teammate, and agent after that inherits it as a merge, not a fork.