Control Flow
Pipelines aren't always a straight line. Sometimes you need to run two things at once, choose between paths, or repeat a step for each item in a list. That's what control flow is for.
The good news: you don't need to configure anything for the basic case. Steps just run in order, one after another. Control flow is only needed when you want something different from that default.
Sequential execution (the default)
ImplementedSteps run in declaration order. The second step starts when the first finishes. No configuration needed.
steps:
- name: fetch_profile
action: ai
prompt: "Describe the career history of {{ input.person_name }}"
- name: write_bio
action: ai
prompt: "Write a LinkedIn bio based on this profile: {{ fetch_profile.text }}"write_bio waits for fetch_profile to complete, then uses its output. That's it.
Parallel branches
Spec OnlyBut what if two steps don't depend on each other? Running them sequentially wastes time. Use parallel to run them simultaneously.
steps:
- name: research
parallel:
- name: search_web
action: ai
prompt: "Find recent news about {{ input.topic }}"
- name: search_papers
action: ai
prompt: "Find academic papers about {{ input.topic }}"
- name: synthesize
action: ai
prompt: |
Synthesize these sources:
News: {{ search_web.text }}
Papers: {{ search_papers.text }}search_web and search_papers run at the same time. synthesize waits for both to finish before starting.
Not yet implemented
Parallel execution is defined in the spec but not yet implemented in the JigSpec runtime. Steps run sequentially even when listed in a parallel block. This is on the roadmap.
Conditional routing
Spec OnlyWhat if you need to choose which path to take based on data? Use condition.
steps:
- name: classify
action: ai
prompt: "Classify this ticket as 'bug', 'feature', or 'question': {{ input.message }}"
- name: route
use: condition
when:
- if: "{{ classify.text }} == 'bug'"
then: handle_bug
- if: "{{ classify.text }} == 'feature'"
then: handle_feature
- else: answer_question
- name: handle_bug
action: ai
prompt: "Write a bug report template for: {{ input.message }}"
- name: handle_feature
action: ai
prompt: "Write a feature request for: {{ input.message }}"
- name: answer_question
action: ai
prompt: "Answer this question helpfully: {{ input.message }}"The condition step evaluates the when clauses in order. The first match wins. else is the fallback.
Not yet implemented
Conditional routing is defined in the spec but not yet implemented in the JigSpec runtime. This is on the roadmap.
Loops
Spec OnlyWhat if you need to apply the same step to every item in a list? Use loop.
steps:
- name: extract_items
action: ai
prompt: "Extract the line items from this invoice as a JSON list: {{ input.invoice_text }}"
output_schema:
items:
- description: string
amount: number
- name: categorize_each
use: loop
over: "{{ extract_items.data.items }}"
as: item
max_loops: 50
step:
name: categorize
action: ai
prompt: "Categorize '{{ item.description }}' as: office, travel, software, or equipment"For each item in the list, the inner step runs with item bound to that element. The loop collects all results.
Output: categorize_each.results — an array of each iteration's output.
The max_loops field is a safety limit. If the input list is longer than max_loops, the pipeline fails with a clear error rather than running indefinitely.
Not yet implemented
Loops are defined in the spec but not yet implemented in the JigSpec runtime. This is on the roadmap.
Start simple
Most pipelines only need sequential execution. Reach for parallel, conditional, and loops only when the simpler approach has a real cost — an extra second of latency, an untaken branch, a repeated prompt.