Skip to content

Pipelines

A pipeline is a multi-step AI workflow where each step runs one persona in an isolated workspace. Pipelines enable complex AI workflows by breaking tasks into focused steps with clear boundaries.

yaml
kind: WavePipeline
metadata:
  name: ops-pr-review
steps:
  - id: analyze
    persona: navigator
    exec:
      type: prompt
      source: "Analyze: {{ input }}"

Use pipelines when you need coordinated AI tasks that build on each other's outputs.

Pipeline Structure

Every pipeline has three main sections:

SectionPurpose
metadataName, description, and pipeline identity
inputHow the pipeline receives its input
stepsThe sequence of AI tasks to execute

Dependency Patterns

Linear Dependencies

Steps execute in sequence when dependencies are specified:

yaml
steps:
  - id: analyze
    persona: navigator
    exec:
      type: prompt
      source: "Analyze the codebase for: {{ input }}"
    output_artifacts:
      - name: analysis
        path: .wave/output/analysis.json

  - id: implement
    persona: craftsman
    dependencies: [analyze]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: analyze
          artifact: analysis
          as: context
    exec:
      type: prompt
      source: "Implement based on the analysis."

Parallel Execution (Fan-Out)

Steps without mutual dependencies run in parallel. This pattern is useful when you need multiple perspectives on the same input:

yaml
steps:
  - id: navigate
    persona: navigator
    exec:
      type: prompt
      source: "Analyze: {{ input }}"
    output_artifacts:
      - name: analysis
        path: .wave/output/analysis.json
        type: json

  - id: security
    persona: auditor
    dependencies: [navigate]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: navigate
          artifact: analysis
          as: context
    exec:
      type: prompt
      source: "Security review"
    output_artifacts:
      - name: findings
        path: .wave/output/security.md
        type: markdown

  - id: quality
    persona: auditor
    dependencies: [navigate]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: navigate
          artifact: analysis
          as: context
    exec:
      type: prompt
      source: "Quality review"
    output_artifacts:
      - name: findings
        path: .wave/output/quality.md
        type: markdown

In this example, security and quality run in parallel after navigate completes.

Convergence (Fan-In)

Multiple parallel steps can feed into a single summary step:

yaml
steps:
  # ... parallel steps above ...

  - id: summary
    persona: summarizer
    dependencies: [security, quality]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: security
          artifact: findings
          as: security_report
        - step: quality
          artifact: findings
          as: quality_report
    exec:
      type: prompt
      source: "Synthesize all findings into a final report"

Independent Parallel Tracks

When two or more step sequences have no shared upstream dependency, they run as fully independent parallel tracks from the start. This is distinct from fan-out, where parallel steps share a common ancestor. Independent tracks converge only at a final merge step:

yaml
steps:
  # Track A — starts immediately
  - id: quality-scan
    persona: navigator
    exec:
      type: prompt
      source: "Scan for code quality issues: {{ input }}"
    output_artifacts:
      - name: quality_scan
        path: .wave/output/quality-scan.json
        type: json

  - id: quality-detail
    persona: navigator
    dependencies: [quality-scan]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: quality-scan
          artifact: quality_scan
          as: scan_results
    exec:
      type: prompt
      source: "Deepen the quality analysis"
    output_artifacts:
      - name: quality_report
        path: .wave/output/quality-detail.md
        type: markdown

  # Track B — starts immediately (no dependency on Track A)
  - id: audit-security
    persona: navigator
    exec:
      type: prompt
      source: "Scan for security vulnerabilities: {{ input }}"
    output_artifacts:
      - name: security_scan
        path: .wave/output/audit-security.json
        type: json

  - id: security-detail
    persona: navigator
    dependencies: [audit-security]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: audit-security
          artifact: security_scan
          as: scan_results
    exec:
      type: prompt
      source: "Deepen the security analysis"
    output_artifacts:
      - name: security_report
        path: .wave/output/security-detail.md
        type: markdown

  # Merge — converges both tracks
  - id: merge
    persona: summarizer
    dependencies: [quality-detail, security-detail]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: quality-detail
          artifact: quality_report
          as: quality_findings
        - step: security-detail
          artifact: security_report
          as: security_findings
    exec:
      type: prompt
      source: "Synthesize quality and security findings"

In this example, Track A (quality-scanquality-detail) and Track B (audit-securitysecurity-detail) run simultaneously from the start. The merge step waits for both tracks to complete before synthesizing results.

See .wave/pipelines/dual-analysis.yaml for a complete working example of this pattern.

Dependency Visualization

The following diagram shows how dependencies create the execution flow:

The independent parallel tracks pattern creates a different topology — two tracks with no shared ancestor:

Verifying Parallel Execution

Wave provides several ways to confirm that steps executed concurrently rather than sequentially.

Audit Logs

Each pipeline run produces timestamped events in .wave/traces/. Look for STEP_START and STEP_END entries with RFC 3339 timestamps:

