Opened 8 hours ago
Last modified 6 hours ago
#64989 assigned enhancement
Abilities API: Add execution lifecycle filters to WP_Ability methods
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 7.1 | Priority: | normal |
| Severity: | normal | Version: | 6.9 |
| Component: | AI | Keywords: | abilities |
| Focuses: | Cc: |
Description
The Abilities API (#64098, WordPress 6.9) provides two registration-phase filters (wp_register_ability_args, wp_register_ability_category_args) and two execution-phase actions (wp_before_execute_ability, wp_after_execute_ability). The execution actions are observation-only — no hook exists that lets plugins transform input, modify output, override permission decisions, or short-circuit execution.
This ticket proposes four new filters placed inside WP_Ability public methods and the execute() pipeline.
Background
Prior exploration
abilities-api#37 proposed four filters (ability_input_schema, ability_output_schema, ability_permission_result, ability_execute_result) placed inside read-only getters on WP_Ability. It was closed as "not planned" because getter-level filters fire many times during serialization (REST API schema responses, MCP tool listing, any introspection), introducing performance and behavioral unpredictability. Schema modification at read time also conflates registration-time concerns with execution-time concerns — registration-time customization is already handled by wp_register_ability_args.
This proposal targets operational methods instead — normalize_input(), check_permissions() — which fire at decision points, typically once per request. This follows the same approach as the validation filters proposed in #64311.
Demonstrated need from plugins
Two consumers of the Abilities API have independently built parallel hook systems because core lacks execution-time filters:
MCP Adapter (WordPress/mcp-adapter) implements mcp_adapter_-prefixed hooks for validation toggling, caching, and post-execution audit. The team has decided to maintain their own lower-level filters on top of whatever core provides (mcp-adapter#151), but richer core hooks would reduce the scope of what the adapter needs to re-implement.
AI Plugin (WordPress/ai) needs input transformation (prompt enrichment), output transformation (response formatting, safety filtering), and failure handling. ai#304 proposes wpai_-prefixed hooks for these concerns. Whether the AI plugin should add its own execution-lifecycle filters or rely on core is an open question this ticket aims to resolve.
Both plugins validate a three-tier extensibility pattern (core → protocol adapter → domain plugin) where richer core hooks reduce fragmentation.
Proposal
Four new filters. Two live inside public WP_Ability methods (normalize_input(), check_permissions()), ensuring consistent behavior across all call sites — execute(), REST API, WP-CLI. Two live on execute() itself because they govern orchestration flow. This mirrors how rest_pre_dispatch short-circuits the REST API dispatch pipeline.
Proposed execution lifecycle
┌─ wp_pre_execute_ability (filter) ──── can short-circuit │ ├─ validate_input() │ ├─ normalize_input() │ └─ wp_ability_normalize_input (filter) ── inside method │ ├─ check_permissions() │ ├─ permission_callback() │ └─ wp_ability_permission_result (filter) ── inside method │ ├─ wp_before_execute_ability (action) ── existing │ ├─ do_execute() │ ├─ wp_ability_execute_result (filter) ── transform output │ ├─ validate_output() │ ├─ wp_after_execute_ability (action) ── existing │ └─ return result
1. wp_pre_execute_ability — Short-circuit
$pre = apply_filters( 'wp_pre_execute_ability', null, $ability_name, $input, $ability );
if ( null !== $pre ) {
return $pre;
}
Fires in execute() before input validation. Returns non-null to bypass the entire pipeline. Modeled on rest_pre_dispatch.
Use cases: Cached responses, rate limiting, maintenance mode, test mocking.
2. wp_ability_normalize_input — Input transformation
// Inside WP_Ability::normalize_input(), after built-in normalization:
$input = apply_filters( 'wp_ability_normalize_input', $input, $ability_name, $ability );
Fires inside normalize_input() after the method's built-in logic. The filter receives schema-validated, normalized data. Returning a WP_Error halts execution.
Use cases: AI prompt enrichment, parameter defaulting beyond what JSON Schema handles, injecting caller metadata.
3. wp_ability_permission_result — Permission override
// Inside WP_Ability::check_permissions(), after permission_callback returns:
$permission = apply_filters( 'wp_ability_permission_result', $permission, $ability_name, $input, $ability );
Fires inside check_permissions() after permission_callback returns. check_permissions() is called from execute(), from the REST API permissions_check callback, and from WP-CLI's wp ability can-run — placing the filter inside the method ensures overrides apply consistently regardless of call site.
Use cases: MCP Adapter transport-level permission layering, multi-factor authorization, temporary permission elevation for batch operations.
4. wp_ability_execute_result — Output transformation
$result = apply_filters( 'wp_ability_execute_result', $result, $ability_name, $input, $ability );
Fires in execute() after do_execute() returns, before output validation. Filters must return data that conforms to output_schema — output validation remains the final integrity gate.
Use cases: AI response formatting, stripping internal metadata, content safety filtering, response enrichment.
Design principles
- Filters inside operational methods, not getters.
normalize_input()andcheck_permissions()fire at decision points, typically once per request — unlike the getters targeted in abilities-api#37. Placing filters here ensures consistent behavior across all call sites. - Validation remains authoritative. Input transformation fires after input validation; output transformation fires before output validation. Filters cannot bypass schema validation.
- Short-circuit stays on
execute().wp_pre_execute_abilitygoverns orchestration flow, not individual method behavior. - Naming follows established patterns.
wp_prefix per 6.9 convention.wp_ability_normalize_inputandwp_ability_permission_resultname the method and what they filter.wp_pre_execute_abilitymirrorsrest_pre_dispatch.
Related
- #64098 — Introduce Abilities API
- abilities-api#37 — Prior filter proposal (closed, lessons incorporated)
- #64311 — Validation hooks proposal (same pattern)
- ai#304 — AI plugin hook audit
- mcp-adapter#151 — MCP Adapter filters