Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions agents-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@
require_once AGENTS_API_PATH . 'src/Channels/register-agents-chat-jsonrpc-route.php';
require_once AGENTS_API_PATH . 'src/Channels/register-agents-dispatch-message-ability.php';
require_once AGENTS_API_PATH . 'src/Workflows/class-wp-agent-workflow-bindings.php';
require_once AGENTS_API_PATH . 'src/Workflows/class-wp-agent-workflow-step-type-registry.php';
require_once AGENTS_API_PATH . 'src/Workflows/register-workflow-step-types.php';
require_once AGENTS_API_PATH . 'src/Workflows/class-wp-agent-workflow-spec-validator.php';
require_once AGENTS_API_PATH . 'src/Workflows/class-wp-agent-workflow-spec.php';
require_once AGENTS_API_PATH . 'src/Workflows/class-wp-agent-workflow-run-result.php';
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"php tests/context-authority-smoke.php",
"php tests/guidelines-substrate-smoke.php",
"php tests/workflow-bindings-smoke.php",
"php tests/workflow-step-type-registry-smoke.php",
"php tests/workflow-spec-validator-smoke.php",
"php tests/workflow-runner-smoke.php",
"php tests/workflow-parallel-smoke.php",
Expand Down
30 changes: 26 additions & 4 deletions docs/channels-workflows-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,21 +226,43 @@ Bridge authorization is intentionally separate from queue state. Connectors can

## Workflow specs and validation

Workflows are deterministic recipes composed of inputs, triggers, and steps. Agents API provides the spec value object, structural validator, in-memory registry, runner, default step handlers, store/recorder interfaces, optional Action Scheduler bridge and branch executor, and canonical abilities. It does not ship a durable workflow runtime, editor UI, product-specific step types, or run-history database.
Workflows are deterministic recipes composed of inputs, triggers, and steps. Agents API provides the spec value object, structural validator, step-type registry, in-memory workflow registry, runner, store/recorder interfaces, optional Action Scheduler bridge and branch executor, and canonical abilities. It does not ship a durable workflow runtime, editor UI, product-specific step types, or run-history database.

`WP_Agent_Workflow_Spec_Validator::validate( array $spec ): array` performs structural validation only:

- `id` is required and non-empty;
- `inputs`, when present, must be a map;
- `steps` must be a non-empty list;
- each step needs an `id` and `type`;
- built-in step types are `ability`, `agent`, `foreach`, and `parallel`;
- consumers can extend known step types through `wp_agent_workflow_known_step_types`;
- known step types and their field validation come from `WP_Agent_Workflow_Step_Type_Registry` (see below);
- `wp_action` and `cron` trigger shapes are checked;
- forward or unknown `${steps.<id>.output.*}` binding references are reported.

The validator does not check whether referenced agents or abilities exist; the runner handles that at execution time.

### The step-type registry

`WP_Agent_Workflow_Step_Type_Registry` pairs one step type name with its runner handler and its field validation contract, so a type can't validate and then fail at run time with no handler, or vice versa. Built-in types (`ability`, `agent`, `foreach`, `parallel`) are registered lazily on first access — no `init` hook dependency, so the registry behaves identically inside and outside a full WordPress bootstrap.

Register a step type with `register_workflow_step_type( string $type, array $args ): bool`:

```php
register_workflow_step_type(
'branch',
array(
'handler' => 'my_plugin_branch_step_handler', // callable( array $resolved_step, array $context ): array|WP_Error
'required' => array( 'condition' ), // simple non-empty-string required-field check
'validate' => 'my_plugin_validate_branch_step', // callable( array $step, string $path ): list<{path,code,message}>
)
);
```

`required` covers the common case (a field must be a non-empty string); write a `validate` callback for anything more specific — shape checks, mutually-exclusive fields, recursion into nested `steps`, etc. Registering an already-registered type keeps the first registration and triggers `_doing_it_wrong` (or the non-WordPress equivalent) — no silent overwrite.

The runner and the reconcile branch resolver (`agents_workflow_resolve_step_handlers()`) both read their handler map from `WP_Agent_Workflow_Step_Type_Registry::handlers()`, so one registration reaches structural validation, execution, and async branch reconciliation — a consumer no longer registers two unrelated filters that can drift apart.

