wp-abilities-audit: add new skill for surveying a plugin's REST surface and producing a structured audit doc#46
Draft
rtio wants to merge 25 commits intoWordPress:trunkfrom
Draft
Conversation
Agents that conflate ability registration with consumer exposure end up
re-registering abilities every time the projection question reopens
(token budgets, MCP shape, Command Palette conventions). Treating
those as one decision couples a stable layer (capabilities) to a
volatile one (consumer constraints).
This reference captures a three-layer model: domain capability,
optional workflow, and projection. Adds the use-case-contract test
("would a human ever do this in admin?"), the "same code path as the
UI" rule, and a decision order (domain first, projection second,
workflow third). Worked example walks a generic Notifications plugin
through three projections off the same three domain registrations.
Abilities that re-implement business logic instead of consuming the same code path as the UI / REST surface drift apart over time: permissions diverge, validation rules drop, side-effects fire on UI-driven writes but not ability-driven ones. None of those breaks immediately; the divergence becomes discoverable only when an agent invokes the ability in production. This reference captures three execute-callback shapes (re-implement / delegate / shared service), the metric-trap argument for preferring the third shape on telemetry-emitting endpoints, the MCP exposure rule (don't expose REST + ability for the same op to the same client), and the AGENTS.md sync rule for keeping registrations in lockstep when the underlying code path changes. Worked example walks a generic plugin through extracting a service class that ability + REST + UI all consume.
Plugin authors landing abilities on top of an existing REST surface have to choose domain-layer granularity. The three observed shapes (action-bundle behind a single ability, REST-atomization at one ability per HTTP method, semantic-intent at one ability per user-meaningful question) each solve a different problem, and the naive default — atomize because the REST surface already does — leaks HTTP plumbing into the agent's tool list. This reference recommends semantic-intent at the domain layer: abilities map to user-meaningful actions, filter parameters live in input_schema, writes stay narrow at one state transition per ability. Five rules cover read grouping, write narrowness, filter-vs-name choice, zero-arg overview abilities, and the one-sentence explainability test. Two worked examples (Jetpack Forms and a generic Tickets plugin) show the heuristic in action without committing the reader to a specific projection-layer pattern.
The procedure section previously jumped from "Confirm version constraints" to "Find existing usage" without surfacing the design-time decisions that should precede registration: which layer to register at (domain vs projection) and how to choose granularity. Agents following the procedure verbatim reach for syntax before reasoning about shape. Adds a layer-model paragraph at the top of the procedure pointing at domain-vs-projection, plus two cross-links in step 4 — to grouping-heuristic for granularity decisions and shared-core-service for the drift-prevention pattern (including the AGENTS.md sync rule).
Five points where references/php-registration.md drifted from wordpress-develop/src/wp-includes/abilities-api/class-wp-ability.php: 1. permission_callback was marked optional. Per prepare_properties() at class-wp-ability.php:287-292, it throws InvalidArgumentException when missing — required on every registration with no implicit default. 2. The skill placed `readonly` at meta.readonly. Core puts the three semantic annotations at meta.annotations.* (lines 38-51 + validation at 313-317). Updated the table to show meta.annotations.readonly, .destructive, .idempotent at the canonical path. 3. Two of the three annotations (destructive, idempotent) were missing entirely. All three are first-class core annotations seeded via $default_annotations at lines 326-336. 4. Required-vs-optional was not flagged on label, description, category, execute_callback, or permission_callback — all five throw on missing per lines 262-292. Added a "Required?" column. 5. The recommended-patterns line "Always include a permission_callback for abilities that modify data" framed it as a write-only practice. Core requires it for all registrations. Reworded. Adjacent fix while in the file: the namespacing example said `my-plugin:feature.edit` (colon-and-dot) but every other example in this skill uses slash-separated `my-plugin/get-info`. Aligned. Updated the example registration block to register a permission_callback and all three meta.annotations keys, so the example matches the contract the table now describes. Refs WordPress#44 review-id 4207902432
The inclusion test in references/domain-vs-projection.md framed the ability question as "would a human ever do this in admin?". That lens encodes a Woo/Jetpack-implementer's bias and excludes a real and common case: abilities scoped to public authed/unauthed users on the site front end (storefront `get-my-orders`, events `list-available-tickets`, profile `update-public-profile`), and abilities invoked as part of a workflow chain by another plugin or an agent rather than by direct human input at all. Reframed the test as "would a human intentionally do this through a supported UI or workflow?". That criterion: - Keeps admin-side abilities qualifying (the original case). - Adds public-facing UIs and end-user self-service flows. - Covers workflow / chain-of-actions invocation paths where the human intent is upstream of the ability call. - Preserves the exclusion of internal-only plumbing (cache invalidation, scheduler ticks, debug snapshots, lifecycle bookkeeping). Also dropped the asymmetric-test sentence that read "anything that isn't admin-meaningful almost certainly isn't ability-meaningful" — the same point survives in the rewritten version without the admin-only framing. Refs WordPress#44 review-id 4207902432
Same admin-only blind spot as the inclusion test in domain-vs-projection.md (fixed in the previous commit), surfacing in the opening sentence of references/shared-core-service.md. The lockstep rule applies wherever the ability mirrors a supported human workflow — admin screen, public-facing UI, customer self-service flow, or a plugin/agent-driven workflow — not only admin actions. Refs WordPress#44 review-id 4207902432
…eric responses scenario Worked example A in references/grouping-heuristic.md was anchored to a specific Automattic plugin's source layout, including a hardcoded link to a file path under Automattic/jetpack/projects/packages/forms/. Two problems: 1. WordPress/agent-skills is a generic, plugin-agnostic skill repo per CONTRIBUTING.md. Worked examples should not lean on one canonical plugin family. 2. File-path links into a fast-moving codebase rot — a rename or restructure makes the reference broken or worse, misleading. Worked example B was already generic (Tickets plugin); aligning A to the same approach. The semantic-intent table row now uses a generic `feedback/get-responses` example. The worked example A reframes around a generic feedback or form-response plugin with the same structural lessons (filters in `input_schema`, one write for "modify a response", a zero-arg dashboard-summary ability). The hardcoded canonical-file link is removed entirely. Refs WordPress#44 review-id 4207902432
…ern, not a real callable
The shared-core-service reference repeatedly wrote
`delegate_to_rest_controller(...)` in code-shaped backticks (table row
example, body prose at four locations, a rule-of-thumb bullet). Read in
isolation, that string looks like a callable a plugin author should be
able to find or import. It is not — at least not in PR-44's scope. The
canonical helper of that name lives in another reference that is the
subject of a separate forthcoming PR; PR-44 cannot point at it without
phantom-dependency.
Two changes:
1. Replaced every `delegate_to_rest_controller(...)` occurrence with
prose ("the delegation pattern", "the delegate-to-REST-controller
route") so the text describes a pattern rather than naming a
non-existent function.
2. Added a "What the delegation pattern looks like" subsection between
the three-shapes table and the metric-trap discussion, with a small
inline example showing the actual mechanism: construct a
WP_REST_Request, set params, dispatch via rest_do_request(), handle
the error case, return data. Three core symbols cited
(WP_REST_Request, rest_do_request, WP_Error) verified against
wordpress-develop trunk; method calls (set_query_params, is_error,
as_error, get_data) similarly verified.
The example is intentionally minimal and self-contained. It does not
forward-reference any helper that lives outside this PR. A `php -l`
smoke-test on the extracted block on PHP 7.2 returns no syntax errors.
Refs WordPress#44 review-id 4207902432
…fects
The shared-core-service reference centred its argument for service
extraction on a "metric trap" — telemetry double-counting under agent
traffic. A reviewer pointed out that telemetry is one of several side
effects a REST handler may emit, and that the doc was elevating it
disproportionately. Plugin authors may track usage at any layer, and
the structural lesson — "do not let an ability trigger UI-scoped side
effects on agent invocations" — applies to audit logs, custom-event
hooks, notifications, cache invalidation, and lock acquisition just as
much as to telemetry.
Restructured the section accordingly:
- Renamed the H2 from "The metric trap" to "Side effects on the
existing REST path".
- Replaced the telemetry-only intro paragraph with a five-bullet list
of side-effect categories: usage telemetry, audit logs, custom-event
hooks, email/notification dispatch, cache/schedule/lock side
effects. Telemetry remains as the lead bullet because metric
double-counting is the only failure mode in this set that is
invisible by default — the system "works" while dashboards drift —
which is worth one sentence even after demotion.
- Generalised the "fix" bullets ("emit telemetry" → "emit its side
effects").
- Replaced the `my_plugin_track_event(...)` calls in both worked-example
PHP blocks with `do_action( 'my_plugin/things_listed', $rows )`, a
generic WP-idiomatic side-effect that does not bake telemetry into
the example. Comments updated to "Side effects (audit log, hooks,
notifications) live on the REST adapter."
- Generalised the rule-of-thumb bullets and the closing
override-paragraph from telemetry-specific to side-effect-general
framing.
- Updated the two stale pointers ("the metric trap that makes that
distinction matter", 'read "the metric trap" below first') to point
at the renamed section.
Net effect: telemetry is mentioned three times in the file (one bullet
in the side-effects list with the silent-failure callout, one
parenthetical in the worked-example closing, one parenthetical in the
rule-of-thumb), down from six. The original concern Darren raised —
telemetry as the centerpiece — is addressed; the genuinely-specific
risk it represents is preserved as one bullet of context.
php -l on both worked-example blocks returns no syntax errors on
PHP 7.2.
Refs WordPress#44 review-id 4207902432
The skill's php-registration reference covers `meta.show_in_rest` and the three `meta.annotations` keys, but does not cover `meta.mcp.public` or `meta.mcp.type`. The bundled WordPress MCP adapter (the package WP plugins pull in for MCP exposure) reads those two keys to decide which abilities to surface and how to project them. An ability registered with `show_in_rest => true` but no `meta.mcp` block is technically valid yet invisible to MCP clients connecting through that adapter — which is the default discovery path for agents. Surfaced as review feedback on a WooPayments pilot PR: a registration that followed the skill exactly was discoverable on the abilities REST namespace but missing from the MCP tool list. The reviewer's note was "we should default all our registered abilities to use this unless we know for sure it shouldn't be exposed." Add both keys to the registration arguments table with their defaults and constraints (mcp.public is bool default false; mcp.type is one of 'tool'/'resource'/'prompt' default 'tool', with silent coercion to 'tool' for any value outside the enum). Add a section that distinguishes the abilities-REST namespace contract (`show_in_rest` → `wp-abilities/v1`) from the MCP adapter projection (`mcp.public` → adapter's default MCP server) so plugin authors don't conflate them. Update the canonical example so the recommended starting shape opts the ability into MCP discovery. Verified against the MCP adapter package source: `mcp.public` read at McpAbilityHelperTrait.php lines 37 and 61; `mcp.type` read at line 75 with the enum guard at line 78. PHP example block re-validated with `php -l`. Refs WordPress#44 Refs Automattic/woocommerce-payments#11679
…nded The reference's table previously listed all three `meta.annotations` keys (`readonly`, `destructive`, `idempotent`) as "Optional (default null)". The post-table paragraph said plugins were "expected to populate them honestly" but did not explain why a `null` annotation is materially worse than a populated one. Observed downstream consequence: an agent following the skill registered an ability and shipped it without any annotations. The reviewer flagged the omission. The skill teaches the keys exist and that they are hints for tooling, but the cell-level "Optional" framing licensed the agent's shortcut. The structural fix is to escalate the tone. Reword the three table cells from "Optional" to "Strongly recommended" while keeping the technically accurate "(default null)" footnote so readers don't misread this as a core-enforced requirement. Expand the post-table paragraph to spell out what `readonly: null` actually broadcasts — "behavior unknown" — and why that is a worse signal than either `true` or `false`. Strengthen the recommended-patterns bullet along the same axis: explicit cost (three lines) vs. cost of omission (opaque safety surface). Verified core behavior at class-wp-ability.php lines 38-51 (the `$default_annotations` array seeds all three keys to `null`) — the table's "default null" claim still holds against trunk. Refs WordPress#44 Refs Automattic/woocommerce-payments#11679
…effect The "Side effects on the existing REST path" section enumerated five semantic side effects of routing an ability through `rest_do_request()` (telemetry, audit logs, custom-event hooks, notifications, cache/lock side effects) but missed a category that is mechanically different but just as important: performance. `rest_do_request()` calls `rest_get_server()`, which is a lazy singleton that fires `rest_api_init` the first time it's instantiated in a request lifecycle. Inside an HTTP REST request that cost is paid before the abilities layer ever runs. Outside one — CLI invocations, cron jobs, agent runs hitting an ability through a non-REST MCP transport — the *first* delegating ability call pays it. Every plugin's `register_rest_route()` callback wires up at that point, which on a cold path is measurably slow. Surfaced as review feedback on a WooPayments pilot PR: the reviewer flagged the delegation pattern with the question "what about using List_Transactions::from_rest_request directly?" — the underlying concern being that the ability is invoked from CLI, agents, and MCP contexts where the REST bootstrap cost lands on the user. Add a sixth bullet to the side-effects list, framed as "performance, not semantics" so readers don't blur it together with the existing five. Cite the lazy-instantiation guard explicitly so the cost-once- per-request behavior is clear and readers don't over-correct toward "never delegate." Add a corresponding rule-of-thumb row for "read predominantly invoked outside REST" and update the override paragraph so the non-REST-context rule joins side-effect and write as a reason to extract a service even when delegation would otherwise be cheapest. Verified against `wp-includes/rest-api.php` lines 619-651: the `empty( $wp_rest_server )` guard at line 623, the instantiation at line 635, and the `do_action( 'rest_api_init', $wp_rest_server )` at line 647. Refs WordPress#44 Refs Automattic/woocommerce-payments#11679
When adopting the Abilities API inside an existing plugin, the plugin's architecture dictates how execute callbacks call into existing business logic. Two patterns cover most real-world plugins, and picking the wrong shape costs in tests, error-code semantics, and bootstrap correctness. Adds a reference covering Pattern A (shared API client, common in plugins talking to a remote service) and Pattern B (zero-arg controllers, common in plugins delegating primarily to WordPress core mechanisms). Includes detection heuristics for each, minimal skeletons for both, a hybrid-case note for plugins that legitimately use both, an anti-pattern warning against inventing a fake API client to fit Pattern A's helper shape, and a quick-pick table.
List-style read abilities backed by an existing REST controller repeat the same four steps in every execute callback: validate the plugin is initialized, build a WP_REST_Request from input, instantiate the controller, and unwrap the response (which can be either a raw array or a WP_REST_Response). At three or more abilities the boilerplate dominates; copy-paste between callbacks is also where drift starts. Adds a reference documenting the canonical delegate_to_rest_controller helper: signature, the three guards every implementation needs (class_exists on the controller, null-check on the shared dependency, is_wp_error short-circuit before unwrap), the dual response-shape unwrap, and explicit guidance on when NOT to use the helper (zero-arg backings, non-REST services). Builds on plugin-family-patterns.md to cover both shape variants.
Agents orchestrating multiple abilities need to know whether a failure was caused by their input, by the plugin not being initialized, or by a transient downstream error — the answer drives retry strategy. When every plugin invents its own WP_Error codes, agents can't reason uniformly across the surface they're given. Adds a reference covering a small standardized vocabulary that handles the 95% case: <plugin>_not_initialized for bootstrap failures, <plugin>_missing_<field> and <plugin>_invalid_<field> for input problems caught in the execute callback, <plugin>_<resource>_data_unavailable for transient backend failures, and ability_invalid_input for the schema-validator path that fires earlier than the callback. Documents why upstream third-party codes (e.g. Stripe's resource_missing) should bubble through verbatim rather than being re-wrapped, plus naming rules and i18n discipline for codes vs messages.
Registering an ability with an input_schema doesn't guarantee that the execute callback sees the schema applied at runtime. Three surprises have shown up in real plugin work after the schema looked correct and unit tests passed. Adds a reference covering the three: (1) the Abilities API does NOT inject schema 'default' values into callback input — agents that omit optional fields receive callback input without those keys, so callbacks must normalize defaults explicitly; (2) pagination parameter-name drift when an ability exposes per_page but the backing controller delegates to an internal class that reads pagesize, silently breaking pagination; (3) PHP empty() false-rejecting "0" and 0 as IDs, which matters for order/post/row IDs that can legitimately be zero. Each gotcha includes detection patterns, the corrective code, and a combined worked example showing all three guards in one callback.
Step 4 (Register abilities) gated on grouping and shared-core but did not yet point at the family-pattern decision (shared-API-client vs zero-arg controllers), the delegate-helper, or the standardized WP_Error vocabulary. Failure modes also did not point at input-schema-gotchas, so debugging "execute returns surprising errors" required re-deriving the three known traps. Adds two cross-links in step 4 (plugin-family-patterns + delegate-helper, plus error-code-vocabulary) and one in Failure modes (input-schema-gotchas).
The reference covered three input-schema gotchas — schema property defaults not being injected, pagination key drift, and `empty()` false-rejecting "0" — all of which assume the callback has already been invoked with some input. It missed a structural concern one layer up: HOW the callback gets invoked, and what runs first. `wp_get_ability(...)->execute()` runs `normalize_input()` then `validate_input()` (then `check_permissions()`) before invoking the callback. Direct invocation of the static method skips all of that. The two paths diverge in three places: 1. Validation rejects null input on the indirect path when the schema declares an object type — the callback never runs, the PHP-level `$input = null` default in the signature is dead code. 2. The schema's top-level `default` IS substituted by `normalize_input()` on null inputs (different layer from the property-level `default` that Gotcha 1 covers). 3. WordPress only forwards `$input` to the callback when the input schema is non-empty; without one, indirect invocation produces `ArgumentCountError` unless the signature has a default. Surfaced on a pilot ability registration when the reviewer noted that indirect callers validate input first, so a callback signature like `function execute( $input = null )` does nothing for them — null gets rejected during validation, never reaching the callback. The reviewer's suggested fix (`default => []` at the schema root) works because `normalize_input()` honors it; documenting that mechanism is what the new gotcha section adds. Add Gotcha 4 covering the direct/indirect split, the three concrete divergences, the fix (top-level schema `default`, ideally `(object) array()` for object-typed schemas to avoid PHP's array/object ambiguity at the validator boundary), and how this relates to Gotcha 1. Rename "Putting the three together" to "Putting gotchas 1-3 together" so the existing example doesn't imply Gotcha 4 is included — Gotcha 4 is structurally about callback-invocation, not callback-robustness, and doesn't fit the same worked example. Verified against `wordpress-develop` `trunk`: `WP_Ability::execute()` order at class-wp-ability.php lines 612-669; `normalize_input()` at lines 444-455; `validate_input()` at 465-496 (delegates to `rest_validate_value_from_schema()` which validates only and does not inject property-level defaults); `invoke_callback()` at 507-526 with the input-schema-gated arg-forwarding at line 509. PHP example block lints clean on PHP 7.2. Refs WordPress#45 Refs Automattic/woocommerce-payments#11679
… bypass The "When NOT to use the helper" section listed two bypass categories (zero-arg backings and non-REST services), but missed the case in the middle: backing methods that DO take `WP_REST_Request` but route straight to a request class via something like `Things_Request::from_rest_request($request)->execute()` — where the controller adds nothing beyond the construction step. Surfaced as feedback on a pilot ability registration where the reviewer suggested calling the request class directly instead of going through the controller. The shape is structurally distinct from both existing bypass categories: it still uses `WP_REST_Request` (so the zero-arg subsection doesn't fit), but bypasses the controller layer (so the non-REST-service subsection doesn't fit either). Add a third "Backing request class can be invoked without the controller" subsection between the introduction and "Zero-arg backing methods", with a worked example that constructs a `WP_REST_Request` purely to satisfy `from_rest_request()`'s signature, then calls the request class directly. Cross-link to `shared-core-service.md` for the adjacent first-call REST bootstrap-cost discussion. Note the explicit tradeoff: bypassing the controller bypasses anything the controller does (validation, side effects), so this shape is for cases where the controller is genuinely a thin wrapper, not when it's doing work worth keeping. Update the rule of thumb so the three shapes (helper, request-class direct, fully direct-path) compose into one decision tree instead of the previous binary helper-or-direct-path framing. PHP example block lints clean on PHP 7.2. Diff hygiene checks pass: no filesystem paths, no private identifiers, no PHP 7.4+ syntax, no internal URLs, no plugin-specific cap strings. Refs WordPress#45 Refs Automattic/woocommerce-payments#11679
A standardized audit document is only useful if the structure is stable
across plugins and consumable without parsing surprises. Without a written
schema, every audit invents its own field names and downstream tools (or
human reviewers) have to map between variants.
Adds the canonical schema reference: required top-level fields (plugin,
repo, branch_audited, audited_at, auditor, baseline_abilities,
capability_gate, plus optional plugin_family), proposed_abilities array
shape (with backing, permission, return_type, effort, annotations, notes,
risks, optional reference_ability), excluded_from_mvp and surfaced_gaps
arrays, plus required prose sections (Controller Inventory, Notes and
Surprises). Documents capability_gate's two legal shapes (single string
vs structured {read, write} object), backing: null semantics for known
gaps, and the full minimal valid example with both a read and a write
ability.
The audit's capability_gate field has to reflect what the controller
actually gates on, not what its docblock says. Two mechanisms cover most
plugins, and naming them explicitly stops auditors from hard-coding one
plugin family's assumptions.
Adds a reference covering Mechanism A (direct: a base controller calls
current_user_can('<some_cap>') once and every route inherits the gate)
and Mechanism B (post-type-backed: controllers extend
WP_REST_Posts_Controller or a subclass; permission resolution is
dynamic at request time, with read and write often resolving to
distinct caps via core's map_meta_cap). Each mechanism gets identifying
signs, a tracing recipe, and a YAML representation example. Includes
guidance for the legacy compound-string capability_gate form
(accepted-but-not-preferred) and the __return_true REST-layer pitfall
(record verbatim, but never copy into the ability's own
permission_callback).
The first step of every audit is producing an exhaustive list of the plugin's REST controllers. Real plugins use at least two layouts (the standard includes/admin/class-*-rest-*-controller.php glob, and arbitrary non-standard locations like includes/api/, src/Rest/, or monorepo package directories), and missing controllers means missing abilities. Adds a reference documenting the two enumeration paths — glob for standard layouts, grep as the universal fallback — with explicit guidance on when each works, how to combine them for monorepos, and how to handle inherited routes (record on the child class with backing.inherited_from + null line numbers). Closes with an exhaustiveness rule (the Controller Inventory table must list every controller found, not just MVP-backers) so reviewers can answer "why isn't X in the MVP?" by pointing at the inventory.
A skill that produces a structured audit doc needs an eval scenario
demonstrating both routing (which skill to invoke for "I want to plan
abilities for plugin X") and the produced-doc shape (YAML block, prose
sections, structured capability_gate, surfaced_gaps, etc).
Adds eval/scenarios/wp-abilities-audit.json. Scenario covers a plugin
with a non-standard REST layout (includes/api/), a post-type-backed
base controller with distinct read/write capabilities, and a missing
backing endpoint that should surface as a gap. Success criteria
include routing to wp-abilities-audit, structured {read, write}
capability_gate, at least 3 proposed_abilities with at least one read
and one write, and a non-empty surfaced_gaps array.
When planning Abilities API adoption on a non-trivial plugin, the gap between "we have a REST surface" and "we have a coherent set of abilities to register" is real survey work — enumerate controllers, trace capability gates, decide granularity, identify gaps. Without a standardized output, every survey produces a differently-shaped artifact and downstream readers (or tooling) have to re-derive the shape each time. Adds the wp-abilities-audit skill: a 7-step procedure that consumes a plugin checkout, runs controller enumeration (references controller-enumeration.md), traces capability gates (references capability-gate-tracing.md), proposes abilities via semantic-intent grouping (references wp-abilities-api/grouping-heuristic.md), and writes a doc conforming to the canonical schema in audit-schema.md. The doc is plugin-agnostic — any WP plugin that exposes a REST surface is in scope.
5e6a82f to
2441fb0
Compare
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.
Adds a new skill
wp-abilities-auditthat surveys a plugin's REST surface and produces a structured audit document proposing Abilities API registrations. The audit doc is a planning artifact — readable by humans, parseable by tooling — that captures controller inventory, capability gates, proposed ability shapes, excluded items, and surfaced gaps in one place.Depends on #44 and #45. This branch stacks on top of both: it inherits the strategy-layer references (#44) and the mechanics-layer references (#45) that the new skill cross-links into. For a focused view of just this PR's 5 commits, see
feat/abilities-api-mechanics-refs...feat/abilities-audit-skillon the fork.What lands here
skills/wp-abilities-audit/SKILL.mdreferences/audit-schema.md{read, write}). Backing: null semantics. Required prose sections. Includes a copy-pasteable minimal example.references/capability-gate-tracing.mdcurrent_user_can()once) and Mechanism B (post-type-backed: controllers extendWP_REST_Posts_Controller-family, permission resolves dynamically via core'smap_meta_cap()). Tracing recipes and YAML representation for each.references/controller-enumeration.mdeval/scenarios/wp-abilities-audit.jsonThis PR's substantive content (commits on top of #45): 5 files, 756 added lines.
Verification
node eval/harness/run.mjspasses.capability-gate-tracing.md) parses on PHP 7.2.audit-schema.md,capability-gate-tracing.md,controller-enumeration.md(this PR), and towp-abilities-api/references/grouping-heuristic.md(wp-abilities-api: add three strategy-layer references (domain-vs-projection, shared-core-service, grouping-heuristic) #44, in this branch's base).Reviewer notes
plugin_family— it's optional and free-form rather than enum-validated, so the field can carry whatever classification a particular project finds useful (or be omitted entirely).capability-gate-tracing.mdlead with a generic CPT plugin (Mechanism B); the WooCommerce-style cap names appear as a sidebar showing the same mechanism with project-specific cap strings.