Overview
Workflow nodes let you execute another SirenSpec workflow inline as part of a parent workflow — enabling sub-workflow composition, code reuse, and modular workflow design. Any node withtype: workflow is a workflow node.
When to Use Workflow Nodes
Use workflow nodes when:- You want to compose modular, reusable workflow templates
- A sub-workflow is shared across multiple parent workflows
- You need to organize complex workflows into logical stages (e.g., extraction → analysis → synthesis)
- You want to keep workflow files small and maintainable
- You want to run tasks in parallel (use
swrmorfactoryinstead) - You are passing the entire workflow context to a sub-workflow (inputs isolate the sub-workflow’s context by design)
Node Fields
| Field | Required | Type | Default | Description |
|---|---|---|---|---|
type | Yes | "workflow" | — | Node type discriminator. Must be "workflow". |
ref | Yes | string | — | File path or registry name for the sub-workflow. |
inputs | No | dict[string, string] | {} | Template strings bound to the sub-workflow’s input context. |
writes | No | string | "output.<node_id>" | Dot-notation path where sub-workflow output is written. |
max_depth | No | integer | 10 | Maximum nesting depth before a ValidationError is raised. |
ref — Sub-Workflow Reference
The ref field accepts two forms:
File Path
Relative or absolute paths to YAML workflow files, resolved at execution time:Named Registry Entry
If aWorkflowRegistry is passed to execute(), ref can be a string name:
inputs — Context Isolation
The sub-workflow’s context is initialized with only the keys declared in inputs. It does not inherit the parent’s full working context.
This ensures clean separation of concerns: the sub-workflow only sees what the parent explicitly passes.
inputs are resolved against the parent’s context:
{{ extract.output }}— reads from the parent’sworkingoroutput{{ env.API_KEY }}— reads environment variables{{ inputs.message }}— reads the parent’s initial input
inputs.
Inside the sub-workflow, these become available under {{ inputs.* }}:
writes — Output Path
By default, sub-workflow output is written to output.<node_id>. You can customize this path:
writes, output is written to output.run_child:
writes: output.analysis, output is written to output.analysis:
max_depth — Nesting Limit
Workflow nodes can nest arbitrarily deep, but to prevent infinite recursion cycles, each node has a nesting depth limit (default: 10).
max_depth, execution raises ValidationError:
Output Shape
After a workflow node executes, its sub-workflow’s output dict is written to the parent context:| Context path | Value |
|---|---|
output.<node_id> (or custom writes path) | The sub-workflow’s output dict (keys are sub-node IDs). |
working.<node_id>.sub_workflow_trace | The sub-workflow’s full execution trace. |
Accessing Sub-Node Outputs
Reference individual sub-node outputs via the output dict:Complete Example
Parent workflow (parent.yaml):
analysis.yaml):
Error Handling
ValidationError: Max workflow nesting depth exceeded
Raised when a workflow node is executed at a depth >= its max_depth. Increase max_depth or simplify your workflow nesting:
FileNotFoundError
Raised when ref points to a non-existent file:
KeyError
Raised when ref is a named string and the registry does not contain that workflow:
Template Interpolation
Ininputs values, you can use all standard SirenSpec template syntax:
| Placeholder | Description |
|---|---|
{{ inputs.message }} | The parent’s initial input message. |
{{ working.* }} | Any value written to the parent’s working context. |
{{ output.* }} | Any value written to the parent’s output context. |
{{ env.VAR_NAME }} | Environment variables. |
JSON Schema
Thesirenspec.schema.json artifact validates workflow nodes inline with other node types. IDEs that support JSON Schema (VS Code, JetBrains) will autocomplete type: workflow, ref, and all fields automatically.