The wordpress-source AGENTS.md section has drifted from its purpose, and the read-only root set it shares with the runtime permission layer is missing half of WordPress core.
1. The section's purpose is capability, not restriction
It exists so the agent stops guessing at WordPress and reads the source actually running underneath it. That is what lets a cheap model be competent about WordPress without skills or fine-tuning — Direct Reference was the point, Read-Only was the qualifier.
The managed variant added in #315 inverted that. It is now an editable list, a read-only list, a justification, a production contract, and a "not source" appendix — with "verify core APIs, hooks, conventions, and runtime behavior" surviving as one clause in the middle.
The restriction half is also redundant. lib/source-policy.sh makes the runtime enforce it; the agent cannot write to a denied path. Prose enforces nothing, so restating the deny list buys no safety while crowding out the one thing prose is uniquely good at. Enumerating rather than generalising still matters (#318) — but only so the agent is not misinformed about what it may change, which is a far smaller job than the one the section currently does.
2. wp-admin/ is neither referenced nor denied
wp-admin/ and wp-includes/ are siblings. Core is split across both, plus root bootstrap files.
As reference, the section points only at wp-includes/. That is half of core. wp-admin/ holds the list tables, get_plugins(), media handling, and the upgrade routines — an agent asked to understand or extend admin behavior is pointed away from the code that implements it. This directly undercuts the section's actual purpose.
As policy, _source_policy_all_roots is:
wp-content/plugins
wp-content/themes
wp-includes
So on every install, engineering and managed alike, the agent may edit wp-admin/** and every root PHP file — including wp-config.php, which holds the database credentials, the salts, and the constants that govern the agent itself (DATAMACHINE_COMPOSE_AGENTS_MD, DATAMACHINE_WORKSPACE_PATH). An agent that can rewrite wp-config.php can switch off its own AGENTS.md composition.
Confirmed live on h44lacrosse.com. This predates the posture work; the three globs are original.
3. wp-config.php needs an opt-in, not a blanket deny
Most managed installs should never touch it, but there are legitimate cases (toggling WP_DEBUG, memory limits, a constant a plugin requires).
It cannot reuse --managed-source. That flag means editable and captured, and it is what makes the "your work is recorded" promise true. wp-config.php is not captured by a component harvest. Declaring it as a managed source would have AGENTS.md assert a safety property that does not hold for that path — the #318 bug again.
Two categories, with different prose:
| declaration |
editable |
captured |
guidance says |
--managed-source <dir> |
yes |
yes |
this site's own source, recorded by the operator's capture |
--managed-writable <path> |
yes |
no |
you may change this; nothing records it, so a rebuild loses it |
4. Glob trap for whoever implements this
OpenCode's matcher (packages/opencode/src/util/wildcard.ts) converts * to .*, which spans slashes. A pattern like wp-*.php intended for root files would also match wp-content/plugins/acme/wp-thing.php and over-deny inside an owned component.
Root files must be exact literals (wp-config.php, wp-settings.php, …). Wildcard.match anchors ^…$, so a literal matches only that exact relative path. Directory roots keep /**.
This also means source_policy_edit_rules should emit a kind (dir / file) alongside each path, because the three runtimes format them differently: OpenCode appends /** to directories, Claude Code wraps in Edit($site/…), and Codex uses bare directory names in its filesystem profile.
Proposed shape
## WordPress Source
The WordPress running this site is on disk underneath you. Read it to verify core
APIs, hooks, conventions, and runtime behavior instead of relying on assumptions:
`wp-admin/` and `wp-includes/` for core, `wp-content/` for everything installed here.
This site's own source, which is yours to change:
- <enumerated owned paths>
Everything else is reference material.
Reference first. Ownership second. Consequence in a line or two. The permission layer does the rest. The production contract and capture caveats are real but are a different concern and should not swell this section.
Supersedes #320, which fixed false claims inside a shape that is itself wrong.
The
wordpress-sourceAGENTS.md section has drifted from its purpose, and the read-only root set it shares with the runtime permission layer is missing half of WordPress core.1. The section's purpose is capability, not restriction
It exists so the agent stops guessing at WordPress and reads the source actually running underneath it. That is what lets a cheap model be competent about WordPress without skills or fine-tuning —
Direct Referencewas the point,Read-Onlywas the qualifier.The managed variant added in #315 inverted that. It is now an editable list, a read-only list, a justification, a production contract, and a "not source" appendix — with "verify core APIs, hooks, conventions, and runtime behavior" surviving as one clause in the middle.
The restriction half is also redundant.
lib/source-policy.shmakes the runtime enforce it; the agent cannot write to a denied path. Prose enforces nothing, so restating the deny list buys no safety while crowding out the one thing prose is uniquely good at. Enumerating rather than generalising still matters (#318) — but only so the agent is not misinformed about what it may change, which is a far smaller job than the one the section currently does.2.
wp-admin/is neither referenced nor deniedwp-admin/andwp-includes/are siblings. Core is split across both, plus root bootstrap files.As reference, the section points only at
wp-includes/. That is half of core.wp-admin/holds the list tables,get_plugins(), media handling, and the upgrade routines — an agent asked to understand or extend admin behavior is pointed away from the code that implements it. This directly undercuts the section's actual purpose.As policy,
_source_policy_all_rootsis:So on every install, engineering and managed alike, the agent may edit
wp-admin/**and every root PHP file — includingwp-config.php, which holds the database credentials, the salts, and the constants that govern the agent itself (DATAMACHINE_COMPOSE_AGENTS_MD,DATAMACHINE_WORKSPACE_PATH). An agent that can rewritewp-config.phpcan switch off its own AGENTS.md composition.Confirmed live on h44lacrosse.com. This predates the posture work; the three globs are original.
3.
wp-config.phpneeds an opt-in, not a blanket denyMost managed installs should never touch it, but there are legitimate cases (toggling
WP_DEBUG, memory limits, a constant a plugin requires).It cannot reuse
--managed-source. That flag means editable and captured, and it is what makes the "your work is recorded" promise true.wp-config.phpis not captured by a component harvest. Declaring it as a managed source would have AGENTS.md assert a safety property that does not hold for that path — the #318 bug again.Two categories, with different prose:
--managed-source <dir>--managed-writable <path>4. Glob trap for whoever implements this
OpenCode's matcher (
packages/opencode/src/util/wildcard.ts) converts*to.*, which spans slashes. A pattern likewp-*.phpintended for root files would also matchwp-content/plugins/acme/wp-thing.phpand over-deny inside an owned component.Root files must be exact literals (
wp-config.php,wp-settings.php, …).Wildcard.matchanchors^…$, so a literal matches only that exact relative path. Directory roots keep/**.This also means
source_policy_edit_rulesshould emit a kind (dir/file) alongside each path, because the three runtimes format them differently: OpenCode appends/**to directories, Claude Code wraps inEdit($site/…), and Codex uses bare directory names in its filesystem profile.Proposed shape
Reference first. Ownership second. Consequence in a line or two. The permission layer does the rest. The production contract and capture caveats are real but are a different concern and should not swell this section.
Supersedes #320, which fixed false claims inside a shape that is itself wrong.