Make WordPress Core

Opened 8 hours ago

Last modified 6 hours ago

#64989 assigned enhancement

Abilities API: Add execution lifecycle filters to WP_Ability methods

Reported by: gziolo's profile gziolo Owned by: gziolo's profile gziolo
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() and check_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_ability governs orchestration flow, not individual method behavior.
  • Naming follows established patterns. wp_ prefix per 6.9 convention. wp_ability_normalize_input and wp_ability_permission_result name the method and what they filter. wp_pre_execute_ability mirrors rest_pre_dispatch.



Change History (2)

#1 @gziolo
8 hours ago

  • Owner set to gziolo
  • Status changed from new to assigned

This ticket was mentioned in Slack in #core-ai by gziolo. View the logs.


6 hours ago

Note: See TracTickets for help on using tickets.