Queuety
Workflows

Workflow Dependencies

Use workflow dependency waits when one top-level workflow should continue only after other workflows have completed. This gives you cross-workflow coordination without forcing everything into one giant workflow definition.

These waits pair especially well with Async Handoffs, where one workflow first spawns independent child workflows and then joins them later.

Queuety provides two builder methods:

  • await_workflow() for one workflow
  • await_workflows() for several workflows with all or any semantics

Wait for one workflow

$research_id = Queuety::workflow( 'research_run' )
    ->then( PlanResearchStep::class )
    ->fan_out( 'tasks', ExecuteResearchTask::class, 'results' )
    ->dispatch( [ 'brief_id' => 42 ] );

$editorial_id = Queuety::workflow( 'editorial_run' )
    ->await_workflow( $research_id, 'research' )
    ->then( DraftBriefStep::class )
    ->dispatch();

When research_run completes, editorial_run resumes. With result_key set, the completed dependency state is stored under that key:

$state['research']; // public state from the completed research workflow

Without a result_key, the dependency workflow's public state is merged into the waiting workflow's top-level state.

Wait for several workflows

use Queuety\Enums\WaitMode;

Queuety::workflow( 'synthesis_run' )
    ->await_workflows(
        [ $research_id, $editorial_id, $legal_id ],
        WaitMode::All,
        'dependencies'
    )
    ->then( PublishPacketStep::class )
    ->dispatch();
  • WaitMode::All resumes only when every listed workflow has completed.
  • WaitMode::Any resumes when the first listed workflow completes.

With result_key, multi-workflow waits store completed dependency state keyed by workflow ID:

[
    '42' => [ 'summary' => '...' ],
    '43' => [ 'summary' => '...' ],
]

Without a result_key, the completed dependency state is merged into the waiting workflow's top-level state in dependency order.

Resolve dependency IDs from workflow state

You can defer the dependency list until runtime by reading workflow IDs from public state:

Queuety::workflow( 'agent_follow_up' )
    ->await_workflow( 'parent_workflow_id', 'parent' )
    ->then( FinalizeAgentRunStep::class )
    ->dispatch( [ 'parent_workflow_id' => $workflow_id ] );

For several dependencies:

Queuety::workflow( 'aggregator' )
    ->await_workflows( 'dependency_ids', WaitMode::Any, 'winner' )
    ->dispatch( [ 'dependency_ids' => [ 12, 18, 22 ] ] );

What happens while waiting

When a workflow reaches one of these steps it moves to waiting_workflow status. No worker stays attached to it while it waits.

$state = Queuety::workflow_status( $workflow_id );
echo $state->status->value; // 'waiting_workflow'
echo $state->wait_type;     // 'workflow'
print_r( $state->waiting_for ); // ['42', '43']

If a dependency has already completed before the wait step is reached, the workflow continues immediately.

Failure semantics

Workflow dependency waits are success-oriented:

  • await_workflow() and WaitMode::All fail if any required dependency fails or is cancelled.
  • WaitMode::Any fails only when every dependency has reached a terminal state and none completed successfully.

This makes workflow waits useful for planner/executor systems where one workflow can fan out work, and another can wait on all results or on the first successful branch that finishes.

On this page