Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 47 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ operator-entrypoints/wp-coding-agents-setup/setup.md
| `--runtime <name>` | Coding runtime: `opencode`, `claude-code`, or `codex`. Auto-detected when omitted. |
| `--posture <name>` | `engineering` (default) or `managed`. See [Install Posture](#install-posture). |
| `--managed-source <path>` | wp-content path the site owns and may edit under `--posture managed`. Repeatable. |
| `--managed-writable <path>` | Denied path to re-open for editing (e.g. `wp-config.php`). Not captured. Repeatable. |
| `--log-path <path>` | Absolute path outside the site root the agent may read. Repeatable. |
| `--local` | Local machine mode. Skips server infrastructure. |
| `--existing` | Add to an existing WordPress install. |
| `--wp-path <path>` | WordPress root path. Implies `--existing`. |
Expand Down Expand Up @@ -192,13 +194,20 @@ guidance the agent reads.

| | `engineering` (default) | `managed` |
| --- | --- | --- |
| `wp-content/themes/`, `wp-content/plugins/` | read-only reference | read-only **except declared owned paths** |
| `wp-includes/` | read-only | read-only |
| WordPress core (`wp-admin/`, `wp-includes/`, root bootstrap) | read-only | read-only |
| `wp-content/mu-plugins/`, `wp-config.php` | read-only | read-only, opt-in |
| `wp-content/plugins/`, `wp-content/themes/` | read-only | read-only **except declared owned paths** |
| `wp-content/uploads/` | writable (agent memory) | writable (agent memory) |
| Data Machine Code | installed | not installed |
| Workspace, git, GitHub | the agent's workflow | not present |
| How changes reach version control | the agent commits and opens pull requests | captured out-of-band by the operator |
| Runtimes | all | `opencode` only |

Both postures point the agent at the installed WordPress source as reference —
that is the section's purpose, and it is why a small model can be competent
about WordPress here without skills or fine-tuning. Reading is never
restricted. Everything below is about *writing*.

**Engineering** is the developer setup: the installed tree is reference
material, and every code change happens in a Data Machine Code workspace so it
is tracked in git and reviewed through GitHub.
Expand All @@ -209,28 +218,51 @@ plugins directly and its changes are live on save; something outside the box —
for example a scheduled `homeboy harvest` — captures them into git as restore
points.

Managed does **not** open `wp-content`. Those directories also hold commerce and
payment plugins, third-party code, the stock themes, and the agent's own Data
Machine runtime — none of which the site owns, none of which the operator's
capture records, and all of which an update would overwrite anyway. Instead you
declare exactly what the site owns:
Managed does **not** open `wp-content`. Those directories hold third-party
code, the stock themes, and the agent's own runtime, none of which the site
owns or the operator's capture records. Declare exactly what the site owns:

```bash
./setup.sh --posture managed \
--managed-source wp-content/themes/acme \
--managed-source wp-content/plugins/acme-core
```

Everything else stays read-only. **The declared set must match what the
operator's capture records** — a path the agent can edit but nothing records is
a path where it silently loses work. Declaring nothing fails closed: the agent
gets no editable source and the generated guidance says so.
**The declared set must match what the operator's capture records** — a path
the agent can edit but nothing records is a path where it silently loses work.
Declaring nothing fails closed: no editable source, and the guidance says so.

### Declared exceptions

`wp-config.php` and `wp-content/mu-plugins/` are agent governance — they hold
the database credentials and the mu-plugin that generates AGENTS.md itself, so
an agent able to edit them can rewrite its own instructions. They are denied by
default and re-opened only on request:

```bash
--managed-writable wp-config.php
```

These are **editable but not captured**, and the generated guidance says so
explicitly. Conflating them with `--managed-source` would have AGENTS.md
promise that the work is recorded when it is not.

### Log access

A coding agent that cannot read the PHP error log cannot recover the site it
just broke. OpenCode gates paths outside the site root behind
`external_directory`, which defaults to `ask` — and an autonomous agent has
nobody to ask. Declare the host's log paths so they are readable (never
writable):

```bash
--log-path /var/log/nginx --log-path /var/log/php8.4-fpm.log
```

A single source of truth (`lib/source-policy.sh`) derives the runtime
permissions and the generated guidance from the chosen posture and that
declared set, so the prose cannot tell the agent to do something the
permissions then block — or imply a directory is editable when only two paths
inside it are.
permissions and the generated guidance from the posture and these
declarations, so the prose cannot tell the agent to do something the
permissions then block.

