From e79fca1f1899e3ac27e2349c71545da3b55755c2 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 19 May 2026 21:03:15 -0400 Subject: [PATCH] Add pending action observer contract --- README.md | 1 + agents-api.php | 1 + docs/channels-workflows-operations.md | 5 ++- ...class-wp-agent-pending-action-observer.php | 43 +++++++++++++++++++ tests/bootstrap-smoke.php | 1 + tests/pending-action-store-contract-smoke.php | 25 +++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/Approvals/class-wp-agent-pending-action-observer.php diff --git a/README.md b/README.md index 6e483ac..018888f 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,7 @@ wp_register_agent( - `AgentsAPI\AI\Approvals\WP_Agent_Pending_Action` - `AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Status` - `AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Store` +- `AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Observer` - `AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Resolver` - `AgentsAPI\AI\Approvals\WP_Agent_Pending_Action_Handler` - `AgentsAPI\AI\Context\WP_Agent_Context_Authority_Tier` diff --git a/agents-api.php b/agents-api.php index 63e266f..c55fd1e 100644 --- a/agents-api.php +++ b/agents-api.php @@ -69,6 +69,7 @@ require_once AGENTS_API_PATH . 'src/Approvals/class-wp-agent-pending-action-status.php'; require_once AGENTS_API_PATH . 'src/Approvals/class-wp-agent-pending-action.php'; require_once AGENTS_API_PATH . 'src/Approvals/class-wp-agent-approval-decision.php'; +require_once AGENTS_API_PATH . 'src/Approvals/class-wp-agent-pending-action-observer.php'; require_once AGENTS_API_PATH . 'src/Approvals/class-wp-agent-pending-action-handler.php'; require_once AGENTS_API_PATH . 'src/Approvals/class-wp-agent-pending-action-resolver.php'; require_once AGENTS_API_PATH . 'src/Approvals/register-pending-action-abilities.php'; diff --git a/docs/channels-workflows-operations.md b/docs/channels-workflows-operations.md index f91a8a6..e62369e 100644 --- a/docs/channels-workflows-operations.md +++ b/docs/channels-workflows-operations.md @@ -182,10 +182,13 @@ Approval primitives live in `src/Approvals/`: - `WP_Agent_Pending_Action_Status`: pending/accepted/rejected/expired/deleted vocabulary. - `WP_Agent_Approval_Decision`: resolver decision value object. - `WP_Agent_Pending_Action_Store`: durable queue/audit interface. +- `WP_Agent_Pending_Action_Observer`: lifecycle observer contract for stores that emit stored/resolved/expired events. - `WP_Agent_Pending_Action_Resolver`: accept/reject resolution contract. - `WP_Agent_Pending_Action_Handler`: product handler contract for permission checks and applying/rejecting proposals. -Consumers own the database tables, REST routes, admin/chat UI, queues, product-specific apply/reject handlers, and authorization ceilings for transcript and approval materialization. +Store implementations own observer registration and invocation. Observers should tolerate duplicate lifecycle calls and avoid throwing; stores should defensively catch observer failures so notification, logging, or metrics observers cannot break durable approval state transitions. + +Consumers own the database tables, REST routes, admin/chat UI, queues, product-specific apply/reject handlers, event adapters, and authorization ceilings for transcript and approval materialization. ## Operational failure behavior diff --git a/src/Approvals/class-wp-agent-pending-action-observer.php b/src/Approvals/class-wp-agent-pending-action-observer.php new file mode 100644 index 0000000..c1186c3 --- /dev/null +++ b/src/Approvals/class-wp-agent-pending-action-observer.php @@ -0,0 +1,43 @@ + $parameter->getName(), $delete_parameters ), 'delete accepts action ID only', $failures, $passes ); agents_api_smoke_assert_equals( 'string', (string) $delete_parameters[0]->getType(), 'delete action ID is string', $failures, $passes ); +$observer_reflection = new ReflectionClass( 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action_Observer' ); +$observer_methods = array(); +foreach ( $observer_reflection->getMethods() as $method ) { + $observer_methods[ $method->getName() ] = $method; +} + +echo "\n[3] Pending action observer exposes stored, resolved, and expired lifecycle hooks:\n"; +agents_api_smoke_assert_equals( array( 'on_stored', 'on_resolved', 'on_expired' ), array_keys( $observer_methods ), 'observer exposes lifecycle methods', $failures, $passes ); +agents_api_smoke_assert_equals( 'void', (string) $observer_methods['on_stored']->getReturnType(), 'on_stored returns void', $failures, $passes ); +agents_api_smoke_assert_equals( 'void', (string) $observer_methods['on_resolved']->getReturnType(), 'on_resolved returns void', $failures, $passes ); +agents_api_smoke_assert_equals( 'void', (string) $observer_methods['on_expired']->getReturnType(), 'on_expired returns void', $failures, $passes ); + +$stored_parameters = $observer_methods['on_stored']->getParameters(); +$resolved_parameters = $observer_methods['on_resolved']->getParameters(); +$expired_parameters = $observer_methods['on_expired']->getParameters(); +agents_api_smoke_assert_equals( array( 'action' ), array_map( static fn( ReflectionParameter $parameter ): string => $parameter->getName(), $stored_parameters ), 'on_stored accepts action', $failures, $passes ); +agents_api_smoke_assert_equals( 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action', (string) $stored_parameters[0]->getType(), 'on_stored action is pending action', $failures, $passes ); +agents_api_smoke_assert_equals( array( 'action', 'decision', 'resolver' ), array_map( static fn( ReflectionParameter $parameter ): string => $parameter->getName(), $resolved_parameters ), 'on_resolved accepts action, decision, and resolver', $failures, $passes ); +agents_api_smoke_assert_equals( 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action', (string) $resolved_parameters[0]->getType(), 'on_resolved action is pending action', $failures, $passes ); +agents_api_smoke_assert_equals( 'AgentsAPI\\AI\\Approvals\\WP_Agent_Approval_Decision', (string) $resolved_parameters[1]->getType(), 'on_resolved decision is approval decision', $failures, $passes ); +agents_api_smoke_assert_equals( 'string', (string) $resolved_parameters[2]->getType(), 'on_resolved resolver is string', $failures, $passes ); +agents_api_smoke_assert_equals( array( 'action' ), array_map( static fn( ReflectionParameter $parameter ): string => $parameter->getName(), $expired_parameters ), 'on_expired accepts action', $failures, $passes ); +agents_api_smoke_assert_equals( 'AgentsAPI\\AI\\Approvals\\WP_Agent_Pending_Action', (string) $expired_parameters[0]->getType(), 'on_expired action is pending action', $failures, $passes ); + agents_api_smoke_finish( 'Agents API pending action store contract', $failures, $passes );