Workflow (steps and control flow)
An ordered list of steps. Each step is isolated; pass data explicitly via data:. Control flow is defined by next.
Required keys
- workflow: list of steps
- Each step:
step(unique id), optionaltype(default is an operation; special: start/end)
Step keys (common)
- step: unique name
- desc: human-readable description
- type: http | python | iterator | duckdb | postgres | workbook | playbook | start | end
- data: inputs for the step (templated)
- implementation fields: code | command | sql | endpoint | path | ... (depends on type)
- retry: bounded retry policy (max_attempts, initial_delay, backoff_multiplier, max_delay, retry_when, stop_when) — see
steps/retry.md - assert: input/output contracts (expects, returns)
- sink: persist outputs to variables or storages
- next: transitions to subsequent steps
Transitions (next)
- Single continuation: one entry
- Conditional branches: multiple entries with
when - Parallel fan-out: multiple entries without
when(run concurrently) - Iterator: use
type: iteratorto fan out per item and aggregate - Playbook composition:
type: playbookexecutes another playbook (modular reuse); optionalreturn_stepselects a sub-result - Retry: after each attempt the runtime evaluates
stop_whenthenretry_when; schedules another attempt if conditions match and attempt < max_attempts
Data passing rules
data:is evaluated before the step executes and passed into the step implementation- Step result becomes
this.dataduring the step and<step>.dataafterwards - Use
savewhen you need named variables or external persistence
Common patterns (fragments)
- Linear chain: start → A → B → end
- Conditional route: branch on a flag (true/false)
- Parallel fan-out: two or more next steps with no conditions
- Per-item loop: iterator over a list with inner task and aggregated save
Tips
- Keep step names stable for easy referencing (
<step>.data). - Use
assertfor clearer failures and contract validation. - Prefer small steps with single responsibility for better reuse and testing.
Minimal structure:
workflow:
- step: start
next:
- step: my_step
- step: my_step
type: python
data:
some_input: "{{ workload.message }}"
code: |
def main(some_input):
return {"echo": some_input}
next:
- step: end
- step: end
type: end