Managed is currently supported on the `opencode` runtime only. OpenCode
evaluates permissions with `findLast` over rules in config key order, so a
Expand Down
25 changes: 15 additions & 10 deletions guidance/wordpress-source.engineering.sh
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
#!/bin/bash
# guidance/wordpress-source.engineering.sh — installed WordPress source, engineering posture.
#
# The agent treats the installed tree as reference material and makes every code
# change in a Data Machine Code workspace, tracked in git and reviewed through
# GitHub. Matches the deny rules lib/source-policy.sh hands the runtimes for this
# posture: all three roots read-only, workspace granted.
# Same purpose as the managed variant: CAPABILITY. The agent should be an expert
# on its own runtime by reading the WordPress actually installed here rather
# than relying on training data. The read-only boundary is a qualifier, not the
# point — see the header of wordpress-source.managed.sh and #322.
#
# What differs from managed is only where changes land: engineering routes them
# through a Data Machine Code workspace so they are tracked in git and reviewed.

guidance_id() { printf 'wordpress-source'; }
guidance_priority() { printf '1'; }
guidance_label() { printf 'WordPress Source'; }
guidance_description() { printf 'Direct-reference and read-only boundaries for installed WordPress source.'; }
guidance_description() { printf 'Points the agent at the installed WordPress source as read-only reference.'; }
guidance_freshness() { printf 'static'; }
guidance_conditions() { printf 'Registered by wp-coding-agents on engineering-posture installations.'; }

