steps docs
start here Writing pipelines resourcesexprcontrol-flowagentsattempts-timeoutworkspaceinfratemplatingmcpcomplete Reference webagents-internalsaws-workersgcp-workersconformance

Documentation

Read the page for what you're doing. Nothing here needs to be read in order, except that resources.md is the one most people need second, right after the quick start.

Every YAML example in these docs is a complete pipeline, extracted and executed by the test suite (e2e/docs_test.go). An example that needs docker, btrfs, the network, a CLI, or real credentials says which one; everything else runs exactly as shown, and a doc example that stops working fails the build. Read them via steps docs in a terminal, /docs in the web UI, or the files themselves.

The assert: blocks you see in them are load-bearing, not decoration. Every executed example verifies its own behavior — which steps ran in what order, what a command printed, what a model decided, what a step wrote — so the examples are regression tests for the features they document, and the claims in the prose beside them are checked rather than asserted by an author. A separate mutation suite proves those assertions can actually fail: it rewrites each one to something the pipeline does not satisfy and requires the build to go red. See assert:.

Writing pipelines

PageWhat it coversLength
resources.mdResources and resource types: the built-in git, the check/in/out contract, version:, trigger:short
expr.mdExpression resource types: expr:, the batched http(), env()/file(), when to use it instead of shellmedium
control-flow.mdwhen: guards, hooks, to:/verdicts: routing, do:/in_parallel:/race:/across:, assert:, approvalslong
agents.mdAgent steps: tools, prompts, verdicts, sub-agents, fix:, budgets, failover, CLI agents, ensembleslong
attempts-timeout.mdattempts: and timeout:, and how they interactshort
workspace.mdinputs:/outputs:, per-step isolation (always on), read-modify-write artifactsshort
infra.mdContainerized execution (image:) and cross-job triggers (steps web)medium
templating.md{{ }} in resource commands and custom tools, shellquote, ((var)), load_var:short
mcp.mdMCP servers as tool sources and as resource-type backendsmedium
complete.mdA full pipeline putting the pieces togethershort

Reference

PageWhat it covers
web.mdThe daemon: steps pipeline set, the browser UI, run transcripts, the dependency graph, live runs, triggering, and one state database for every pipeline it holds
agents-internals.mdHow agent steps work underneath: transport, tool-call repair, compaction, caching
aws-workers.mdStanding up an aws:// worker by hand with the AWS CLI: IAM, security group, launch template, instance, bucket — and running a pipeline on it
gcp-workers.mdStanding up a gcp:// worker by hand with gcloud: the IAP firewall rule, instance template, instance — and running a pipeline on it
conformance.mdWhich Concourse behaviors steps matches, which it doesn't, and which are verified

Commands

steps run <pipeline>        run one job (--resume <id> continues a failed one,
                            --replay <id> --from <step> re-runs one step of one)
                            --worker <tag>=<url> places tags: steps on a machine
steps test <pipeline>       run every job and check assert: directives
steps validate <pipeline>   check the file, and that this machine can run it
steps plan <pipeline>       show what a run would execute vs skip
steps validate --live       also probe the models and MCP servers themselves
                            (--job <name> narrows it to one job)

steps web                   the daemon: serve the UI, hold the pipelines set
                            into it, poll trigger: true resources, run jobs
steps pipeline set -c f.yml upload a pipeline into a daemon — the only way a
                            served pipeline changes (list|get|pause|unpause|
                            rename|destroy for the rest; --target names one)

steps runs -p <name>        what ran, newest first, each row naming the
                            configuration it executed (steps|queue|cost|where
                            for the other four views; runs steps says why)
steps runs                  with no -p: every pipeline in the state file
steps jobs -p <name>        list jobs the circuit breaker paused
                            (steps jobs resume <job> -p <name> clears one)
steps approvals -p <name>   list approval: steps waiting for a decision
                            (steps approvals approve|reject <id> -p <name>)
steps questions -p <name>   list ask_user questions waiting for an answer
                            (steps questions answer <id> <answer> -p <name>)
steps mcp tools|login       inspect or authorize mcp_servers: entries
steps docs [page]           read these docs in the terminal

The read commands default to a daemon's .steps/steps.db. A local steps run or steps test keeps its state beside the YAML instead, so reaching it takes --db — which a parked approval: or question prints for you: steps approvals approve 1 -p pipeline --db .steps/pipeline.yml.db.

Two of these answer most "why is it doing that?" questions: steps plan explains what the cache would skip, and steps runs steps shows what previous runs actually did.

A third answers "did the pipeline change?" — steps runs carries a CONFIG column, the hash of the configuration each run was started from. Two runs of one file agree; an edit between them does not, and a run whose rows disagree with the last green one is a run that executed something else. The hash covers everything the configuration is made of: the pipeline file after ((var)) substitution — so one file under two --vars-files is two configurations — and every file it includes, so editing a run_file: script is editing the pipeline. A run started by something that loaded no file at all shows -.

steps validate answers a third: will this run at all? It checks the file — syntax, references, field placement — and then the things the file depends on that live outside it:

It reports all of them at once, because finding them one run at a time is the problem:

$ steps validate pipeline.yml
steps: error: pipeline.yml cannot run here:
  agent "coder"  $OPENROUTER_API_KEY is not set (source.api_key_env)
  mcp "gopls"    command "gopls" not found on PATH

steps validate --live answers the run-time version of the same question: not "is this pipeline runnable" but "is it runnable right now". It sends a minimal request to every model the pipeline reaches and starts every MCP server it grants; --job <name> narrows that to one job.

steps run does this automatically, before any step executes. A plan like plan -> code -> check -> review -> publish used to discover a dead model half an hour in, with everything before it paid for and thrown away. Now it fails in seconds, saying explicitly that nothing ran:

$ steps run pipeline.yml --job self-build
steps: error: job "self-build": preflight failed, no steps were run:
  agent "coder": model "deepseek-v4-flash": no response within 30s
    (other models on this endpoint responded — the model itself looks unavailable, not the endpoint or the key)

Tuning, all optional:

defaults:
  preflight:
    disabled: false   # true skips it entirely
    timeout: 30s      # per check
    cache: 5m         # a target verified this recently is trusted

agents:
- name: coder
  preflight: false    # opt one agent out — e.g. a local model slow to WAKE

The cache is what makes this usable under steps web: without it every poll interval would pay for a probe request against every model. --no-preflight skips it for one invocation. What it does not do: preflight catches "broken before we start", not "breaks halfway through" — failing over mid-run is fallback:'s territory.

Add --syntax-only to steps validate to check the file alone — the right flag for a pre-commit hook or a CI lint that should not need the pipeline's production credentials on hand.

Editor support

steps.schema.json is a JSON Schema for the pipeline format. Point your editor at it with a modeline on the first line of a pipeline:

# yaml-language-server: $schema=./steps.schema.json

That gives completion and inline errors while you type. steps validate remains the authority — it checks rules a schema can't express, like whether a to: target exists in the same segment — but the schema catches misspelled keys at the keystroke rather than the run.

Re-running one step: --replay

Agent steps are never content-cached, and unskippable propagates forward — so editing the last agent step's prompt re-runs every step before it, at full price. That is the single most expensive thing about authoring an agent pipeline.

steps run pipeline.yml --replay r-8f2a1c --from synthesizer

That forks the recorded run and executes from synthesizer onward. It does not consult the merkle cache: state comes from the source run's workspace (the artifacts earlier steps produced are already on disk), its recorded run_context, and its step record.