Skip to content

Control flow

Calls another workflow in the same .rigg project.

- id: call_plan
type: workflow
with:
workflow: plan
inputs:
requirements: ${{ inputs.requirements }}
retries: 3
dry_run: true

Rules:

  • with.workflow must be a static string, not a template expression
  • String with.inputs values are rendered as templates in the caller scope
  • Boolean and number YAML literals pass through unchanged
  • steps.call_plan.result is null in v1, so workflows stay independent and share data through files or git state
  • workflow steps may set retry

Encapsulates steps into a single unit. Internal results are hidden unless exported.

- id: gather
type: group
steps:
- id: log
type: shell
with:
command: git log --oneline -20
stdout:
mode: text
- id: diff
type: shell
with:
command: git diff --stat
stdout:
mode: text
exports:
log: ${{ steps.log.result }}
diff: ${{ steps.diff.result }}

Downstream steps access exports via steps.gather.result.log.

Repeats until a condition is met or max iterations is reached.

- id: fix_loop
type: loop
max: 5 # required
until: ${{ len(steps.review.result.findings) == 0 }} # optional
steps:
- id: review
type: codex
with:
kind: review
target:
type: uncommitted
- id: fix
if: ${{ len(steps.review.result.findings) > 0 }}
retry:
max: 3
delay: 1s
type: codex
with:
kind: turn
prompt: |
Address these review findings:
${{ toJSON(steps.review.result) }}
exports:
clean: ${{ len(steps.review.result.findings) == 0 }}

Loop results always include reason.

  • until_satisfied when until becomes truthy
  • max_reached when the loop uses all iterations

Inside loop bodies, run.* variables are available:

VariableTypeDescription
run.iterationintegerCurrent iteration (1-based)
run.max_iterationsintegerMax iterations value
run.node_pathstringNode path

Conditional execution. First matching if wins. else is the fallback.

- id: decide
type: branch
cases:
- if: ${{ steps.check.result == 'needs_fix' }}
steps:
- id: fix
type: codex
with:
kind: turn
prompt: Fix the issues.
exports:
outcome: ${{ steps.fix.result }}
- else:
steps: []
exports:
outcome: "no issues"

Rules:

  • All cases must export the same shape, or none at all
  • If any case has exports, an else case is required
  • else: takes no value (just else: on its own)

Runs branches concurrently.

- id: checks
type: parallel
branches:
- id: unit # id required on each branch
steps:
- id: run_unit
type: shell
with:
command: npm test
- id: lint
steps:
- id: run_lint
type: shell
with:
command: npm run lint
exports:
unit: ${{ steps.run_unit.result }}
lint: ${{ steps.run_lint.result }}

Exports can reference steps.* from any branch in the parallel block.

All control flow types (group, loop, branch, parallel) support exports to promote internal results to the outer scope:

exports:
name: ${{ steps.inner_step.result }}
field: ${{ steps.inner_step.result.nested_field }}

Without exports, inner step results are invisible to downstream steps.