guidance_render() {
cat <<'MD'
## WordPress Source (Direct Reference, Read-Only)

Use the installed WordPress source to verify core APIs, hooks, conventions, and runtime behavior instead of relying on assumptions. Search and read these directories directly when working on WordPress:
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-includes/` — core internals: the hook system, query, HTTP, database, and template APIs.
- `wp-admin/` — the other half of core: admin screens, list tables, media and upgrade routines.
- `wp-content/plugins/` and `wp-content/themes/` — every extension installed here.

- `wp-content/plugins/` — plugin source (read-only)
- `wp-content/themes/` — theme source (read-only)
- `wp-includes/` — WordPress core (read-only)
Grep and read these freely. They are the ground truth for how this site actually behaves.

These paths are **read-only references**. Make code changes in the configured managed workspace, not in the installed source tree.
These paths are **read-only reference**. Make code changes in the configured managed workspace, not in the installed source tree.
MD
}
117 changes: 64 additions & 53 deletions guidance/wordpress-source.managed.sh
Original file line number Diff line number Diff line change
@@ -1,75 +1,86 @@
#!/bin/bash
# guidance/wordpress-source.managed.sh — installed WordPress source, managed posture.
#
# Managed agentic hosting: the agent edits the site's OWN theme and plugins in
# place at the owner's request. There is no workspace, no git, and no GitHub in
# its world.
# WHAT THIS SECTION IS FOR
#
# The editable set is enumerated from source_policy_owned_sources, never
# described as a directory. An earlier version of this file said "this site's
# theme and plugins" while the permission layer opened `wp-content/plugins/**`
# wholesale — which on a real install meant WooCommerce, a payment gateway, and
# the agent's own Data Machine runtime. Prose that generalises where the policy
# enumerates is how that gap opens. If a path is not in the declared set it is
# not editable, and this section must say so by name.
# 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 small model be competent about WordPress with no skills and no
# fine-tuning. Reference is the point; the ownership boundary is a qualifier.
#
# Restriction is the permission layer's job and is already enforced there
# (lib/source-policy.sh). Prose enforces nothing, so restating the deny list
# here buys no safety while crowding out the one thing prose is uniquely good
# at. The boundary appears only so the agent is not MISINFORMED about what it
# may change — a far smaller job than policing it. See #322.
#
# TWO RULES WHEN EDITING THIS FILE
#
# 1. Enumerate, never generalise. The editable set comes from
# source_policy_owned_sources and is printed path by path. An earlier
# version said "this site's theme and plugins" while the policy opened
# `wp-content/plugins/**` wholesale — #318.
#
# 2. Assert only what wp-coding-agents actually knows. This text ships to EVERY
# managed install, so it must not describe one site's stack as if it were
# universal. An earlier version named "commerce and payment code" and "the
# site's ability to take money" (there may be no store), and listed one
# operator's harvest excludes verbatim — a config this tool cannot read.
# Describe the CATEGORY and the REASON; leave the specifics to the
# operator. #320.

guidance_id() { printf 'wordpress-source'; }
guidance_priority() { printf '1'; }
guidance_label() { printf 'WordPress Source'; }
guidance_description() { printf 'Editable owned source, read-only everything else, and the live-production contract.'; }
guidance_description() { printf 'Points the agent at the installed WordPress source and names the trees this site owns.'; }
guidance_freshness() { printf 'conditional'; }
guidance_conditions() { printf 'Registered on managed-posture installations; the editable path list is generated from the declared managed sources.'; }
guidance_conditions() { printf 'Registered on managed-posture installations; the owned-source list is generated from the declared managed sources.'; }

guidance_render() {
local owned
local owned writable
owned="$(source_policy_owned_sources)"
writable="$(source_policy_writable_paths)"

printf '%s\n' '## WordPress Source'
printf '\n'
printf '%s\n' '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:'
printf '\n'
printf '%s\n' '- `wp-includes/` — core internals: the hook system, query, HTTP, database, and template APIs.'
printf '%s\n' '- `wp-admin/` — the other half of core: admin screens, list tables, media and upgrade routines.'
printf '%s\n' '- `wp-content/plugins/` and `wp-content/themes/` — every extension installed here, including the ones this site depends on.'
printf '\n'
printf '%s\n' 'Grep and read these freely. They are the ground truth for how this site actually behaves.'
printf '\n'

if [ -z "$owned" ]; then
# Fail closed, loudly. Never imply a directory is editable.
printf '%s\n' 'This install declares no editable source. Every file under `wp-content/` and `wp-includes/` is **read-only** to you.'
printf '\n'
printf '%s\n' 'Read them freely to verify APIs, hooks, and runtime behavior. If a task requires changing code, stop and tell the operator that no editable source is configured.'
if [ -z "$owned" ] && [ -z "$writable" ]; then
printf '%s\n' 'Nothing on this install is declared as editable, so treat all of it as reference. If a task requires changing code, say that no editable source is configured rather than picking somewhere to edit.'
return 0
fi

printf '%s\n' 'You edit this site directly. There is no separate checkout, no workspace, and no pull request step.'
printf '\n'
printf '%s\n' '### Editable — this is the complete list'
printf '\n'
printf '%s\n' 'These are the source trees this site owns. Nothing else is editable, no matter where it lives:'
printf '\n'
printf '%s\n' "$owned" | while IFS= read -r path; do
[ -n "$path" ] || continue
printf -- '- `%s/`\n' "$path"
done
printf '\n'
printf '%s\n' '### Read-only — everything else'
printf '\n'
printf '%s\n' 'Read these to verify APIs, hooks, conventions, and runtime behavior. Never edit them:'
printf '\n'
printf '%s\n' '- `wp-includes/` — WordPress core.'
printf '%s\n' '- The rest of `wp-content/plugins/` — third-party plugins, including commerce and payment code, and the runtime that gives you memory and tools. Editing these breaks your own environment or the site'"'"'s ability to take money, and an update erases the change anyway.'
printf '%s\n' '- The rest of `wp-content/themes/` — bundled and third-party themes.'
printf '%s\n' '### What is yours to change'
printf '\n'
printf '%s\n' 'To change behavior that lives in code you may not edit, change the site'"'"'s own theme or plugin instead — a hook, a filter, or a template override. If that is genuinely impossible, say so rather than editing outside the list.'
printf '\n'
printf '%s\n' '### Working on production'
printf '\n'
printf '%s\n' '- **Your edits are live the moment you save them.** There is no staging environment, no review gate, and no deploy step between you and the public site. A syntax error is a down site.'
printf '%s\n' '- **Verify before you leave a change in place.** Load the affected page or run the relevant WP-CLI command. You are the only check that runs before visitors see it.'
printf '%s\n' '- **Work is unbacked until it is captured.** The editable list above is exactly what the operator'"'"'s out-of-band capture records, and each capture is a restore point. Between your edit and the next capture, the live file is the only copy.'
printf '%s\n' '- **Rollback is an operator action.** You cannot restore a previous capture yourself. If something breaks and you cannot fix it forward, say so plainly and immediately rather than continuing to edit.'
printf '\n'
printf '%s\n' '### Changes that are never captured'
printf '\n'
printf '%s\n' 'Dependency trees, lockfiles, and build output are installed or generated rather than authored, so a capture skips them even inside the editable list. Editing them produces a change that is **never recorded and is destroyed by the next deploy**:'
printf '\n'
printf '%s\n' '- `vendor/` and `node_modules/` — installed dependency trees'
printf '%s\n' '- `composer.lock`, `package-lock.json`, `package.json` — dependency manifests'
printf '%s\n' '- generated build output'

if [ -n "$owned" ]; then
printf '%s\n' 'This site owns these, and they are the complete list. You edit them in place — there is no checkout, no workspace, and no pull request step:'
printf '\n'
printf '%s\n' "$owned" | while IFS= read -r path; do
[ -n "$path" ] || continue
printf -- '- `%s/`\n' "$path"
done
printf '\n'
fi

if [ -n "$writable" ]; then
printf '%s\n' 'You may also change these, but **nothing captures them** — a rebuild or a migration will not carry the change, so make it only when asked and tell the operator you did:'
printf '\n'
printf '%s\n' "$writable" | while IFS= read -r path; do
[ -n "$path" ] || continue
printf -- '- `%s`\n' "$path"
done
printf '\n'
fi

printf '%s\n' 'Everything else is reference material. To change behavior that lives in code you do not own, use a hook, a filter, or a template override in the source above. If that is genuinely impossible, say so rather than editing outside the list.'
printf '\n'
printf '%s\n' 'If a task genuinely requires changing one of these, stop and tell the operator. It needs to happen in the source repository, not here.'
printf '%s\n' '**Your edits are live the moment you save them.** There is no staging environment and no review gate between you and the public site, so verify each change — load the affected page or run the relevant WP-CLI command — before you leave it in place.'
}
Loading
Loading