Config-as-Code Pipelines

Define multi-agent workflows in .caged.yaml — with DAG-based execution, crash recovery, cost gates, and human approval checkpoints.

Why Agent Pipelines?

Unlike traditional CI/CD (shell commands running builds), agent pipelines let AI agents do the work:

Traditional CI Agent Pipeline
npm run build Agent writes the code
npm test Agent writes + runs tests
Human creates PR Agent creates the PR
Human reviews Agent reviews, human approves
Fixed commands Dynamic prompts with context

Define these workflows alongside your code — version controlled, reproducible, portable.

Agent Stage Type

The type: agent stage is the core building block. It spawns a sandbox, runs an AI agent inside, and captures the session:

stages:
  - name: implement-feature
    type: agent
    agent: claude-code  # or: aider, codex, custom
    prompt: |
      Implement the following feature:
      $FEATURE_SPEC
      
      Requirements:
      - Write production-quality code
      - Add unit tests with >80% coverage
      - Commit changes with conventional commits
    template: node-20
    timeout: 30m
    budget: 15.00

The agent runs autonomously until it completes, hits the budget, or times out.

Real-World Example: Feature Implementation

An AI agent implements a feature, another agent reviews it, human approves, then it deploys:

# .caged.yaml — Agent-driven feature implementation
template: node-20
budget: 50.00
network_mode: allowlist
allowed_hosts:
  - api.anthropic.com
  - api.openai.com
  - github.com
  - registry.npmjs.org
secrets:
  - ANTHROPIC_API_KEY
  - GITHUB_TOKEN

pipelines:
  - name: agent-feature
    description: AI agents implement features with human oversight
    
    stages:
      # 1. Agent implements the feature
      - name: implement
        type: agent
        agent: claude-code
        prompt: |
          Implement the following feature:
          $FEATURE_SPEC
          
          Requirements:
          - Write production-quality code
          - Add unit tests with >80% coverage
          - Update documentation
          - Commit changes with conventional commits
        template: node-20
        timeout: 30m
        budget: 15.00

      # 2. Different agent reviews the implementation
      - name: code-review
        type: agent
        agent: aider
        prompt: |
          Review the changes made in the previous step:
          - Check for bugs, security issues, performance problems
          - Verify tests are comprehensive
          - Suggest improvements
          
          Output a structured review with PASS/FAIL and findings.
        depends_on: [implement]
        timeout: 10m
        budget: 5.00

      # 3. Run automated tests
      - name: test
        type: command
        command: npm test && npm run lint
        depends_on: [code-review]

      # 4. Gates before human review
      - name: quality-gate
        type: gate
        depends_on: [test]
        gate:
          trust_above: 70
          cost_below: 25.00

      # 5. Human reviews agent's work
      - name: human-approval
        type: await_approval
        depends_on: [quality-gate]
        approval:
          message: |
            Agent implemented: $FEATURE_SPEC
            
            Review the diff and agent's work before merging.
          channels: [slack, dashboard]
          sla_timeout: 24h

      # 6. Agent addresses feedback if any
      - name: address-feedback
        type: agent
        agent: claude-code
        prompt: |
          Address the reviewer's feedback:
          $APPROVAL_COMMENTS
        depends_on: [human-approval]
        condition:
          if: approval.has_comments
        timeout: 15m

      # 7. Merge and deploy
      - name: merge
        type: command
        command: git push origin HEAD:main
        depends_on: [address-feedback]

This pipeline:

  1. Agent implements — Claude Code writes code, tests, docs
  2. Agent reviews — Different agent (Aider) provides independent review
  3. Tests run — Automated validation
  4. Gates check — Trust score and cost limits
  5. Human approves — Review the agent's work
  6. Agent fixes — Address any feedback
  7. Merge — Ship it

More Real-World Pipelines

Bug Fix Pipeline

Agent investigates and fixes bugs from issue tracker:

pipelines:
  - name: bug-fix
    description: Agent investigates and fixes reported bugs
    
    stages:
      - name: investigate
        type: agent
        agent: claude-code
        prompt: |
          Investigate this bug report:
          $BUG_REPORT
          
          1. Reproduce the issue
          2. Find the root cause
          3. Document your findings
        timeout: 15m
        budget: 5.00

      - name: fix
        type: agent
        agent: claude-code
        prompt: |
          Fix the bug you investigated.
          - Write a failing test first
          - Implement the fix
          - Verify all tests pass
          - Commit with "fix: <description>"
        depends_on: [investigate]
        timeout: 20m
        budget: 10.00

      - name: verify
        type: command
        command: npm test
        depends_on: [fix]

      - name: approve
        type: await_approval
        depends_on: [verify]
        approval:
          message: "Bug fix ready for review. See diff."
          sla_timeout: 4h

