wordpress-agent-kit 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/skills/blueprint/SKILL.md +418 -0
- package/.github/skills/wp-abilities-api/SKILL.md +12 -0
- package/.github/skills/wp-abilities-api/references/delegate-helper-pattern.md +241 -0
- package/.github/skills/wp-abilities-api/references/domain-vs-projection.md +113 -0
- package/.github/skills/wp-abilities-api/references/error-code-vocabulary.md +123 -0
- package/.github/skills/wp-abilities-api/references/grouping-heuristic.md +89 -0
- package/.github/skills/wp-abilities-api/references/input-schema-gotchas.md +265 -0
- package/.github/skills/wp-abilities-api/references/php-registration.md +47 -20
- package/.github/skills/wp-abilities-api/references/plugin-family-patterns.md +233 -0
- package/.github/skills/wp-abilities-api/references/shared-core-service.md +184 -0
- package/.github/skills/wp-abilities-audit/SKILL.md +199 -0
- package/.github/skills/wp-abilities-audit/references/audit-schema.md +300 -0
- package/.github/skills/wp-abilities-audit/references/capability-gate-tracing.md +197 -0
- package/.github/skills/wp-abilities-audit/references/controller-enumeration.md +116 -0
- package/.github/skills/wp-abilities-verify/SKILL.md +215 -0
- package/.github/skills/wp-abilities-verify/references/annotation-correctness.md +154 -0
- package/.github/skills/wp-abilities-verify/references/audit-schema-validation.md +131 -0
- package/.github/skills/wp-abilities-verify/references/permission-roundtrip.md +190 -0
- package/.github/skills/wp-abilities-verify/references/runtime-harness.md +462 -0
- package/.github/skills/wp-abilities-verify/references/schema-lints.md +118 -0
- package/.github/skills/wp-abilities-verify/references/static-enumeration.md +126 -0
- package/.github/skills/wp-plugin-directory-guidelines/SKILL.md +133 -0
- package/.github/skills/wp-plugin-directory-guidelines/references/gpl-compliance.md +217 -0
- package/.github/skills/wp-plugin-directory-guidelines/references/guideline-review-checklist.md +592 -0
- package/.github/skills/wp-plugin-directory-guidelines/references/naming-rules.md +121 -0
- package/.github/skills/wp-project-triage/scripts/detect_wp_project.mjs +22 -4
- package/README.md +6 -3
- package/dist/lib/api.js +30 -19
- package/dist/lib/installer.js +0 -2
- package/extensions/wp-agent-kit/index.ts +146 -324
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Shared core service — keeping abilities in lockstep with REST and UI
|
|
2
|
+
|
|
3
|
+
When an ability mirrors something a human can already do through a supported UI or workflow, the ability MUST consume the same code path as that UI or workflow — same permissions, same validation, same business rules. Three call sites for the same operation (UI, REST, ability — possibly CLI too) drift apart over time unless they all delegate to a shared service.
|
|
4
|
+
|
|
5
|
+
The default shape this reference recommends is a shared service that the ability, the REST controller, and the UI all consume. Routing the ability through the existing REST controller (the "delegation pattern" below) is a conditional shortcut — fine when the controller is a pure data-fetch and the ability runs predominantly inside REST contexts, but the wrong default for any ability with real growth surface. This reference covers when the shortcut is safe, when it isn't, and the side effects on the existing REST path that most often disqualify it.
|
|
6
|
+
|
|
7
|
+
Read `domain-vs-projection.md` first — abilities are use-case contracts at the domain layer; UI/REST/CLI/MCP are projections. This reference is the implementation mechanism that keeps those projections honest.
|
|
8
|
+
|
|
9
|
+
## Why this matters
|
|
10
|
+
|
|
11
|
+
A registered ability that re-implements logic instead of calling into existing code paths is a drift hazard. The drift surfaces in predictable ways:
|
|
12
|
+
|
|
13
|
+
- A new filter added to the UI listing doesn't propagate to the ability.
|
|
14
|
+
- A permission tightened on the REST controller leaves the ability under-gated.
|
|
15
|
+
- A validation rule added to the admin form is missing from the ability.
|
|
16
|
+
- A status transition gains a side-effect (audit log, webhook) that fires on UI-driven writes but not ability-driven writes.
|
|
17
|
+
|
|
18
|
+
None of these break the ability immediately. They become discoverable only when an agent invokes the ability and the result quietly diverges from what the same operation in the UI would produce. By that point the ability is in production and the divergence is hard to detect without a contract test you almost certainly do not have.
|
|
19
|
+
|
|
20
|
+
The fix is to keep one source of business logic and treat ability/REST/UI/CLI as adapters over it.
|
|
21
|
+
|
|
22
|
+
## Three shapes for the ability execute callback
|
|
23
|
+
|
|
24
|
+
| Shape | Example | Verdict |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **Re-implement** the logic in the execute callback. | The callback runs its own SQL, applies its own permission checks, builds its own response. | **Avoid.** Guaranteed drift the first time the original code path changes. |
|
|
27
|
+
| **Delegate to the existing REST controller** via `WP_REST_Request`. | The ability builds a `WP_REST_Request` from its input, dispatches via `rest_do_request()`, and returns the response data. The dispatched request flows through the registered controller's permission check, validation, and handler. | **Conditional shortcut.** Acceptable only when *all three* hold: the backing handler is a pure data-fetch (read-only, no side effects), the operation is itself a read, and the ability runs predominantly inside REST contexts. Read "Side effects on the existing REST path" below before assuming the conditions hold. |
|
|
28
|
+
| **Extract a service class** that ability + REST controller + UI handler all consume. | `My_Plugin::get_things_service()->list( $args )` called from the ability, the REST controller, and an admin-side handler. | **Default.** Choose this unless every delegation condition above holds. It is the only shape that removes drift entirely and the only shape that scales when an endpoint later grows a side effect, a write counterpart, or a non-REST caller. |
|
|
29
|
+
|
|
30
|
+
The third row is the default because it is the only one that decouples the ability (a domain-layer capability) from the REST transport. The middle row is a real shortcut — there is no point extracting a service class for `get_option('something')` — but its applicability is bounded enough that it should be reached for deliberately, not by default. Extracting a service is more invasive at first (it usually means refactoring the existing REST controller to call the service rather than embed the logic), but it is also the move that holds up under change.
|
|
31
|
+
|
|
32
|
+
## What the delegation pattern looks like
|
|
33
|
+
|
|
34
|
+
The middle row of the table — "delegate to the existing REST controller" — is a pattern, not a single canonical helper. The minimal shape is small enough to inline in an ability's `execute_callback`:
|
|
35
|
+
|
|
36
|
+
```php
|
|
37
|
+
public static function execute_get_things( $input = null ) {
|
|
38
|
+
$request = new WP_REST_Request( 'GET', '/my-plugin/v1/things' );
|
|
39
|
+
$request->set_query_params( (array) $input );
|
|
40
|
+
|
|
41
|
+
$response = rest_do_request( $request );
|
|
42
|
+
if ( $response->is_error() ) {
|
|
43
|
+
return $response->as_error();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return $response->get_data();
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The five moves: construct the `WP_REST_Request` with the right HTTP method and route; copy the ability's `$input` onto the request as query params (or body params for writes); dispatch via `rest_do_request()`, which routes through the registered controller including its `permission_callback` and any side effects; convert an error response back to a `WP_Error` so the ability's caller sees a normal error; otherwise return the data.
|
|
51
|
+
|
|
52
|
+
In a real codebase you would usually extract this into a small private helper so multiple ability callbacks can share the boilerplate. The shape of that helper is out of scope for this reference — the point here is that the delegation pattern is mechanically simple and does not depend on any particular framework helper to be in place.
|
|
53
|
+
|
|
54
|
+
## Side effects on the existing REST path
|
|
55
|
+
|
|
56
|
+
A delegating ability re-uses the REST controller's full code path, including any side effects the controller emits on every call:
|
|
57
|
+
|
|
58
|
+
- **Usage telemetry / analytics events** — the ability inflates dashboards with agent traffic and the human-vs-agent provenance signal is lost. Metric double-counting is the silent failure mode — the system "works" while the dashboards drift.
|
|
59
|
+
- **Audit logs** — entries get attributed to a "REST" actor that is actually an agent. Forensics gets noisier.
|
|
60
|
+
- **Custom-event hooks** (`do_action(...)`) — listeners on those hooks now fire on every ability invocation, with surprise side-effects in unrelated subsystems.
|
|
61
|
+
- **Email / notification dispatch** — agent-driven calls trigger user-visible notifications that should not have been sent.
|
|
62
|
+
- **Cache invalidation, schedule rescheduling, lock acquisition** — harmless when intended; harmful when fired by traffic the original handler did not anticipate.
|
|
63
|
+
- **First-call REST bootstrap cost** (performance, not semantics). `rest_do_request()` calls `rest_get_server()`, which lazily instantiates `WP_REST_Server` and fires `rest_api_init` the first time it's invoked in a request lifecycle. In a normal HTTP REST request the cost is paid before the abilities layer even runs; in CLI / cron / agent / non-REST MCP transports, the *first* ability that delegates pays it — every plugin's `register_rest_route()` callback wires up at this point. The cost is one-time per request lifecycle (`rest_get_server()` guards on `if ( empty( $wp_rest_server ) )`), but on a cold path the first invocation is measurably slow. If the ability is expected to run predominantly outside REST contexts, prefer calling the underlying service or request class directly over going through `rest_do_request()`.
|
|
64
|
+
|
|
65
|
+
The fix is the third row of the table above:
|
|
66
|
+
|
|
67
|
+
- Extract the business logic into a service class.
|
|
68
|
+
- Have the REST controller call the service AND emit its side effects as a thin adapter.
|
|
69
|
+
- Have the ability call the service directly, NOT the REST controller. The ability emits its own ability-tagged side effects, or none.
|
|
70
|
+
|
|
71
|
+
This way side effects stay scoped to the surface that triggers them, and the ability still consumes the same business logic as the UI.
|
|
72
|
+
|
|
73
|
+
If the existing REST endpoint is a pure data-fetch with no side effects, the delegation pattern is a fine shortcut.
|
|
74
|
+
|
|
75
|
+
## MCP exposure rule
|
|
76
|
+
|
|
77
|
+
Do not expose REST AND ability for the same operation to the same MCP client. Pick one.
|
|
78
|
+
|
|
79
|
+
The ability is the agent contract: schema-typed, permission-gated, semantic-intent-named. The REST endpoint stays in place for non-agent integrations (UI, third-party clients that already exist, internal services that consume the JSON contract). The MCP layer surfaces the ability and elides the REST endpoint.
|
|
80
|
+
|
|
81
|
+
Exposing both produces a "which surface should I use?" question for any LLM that sees both — and the answer affects metrics, logging, and error handling. Pick the ability.
|
|
82
|
+
|
|
83
|
+
## The `AGENTS.md` rule
|
|
84
|
+
|
|
85
|
+
Add a line to the plugin's `AGENTS.md` (under whichever H2 covers "when changing code in this area"):
|
|
86
|
+
|
|
87
|
+
> When you change the code path behind a registered ability, check whether the ability needs to update too. A new filter on the underlying listing usually means the ability should expose the same filter. A permission change means the ability's gate likely needs to follow. A new side-effect on a write may change what we promise the ability does.
|
|
88
|
+
|
|
89
|
+
This shifts the burden from "remember to update the ability" to "be reminded by the LLM working on the change." It costs nothing at write time and prevents the most common source of drift.
|
|
90
|
+
|
|
91
|
+
## Worked example — extracting a service
|
|
92
|
+
|
|
93
|
+
Generic plugin with a `Things` resource. Before extraction, the REST controller embeds the listing logic and the ability re-runs the same filter/sort/paginate code:
|
|
94
|
+
|
|
95
|
+
```php
|
|
96
|
+
// includes/admin/class-rest-things-controller.php
|
|
97
|
+
class My_Plugin_REST_Things_Controller {
|
|
98
|
+
public function get_things( WP_REST_Request $request ) {
|
|
99
|
+
$args = self::sanitize_query_args( $request->get_params() );
|
|
100
|
+
$rows = $this->repo->find( $args );
|
|
101
|
+
|
|
102
|
+
// Side effects (audit log, hooks, notifications) live on the REST adapter.
|
|
103
|
+
do_action( 'my_plugin/things_listed', $rows );
|
|
104
|
+
|
|
105
|
+
return rest_ensure_response( array_map( [ $this, 'format' ], $rows ) );
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/Internal/Abilities/Abilities_Registrar.php
|
|
110
|
+
public static function execute_get_things( $input = null ) {
|
|
111
|
+
// PROBLEM: re-implements sanitization, repo call, formatting.
|
|
112
|
+
// Drifts the first time the controller's get_things() changes.
|
|
113
|
+
$args = MyPlugin\Sanitize::query_args( (array) $input );
|
|
114
|
+
$rows = ( new MyPlugin\Things_Repo() )->find( $args );
|
|
115
|
+
return array_map( [ MyPlugin\Things_Formatter::class, 'format' ], $rows );
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
After extraction, both paths consume `Things_Service::list()`:
|
|
120
|
+
|
|
121
|
+
```php
|
|
122
|
+
// src/Service/class-things-service.php
|
|
123
|
+
class Things_Service {
|
|
124
|
+
public function list_things( array $args ) {
|
|
125
|
+
$clean = Sanitize::query_args( $args );
|
|
126
|
+
$rows = $this->repo->find( $clean );
|
|
127
|
+
return array_map( array( Things_Formatter::class, 'format' ), $rows );
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// includes/admin/class-rest-things-controller.php
|
|
132
|
+
class My_Plugin_REST_Things_Controller {
|
|
133
|
+
public function get_things( WP_REST_Request $request ) {
|
|
134
|
+
$rows = $this->service->list_things( $request->get_params() );
|
|
135
|
+
|
|
136
|
+
// Side effects (audit log, hooks, notifications) stay on the REST adapter — clean.
|
|
137
|
+
do_action( 'my_plugin/things_listed', $rows );
|
|
138
|
+
|
|
139
|
+
return rest_ensure_response( $rows );
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/Internal/Abilities/Abilities_Registrar.php
|
|
144
|
+
public static function execute_get_things( $input = null ) {
|
|
145
|
+
if ( ! class_exists( '\MyPlugin\Things_Service' ) ) {
|
|
146
|
+
return new WP_Error( 'myplugin_not_initialized', __( 'My Plugin is not initialized.', 'my-plugin' ) );
|
|
147
|
+
}
|
|
148
|
+
return MyPlugin::get_things_service()->list_things( (array) $input );
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The ability and the REST endpoint now share business logic. Side effects (audit, hooks, notifications, any telemetry) stay on the REST adapter and do not fire on agent-driven invocations. The next person to add a filter parameter changes one place — the service — and both call sites pick it up.
|
|
153
|
+
|
|
154
|
+
## Rule of thumb
|
|
155
|
+
|
|
156
|
+
Start from the assumption that the ability extracts (or consumes an already-extracted) service. Walk back to delegation only when every condition for it holds.
|
|
157
|
+
|
|
158
|
+
- **Write of any kind** → extract a service. Drift is most damaging on writes (lost validation, missing audit hooks). Non-negotiable.
|
|
159
|
+
- **No existing REST endpoint** → start at the service. The first ability you ship is also the right time to add the structure that a future REST endpoint will consume.
|
|
160
|
+
- **Read where the REST handler does more than data-fetch** (audit, hooks, notifications, telemetry) → extract a service. Don't fire UI-scoped side effects on agent invocations.
|
|
161
|
+
- **Read predominantly invoked outside REST** (CLI, cron, agent, non-REST MCP transport) → prefer direct invocation of the service or the underlying request class. Delegation pays the first-call REST bootstrap cost on every cold path.
|
|
162
|
+
- **Read with no side effects on the REST path, light logic, predominantly invoked through REST** → the delegation pattern is acceptable as a shortcut. Drift risk is bounded because the REST controller is one short hop away — but only as long as those three conditions hold.
|
|
163
|
+
|
|
164
|
+
The bias is intentional. Service-extraction is the only shape that holds up when the REST handler later grows a side effect, when a write counterpart shows up, or when a non-REST caller appears (a new CLI command, a cron job, an agent invocation off a non-REST MCP transport). Delegation re-couples the domain layer to the REST transport; that re-coupling is fine when the conditions hold *and* unlikely to change, but the cost of unwinding it later is higher than starting at the service.
|
|
165
|
+
|
|
166
|
+
## Escape hatch — when re-implementation is OK
|
|
167
|
+
|
|
168
|
+
Two narrow cases:
|
|
169
|
+
|
|
170
|
+
1. **The ability is read-only and the backing has no extractable shape.** Sometimes the "logic" is one line — `get_option( 'something' )` — and a service class is overkill. Inline it.
|
|
171
|
+
2. **The plugin is single-purpose and will not grow surfaces.** A 200-line plugin with one ability and no REST surface to drift against can keep logic in the execute callback. The drift risk only shows up at 2+ adapters.
|
|
172
|
+
|
|
173
|
+
In both cases, leave a `// TODO: extract to service if a REST/UI surface gets added` comment so the next person sees the trigger condition.
|
|
174
|
+
|
|
175
|
+
## Plugin-family override
|
|
176
|
+
|
|
177
|
+
This reference describes the generic baseline. Specific plugin families can — and routinely do — tighten the rules further. A payments-family plugin might forbid the delegation pattern outright on the grounds that no payments endpoint is safe to treat as side-effect-free. A subscription plugin might require service extraction even for one-line option reads if the option is read in more than one transport. A plugin that ships its own MCP transport might rule out delegation for any ability exposed through it.
|
|
178
|
+
|
|
179
|
+
When the plugin you are working in is one of those, the local rules win. Check the plugin's `AGENTS.md`, contributor guide, or ability-registration playbook before reaching for the delegation shortcut taught here.
|
|
180
|
+
|
|
181
|
+
## Related references
|
|
182
|
+
|
|
183
|
+
- `domain-vs-projection.md` — the layer model that puts shared services at the domain layer and projections (REST / MCP / CLI / UI) above.
|
|
184
|
+
- `grouping-heuristic.md` — orthogonal: this reference covers "same code path"; that one covers "how many abilities."
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wp-abilities-audit
|
|
3
|
+
description: "Audit a WordPress plugin's REST surface and produce a standardized audit document proposing Abilities API registrations. Produces a markdown doc with a YAML schema and prose sections that humans and agents can both consume when planning a registration rollout. Works on any WP plugin."
|
|
4
|
+
license: GPL-2.0-or-later
|
|
5
|
+
compatibility: "Targets WordPress 6.9+ (PHP 7.2.24+). Filesystem-based agent with bash + node. Requires access to the plugin checkout; some workflows benefit from WP-CLI but don't require it."
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# WP Abilities Audit
|
|
9
|
+
|
|
10
|
+
Produce a standardized audit document for a WordPress plugin's REST surface,
|
|
11
|
+
proposing a set of Abilities API registrations grouped by semantic intent. The
|
|
12
|
+
audit doc is a planning artifact for implementers — humans, agents, or both —
|
|
13
|
+
that captures the controller inventory, capability gates, and proposed
|
|
14
|
+
ability shapes in a structured form. A reviewer reading the doc can scope the
|
|
15
|
+
work without re-deriving the survey.
|
|
16
|
+
|
|
17
|
+
This skill works on any plugin that exposes a REST surface. Plugin
|
|
18
|
+
classification (for purposes of the optional `plugin_family` annotation) is
|
|
19
|
+
the user's call; the workflow itself is plugin-agnostic.
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- The task is "register Abilities API abilities for a WP plugin" and no audit
|
|
24
|
+
doc exists yet.
|
|
25
|
+
- Planning participation in a multi-plugin abilities rollout and need a
|
|
26
|
+
shareable, standardized audit artifact.
|
|
27
|
+
- Pre-flight checking a plugin's agent-readiness before implementing abilities.
|
|
28
|
+
- A PM or non-implementer wants to scope the work before engineering picks it up.
|
|
29
|
+
|
|
30
|
+
## Inputs required
|
|
31
|
+
|
|
32
|
+
1. **Plugin checkout path** — working tree of the plugin to audit.
|
|
33
|
+
2. **Triage output** — run `wp-project-triage` first if not already done. The
|
|
34
|
+
audit consumes `signals.usesAbilitiesApi`, `versions.wordpress`, and
|
|
35
|
+
`project.kind` from the report.
|
|
36
|
+
3. **Auditor identity** — name and team or context, recorded in the audit's
|
|
37
|
+
`auditor` field.
|
|
38
|
+
4. **Output path** — where the audit doc should land. Default explicit over
|
|
39
|
+
implicit; ask if not provided rather than writing into the plugin worktree.
|
|
40
|
+
|
|
41
|
+
## Prerequisites
|
|
42
|
+
|
|
43
|
+
- `wp-project-triage` has run successfully and classified the plugin.
|
|
44
|
+
- The plugin has at least one REST controller. If enumeration finds zero
|
|
45
|
+
controllers, the audit doesn't apply — see "Failure modes" below.
|
|
46
|
+
|
|
47
|
+
## Procedure
|
|
48
|
+
|
|
49
|
+
### 1. Enumerate REST controllers
|
|
50
|
+
|
|
51
|
+
Read `references/controller-enumeration.md` now — it covers the two observed
|
|
52
|
+
enumeration paths (glob for standard layouts, grep as the universal fallback)
|
|
53
|
+
and when to use each.
|
|
54
|
+
|
|
55
|
+
Record every controller class + file + REST base + routes in a "Controller
|
|
56
|
+
Inventory" table. The inventory is exhaustive even though only a subset
|
|
57
|
+
becomes proposed abilities.
|
|
58
|
+
|
|
59
|
+
### 2. For each controller, extract the backing fields
|
|
60
|
+
|
|
61
|
+
For every controller found, extract the fields the audit schema requires:
|
|
62
|
+
class, file, HTTP method, route, route-registration line number, callback
|
|
63
|
+
name, callback line number, permission callback, whether the callback takes
|
|
64
|
+
a `WP_REST_Request` argument or is zero-arg, and the return type.
|
|
65
|
+
|
|
66
|
+
Read `references/audit-schema.md` now for the exact field list and the shape
|
|
67
|
+
of `proposed_abilities` entries. Line-number fields may be `null` for
|
|
68
|
+
inherited callbacks — the schema allows this and pairs it with an optional
|
|
69
|
+
`inherited_from` field.
|
|
70
|
+
|
|
71
|
+
### 3. Confirm capability gate(s)
|
|
72
|
+
|
|
73
|
+
Trace each controller's `permission_callback` to its `current_user_can()` call
|
|
74
|
+
(or to the post-type capability machinery if the controller extends a
|
|
75
|
+
post-type-backed base).
|
|
76
|
+
|
|
77
|
+
Read `references/capability-gate-tracing.md` now — it documents the two
|
|
78
|
+
common mechanisms (direct `check_permission()` vs post-type-backed
|
|
79
|
+
`wc_rest_check_post_permissions()`) and how to represent each in the schema.
|
|
80
|
+
Note explicitly whether read and write gates differ: compound gates are
|
|
81
|
+
represented as a `{read, write}` object, not a single string.
|
|
82
|
+
|
|
83
|
+
### 4. Propose abilities using semantic-intent grouping
|
|
84
|
+
|
|
85
|
+
Do NOT atomize one ability per HTTP method. Apply the semantic-intent grouping
|
|
86
|
+
heuristic — it's the only grouping rule this skill uses.
|
|
87
|
+
|
|
88
|
+
Read `../wp-abilities-api/references/grouping-heuristic.md` now — do NOT
|
|
89
|
+
re-derive the rules here. Short version: one ability per real-world question
|
|
90
|
+
or state transition, with filter parameters in `input_schema` collapsing N
|
|
91
|
+
variants into 1.
|
|
92
|
+
|
|
93
|
+
**Apply the use-case sanity check before populating any candidate.** Per
|
|
94
|
+
`../wp-abilities-api/references/domain-vs-projection.md`'s use-case-contract
|
|
95
|
+
test: would a human or agent intentionally perform this behavior through a
|
|
96
|
+
supported plugin workflow? If yes, the candidate is a real ability —
|
|
97
|
+
proceed to fill in fields. If no, the route is internal transport plumbing
|
|
98
|
+
(cache invalidation, scheduler ticks, bookkeeping endpoints, debug
|
|
99
|
+
introspection) — keep it in the Controller Inventory section for
|
|
100
|
+
completeness, but do NOT promote it to `proposed_abilities`. The route may
|
|
101
|
+
be useful to inventory; the proposed ability must represent a real
|
|
102
|
+
user/operator question or action.
|
|
103
|
+
|
|
104
|
+
For each proposed ability that passes the sanity check, fill in every
|
|
105
|
+
field in the `proposed_abilities` schema: `name`, `intent`, `backing`,
|
|
106
|
+
`permission`, `return_type`, `effort` (S/M/L), `annotations`
|
|
107
|
+
(readonly/destructive/idempotent), `notes`, `risks`, `use_case_fit`,
|
|
108
|
+
`side_effects`, `seed_data_needs`.
|
|
109
|
+
|
|
110
|
+
The last three are the implementation-readiness facts the implementer
|
|
111
|
+
and the verify-mode tooling both need: which human/agent workflow this
|
|
112
|
+
ability serves (`use_case_fit`), what the backing path emits on every
|
|
113
|
+
call (`side_effects` — empty array is a fact, not a missing value), and
|
|
114
|
+
what representative data must exist in the test environment for the
|
|
115
|
+
ability to execute through the public boundary (`seed_data_needs`).
|
|
116
|
+
|
|
117
|
+
### 5. Surface gaps and deferred items
|
|
118
|
+
|
|
119
|
+
Three buckets:
|
|
120
|
+
|
|
121
|
+
- **`excluded_from_mvp`** — candidates intentionally deferred for risk reasons
|
|
122
|
+
(real-money writes, irreversible state changes, or prerequisite design
|
|
123
|
+
work). Each entry gets a one-sentence reason.
|
|
124
|
+
- **`surfaced_gaps`** — MVP candidates with no backing endpoint (ability with
|
|
125
|
+
`backing: null`), plus high-value endpoints discovered during enumeration
|
|
126
|
+
that aren't in the MVP list but would be easy future wins.
|
|
127
|
+
- **Risks per ability** — anything about a backing endpoint that the
|
|
128
|
+
implementer must handle (no idempotency key, two-phase behavior,
|
|
129
|
+
state-transition caveats, zero-arg endpoints registered with
|
|
130
|
+
`permission_callback => '__return_true'` that must NOT copy that into the
|
|
131
|
+
ability registration).
|
|
132
|
+
|
|
133
|
+
### 6. Write the audit doc
|
|
134
|
+
|
|
135
|
+
Write to the explicit output path collected in "Inputs required". The
|
|
136
|
+
document structure must match `references/audit-schema.md` exactly:
|
|
137
|
+
|
|
138
|
+
1. `Last updated: YYYY-MM-DD HH:MM` header.
|
|
139
|
+
2. YAML block with all required top-level metadata + `proposed_abilities`,
|
|
140
|
+
`excluded_from_mvp`, `surfaced_gaps`.
|
|
141
|
+
3. "Controller Inventory" table.
|
|
142
|
+
4. "Notes and Surprises" prose section.
|
|
143
|
+
|
|
144
|
+
A copy-pasteable minimal example showing the full shape lives in
|
|
145
|
+
`references/audit-schema.md` under "Minimal valid example" — start there
|
|
146
|
+
when authoring a new audit.
|
|
147
|
+
|
|
148
|
+
### 7. (Optional) Designate a reference implementation ability
|
|
149
|
+
|
|
150
|
+
Set `reference_ability: true` on the first ability an implementer should
|
|
151
|
+
land — typically the smallest, safest, highest-leverage read. This gives
|
|
152
|
+
downstream workflows a deterministic starting point.
|
|
153
|
+
|
|
154
|
+
## Verification
|
|
155
|
+
|
|
156
|
+
- The audit conforms to `references/audit-schema.md` (all required top-level
|
|
157
|
+
fields present, at least one entry in `proposed_abilities`, annotations
|
|
158
|
+
complete on every ability).
|
|
159
|
+
- `capability_gate` is a string for single-cap plugins or a `{read, write}`
|
|
160
|
+
object for post-type-backed plugins.
|
|
161
|
+
- Every ability with `backing: null` also appears in `surfaced_gaps`.
|
|
162
|
+
- The doc round-trips through the validator in `audit-schema.md` "Known
|
|
163
|
+
limitations" without errors.
|
|
164
|
+
|
|
165
|
+
## Failure modes / debugging
|
|
166
|
+
|
|
167
|
+
- **Plugin has no REST controllers** — audit doesn't apply. Consider
|
|
168
|
+
hooks/filters-based abilities (out of scope for this skill's current
|
|
169
|
+
version) or skip abilities adoption for this plugin.
|
|
170
|
+
- **Plugin inherits controllers from another repo** (common for plugins
|
|
171
|
+
extending core post-type-backed controllers like `WP_REST_Posts_Controller`,
|
|
172
|
+
or extension plugins built on a parent's REST classes) — capture with
|
|
173
|
+
`backing.inherited_from: "<parent FQCN>"`. Line-number fields may be
|
|
174
|
+
`null` per the schema.
|
|
175
|
+
- **Compound capability gate (distinct read/write caps)** — use the
|
|
176
|
+
structured `{read, write}` form documented in
|
|
177
|
+
`references/capability-gate-tracing.md`. Don't smuggle a `/`-separated
|
|
178
|
+
string into a field typed as a single cap.
|
|
179
|
+
- **Ambiguous grouping** — route to
|
|
180
|
+
`../wp-abilities-api/references/grouping-heuristic.md`. Do not invent
|
|
181
|
+
alternative grouping rules in the audit doc.
|
|
182
|
+
- **Zero-arg endpoints with `permission_callback => '__return_true'`** —
|
|
183
|
+
legal at the REST layer, but the ability's own `permission_callback` must
|
|
184
|
+
match the plugin's merchant gate. Never promote `'__return_true'` into an
|
|
185
|
+
ability registration. Note this in the ability's `risks`.
|
|
186
|
+
- **Output path defaults to plugin worktree** — always ask the user for an
|
|
187
|
+
explicit output directory (e.g. their vault `plans/`). Writing the audit
|
|
188
|
+
into the plugin's own git history pollutes the worktree and buries the
|
|
189
|
+
artifact.
|
|
190
|
+
|
|
191
|
+
## Escalation
|
|
192
|
+
|
|
193
|
+
- If the plugin uses an enumeration convention not covered by
|
|
194
|
+
`references/controller-enumeration.md` (neither the standard glob nor the
|
|
195
|
+
grep fallback produces a complete inventory), update that reference with
|
|
196
|
+
the new convention and open a PR so future audits cover it deterministically.
|
|
197
|
+
- If capability tracing hits a mechanism not covered by
|
|
198
|
+
`references/capability-gate-tracing.md`, extend that file rather than
|
|
199
|
+
encoding the new case in the audit's "Notes and Surprises" only.
|