diff --git a/inc/Abilities/Engine/RunFlowAbility.php b/inc/Abilities/Engine/RunFlowAbility.php index 7e77b27f..b1d6cc73 100644 --- a/inc/Abilities/Engine/RunFlowAbility.php +++ b/inc/Abilities/Engine/RunFlowAbility.php @@ -43,18 +43,22 @@ private function registerAbility(): void { 'type' => 'object', 'required' => array( 'flow_id' ), 'properties' => array( - 'flow_id' => array( + 'flow_id' => array( 'type' => 'integer', 'description' => __( 'Flow ID to execute.', 'data-machine' ), ), - 'job_id' => array( + 'job_id' => array( 'type' => array( 'integer', 'null' ), 'description' => __( 'Pre-created job ID (optional, for API-triggered executions).', 'data-machine' ), ), - 'initial_data' => array( + 'initial_data' => array( 'type' => 'object', 'description' => __( 'Optional initial engine data to merge (e.g. webhook payloads, API context).', 'data-machine' ), ), + 'respect_paused' => array( + 'type' => 'boolean', + 'description' => __( 'Internal scheduler safety flag. When true, paused flows are skipped.', 'data-machine' ), + ), ), ), 'output_schema' => array( @@ -91,7 +95,7 @@ private function registerAbility(): void { /** * Execute the run-flow ability. * - * @param array $input Input with flow_id, optional job_id and initial_data. + * @param array $input Input with flow_id, optional job_id, initial_data, and respect_paused. * @return array Result with success status and execution details. */ public function execute( array $input ): array { @@ -107,10 +111,12 @@ public function execute( array $input ): array { ); } - // Check if flow is paused (enabled=false). Safety net for AS hooks - // that were already queued before the flow was paused. + // Check if flow is paused only for scheduler-triggered executions. + // Direct ability/manual runs are allowed even when recurring schedules + // are paused. $scheduling_config = $flow['scheduling_config'] ?? array(); - if ( ! \DataMachine\Core\Database\Flows\Flows::is_flow_enabled( $scheduling_config ) ) { + $respect_paused = true === ( $input['respect_paused'] ?? false ); + if ( $respect_paused && ! \DataMachine\Core\Database\Flows\Flows::is_flow_enabled( $scheduling_config ) ) { do_action( 'datamachine_log', 'info', diff --git a/inc/Engine/Actions/Engine.php b/inc/Engine/Actions/Engine.php index 7debb85b..64431be7 100644 --- a/inc/Engine/Actions/Engine.php +++ b/inc/Engine/Actions/Engine.php @@ -96,8 +96,9 @@ function ( $flow_id, $job_id = null ) { if ( $ability ) { $ability->execute( array( - 'flow_id' => $flow_id, - 'job_id' => $job_id ? (int) $job_id : null, + 'flow_id' => $flow_id, + 'job_id' => $job_id ? (int) $job_id : null, + 'respect_paused' => true, ) ); } diff --git a/tests/run-flow-paused-manual-smoke.php b/tests/run-flow-paused-manual-smoke.php new file mode 100644 index 00000000..44178b12 --- /dev/null +++ b/tests/run-flow-paused-manual-smoke.php @@ -0,0 +1,36 @@ + array(", $run_flow_src, 'run-flow ability exposes scheduler safety input' ); +assert_run_flow_paused_contains( '$respect_paused = true === ( $input[\'respect_paused\'] ?? false );', $run_flow_src, 'run-flow defaults paused guard off for direct/manual execution' ); +assert_run_flow_paused_contains( 'if ( $respect_paused && ! \\DataMachine\\Core\\Database\\Flows\\Flows::is_flow_enabled( $scheduling_config ) )', $run_flow_src, 'paused guard only runs when scheduler safety flag is set' ); +assert_run_flow_paused_contains( "'respect_paused' => true,", $engine_src, 'Action Scheduler hook bridge preserves paused-flow safety' ); + +echo "OK ({$assertions} assertions)\n";