Dependency Upgrade Pipeline

Agent upgrades dependencies and fixes breaking changes:

pipelines:
  - name: dep-upgrade
    description: Agent upgrades dependencies safely
    
    stages:
      - name: audit
        type: command
        command: npm audit --json > audit.json
        
      - name: upgrade
        type: agent
        agent: claude-code
        prompt: |
          Upgrade all dependencies with known vulnerabilities.
          Read audit.json for the list.
          
          For each upgrade:
          1. Update package.json
          2. Run npm install
          3. Fix any breaking changes
          4. Ensure tests pass
          5. Commit each upgrade separately
        depends_on: [audit]
        timeout: 45m
        budget: 20.00

      - name: test
        type: command
        command: npm test
        depends_on: [upgrade]

      - name: approve
        type: await_approval
        depends_on: [test]
        approval:
          message: "Dependency upgrades complete. Review changes."

Code Migration Pipeline

Agent migrates codebase (e.g., JS → TypeScript):

pipelines:
  - name: migrate-to-typescript
    description: Agent converts JavaScript to TypeScript
    
    stages:
      - name: setup
        type: command
        command: |
          npm install typescript @types/node --save-dev
          npx tsc --init
          
      - name: migrate-batch
        type: agent
        agent: claude-code
        prompt: |
          Convert the next batch of JavaScript files to TypeScript.
          
          Rules:
          - Convert 5-10 files per batch
          - Add proper type annotations
          - Fix any type errors
          - Ensure tests still pass
          - Track progress in MIGRATION.md
        depends_on: [setup]
        timeout: 30m
        budget: 15.00
        
      - name: typecheck
        type: command
        command: npx tsc --noEmit
        depends_on: [migrate-batch]

      - name: review-batch
        type: await_approval
        depends_on: [typecheck]
        approval:
          message: "Migration batch complete. Review before continuing."
          sla_timeout: 2h

      - name: continue-or-done
        type: gate
        depends_on: [review-batch]
        gate:
          # Custom expression: continue if JS files remain
          if: "count(glob('**/*.js')) > 0"

Parallel Agent Execution

Run multiple agents in parallel when they don't depend on each other:

pipelines:
  - name: multi-agent-review
    stages:
      - name: implement
        type: agent
        agent: claude-code
        prompt: "Implement feature: $FEATURE_SPEC"
        timeout: 30m

      # These three run in parallel after implement
      - name: security-review
        type: agent
        agent: claude-code
        prompt: "Security review: Check for vulnerabilities, injection risks, auth issues."
        depends_on: [implement]
        timeout: 10m
        budget: 3.00

      - name: perf-review
        type: agent
        agent: aider
        prompt: "Performance review: Check for N+1 queries, memory leaks, bottlenecks."
        depends_on: [implement]
        timeout: 10m
        budget: 3.00

      - name: test-coverage
        type: agent
        agent: codex
        prompt: "Add missing test cases to achieve 90% coverage."
        depends_on: [implement]
        timeout: 15m
        budget: 5.00

      # Waits for all three reviews
      - name: human-review
        type: await_approval
        depends_on: [security-review, perf-review, test-coverage]
        approval:
          message: "Three agents reviewed. Check their findings."

DAG visualization:

           implement
          /    |    \
   security  perf  coverage
          \    |    /
         human-review

Gate Stages

Add guardrails before expensive operations:

stages:
  - name: implement
    type: agent
    agent: claude-code
    prompt: "Build the feature"
    budget: 20.00

  - name: cost-gate
    type: gate
    depends_on: [implement]
    gate:
      cost_below: 15.00  # Fail if agent spent too much

  - name: trust-gate
    type: gate
    depends_on: [cost-gate]
    gate:
      trust_above: 75  # Fail if trust score dropped

  - name: deploy
    type: command
    command: npm run deploy
    depends_on: [trust-gate]

Gates check instantly:

  • cost_below — fails if run cost exceeds threshold (USD)
  • trust_above — fails if agent trust score is below threshold (0-100)

Pipeline Defaults

Set defaults that apply to all stages:

pipelines:
  - name: agent-pipeline
    defaults:
      template: node-20
      timeout: 20m
      budget: 10.00
      retry:
        max_attempts: 2
        backoff: 30s

    stages:
      - name: task-a
        type: agent
        agent: claude-code
        prompt: "Do task A"
        # Inherits: template, timeout, budget, retry

      - name: task-b
        type: agent
        agent: claude-code
        prompt: "Do task B"
        budget: 25.00  # Override default
        depends_on: [task-a]

Conditional Execution

Run stages only when conditions are met:

