From d91d36bd762f8e8f16edec20477f180724ca5ac7 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Thu, 18 Jun 2026 08:21:42 -0400 Subject: [PATCH 1/2] Handle workflow step handler exceptions --- .../class-wp-agent-workflow-step-executor.php | 23 ++++++++++++--- tests/workflow-runner-smoke.php | 29 +++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/Workflows/class-wp-agent-workflow-step-executor.php b/src/Workflows/class-wp-agent-workflow-step-executor.php index e9fa0b3..f423994 100644 --- a/src/Workflows/class-wp-agent-workflow-step-executor.php +++ b/src/Workflows/class-wp-agent-workflow-step-executor.php @@ -49,10 +49,25 @@ public function execute( array $step, WP_Agent_Workflow_Run_Context $context ): } $context_array = $context->to_array(); - $resolved = 'foreach' === $type - ? self::expand_foreach_outer_step( $step, $context_array ) - : WP_Agent_Workflow_Bindings::expand( $step, $context_array ); - $step_output = call_user_func( $handler, $resolved, $context_array ); + try { + $resolved = 'foreach' === $type + ? self::expand_foreach_outer_step( $step, $context_array ) + : WP_Agent_Workflow_Bindings::expand( $step, $context_array ); + $step_output = call_user_func( $handler, $resolved, $context_array ); + } catch ( \Throwable $throwable ) { + $record['status'] = WP_Agent_Workflow_Run_Result::STATUS_FAILED; + $record['ended_at'] = time(); + $record['error'] = array( + 'code' => 'handler_exception', + 'error_type' => 'handler_exception', + 'message' => $throwable->getMessage(), + 'data' => array( + 'exception' => get_class( $throwable ), + ), + ); + + return $record; + } if ( is_wp_error( $step_output ) ) { $record['status'] = WP_Agent_Workflow_Run_Result::STATUS_FAILED; diff --git a/tests/workflow-runner-smoke.php b/tests/workflow-runner-smoke.php index a3ec7c9..f8a6e51 100644 --- a/tests/workflow-runner-smoke.php +++ b/tests/workflow-runner-smoke.php @@ -331,6 +331,35 @@ static function ( array $input ): \WP_Error { smoke_assert( 1, count( $result2->get_steps() ), 'short-circuit stops at first failure', $failures, $passes ); smoke_assert( 'demo_bang', $result2->get_error()['code'], 'top-level error carries failed step code', $failures, $passes ); +// ─── Throwing step handlers become failed terminal run records ───────── + +add_filter( 'wp_agent_workflow_known_step_types', static fn( $types ) => array_merge( (array) $types, array( 'throwing' ) ) ); + +$throwing_spec = WP_Agent_Workflow_Spec::from_array( + array( + 'id' => 'demo/throwing-handler', + 'steps' => array( + array( 'id' => 'explode', 'type' => 'throwing' ), + ), + ) +); + +$throwing_recorder = new Capture_Recorder(); +$throwing_result = ( new WP_Agent_Workflow_Runner( + $throwing_recorder, + array( + 'throwing' => static function (): array { + throw new \RuntimeException( 'handler exploded' ); + }, + ) +) )->run( $throwing_spec ); +$throwing_last = end( $throwing_recorder->writes ); + +smoke_assert( WP_Agent_Workflow_Run_Result::STATUS_FAILED, $throwing_result->get_status(), 'handler exception fails run', $failures, $passes ); +smoke_assert( 'handler_exception', $throwing_result->get_error()['code'] ?? '', 'handler exception uses expected error code', $failures, $passes ); +smoke_assert( 'handler_exception', $throwing_result->get_steps()[0]['error']['error_type'] ?? '', 'handler exception records expected error_type', $failures, $passes ); +smoke_assert( WP_Agent_Workflow_Run_Result::STATUS_FAILED, $throwing_last['status'] ?? '', 'recorder receives terminal failed update for handler exception', $failures, $passes ); + // ─── continue_on_error keeps running ───────────────────────────────── $result3 = ( new WP_Agent_Workflow_Runner( null ) )->run( $bad_spec, array(), array( 'continue_on_error' => true ) ); From eb9f4da954b09f886ac8a21744cf8b4206fec951 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Thu, 18 Jun 2026 08:39:00 -0400 Subject: [PATCH 2/2] Fix workflow smoke ability stub metadata --- tests/workflow-runner-smoke.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/workflow-runner-smoke.php b/tests/workflow-runner-smoke.php index f8a6e51..c9a1b6c 100644 --- a/tests/workflow-runner-smoke.php +++ b/tests/workflow-runner-smoke.php @@ -99,6 +99,8 @@ function workflow_runner_smoke_register_ability( string $name, \Closure $handler $GLOBALS['__abilities'][ $name ] = new WP_Ability( $name, array( + 'label' => $name, + 'description' => 'Workflow runner smoke stub.', 'input_schema' => array( 'type' => 'object' ), 'execute_callback' => $handler, )