2026-01-15T10:00:01.123Z  STEP_START  quality-scan
2026-01-15T10:00:01.456Z  STEP_START  audit-security    ← started ~300ms later
2026-01-15T10:00:15.789Z  STEP_END    quality-scan
2026-01-15T10:00:18.234Z  STEP_END    audit-security
2026-01-15T10:00:18.567Z  STEP_START  merge             ← started after both ended

Overlapping STEP_START/STEP_END intervals prove the steps ran concurrently. If audit-security started before quality-scan ended, they were running in parallel.

Status Display

The wave status command shows per-step elapsed timers. When steps run concurrently, you will see multiple steps in running state simultaneously:

bash
wave status <run-id>

JSON Logs

For machine-parseable verification, use JSON-formatted logs:

bash
wave logs --format json <run-id>

Each event includes a nanosecond-precision timestamp. Compare step_start times across independent steps — timestamps within the same second confirm concurrent scheduling by the DAG executor.

Artifact Patterns

Artifacts are the primary mechanism for passing data between steps.

Producing Artifacts

Declare what a step outputs:

yaml
output_artifacts:
  - name: analysis        # Artifact identifier
    path: .wave/output/data.json  # Where the step writes it
    type: json             # Type hint for consumers

Consuming Artifacts

Inject artifacts from previous steps:

yaml
memory:
  strategy: fresh
  inject_artifacts:
    - step: analyze      # Source step
      artifact: analysis  # Artifact name
      as: context         # Mount name in workspace

The artifact appears at .wave/artifacts/<as-name> in the step's workspace.

Artifact Types

TypeDescriptionBest For
jsonStructured dataAnalysis results, configs
markdownFormatted textReports, documentation
fileSingle fileCode, configs
directoryFolderMultiple files, assets

Multi-Artifact Injection

A step can consume multiple artifacts:

yaml
memory:
  strategy: fresh
  inject_artifacts:
    - step: analyze
      artifact: code_analysis
      as: code
    - step: security
      artifact: findings
      as: security
    - step: quality
      artifact: findings
      as: quality

All artifacts are available under .wave/artifacts/:

  • .wave/artifacts/code
  • .wave/artifacts/security
  • .wave/artifacts/quality

Memory Strategies

Control how context flows between steps:

StrategyBehaviorUse When
freshClean slate, only injected artifactsMost cases (recommended)
inheritCarry forward previous contextContinuation tasks

Fresh memory is recommended to prevent context pollution and ensure reproducible results.

Outcomes

Outcomes extract structured results — such as PR URLs, issue links, or deployment URLs — from step artifacts into the pipeline output summary. Declare outcomes on any step that produces a JSON artifact containing values you want to surface.

yaml
steps:
  - id: create-pr
    persona: craftsman
    exec:
      type: prompt
      source: "Create a pull request"
    output_artifacts:
      - name: result
        path: .wave/output/result.json
        type: json
    outcomes:
      - type: pr
        extract_from: .wave/output/result.json
        json_path: ".pr_url"
        label: "Pull Request"

Supported outcome types: pr, issue, url, deployment. See Outcomes for array extraction, field reference, and advanced examples.

Running Pipelines

Execute a pipeline with input:

bash
wave run ops-pr-review "Review authentication changes"

Check pipeline status:

bash
wave status ops-pr-review

View artifacts from a run:

bash
wave artifacts <run-id>

Complete Example

A production-ready code review pipeline:

yaml
kind: WavePipeline
metadata:
  name: ops-pr-review
  description: "Multi-perspective code review with security and quality checks"

input:
  source: cli

steps:
  - id: diff-analysis
    persona: navigator
    workspace:
      mount:
        - source: ./
          target: /src
          mode: readonly
    exec:
      type: prompt
      source: |
        Analyze the changes: {{ input }}
        Output as JSON with files, modules, and breaking changes.
    output_artifacts:
      - name: diff
        path: .wave/output/diff.json
        type: json

  - id: security-review
    persona: auditor
    dependencies: [diff-analysis]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: diff-analysis
          artifact: diff
          as: changes
    exec:
      type: prompt
      source: "Review .wave/artifacts/changes for security vulnerabilities"
    output_artifacts:
      - name: security
        path: .wave/output/security.md
        type: markdown

  - id: quality-review
    persona: auditor
    dependencies: [diff-analysis]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: diff-analysis
          artifact: diff
          as: changes
    exec:
      type: prompt
      source: "Review .wave/artifacts/changes for code quality issues"
    output_artifacts:
      - name: quality
        path: .wave/output/quality.md
        type: markdown

  - id: final-verdict
    persona: summarizer
    dependencies: [security-review, quality-review]
    memory:
      strategy: fresh
      inject_artifacts:
        - step: security-review
          artifact: security
          as: security_findings
        - step: quality-review
          artifact: quality
          as: quality_findings
    exec:
      type: prompt
      source: |
        Synthesize findings into: APPROVE / REQUEST_CHANGES / NEEDS_DISCUSSION
    output_artifacts:
      - name: verdict
        path: .wave/output/verdict.md
        type: markdown

Next Steps

Released under the MIT License.