Writing great workflows
Good workflows combine deterministic steps with focused agent work. Aim for small, clear steps that you can verify.
Choose the right step type
runfor deterministic commands and file discovery.promptfor short, single-turn reasoning.structured_promptwhen you need structured JSON for follow-up stepsagentfor multi-step work that touches the codebase.
Use extends: Coding for code changes. Use extends: Default for planning, reporting, or coordination.
Make deterministic work deterministic
If a command can produce a list of files or checks, do it with run and pass the results to the agent.
- id: scan
run: find src -name "*.ts"
- name: Plan the changes
structured_prompt:
prompt: "Group the files by migration pattern."
schema:
type: object
required: [patterns]
properties:
patterns:
type: array
items:
type: object
required: [name, files]
properties:
name: { type: string }
files:
type: array
items: { type: string }
Break large work into small units
Use for_each to keep agent work scoped to a single item.
- name: Apply changes per file
for_each:
from: '{{ outputs.scan | split(pat="\n") | filter(value) | json_encode() }}'
agent:
extends: Coding
instructions: "Update only {{ for_each.value }}."
constraints:
- "Do not edit unrelated files."
Use artifacts to lock in decisions
When a step produces a plan or guide you want to reuse, save it as an artifact. This keeps the workflow stable across runs and makes review easier.
Design for failure
- Use
continue_on_error: truewhen a single failure should not block the run. - Inspect
errors.<step>to decide what to retry. - Use task graphs to branch on success or failure.
- id: migrate_one
agent:
extends: Coding
instructions: "Migrate the module."
continue_on_error: true
- id: summarize_failures
prompt: |
Failed items: {{ errors.migrate_one | json_encode(pretty=true) }}
Keep templates readable
Prefer step ids over numeric indexes so templates stay stable when you reorder steps.