Collectors: catalogue registered WordPress Abilities and metadata
- Dominant language
- TypeScript
- Stars
- 3
- Forks
- 0
- Avg merge
- 1h 8m
- Merged PRs (30d)
- 14
Description
## Problem
Wesper 0.4.1 does not collect registered WordPress Abilities. The manifest accepts unknown top-level extensions, but has no typed Abilities section, collector implementation or coverage reporting for it. Abilities were explicitly deferred in #14.
## Solution
Add an optional `abilities` section containing registered ability definitions and category metadata. Collect the standard descriptive fields, input/output schemas and arbitrary JSON metadata exposed by the WordPress API. Keep collection read-only and vendor-neutral.
WP-CLI should describe the registry available in the selected WordPress bootstrap. REST should describe the authenticated, REST-visible subset. Neither transport establishes whether an ability can execute successfully for a particular user and input.
## Proposed contract diff
Illustrative TypeScript contract, not an implemented patch. Existing manifests remain valid with `contextVersion: 1` and no `abilities` section.
```diff
interface SiteContext {
// Existing sections remain unchanged.
+ abilities?: {
+ scope: 'runtime-registry' | 'rest-visible';
+ items: Array<{
+ name: string;
+ label?: string;
+ description?: string;
+ category?: string;
+ inputSchema?: JsonValue;
+ outputSchema?: JsonValue;
+ meta?: Record;
+ }>;
+ categories?: Array<{
+ slug: string;
+ label?: string;
+ description?: string;
+ meta?: Record;
+ }>;
+ };
}
```
`JsonValue` means the existing recursive JSON value contract. Require stable identifiers and unique ability names/category slugs. Preserve reported values, including `false`, `null`, empty schemas and nested metadata; do not replace missing evidence with defaults. Map the standard `input_schema` and `output_schema` keys to Wesper's camelCase fields. Keep keys inside schemas and `meta` unchanged.
Preserve all JSON-safe metadata exposed through the supported API, including `meta.annotations`, `meta.show_in_rest` and plugin-defined extensions. Annotations are reported declarations, not verified behaviour. Keep unknown keys rather than maintaining a vendor allowlist. Do not serialise callbacks, closures or arbitrary PHP object internals. If a value cannot be represented safely, report the omission through a warning rather than silently claiming complete metadata.
Categories are separate registry records, so their labels, descriptions and exposed metadata are retained even when no collected ability references them. An omitted category registry means unavailable evidence; `categories: []` means it was read and empty. Do not infer plugin ownership from an ability namespace or a metadata field.
## Collection approach
```diff
WP-CLI: existing single-process WordPress bootstrap → wp eval
+ feature-detect wp_get_abilities / wp_get_ability_categories
+ read registries through their public API and supported getters
+ map descriptive properties, schemas and metadata into abilities
REST: existing authenticated core REST transport
+ GET wp-abilities/v1/abilities, following pagination
+ GET wp-abilities/v1/categories, following pagination
shared normalisation → validation → redaction → canonical hash
+ validate ability/category records and unique identifiers
+ sort registry arrays by identifier; preserve schema/meta array order
+ report abilities coverage and summary counts
```
### WP-CLI
Use `wp_get_abilities()` and the public ability getters for name, label, description, category, input schema, output schema and metadata. Read categories through `wp_get_ability_categories()` and their supported getters. Allow the public registry API to perform its normal lazy initialisation; do not manually replay registration hooks. Feature-detect APIs rather than gating solely on a WordPress version, so an API supplied by a plugin can also be discovered. See the [WordPress PHP reference](https://developer.wordpress.org/apis/abilities-api/php-reference/).
Keep the existing `execFile` argv transport and selected site context. Include abilities regardless of their REST exposure. Describe only registrations observed in that bootstrap; registrations conditional on a different request or user may differ. An absent API produces unavailable evidence, while a successful empty registry produces `items: []`.
### REST
Use the existing authenticated transport, URL handling, timeout, response-size and request-budget controls. Read only the core Abilities and categories discovery endpoints. Follow pagination for both collections; an interrupted or bounded read must be marked partial. REST only lists abilities opted into exposure, so `scope: 'rest-visible'` must never imply a complete PHP registry. Mark that limitation explicitly in coverage, including when the returned list is empty. See the [WordPress REST reference](https://developer.wordpress.org/apis/abilities-api/rest-api-endpoints/).
Keep route absence, authentication failure and malformed responses distinct from an empty collection. Preserve valid ability evidence if category collection fails, with a category-specific warning. Never call an ability execution endpoint, including an endpoint that executes via GET.
### Normalisation and compatibility
Reuse the existing schema, redaction, warnings and canonical hashing mechanisms. Preserve arbitrary JSON schema keywords and metadata keys after credential redaction. Invalid evidence must not discard unrelated manifest sections. Preserve valid entries where possible and mark any skipped entries or metadata as partial. Diagnostics must not leak the rejected raw values.
Add ability counts and coverage to summaries. Keep the existing required strict surfaces unchanged. In particular, adding an optional section must not manufacture an actionable missing-section warning for every legacy manifest. Test the interaction with `provenance.partial`, `allWarnings()` and strict collection explicitly rather than simply appending `abilities` to the current mandatory surface list.
## Implementation map
| Area | Read first | Required change |
|---|---|---|
| Manifest contract | `src/schema.ts`, `src/types.ts`, `schemas/site-context-v1.schema.json` | Typed optional section, identifier validation, generated schema |
| Registry collection | `src/collector/wpcli.ts` | Public API discovery within the existing process |
| REST discovery | `src/collector/rest.ts` | Paginated core discovery routes using existing transport controls |
| Evidence handling | `src/collector/normalize.ts`, `src/warnings.ts`, `src/summary.ts` | Stable normalisation, metadata preservation, coverage and counts |
| Proof and documentation | Collector/schema tests, `integration/wordpress/`, `README.md` | Fixtures, real WordPress proof, documented transport limits and updated scope |
## Acceptance criteria
- [ ] A real WordPress fixture with two synthetic plugin namespaces produces both registered ability definitions, their category records, schemas and nested custom metadata through WP-CLI.
- [ ] The fixture includes one ability exposed in REST and one hidden from REST. REST returns the exposed subset and explicit coverage limits; WP-CLI reports both.
- [ ] Execution and permission callbacks in the fixture record or throw if called. Collection invokes neither. REST request assertions reject execution routes even when the method is GET.
- [ ] Tests cover an unavailable API, a successfully empty registry, multiple REST pages, an interrupted page and category-only failure without losing valid abilities or unrelated sections.
- [ ] Schema tests reject duplicate identifiers and malformed records. Nested schema keywords, custom metadata and false/null values survive validation; unsafe values produce redacted warnings.
- [ ] Reordering registry entries leaves `sourceHash` unchanged; changing collected metadata changes it. Credential-like values are redacted before hashing and output.
- [ ] Existing 0.4.1 manifests still validate, and absent optional Abilities evidence does not introduce a new strict requirement or actionable legacy warning.
- [ ] Run `npm run generate:schema` and `npm run verify`, plus the real WordPress integration test with an Abilities-capable fixture. Record actual results and any untested transport or version limits in the implementation PR.
## Out of scope
Ability execution, permission evaluation, callback source inspection, inferred ownership, arbitrary action/filter discovery, custom plugin endpoints and MCP adapters.
## Evidence
Proposal based on source inspection at `ec3de3f3931c039089eed7cd73d2246b3f0c88e5` and the official WordPress references above. No implementation or Abilities integration test has been run for this issue. The diffs describe the proposed contract and collection flow.
Contributor guide
Research direction
Start with src/schema.ts, src/types.ts, schemas/site-context-v1.schema.json, and the collector entry points in src/collector/wpcli.ts and src/collector/rest.ts; then read the normalization, warnings, summary, and existing collector tests. Done means optional Abilities evidence is collected and validated through both transports, preserves safe metadata and coverage diagnostics, keeps legacy manifests valid, and passes npm run generate:schema, npm run verify, and the specified integration coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript, wordpress
- Domain
- api, backend-api-design, testing-qa
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100