The tools, and what they actually are
Setting Up a Repository an Agent Can Work In
The AGENTS.md file that stops an agent guessing, the CLAUDE.md precedence trap that silently disables it, and what WordPress's own repositories put in theirs.
You open a repository, point an agent at it, and ask for a settings page. It produces one that uses a serialised array in a single option, escapes nothing on output, and puts the callback in the main plugin file. None of that is a model failure. It is a missing specification: you asked a competent stranger to work in your house and did not tell them where the light switches are.
The instruction file is the cheapest thing in this course and the one that changes the most. Before it, the agent fills every gap from training data. After it, the gaps it fills are the ones you left open on purpose. Twenty lines is enough to move a run from "plausible plugin" to "plugin that matches this repository".
The file is a channel, not a knowledge base
AGENTS.md is not an encyclopedia of WordPress. Anything you paste into it
competing with the model's training data is a distraction, and anything you copy from
documentation will be stale in a year while the documentation will not. The file's job is to
carry the facts the model cannot get anywhere else: what this repository is, which commands
are safe to run, and which decisions have already been made.
WordPress's own convention page states this in the project's own words. The root
AGENTS.md is loaded in every session, stays lean, and routes to public
contributor documentation. Directory-level files load when an agent works with files in that
directory. Skills are thin and task-scoped. Compatibility files such as
CLAUDE.md are one-line redirects, with the content living in
AGENTS.md in every case.
The guiding principle behind all of it is worth quoting to yourself when you are tempted to paste in a tutorial: agent files should route to canonical docs and never fork their content, because agent-only files are easy to let rot.
What WordPress actually puts in its own files
The two best worked examples in existence are WordPress's, and they are worth reading in full before you write your own. They are also different shapes, which is instructive.
WordPress/gutenberg's AGENTS.md is 104 lines and
is written for someone working inside a large JavaScript and PHP monorepo. It opens with the
development environment: switch the Node version, install Composer dependencies, and check the
test environment's status before starting it. It then documents the trap that
npm run build never fails on type errors, so TypeScript and checked JavaScript
changes need a separate npm run typecheck. It lists the directories that matter —
/lib/ for PHP, /phpunit/ for PHP tests, /schemas/json/
for the block.json, theme.json and font-collection.json
schemas published to schemas.wp.org. It gives the code-quality commands as raw
binaries, vendor/bin/phpcs to check and vendor/bin/phpcbf to fix.
The part worth stealing is the architecture section. It records decisions an agent would
otherwise get wrong because nothing in the code says them out loud: the three editor layers and
the rule that lower layers must not depend on higher ones, the requirement that a change to one
site-editor implementation be mirrored in the other in the same commit, the instruction to work
with the block tree through APIs rather than the serialised HTML, and the styles cascade where
WordPress defaults sit below theme.json, which sits below user preferences.
wp-cli/wp-cli's AGENTS.md is 140 lines and is the
single most useful WordPress agent file for plugin developers, because almost everything in it
transfers. Its core is a change-to-command table:
| You changed | Run |
|---|---|
| Any PHP file | composer phpcs -- <file> and composer phpstan |
| Logic with unit test coverage | composer phpunit -- --filter <TestName> |
| One feature file | composer behat -- features/<name>.feature |
Three details in that file are worth copying wholesale. It prescribes
export NO_COLOR=1 and export WP_CLI_TEST_QUIET=1 so tool output
stays readable when it is being read by a program instead of a person. It warns that the full
composer test target installs WordPress from scratch once per scenario and takes
tens of minutes, with the explicit instruction not to run it for a two-line change — that is
how you stop an agent burning an afternoon to check a typo. And it says that a green
composer test is not a green CI, because AST checks and typo detection run
separately on every pull request.
The trap: a local override can disable your instructions
Now the part that breaks setups that look correct. The WordPress-recommended compatibility
pattern is a CLAUDE.md containing exactly one line, @AGENTS.md, which
is what WordPress/gutenberg does. Claude Code reads that redirect happily — but its
documented rule is that it reads AGENTS.md only when no CLAUDE.md or
CLAUDE.local.md exists in the working directory or above it. Reading
AGENTS.md directly requires version 2.1.277 or newer, and the behaviour is
controlled by a Project instructions setting whose default prefers the Claude file.
So consider a reader who follows the WordPress pattern exactly, then adds a
CLAUDE.local.md for their own machine-specific notes and gitignores it. The
documented consequence is that the redirect stops being read and the agent loses the entire
instruction file, silently. Nothing errors. Nothing warns. The agent behaves as though
you never wrote AGENTS.md, which looks exactly like a model quality problem and
gets diagnosed as one.
Two ways out of the precedence trap
Either set Project instructions to read both files, or do not keep a
CLAUDE.local.md in a repository that relies on AGENTS.md. If you
are not sure which state you are in, run the quote test from the lab below — it answers the
question in one prompt, and the answer is a fact rather than a configuration belief. Other
agents handle precedence differently: OpenCode treats AGENTS.md as primary with
CLAUDE.md as a fallback and takes the first match, so both files can exist
without this particular failure.
Write the file that stops the guessing
Here is a starting AGENTS.md for a WordPress plugin repository. It is short on
purpose. Everything in it is something this repository knows and the model does not.
# Acme Notices
WordPress plugin, PHP 8.3+, targets WordPress 7.0.x. No build step for PHP.
JavaScript and CSS are built with @wordpress/scripts.
## Commands
- Lint PHP: `composer phpcs -- <file>`
- Fix PHP style: `composer phpcbf -- <file>`
- Static analysis: `composer phpstan`
- Lint JS and CSS: `npm run lint:js` and `npm run lint:css`
- Build assets: `npm run build`
- Run the plugin: `wp plugin activate acme-notice` from the WordPress root
## Rules
- Every PHP file opens with an ABSPATH guard.
- Escape on output, sanitise on input, capability-check before every write.
- The text domain is `acme-notice`. Do not introduce a second one.
- Admin screens live in `includes/admin/`. Do not add new top-level menu items.
- Do not edit files in `build/`; they are generated.
## Do not trust
- A green `npm run build` says nothing about whether a WordPress API exists.
- `php -l` says nothing about whether a hook name is correct.
- If a verification command cannot run, say so and stop. Do not write a stub
of a WordPress function and test against it.
## Docs
- Plugin handbook: https://developer.wordpress.org/plugins/
- Coding standards are enforced by `phpcs.xml.dist` in this repo.
The last section before the docs is the one that matters most in practice, and it exists
because of a specific failure. In a documented agent run, the agent hand-wrote stubs of
wp_kses_post() and get_block_wrapper_attributes(), tested its own
code against those stubs, reported green, and noted in its summary that its WP-CLI command
could not be run because no WordPress was present. The stubs happened to match real WordPress
that time. An agent that stubs the framework cannot detect a wrong belief about the framework,
and telling it not to is cheaper than reviewing for the difference.
Make the file load-bearing and test that it does
A rules file nobody reads is worse than none, because it creates the belief that you have covered the problem. Two habits fix that. The first is to keep the file small enough that reading it costs the agent almost nothing, which is why the WordPress convention pushes content out into linked documentation instead of inlining it. The second is to test the file rather than trusting it.
Take a WordPress plugin or theme repository you already have, or create an empty one with a single PHP file in it. Then do four things.
- Write an
AGENTS.mdthat answers five questions, in this order: what this repository is; which commands to run after a change; which conventions are not obvious from the code; what must never be touched; and what a passing check does not prove. - Add a claim that exists nowhere else in the repository. A convention the code does not follow yet, such as "new admin screens must register under the existing Acme menu".
- Ask the agent, in a fresh session, to quote the repository's rule about where a new admin screen goes, and to state which files it read to find that rule. Record both answers.
- Create a
CLAUDE.local.mdcontaining one line of your own notes. Ask the same question again in a new session. Record what changes.
Show the expected result, the reasoning, and what to do about each outcome
Step 3 has three possible outcomes, and each one tells you something different.
If the agent quotes the convention and names AGENTS.md as the source,
the file is being read and you are done. This is the common result, because most agents look
for the convention without being told.
If the agent quotes the convention but cannot say where it read it, treat that as a pass on the file and a warning about the agent's reporting. The content arrived; the transparency did not. The practical consequence is that you cannot use "where did you learn that" as a diagnostic, so rely on the next test instead.
If the agent invents a plausible answer — "admin pages usually go in
includes/admin/" — the file is not being read. Check the filename and its
location first, then check whether a higher-precedence compatibility file exists.
Step 4 is where the interesting failure lives, and it is worth being precise about why. The
documented Claude Code rule is that AGENTS.md is read only when no
CLAUDE.md or CLAUDE.local.md exists in the working directory or
above it. So with a CLAUDE.md that redirects to @AGENTS.md, adding a
CLAUDE.local.md is enough to lose the whole instruction file. The agent does not
warn you. The behaviour degrades to exactly what it was before you wrote
AGENTS.md, which is why people attribute this to model quality and buy a more
expensive plan.
There are two correct responses and one wrong one. Correct: delete the
CLAUDE.local.md from repositories that depend on AGENTS.md, or set
Project instructions so both files are read. Also correct: test after every change to the
instruction layer, because this is a configuration that can regress without any code
changing. Wrong: keeping a machine-local override for convenience and assuming the redirect
still works, then wondering for a week why the agent stopped following your conventions.
One more thing belongs in the file before you commit it, and it is the section most people omit: what a passing check does not prove. Your repository almost certainly cannot run WPCS and PHPStan on every edit — the research environment for this course could not, because neither Composer nor PHPCS was installed. Writing down that gap is what stops an agent from filling it with a stub. A rules file that says "if you cannot run it, say so" is the difference between an agent that reports a limitation and one that reports success.
Official reference
The WordPress developer reference for what this lesson uses.
Progress
Mark this lesson done and it stays marked in this browser.