**Back-compat, one release only:** the legacy `wp_agent_workflow_known_step_types` and `wp_agent_workflow_step_handlers` filters are still applied on top of the registry's output, so an unmigrated consumer keeps working. Both filters are deprecated as of the registry's introduction and will be removed in a future release; a type registered only through these legacy filters gets no field validation. `WP_Agent_Workflow_Spec_Validator::KNOWN_STEP_TYPES` is likewise kept for back-compat only — it is no longer the source of truth for known step types, `WP_Agent_Workflow_Step_Type_Registry::types()` is.

## Workflow runner

`WP_Agent_Workflow_Runner` executes one `WP_Agent_Workflow_Spec` in order:
Expand All @@ -266,7 +288,7 @@ The default `parallel` handler uses `wp_agent_workflow_step_executor` as its con

The Action Scheduler executor requires a store that supports concurrent writes for parallel branch execution. MySQL and MariaDB can provide that concurrency. SQLite uses a single database-wide writer lock, so Action Scheduler claims and branch execution serialize even when multiple workers are requested. SQLite remains correct for async branch completion, but it provides no parallel speedup. Consumers that require parallel execution should use MySQL or MariaDB; Agents API does not detect or warn about the active database engine at runtime.

Consumers extend the runner through the constructor or the `wp_agent_workflow_step_handlers` filter. Product-specific steps such as `branch` or nested `workflow` belong in consumers.
Consumers extend the runner by registering a step type through `register_workflow_step_type()` (see [The step-type registry](#the-step-type-registry)), or, for a one-off handler override on a single runner instance, through the constructor's `$step_handlers` argument. Product-specific steps such as `branch` or nested `workflow` belong in consumers.

Recorder behavior is conservative: `start()` runs before input validation, per-step `update()` calls follow state changes, and recorder-start failure returns a failed run result without executing steps.

Expand Down
28 changes: 15 additions & 13 deletions src/Workflows/class-wp-agent-workflow-runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,7 @@ public function __construct(
protected ?WP_Agent_Workflow_Run_Recorder $recorder = null,
array $step_handlers = array()
) {
$defaults = array(
'ability' => array( __CLASS__, 'default_ability_handler' ),
'agent' => array( __CLASS__, 'default_agent_handler' ),
'foreach' => array( __CLASS__, 'default_foreach_handler' ),
'parallel' => array( __CLASS__, 'default_parallel_handler' ),
);
$defaults = WP_Agent_Workflow_Step_Type_Registry::handlers();

/**
* Filter the step-type handler map. Consumers add new step types
Expand All @@ -86,9 +81,15 @@ public function __construct(
* if a consumer wants to substitute a different ability / agent
* runtime.
*
* @deprecated 0.14.0 Register step types through
* {@see WP_Agent_Workflow_Step_Type_Registry::register()}
* or `register_workflow_step_type()` instead. This
* filter is kept for one release of back-compat and
* will be removed in a future version.
*
* @since 0.103.0
*
* @param array<string,mixed> $handlers Default + caller-supplied handlers.
* @param array<string,mixed> $handlers Registry + caller-supplied handlers.
*/
$this->step_handlers = (array) apply_filters(
'wp_agent_workflow_step_handlers',
Expand Down Expand Up @@ -1693,15 +1694,16 @@ private static function foreach_cancel_requested( array $context ): bool {
* @return array<string,mixed>
*/
private static function default_step_handlers(): array {
/**
* @deprecated 0.14.0 Register step types through
* {@see WP_Agent_Workflow_Step_Type_Registry::register()}
* or `register_workflow_step_type()` instead. Kept for
* one release of back-compat.
*/
/** @var array<string,mixed> $handlers */
$handlers = (array) apply_filters(
'wp_agent_workflow_step_handlers',
array(
'ability' => array( __CLASS__, 'default_ability_handler' ),
'agent' => array( __CLASS__, 'default_agent_handler' ),
'foreach' => array( __CLASS__, 'default_foreach_handler' ),
'parallel' => array( __CLASS__, 'default_parallel_handler' ),
)
WP_Agent_Workflow_Step_Type_Registry::handlers()
);

return $handlers;
Expand Down
Loading
Loading