Skip to content

Workflow YAML Schema Reference

Complete specification for workflow YAML files.

Overview

Workflows are YAML files that define multi-step tasks for agents to execute. They use a recursive task tree structure with Jinja2 template support.

Top-Level Workflow Fields

name: string                    # Required
description: string             # Required
requires_workdir: boolean       # Optional, default: false
agent_lifecycle: string         # Optional, default: "reuse"
params: object                  # Optional
subtasks: object                # Required

name

Type: string

Required: Yes

Description: Unique identifier for the workflow.

Example:

name: deep-research

description

Type: string

Required: Yes

Description: Human-readable description of what the workflow does.

Example:

description: Performs iterative deep research on a topic

requires_workdir

Type: boolean

Required: No

Default: false

Description: If true, workflow requires --dir to be specified.

Example:

requires_workdir: true

agent_lifecycle

Type: string

Required: No

Default: "reuse"

Values:

  • reuse - Keep agent running, maintain context
  • refresh - Restart agent between tasks

Description: Controls agent server lifecycle during workflow execution.

Example:

agent_lifecycle: refresh

params

Type: object

Required: No

Description: Dictionary of parameter definitions.

Structure:

params:
  param_name:
    range: string        # Type: string or integer
    required: boolean    # Required: true or false

Example:

params:
  query:
    range: string
    required: true
  max_iterations:
    range: integer
    required: false

subtasks

Type: object

Required: Yes

Description: Dictionary of tasks to execute.

Structure:

subtasks:
  task_name:
    # Task fields (see Task Schema below)

Task Schema

Tasks can be defined at any level of the hierarchy. Each task is a dictionary with the following possible fields:

task_name:
  instructions: string              # Optional (terminal node)
  subtasks: object                  # Optional (branch node)
  loop_until: object                # Optional
  success_criteria: object          # Optional
  provider_call: object             # Optional (alternative to instructions)

instructions

Type: string

Required: No (unless no subtasks or provider_call)

Description: Instructions for the agent. Supports Jinja2 templating. Must include completion status.

Example:

instructions: |
  Research {{query}}.
  Write findings to research.md
  COMPLETION_STATUS: COMPLETE

subtasks

Type: object

Required: No

Description: Nested tasks (recursive structure).

Example:

subtasks:
  phase1:
    instructions: "Do phase 1. COMPLETION_STATUS: COMPLETE"
  phase2:
    instructions: "Do phase 2. COMPLETION_STATUS: COMPLETE"

loop_until

Type: object

Required: No

Description: Configuration for looping tasks.

Structure:

loop_until:
  status: string    # Required: status to wait for
  message: string   # Optional: additional instructions

Example:

loop_until:
  status: CONVERGED
  message: |
    If converged, yield status: CONVERGED

success_criteria

Type: object

Required: No

Description: Python code to validate task success.

Structure:

success_criteria:
  python: string       # Required: Python code to execute
  max_retries: integer # Required: number of retries

The Python code must set a variable named result to True or False.

Example:

success_criteria:
  python: |
    import os
    result = os.path.exists("output.txt")
  max_retries: 2

provider_call

Type: object

Required: No (mutually exclusive with instructions)

Description: Call external provider instead of using agent.

Structure:

provider_call:
  provider: string      # Required: provider name
  method: string        # Required: method to call
  params: object        # Required: method parameters
  output_file: string   # Optional: save output to file

Example:

provider_call:
  provider: deep-research-client
  method: research
  params:
    query: "{{topic}}"
    provider: openai
    model: o3-mini
    use_cache: true
  output_file: "research.md"

Complete Example

name: comprehensive-research
description: Multi-stage research with validation

requires_workdir: true
agent_lifecycle: reuse

params:
  topic:
    range: string
    required: true
  max_depth:
    range: integer
    required: false