stages:
  - name: implement
    type: agent
    agent: claude-code
    prompt: "Implement the feature"

  - name: notify-success
    type: command
    command: |
      curl -X POST $SLACK_WEBHOOK \
        -d '{"text":"✅ Agent completed task successfully!"}'
    depends_on: [implement]
    condition:
      on_success: true

  - name: escalate-failure
    type: command
    command: |
      curl -X POST $PAGERDUTY_WEBHOOK \
        -d '{"message":"Agent failed. Human intervention needed."}'
    depends_on: [implement]
    condition:
      on_failure: true

Triggering Pipelines

Once defined in .caged.yaml, trigger pipelines via CLI or API:

# List pipelines
caged pipeline list

# Start a feature implementation
caged pipeline run agent-feature \
  --env FEATURE_SPEC="Add dark mode toggle to settings page"

# Start a bug fix
caged pipeline run bug-fix \
  --env BUG_REPORT="Users report 500 error on /api/users endpoint"

# Check run status
caged pipeline runs agent-feature

Or via API:

curl -X POST https://api.caged.dev/v1/pipelines/agent-feature/runs \
  -H "Authorization: Bearer caged_sk_..." \
  -d '{
    "env": {
      "FEATURE_SPEC": "Add dark mode toggle to settings page"
    }
  }'

Failure Handling

Control what happens when an agent stage fails:

stages:
  - name: primary-agent
    type: agent
    agent: claude-code
    prompt: "Implement the feature"
    on_failure: continue  # Don't fail pipeline

  - name: fallback-agent
    type: agent
    agent: aider  # Try different agent
    prompt: "Complete the task the previous agent couldn't finish"
    depends_on: [primary-agent]
    condition:
      on_failure: true  # Only run if primary failed

Multiple Pipelines

Define different pipelines for different workflows:

pipelines:
  - name: feature
    description: Agent implements new features
    stages:
      - name: implement
        type: agent
        agent: claude-code
        prompt: "$FEATURE_SPEC"
      # ...

  - name: bugfix
    description: Agent investigates and fixes bugs
    stages:
      - name: investigate
        type: agent
        agent: claude-code
        prompt: "$BUG_REPORT"
      # ...

  - name: refactor
    description: Agent refactors code
    stages:
      - name: analyze
        type: agent
        agent: claude-code
        prompt: "Analyze $TARGET_PATH for refactoring opportunities"
      # ...

Shared State Store

Pipelines include a built-in key/value store for sharing data between stages. Instead of passing artifacts via git branches or external storage, stages can read and write to a shared state store scoped to the run.

Writing State from Stages

Agent stages can write to state using the caged state command available in the sandbox:

stages:
  - name: analyze
    type: agent
    agent: claude-code
    prompt: |
      Analyze the codebase and identify files that need refactoring.
      
      When done, save your findings:
      caged state set analysis_results '{"files": [...], "priority": "high"}'

  - name: refactor
    type: agent
    agent: claude-code
    prompt: |
      Read the analysis results and refactor the identified files.
      
      caged state get analysis_results
    depends_on: [analyze]

State Types

State entries support different types for structured data:

Type Description Example Use
string Plain text Status messages, IDs
json JSON object Analysis results, configs
file File reference Generated artifacts
patch Git diff Code changes for review
artifact Binary data Build outputs
# Saving different types via CLI in stage commands
stages:
  - name: generate
    type: command
    command: |
      # String
      caged state set status "completed"
      
      # JSON
      caged state set config '{"debug": true}' --type json
      
      # File reference
      caged state set build_artifact './dist/app.js' --type file
      
      # Git patch
      git diff HEAD~1 | caged state set changes - --type patch

State TTL and Limits

State entries have automatic expiration and size limits:

  • Max key length: 256 characters
  • Max value size: 1 MB per entry
  • Max entries per run: 100
  • Max total size per run: 10 MB
  • Default TTL: 7 days
  • Max TTL: 30 days

Set custom TTL when writing:

# Expires in 1 hour
caged state set temp_data '{"key": "value"}' --ttl 3600

Accessing State via CLI

# List all state for a run
caged pipeline state list <pipeline-id> <run-id>

# Get a specific entry
caged pipeline state get <pipeline-id> <run-id> analysis_results

# Set a value
caged pipeline state set <pipeline-id> <run-id> review_status '"approved"'

# Set from file
caged pipeline state set <pipeline-id> <run-id> config -f config.json

# Delete an entry
caged pipeline state delete <pipeline-id> <run-id> temp_data

Accessing State via API

# List state
curl https://api.caged.dev/v1/pipelines/{id}/runs/{runId}/state \
  -H "Authorization: Bearer caged_sk_..."

# Get single entry
curl https://api.caged.dev/v1/pipelines/{id}/runs/{runId}/state/analysis_results \
  -H "Authorization: Bearer caged_sk_..."

