Skip to content

Consume Abilities API lifecycle filters (WP core PR #11731) once they land #94

@lezama

Description

@lezama

Context

WordPress core now ships nine execution lifecycle hooks on `WP_Ability` (all `@since 7.1.0`), landed across three PRs by @gziolo:

  • PR #11731 — original four: `wp_pre_execute_ability`, `wp_ability_normalize_input`, `wp_ability_permission_result`, `wp_ability_execute_result`. Changeset 62397.
  • PR #11852 — new `wp_ability_invoked` action + enriched `$ability` parameter on `wp_before_execute_ability` / `wp_after_execute_ability`. Trac #65248.
  • PR #10557 — input/output validation filters: `wp_ability_validate_input`, `wp_ability_validate_output`. Trac #64311.

The substrate adopts these through `WP_Agent_Ability_Lifecycle_Bridge` (in `src/Abilities/`). Each hook is wired as a separate slice / PR.

Hook surface and substrate mapping

Hook Type When Substrate use Slice
`wp_ability_invoked` action Top of `execute()`, every call universal telemetry — captures all attempts including the failure modes `wp_ability_execute_result` misses TBD
`wp_pre_execute_ability` filter Pre-normalize, sentinel approval boundary returning `approval_required` envelope with `WP_Agent_Pending_Action` payload TBD — @ibrahimhajjaj
`wp_ability_normalize_input` filter Post-normalize inject `WP_Agent_Execution_Principal` / `WP_Agent_Caller_Context` into ability input TBD
`wp_ability_validate_input` filter Post-schema validation agent-aware input validation layering TBD
`wp_ability_permission_result` filter Post-`permission_callback` route through `WP_Agent_Tool_Access_Policy` + capability ceiling TBD
`wp_before_execute_ability` action Pre-`do_execute` (now with `$ability`) substrate-level pre-execute audit TBD
`wp_ability_execute_result` filter Post-`execute_callback` result observation — emits `agents_api_ability_executed` action #199 (in flight)
`wp_ability_validate_output` filter Post-output validation output-shape audits TBD
`wp_after_execute_ability` action Success path only (now with `$ability`) success-only commit hooks (transcript, telemetry) TBD

Design notes carried into slices

These came out of @ibrahimhajjaj's AbilityGuard work (https://github.com/ibrahimhajjaj/abilityguard) and should be observed by every slice that hooks one of the filters:

  1. Capability detection cannot key off `$wp_version`. The Abilities API also ships as a standalone repo (`WordPress/abilities-api`) and a Composer package on its own release cadence. A WP 7.0 host can have a recent standalone plugin with the filters; a WP 7.1 host can lack them. Slices that need runtime capability detection should reflect on `WP_Ability::execute()` source for the literal filter name. (Most slices don't need detection because the filter just stays idle when not applied.)
  2. Sentinel pass-through is mandatory on `wp_pre_execute_ability`. Core seeds `$pre` with a fresh `WP_Filter_Sentinel` per call. Any handler must `if ( ! ( $pre instanceof WP_Filter_Sentinel ) ) return $pre;` before doing anything, or it clobbers a prior consumer's short-circuit. Smokes should include a multi-consumer coexistence case.
  3. `wp_pre_execute_ability` fires before `wp_before_execute_ability`. Anything that opens a pending row or audit record in a before-action never runs on a short-circuit. The pre-execute handler has to do that work inline.
  4. `wp_ability_execute_result` only sees the post-callback path — it fires inside `do_execute()` after `execute_callback` returned and before `validate_output()`. It does not fire for pre-execute short-circuit, normalize-input failure, validate-input failure, permission denial, or validate-output failure. A complete failure-aware observer needs `wp_ability_invoked` plus the validation filters.
  5. No `duration_ms` at the `wp_ability_execute_result` seam since the outer `execute()` has not returned. Timing belongs in a `wp_after_execute_ability` hook (success path) or a paired `wp_ability_invoked` / `wp_after_execute_ability` measurement.
  6. Approval-required envelope inside the loop is the typed `WP_Agent_Pending_Action` returned in a `WP_Agent_Message::approvalRequired(...)` envelope. The `WP_Error` with `status => 202` pattern (used by AbilityGuard) is appropriate at the REST run-controller boundary but is swallowed by `mediate_tool_calls` and cannot carry the typed payload anyway.

Cross-cutting integration: loop-level approval recognition

`mediate_tool_calls` does not currently recognize `approval_required` envelopes returned from ability execution — it treats every executor return as a normal `tool_result`. The `wp_pre_execute_ability` slice depends on the loop learning to detect the envelope and surface it as an approval pause (`status: 'approval_required'`). This is a paired piece of work with the bridge-side approval handler.

Acceptance criteria

  • README adoption section documenting `WP_Agent_Ability_Lifecycle_Bridge` and the substrate's observable actions
  • Each of the nine hooks wired through a separate PR, sharing the same bridge class
  • Smokes per slice; multi-consumer coexistence smoke for `wp_pre_execute_ability`
  • `mediate_tool_calls` recognizes `approval_required` envelopes returned from tool execution
  • Capability-detection helper that reflects on `WP_Ability::execute()` rather than gating on `$wp_version`

Timing

Hooks are in WP core trunk; `@since 7.1.0`. On WordPress < 7.1 the filters never fire, so registered handlers stay idle — no version gate needed.

AI assistance

  • AI assistance: Yes
  • Tool(s): Claude Code (Opus 4.7)
  • Used for: Mapping the now-nine core filters against this substrate's existing primitives and drafting the adoption plan.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions