Customizing pipelines
Define a project-level pipeline, select it at run time, and override model/effort per step.
The shipped schemas (contractor-base, contractor-lite) come with a default pipeline that's the right starting point for most repos. But the moment you want a different shape — skip the gate, add a pnpm test step, run a fast review-only iteration — you don't fork a schema. You define a named pipeline in contractor/config.yaml and select it at run time.
This guide walks through one end-to-end change: adding a fast-iteration pipeline that implements and reviews without closing, and pinning it as the default for a single blueprint.
1. Define the pipeline
Project-level pipelines live under the pipelines: key in contractor/config.yaml. The same shape is accepted at ~/.contractor/config.yaml for user-level pipelines you want available across every repo on your machine — project pipelines win on name collisions.
# contractor/config.yaml
pipelines:
fast-iteration:
description: Implement and quick-review, no close.
steps:
- id: implement
kind: agent
slashCommand: implement.md
critical: true
needs_prepared_worktree: true
- id: review
kind: agent
slashCommand: review.md
critical: falseSteps come in three kinds: agent (spawns the configured AI backend with a slash-command prompt), shell (runs a command in the worktree), and gate (pauses for a user decision). See Pipelines for the full step reference.
A few rules:
slashCommandreferences a file under.claude/commands/blueprint/. It must exist aftercontractor ai install.critical: true(the default) means a step failure ends the run;critical: falselets the pipeline continue past failure. Usefalsefor non-essential steps like a final close or an advisory review.needs_prepared_worktree: trueinjects a synthetic prepare step before this one if the worktree's prepare marker is missing or stale (see Worktrees).
2. Select the pipeline
Three ways to pick a custom pipeline at run time, in resolution order:
# 1. CLI flag — wins over everything.
contractor run --pipeline fast-iteration
# 2. Per-blueprint pin — set in .contractor.yaml.
# contractor/blueprints/<name>/.contractor.yaml
# pipeline: fast-iteration
# 3. Project default — set in contractor/config.yaml.
# pipeline: fast-iterationIf none of those match, contractor falls back to the schema's pipeline: section, then to the hardcoded contractor-lifecycle default. The full chain is documented in Pipelines § Resolution precedence.
For a one-off run, --pipeline is right. For a blueprint that always wants the same custom pipeline, pin it in .contractor.yaml so you don't have to remember the flag:
# contractor/blueprints/quick-fix/.contractor.yaml
schema: contractor-lite
pipeline: fast-iteration
lifecycle: implementFor a repo-wide default, set pipeline: at the top of contractor/config.yaml. An unknown name fails the run with Unknown pipeline "<name>". Available pipelines: …, listing everything resolvable from the project + global maps.
3. Override model and effort per step
Agent steps accept model and effort overrides. The shipped contractor-base pipeline uses these to pin the implement and review phases to opus[1m] and tune the close phase to effort: low:
pipeline:
steps:
- phase: implement
critical: true
model: opus[1m]
needs_prepared_worktree: true
- phase: review
critical: true
model: opus[1m]
effort: medium
- phase: close
critical: false
model: opus[1m]
effort: lowThe same fields work in a project pipeline:
pipelines:
thorough:
description: Slower, more deliberate implement + multi-pass review.
steps:
- id: implement
kind: agent
slashCommand: implement.md
model: opus[1m]
effort: high
critical: true
needs_prepared_worktree: true
- id: review
kind: agent
slashCommand: review.md
model: opus[1m]
effort: high
critical: trueRun-time overrides via --model and --effort apply to every agent step in the run, while step-level fields apply per step. Use the CLI flags for ad-hoc experiments; bake the per-step values into the pipeline once you know what each step deserves.
effort is one of low, medium, high, max. Only backends that honor reasoning effort (today the Claude backend) react to it.
4. Add a shell gate
A common reason to define a custom pipeline is to insert a shell step — typically a typecheck or scoped test — between agent phases. Shell steps support {{scope}} substitution and when: scope guards, which makes them especially useful in monorepos:
pipelines:
scoped-ship:
description: Implement, scoped tests, review, close.
steps:
- id: implement
kind: agent
slashCommand: implement.md
critical: true
needs_prepared_worktree: true
- kind: shell
command: pnpm --filter @myorg/{{scope}} test
when: scope
critical: true
- id: review
kind: agent
slashCommand: review.md
critical: true
- id: close
kind: agent
slashCommand: close.md
critical: falseThe when: scope guard skips the step when the active blueprint has no scope, so unscoped blueprints sail past it instead of running the whole-monorepo test suite. See the using scopes guide for the full scope mechanics.
5. Verify with contractor doctor
Once you have a pipeline defined, run contractor doctor from the repo root to confirm the configuration parses cleanly. Doctor's "Schema graph" and "Pipelines" sections call out unresolvable references, unknown step kinds, or pipelines that point at slash-command files that don't exist.
If --pipeline fast-iteration rejects with Unknown pipeline, check that:
- The pipeline is defined under
pipelines:(not at the top level) ofcontractor/config.yaml. - The file is valid YAML — a stray indentation error silently drops the whole map.
- You're invoking from inside the repo whose
contractor/config.yamldefines it (or that you've put the definition in~/.contractor/config.yamlfor cross-repo availability).
See also
- Pipelines — step kinds, the default lifecycle, and the resolution chain.
contractor/config.yaml— every field the project config accepts.- Resuming failed runs — re-spawn a pipeline run without re-running its successful steps.