Skip to main content

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

  • run for deterministic commands and file discovery.
  • prompt for short, single-turn reasoning.
  • structured_prompt when you need structured JSON for follow-up steps
  • agent for 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: true when 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.