
# Async Handoffs

Use `start_workflows()` when a workflow needs to hand discovered work off to independent top-level workflows instead of keeping everything inside one run.

If you are explicitly modelling agent runs, `start_agents()` and `wait_for_agents()` are semantic aliases with agent-oriented defaults.

For larger end-to-end examples, see [Agent Orchestration](/docs/workflows/agent-orchestration).

This is useful for agent-style orchestration:

- a planner workflow discovers tasks at runtime
- each task becomes its own durable workflow
- the parent stores those workflow IDs in state
- the parent can later wait for `all`, `any`, `quorum`, or a named started group

## The `->start_workflows()` method

```php
use Queuety\Enums\WaitMode;
use Queuety\Queuety;

$agent_task = Queuety::workflow( 'agent_task' )
    ->then( ResearchTopicStep::class )
    ->then( SummarizeTopicStep::class );

Queuety::workflow( 'brief_research' )
    ->then( PlanTopicsStep::class )
    ->start_workflows(
        items_key: 'topics',
        workflow_builder: $agent_task,
        result_key: 'child_workflow_ids',
        group_key: 'researchers',
    )
    ->wait_for_workflow_group( 'researchers', WaitMode::All, result_key: 'child_results' )
    ->then( SynthesizeBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

In that example:

1. `PlanTopicsStep` writes an array of topic payloads to `$state['topics']`
2. `start_workflows()` dispatches one `agent_task` workflow per topic
3. the started workflow IDs are written to `$state['child_workflow_ids']`
4. `wait_for_workflow_group()` pauses until that started group satisfies the chosen completion condition
5. `SynthesizeBriefStep` receives the completed child states under `$state['child_results']`

## Child payloads

Each item in `items_key` becomes the initial payload for one started workflow.

If the item is an array, its public keys are merged into the child payload:

```php
[
    [ 'topic' => 'pricing', 'source' => 'competitors' ],
    [ 'topic' => 'reviews', 'source' => 'customers' ],
]
```

If the item is scalar, Queuety stores it under `payload_key`:

```php
->start_workflows(
    items_key: 'topic_names',
    workflow_builder: $agent_task,
    payload_key: 'topic',
)
```

## Inheriting parent state

By default, `start_workflows()` merges the parent workflow's public state into every child payload.

That means shared context such as `brief_id`, `account_id`, or `campaign` is automatically available in each started workflow.

The source `items_key` and the `result_key` are excluded from inheritance so each child does not receive the whole task list or the final workflow ID array.

Set `inherit_state: false` when each child should receive only its item payload.

## Why not `run_workflow()`?

`run_workflow()` creates a parent-child relationship where the parent parks on that step until the child completes.

`start_workflows()` is looser:

- the started workflows are independent top-level workflows
- each started workflow can be inspected, retried, or exported on its own
- the parent decides later whether and how to wait on them

That makes `start_workflows()` a better fit for async agent task handoff, while `run_workflow()` remains the better fit for nested composition inside one orchestration tree.

## Waiting later

`start_workflows()` is designed to pair with [Workflow Dependencies](/docs/workflows/dependencies):

```php
use Queuety\Enums\WaitMode;

Queuety::workflow( 'review_board' )
    ->start_workflows( 'review_tasks', $review_task_workflow, 'review_workflow_ids' )
    ->wait_for_workflows( 'review_workflow_ids', WaitMode::Any, 'winner' )
    ->then( FinalizeDecisionStep::class )
    ->dispatch( $payload );
```

- `WaitMode::All` collects all completed child states
- `WaitMode::Any` resumes on the first successful child workflow

This gives you planner/executor orchestration without needing a full runtime-editable DAG engine.

## Agent-friendly aliases

`start_agents()` is an alias for `start_workflows()` with defaults tuned for planner/executor flows:

- `result_key` defaults to `agent_workflow_ids`
- `payload_key` defaults to `agent_task`
- `name` defaults to `start_agents`

`wait_for_agents()` is an alias for `wait_for_workflows()` with defaults tuned for collecting agent results:

- `workflows` defaults to `agent_workflow_ids`
- `result_key` defaults to `agent_results`
- `name` defaults to `wait_for_agents`

`wait_for_agent_group()` is an alias for `wait_for_workflow_group()` with defaults tuned for agent batches.

```php
use Queuety\Enums\WaitMode;

$agent_run = Queuety::workflow( 'agent_run' )
    ->then( ResearchTopicStep::class )
    ->then( SummarizeTopicStep::class );

Queuety::workflow( 'brief_research' )
    ->then( PlanTopicsStep::class )
    ->start_agents( 'agent_tasks', $agent_run, group_key: 'researchers' )
    ->wait_for_agent_group( 'researchers', WaitMode::Quorum, 2 )
    ->then( SynthesizeBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```