# Set entry
curl -X PUT https://api.caged.dev/v1/pipelines/{id}/runs/{runId}/state/my_key \
  -H "Authorization: Bearer caged_sk_..." \
  -d '{
    "value": {"result": "success"},
    "type": "json",
    "ttl_seconds": 86400,
    "created_by": "api"
  }'

# Delete entry
curl -X DELETE https://api.caged.dev/v1/pipelines/{id}/runs/{runId}/state/temp_key \
  -H "Authorization: Bearer caged_sk_..."

Multi-Agent Handoff with State

State is essential for multi-agent pipelines where one agent's output becomes another's input:

pipelines:
  - name: ai-code-review
    stages:
      - name: implement
        type: agent
        agent: claude-code
        prompt: |
          Implement: $FEATURE_SPEC
          
          When complete, save a summary:
          caged state set implementation '{"files_changed": [...], "approach": "..."}'
        budget: 15.00

      - name: security-review
        type: agent
        agent: claude-code
        prompt: |
          Review for security issues.
          
          First, get implementation context:
          caged state get implementation
          
          Save your findings:
          caged state set security_review '{"issues": [...], "severity": "..."}'
        depends_on: [implement]
        budget: 5.00

      - name: compile-report
        type: command
        command: |
          # Combine all reviews into final report
          impl=$(caged state get implementation)
          security=$(caged state get security_review)
          echo "{\"implementation\": $impl, \"security\": $security}" > report.json
          caged state set final_report -f report.json
        depends_on: [security-review]

      - name: human-approval
        type: await_approval
        depends_on: [compile-report]
        approval:
          message: "Review complete. See state entry: final_report"

State keeps agents loosely coupled — they communicate through shared state rather than direct dependencies.


Full Example

A complete .caged.yaml with sandbox config and pipelines:

# .caged.yaml
template: node-20
resources:
  cpu: 4
  memory: 4096
  disk: 20
timeout: 3600
budget: 25.00
network_mode: allowlist
allowed_hosts:
  - api.openai.com
  - registry.npmjs.org
  - github.com
secrets:
  - OPENAI_API_KEY
  - GITHUB_TOKEN

policy:
  template: soc2
  require_approval:
    - "rm -rf *"
    - "drop table"

pipelines:
  - name: agent-ci-cd
    description: AI agent builds, tests, and deploys with human oversight
    defaults:
      template: node-20
      timeout: 15m
      retry:
        max_attempts: 2
        backoff: 30s

    stages:
      - name: clone
        type: command
        command: git clone $REPO_URL repo && cd repo

      - name: install
        type: command
        command: cd repo && npm ci
        depends_on: [clone]
        timeout: 10m

      - name: build
        type: command
        command: cd repo && npm run build
        depends_on: [install]

      - name: test
        type: command
        command: cd repo && npm test
        depends_on: [build]
        retry:
          max_attempts: 3
          backoff: 10s

      - name: lint
        type: command
        command: cd repo && npm run lint
        depends_on: [build]
        on_failure: continue

      - name: security-scan
        type: command
        command: cd repo && npm audit --audit-level=high
        depends_on: [build]
        on_failure: continue

      - name: cost-gate
        type: gate
        depends_on: [test, lint, security-scan]
        gate:
          cost_below: 10.00

      - name: trust-gate
        type: gate
        depends_on: [cost-gate]
        gate:
          trust_above: 75

      - name: approve-deploy
        type: await_approval
        depends_on: [trust-gate]
        approval:
          message: "All checks passed. Deploy to production?"
          channels: [slack, dashboard]
          sla_timeout: 4h
          auto_decision: reject

      - name: deploy
        type: command
        command: cd repo && npm run deploy:prod
        depends_on: [approve-deploy]
        env:
          DEPLOY_ENV: production

      - name: notify-success
        type: command
        command: |
          curl -X POST $SLACK_WEBHOOK \
            -d '{"text":"✅ Deployment successful!"}'
        depends_on: [deploy]
        condition:
          on_success: true

      - name: notify-failure
        type: command
        command: |
          curl -X POST $SLACK_WEBHOOK \
            -d '{"text":"❌ Pipeline failed. Check dashboard."}'
        depends_on: [deploy]
        condition:
          on_failure: true

Triggering Pipelines

Once defined in .caged.yaml, pipelines are synced to Caged when you create a sandbox:

# Create sandbox from repo with pipelines
caged create --repo https://github.com/myorg/myapp

# List available pipelines
caged pipeline list

# Start a pipeline run
caged pipeline run agent-ci-cd

Or via API:

# Pipelines are auto-synced on sandbox creation
# Start a run
curl -X POST https://api.caged.dev/v1/pipelines/agent-ci-cd/runs \
  -H "Authorization: Bearer caged_sk_..." \
  -d '{"env": {"REPO_URL": "https://github.com/myorg/myapp"}}'

Next Steps

Was this page helpful?