Restore the wordpress-source section's purpose; close the wp-admin/wp-config/mu-plugins gaps - #321
Merged
Merged
Conversation
The managed wordpress-source section is generated into the AGENTS.md of every managed install, but it described h44lacrosse.com's stack as if it were universal: - "including commerce and payment code" and "the site's ability to take money" — there may be no store and no gateway - "the runtime that gives you memory and tools" — that is Data Machine, this install's happenstance, not a property of wp-content/plugins - a specific never-captured list (vendor, node_modules, composer.lock, package-lock.json, package.json) — one operator's harvest excludes copied verbatim, from a config this tool does not own and cannot read - "exactly what the operator's capture records" — the intended invariant of the posture, which the tool has no way to verify This is the #314/#318 failure in a third form. Those were prose claiming things the enforced policy could not back; this is prose claiming things about the environment that the tool never inspected. An agent told that editing a plugin breaks the site's ability to take money, on a site with no commerce plugin, has been handed a false model of its own install by the file that is supposed to be its most reliable source of truth. Presenting a guessed exclude list is worse than silence, because the agent will trust it. Now describes the category and the reason and leaves the specifics to the operator: plugins this site did not author, including whatever this agent depends on; an update overwrites the change and nothing captures it, both true regardless of what any plugin does; installed and generated files are not authored source, so ask the operator which paths this site actually captures rather than assume. tests/posture.sh gains a guard asserting the rendered prose names none of WooCommerce, Stripe, commerce, payment, money, composer.lock, package-lock, node_modules, Data Machine, homeboy, harvest.yml, or wp-admin. Verified non-vacuous: reintroducing a single product name fails the suite. The two rules this file must follow are now recorded at the top of it.
…e gaps Closes #322. Supersedes #320, which fixed false claims inside a shape that was itself wrong. PURPOSE. The section exists so the agent is an expert on its own runtime by reading the WordPress installed underneath it — that is what lets a small model be competent about WordPress with no skills and no fine-tuning. Direct Reference was the point; Read-Only was a qualifier. The managed variant added in #315 inverted that into an editable list, a read-only list, a justification, a production contract, and a "not source" appendix, with the reference clause buried in the middle. The restriction half was also redundant. lib/source-policy.sh makes the runtime enforce it; prose enforces nothing. Restating the deny list bought no safety while crowding out the one thing prose is uniquely good at. Both variants now lead with what the source is FOR and name the boundary only so the agent is not misinformed about it. The managed section is roughly half its previous length. CORE WAS HALF-DEFINED. wp-admin and wp-includes are siblings, not nested. The root set was wp-content/plugins, wp-content/themes, wp-includes — so on every install ever created the agent could edit wp-admin and every root PHP file, including wp-config.php, which holds the database credentials and the constants that gate AGENTS.md composition. As reference the same omission pointed the agent away from list tables, media, and the upgrade routines. Both halves fixed by defining core once. wp-content/mu-plugins is now denied too. It is agent governance, not a site extension: wp-coding-agents installs the mu-plugin that GENERATES AGENTS.md there. wp-content/uploads stays open because the agent's memory lives under it. DECLARED EXCEPTIONS. wp-config.php has real uses (WP_DEBUG, memory limits), so --managed-writable re-opens a denied path. It cannot reuse --managed-source: that flag means editable AND captured, which is what makes the "your work is recorded" promise true, and wp-config.php is not captured by a component harvest. Conflating them would assert a safety property that does not hold — the #318 failure. Separate category, separate prose. LOGS. The limitation was backwards. We were strict about editing core (correct; an update overwrites it anyway) and accidentally strict about READING the one thing needed to recover from a fatal. OpenCode gates paths outside the site root behind external_directory, which defaults to ask, and an autonomous agent has nobody to ask — so the most important recovery capability in the design sat behind an unanswerable prompt. --log-path grants read and explicitly denies edit. GLOB TRAP. Root files are exact literals, not globs. OpenCode's matcher turns * into .* which spans slashes, so wp-*.php would also match wp-content/plugins/acme/wp-thing.php and over-deny inside a site's own component. source_policy_edit_rules now emits a dir/file kind so each runtime can format correctly. tests/posture.sh pins the purpose (reference framing present in both variants), the enumerate-never-generalise rule, the writable-is-not-captured distinction, log paths readable but not writable, uploads untouched, ordering, and the #320 guard against naming one install's stack.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #322. Supersedes #320 — that fixed false claims inside a shape that was itself wrong, so it is folded in here.
The section had lost its purpose
It exists so the agent is an expert on its own runtime by reading the WordPress installed underneath it. That is what lets a small free model be competent about WordPress with no skills and no fine-tuning.
Direct Referencewas the point;Read-Onlywas the qualifier.The managed variant from #315 inverted it into 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.
That restriction half was redundant.
lib/source-policy.shmakes the runtime enforce it; prose enforces nothing. Restating the deny list bought no safety while crowding out the one thing prose is uniquely good at.Both variants now lead with what the source is for. The managed section is about half its previous length.
Core was half-defined — in both directions
wp-admin/andwp-includes/are siblings, not nested. The root set was:As policy: every install ever created could edit
wp-admin/**and every root PHP file — includingwp-config.php, which holds the DB credentials, the salts, and the constants gating AGENTS.md composition. An agent able to rewrite it can switch off its own governance. Confirmed live on h44lacrosse.com.As reference: the same omission pointed the agent away from list tables,
get_plugins(), media handling and the upgrade routines — undercutting the section's actual purpose.One definition of core fixes both.
wp-content/mu-plugins/is denied too: it is agent governance, not a site extension — wp-coding-agents installs the mu-plugin that generates AGENTS.md there.wp-content/uploads/stays open, because the agent's memory lives under it.Declared exceptions, not a blanket deny
wp-config.phphas legitimate uses (WP_DEBUG, memory limits), so--managed-writablere-opens a denied path.It deliberately does not reuse
--managed-source. That flag means editable and captured, which is what makes the "your work is recorded" promise true.wp-config.phpis not captured by a component harvest. Conflating them would have AGENTS.md assert a safety property that does not hold — #318 again. Separate category, separate prose: "nothing captures them — a rebuild or migration will not carry the change."The limitation that was backwards
We were strict about editing core (correct — an update overwrites it anyway) and accidentally strict about reading the one thing needed to recover from a fatal.
/var/log/nginx/and/var/log/php8.5-fpm.logsit outside the site root. OpenCode gates those behindexternal_directory, which defaults toask— and an autonomous Discord agent has nobody to ask. So the most important recovery capability in the whole design sat behind a prompt that could never be answered.--log-pathgrants read viaexternal_directoryand explicitly deniesedit, so logs are diagnostic input rather than something to rewrite.Glob trap worth knowing
Root files are exact literals, never globs. OpenCode's matcher turns
*into.*, which spans slashes, sowp-*.phpwould also matchwp-content/plugins/acme/wp-thing.phpand over-deny inside a site's own component.source_policy_edit_rulesnow emits adir/filekind so each runtime formats correctly.Tests
tests/posture.sh(79 assertions) pins:wp-admin/named as referencewp-content/uploads/untouched by any managed ruleWooCommerce,Stripe,commerce,payment,money,composer.lock,node_modules,Data Machine,homeboy,harvest.ymltests/opencode-local-plugin-path.shandtests/repair-opencode-json.shhad frozen copies of the old three-rule map; they now assert the properties that matter and leave the canonical list toposture.sh.Full suite: no new failures (same 7 pre-existing environment failures as
main, none inshell.yml).bash -nclean.Follow-up
h44lacrosse.com needs a re-run after release, with
--managed-writable/--log-pathdecided by the operator. Not part of this PR.