Skip to content
System nominalUTC --:--:--Articles 085
MODULE 01 Lesson 03 / 18 30 MIN

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 changedRun
Any PHP filecomposer phpcs -- <file> and composer phpstan
Logic with unit test coveragecomposer phpunit -- --filter <TestName>
One feature filecomposer 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.

Lab 30 minutes

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.

  1. Write an AGENTS.md that 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.
  2. 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".
  3. 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.
  4. Create a CLAUDE.local.md containing 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.