subtasks:
  # Provider call for initial research
  initial_research:
    provider_call:
      provider: deep-research-client
      method: research
      params:
        query: "{{topic}}"
        use_cache: true
      output_file: "initial.md"

    success_criteria:
      python: |
        import os
        result = os.path.exists("initial.md") and os.path.getsize("initial.md") > 100
      max_retries: 1

  # Agent-based deep dive
  deep_dive:
    subtasks:
      analysis:
        instructions: |
          Read initial.md
          Perform deep analysis on {{topic}}
          Write to analysis.md
          COMPLETION_STATUS: COMPLETE

      synthesis:
        instructions: |
          Read analysis.md
          Create synthesis in synthesis.md
          COMPLETION_STATUS: COMPLETE

  # Iterative refinement
  refine:
    instructions: |
      Review synthesis.md
      Refine and improve
      {% if max_depth %}
      Aim for depth level: {{max_depth}}
      {% endif %}

      If refined enough: COMPLETION_STATUS: DONE
      Otherwise: COMPLETION_STATUS: CONTINUE

    loop_until:
      status: DONE
      message: "When refined enough, yield status: DONE"

  # Final validation
  finalize:
    instructions: |
      Create final report in REPORT.md
      COMPLETION_STATUS: COMPLETE

    success_criteria:
      python: |
        import os
        result = os.path.exists("REPORT.md")
      max_retries: 0

Template Variables

All instructions and provider_call.params fields support Jinja2 templating.

Available Variables

User-defined parameters:

params:
  my_param:
    range: string
    required: true

Use in templates:

instructions: "Process {{my_param}}"

Built-in variables:

  • {{agent_type}} - From --agent-type flag
  • {{skip_permissions}} - From --skip-permissions flag (boolean)

Jinja2 Features

Variables:

instructions: "Research {{topic}}"

Conditionals:

instructions: |
  {% if environment == "production" %}
  Deploy carefully
  {% else %}
  Deploy freely
  {% endif %}

Loops:

instructions: |
  {% for item in items.split(',') %}
  - {{ item }}
  {% endfor %}

Filters:

instructions: "File: {{filename | upper}}"

Defaults:

instructions: "Output: {{output_file | default('output.txt')}}"

Execution Order

Tasks execute in depth-first order:

subtasks:
  A:
    subtasks:
      B:
        instructions: "Task B"  # Executes 1st
      C:
        instructions: "Task C"  # Executes 2nd
  D:
    instructions: "Task D"      # Executes 3rd

Order: B → C → D

Completion Status

All instructions must include a completion status. The agent must output this exact string to signal completion.

Default:

COMPLETION_STATUS: COMPLETE

Custom (for loops):

COMPLETION_STATUS: CONVERGED
COMPLETION_STATUS: DONE
COMPLETION_STATUS: MAX_ITERATIONS

The status appears after loop_until.status or defaults to COMPLETE.

Validation

Workflow files are validated against a Pydantic schema. Common errors:

Missing required fields:

# Error: 'description' is required
name: my-workflow
subtasks: {}

Invalid parameter type:

# Error: range must be 'string' or 'integer'
params:
  x:
    range: float  # Not supported

No terminal nodes:

# Error: Task has neither instructions nor subtasks
subtasks:
  task1:
    loop_until:
      status: DONE

Both instructions and provider_call:

# Error: Mutually exclusive
subtasks:
  task1:
    instructions: "Do something"
    provider_call:
      provider: x

Best Practices

  1. Always include completion status:
instructions: |
  Do work
  COMPLETION_STATUS: COMPLETE  # Required!
  1. Use descriptive task names:
# Good
subtasks:
  gather_requirements:
  design_architecture:
  implement_features:

# Bad
subtasks:
  task1:
  task2:
  task3:
  1. Keep tasks focused:
# Good - focused tasks
subtasks:
  research:
    instructions: "Research topic. COMPLETION_STATUS: COMPLETE"
  analyze:
    instructions: "Analyze findings. COMPLETION_STATUS: COMPLETE"

# Bad - too broad
subtasks:
  do_everything:
    instructions: "Research, analyze, write report. COMPLETION_STATUS: COMPLETE"
  1. Use success criteria for critical tasks:
subtasks:
  critical_task:
    instructions: "Generate config.json. COMPLETION_STATUS: COMPLETE"
    success_criteria:
      python: |
        import json, os
        if not os.path.exists("config.json"):
          result = False
        else:
          with open("config.json") as f:
            json.load(f)  # Validate JSON
          result = True
      max_retries: 2
  1. Document complex templates:
instructions: |
  {# This template processes multiple input files #}
  {% for file in files.split(',') %}
  Process {{ file.strip() }}
  {% endfor %}
  COMPLETION_STATUS: COMPLETE

See Also