Walks one feature ("Add SMS notification opt-in to user settings") through the full Stagecraft pipeline. Every command in this doc is real and every output is captured from an actual run.
If you read one doc to learn Stagecraft, read this one.
Freshness: captured at v0.7.0. Command outputs reflect the pipeline at that release. Re-run the traced pipeline before using this doc for demos or release notes.
npm run consistencywarns when this stamp is more than one minor behind the current version.
Initialize a target project. We're using a temp dir; in real life this is your project root.
TARGET=$(mktemp -d)
devteam init --host claude-code --cwd "$TARGET"Initializing devteam in: /tmp/example-target
Host(s): claude-code
✓ wrote .devteam/config.yml
✓ created pipeline/
✓ created pipeline/gates/
Installing host adapter: claude-code
written: 26, skipped: 0
Next: edit .devteam/config.yml if you need custom routing, then `devteam stage requirements --feature "..."`.
40 files landed:
- 12 role subagents in
.claude/agents/ - 3 slash commands in
.claude/commands/(devteam,audit,audit-quick) - 10 rules docs in
.devteam/rules/ - 13 task skills in
.claude/skills/ - 1
.devteam/config.yml - 1
settings.local.jsonwiring the hooks
Check what to do next:
$ devteam next --cwd "$TARGET"
▶️ run-stage — requirements (stage-01)
stage not started
→ devteam stage requirements
Stagecraft is an orchestrator, not a model. devteam stage <name> renders a prompt for your AI tool. Then it's on you (or --headless mode) to feed that prompt to the model. The model writes an artifact + a gate JSON file. The Stagecraft validator reads the gate and either advances the pipeline or halts it.
There are two ways to feed the prompt to a model. Both produce the same gate file at the end. Pick whichever fits how you want to work.
One terminal. One command. Watch the model's output stream in your terminal. The gate file appears when the model finishes.
devteam stage requirements --feature "Add SMS notification opt-in to user settings" --headlessStep 1. Stagecraft logs that it's dispatching the workstream:
[devteam] dispatching pm → claude-code (headless)
Step 2. The orchestrator spawns claude --print and pipes the rendered stage prompt to it via stdin. Claude's output streams to your terminal as the model works. You'll see:
- Claude acknowledging the prompt and reading its
pmsubagent brief. - Claude reading the brief template (
templates/brief-template.md). - Claude writing
pipeline/brief.mdsection by section. - Claude writing the gate JSON (
pipeline/gates/stage-01.json).
This takes ~20–40 seconds for a small brief on a current Claude model.
Step 3. When Claude finishes, Stagecraft reports a one-line summary:
✓ pm (claude-code): exit 0, 23154ms → pipeline/gates/stage-01.json
exit 0 = model finished cleanly. → pipeline/gates/stage-01.json = the gate file got written. If you see (no gate written) instead, the model didn't write the gate (re-run the stage, or hand-write the gate).
$ ls pipeline/
brief.md gates/
$ cat pipeline/brief.md
# Feature Brief
## Problem
Customers currently can't disable marketing SMS without contacting support…
[full brief]
$ cat pipeline/gates/stage-01.json
{
"stage": "stage-01",
"workstream": "pm",
"host": "claude-code",
"orchestrator": "devteam@0.2.0",
"status": "PASS",
"track": "full",
"timestamp": "2026-05-28T14:32:11Z",
"blockers": [],
"warnings": [],
"acceptance_criteria_count": 5,
"out_of_scope_items": [...],
"required_sections_complete": true
}$ devteam next
▶️ run-stage — design (stage-02)
stage not started
→ devteam stage designStagecraft read the stage-01 gate, saw status: "PASS", and reports the next action. Run it:
$ devteam stage design --feature "Add SMS notification opt-in" --headless…and the loop continues. Each stage takes one command. You can step away while the model works (each --headless stage is await-able).
Useful when you want to see what the subagent is doing — watch the file edits, the model's reasoning, intervene mid-stage if needed.
Step 1. In Terminal 1 (your project directory), render the stage prompt:
$ devteam stage requirements --feature "Add SMS notification opt-in to user settings"A boxed onboarding preamble at the top, then the actual stage prompt:
═══════════════════════════════════════════════════════════════════════
Stage stage-01 (requirements) — 1 workstream to dispatch
═══════════════════════════════════════════════════════════════════════
The block(s) below are prompts to feed to your model. devteam does
NOT call a model — it renders the prompt and validates the gate JSON
the model writes back.
To run this stage, pick one:
1. Inside Claude Code: paste the prompt, OR type
/devteam stage requirements --feature "Add SMS notification opt-in..."
2. Headless from terminal:
devteam stage requirements --feature "..." --headless
When done, each workstream writes pipeline/gates/stage-01*.json.
Then run `devteam next` to see what to do next.
═══════════════════════════════════════════════════════════════════════
──────── workstream: pm (host: claude-code) ────────
# Stage stage-01 — requirements
Workstream: stage-01 (role: pm, host: claude-code)
...
[the actual prompt for the model]
Step 2. Open Claude Code (the CLI or the desktop app) at the same project root.
If you're using the CLI from a second terminal pane:
$ cd /tmp/example-target # same directory you ran devteam init in
$ claudeIf you're using the desktop app: open the app, point it at the same project root.
Step 3. In Claude Code's prompt, type the slash command (Stagecraft installed it at .claude/commands/devteam.md):
/devteam stage requirements --feature "Add SMS notification opt-in to user settings"
Claude Code recognizes /devteam as a slash command, reads .claude/commands/devteam.md, dispatches the PM subagent. You'll see Claude:
- Acknowledge the slash command and read the
pmsubagent brief (.claude/agents/pm.md). - Read the brief template (
templates/brief-template.md). - Write
pipeline/brief.mdsection by section (you'll see theWritetool calls). - Write
pipeline/gates/stage-01.json(anotherWritetool call). - The
Stophook fires (Stagecraft installed it at.claude/settings.local.json):
[gate-validator] ✅ GATE PASS — stage-01/pm (claude-code)
That last line confirms the validator ran on the gate and it passed. If the gate had failed (FAIL or ESCALATE), you'd see those statuses and the hook would exit non-zero.
Switch back to Terminal 1:
$ devteam next
▶️ run-stage — design (stage-02)
stage not started
→ devteam stage designSame as Path A from here. The pipeline state lives in pipeline/gates/; both paths populate it identically.
For the next stage, you can switch paths if you want — run --headless for stages you trust, switch to interactive Claude Code for stages you want to watch.
Don't want to use the slash command? Copy everything between the ──────── workstream: pm divider and the next divider (or end of stage marker), paste it into Claude Code. Claude will recognize the prompt structure and run the PM subagent. The slash command does the same thing — it's just a shortcut.
Stagecraft warns you before printing the prompt:
⚠️ /path/to/your/dir
does not look like an initialised Stagecraft target project (no .devteam/config.yml).
The prompt below will reference role briefs / rules / templates that don't exist yet.
Run this first to lay them down:
devteam init --host claude-code --cwd "/path/to/your/dir"
The prompt still prints (in case you want to read it), but feeding it to a model in an un-init'd directory won't work — the prompt references files (.claude/agents/pm.md, .devteam/rules/, templates/) that don't exist.
devteam next will keep reporting run-stage (or continue-stage for multi-role stages with partial completion). Three options:
- Re-run the stage. Most common cause is the model lost context or skipped the gate step. Re-running overwrites cleanly.
- Hand-write the gate. Look at
rules/gates-core.mdfor the universal required fields and the per-stagerules/stage-NN.mdfor extra fields. Write a JSON file at the expected path. Stagecraft doesn't care how the gate got there. - Escalate. Write the gate with
status: "ESCALATE"and a reason inescalation_reason. Stagecraft halts cleanly and surfaces the situation.
The walkthrough above showed the user-facing flow for stage-01. The rest of this document is reference: what each stage's prompt actually contains, what artifact gets written, what's in the gate JSON. Skim or chase the specific stage you care about.
devteam stage requirements --cwd "$TARGET" --feature "Add SMS notification opt-in to user settings"The CLI emits a prompt aimed at Claude Code:
──────── workstream: pm (host: claude-code) ────────
# Stage stage-01 — requirements
Workstream: stage-01 (role: pm, host: claude-code)
Track: full
Feature: Add SMS notification opt-in to user settings
Use the **pm** subagent (`.claude/agents/pm.md`) for this workstream.
## Objective
Turn the feature request into requirements, acceptance criteria, and scope boundaries.
## Read first
- AGENTS.md
- .devteam/rules/pipeline.md
- .devteam/rules/gates-core.md
- pipeline/context.md
## Allowed writes (enforced by Claude Code hooks at tool-call time)
- pipeline/brief.md
- pipeline/gates/stage-01.json
- pipeline/context.md
## Artifact
Produce `pipeline/brief.md` using `templates/brief-template.md`.
## Gate to write
Write to `pipeline/gates/stage-01.json`. You provide:
```json
{
"stage": "stage-01",
"workstream": "pm",
"status": "PASS|WARN|FAIL|ESCALATE",
...
"acceptance_criteria_count": 0,
"out_of_scope_items": [],
"required_sections_complete": false
}
The orchestrator adds "orchestrator": "devteam@0.1.0" and "host": "claude-code" at validation time.
Inside Claude Code, the PM subagent reads the brief template, drafts `pipeline/brief.md`, and writes the gate. The Stop hook fires the validator. PASS:
[gate-validator] ✅ GATE PASS — stage-01/pm (claude-code)
`devteam next` advances:
$ devteam next --cwd "$TARGET"
## Stages 2 and 3 — Design and clarification
Same shape: Principal drafts `pipeline/design-spec.md` and any ADRs; PM resolves any clarification questions. Each writes its gate; the validator hook accepts each; `next` walks forward.
$ devteam next --cwd "$TARGET"
## Stage 4 — Build (multi-role: 4 workstreams)
This is where the multi-workstream contract pays off. `stage-04` has four roles: `backend, frontend, platform, qa`. The orchestrator decomposes into one prompt per role.
```bash
devteam stage build --cwd "$TARGET" --feature "Add SMS notification opt-in"
Four sections come out, each pointing at a different subagent. Excerpt of the backend workstream:
──────── workstream: backend (host: claude-code) ────────
# Stage stage-04 — build
Workstream: stage-04.backend (role: backend, host: claude-code)
Track: full
Feature: Add SMS notification opt-in
Use the **dev-backend** subagent (`.claude/agents/dev-backend.md`) for this workstream.
## Allowed writes (enforced by Claude Code hooks at tool-call time)
- src/backend/
- src/tests/
- pipeline/pr-backend.md
- pipeline/build-plan.md
- pipeline/gates/stage-04.backend.json
- pipeline/context.md
## Gate to write
Write to `pipeline/gates/stage-04.backend.json`.
Note the per-role allowedWrites — backend can write src/backend/ and src/tests/ but not src/frontend/. Frontend's workstream gets the symmetric narrow set. The Claude Code hooks enforce this at tool-call time, not just in the prompt.
Each dev workstream produces:
- A PR summary (
pipeline/pr-<role>.md) - Their own workstream gate at
pipeline/gates/stage-04.<role>.json
After all four workstreams finish, devteam next reports:
$ devteam next --cwd "$TARGET"
🔀 merge — build (stage-04)
all workstreams complete; merge to produce stage gate
→ devteam merge build
devteam merge build aggregates them:
$ devteam merge build --cwd "$TARGET"
Merged → /tmp/.../pipeline/gates/stage-04.json (status: PASS)
The merged stage gate carries the contributing workstreams:
{
"stage": "stage-04",
"status": "PASS",
"orchestrator": "devteam@0.1.0",
"track": "full",
"timestamp": "2026-05-26T20:00:00Z",
"blockers": [],
"warnings": [],
"workstreams": [
{ "workstream": "backend", "host": "claude-code", "status": "PASS" },
{ "workstream": "frontend", "host": "claude-code", "status": "PASS" },
{ "workstream": "platform", "host": "claude-code", "status": "PASS" },
{ "workstream": "qa", "host": "claude-code", "status": "PASS" }
]
}Aggregate status follows ESCALATE > FAIL > WARN > PASS. If any workstream had FAILed, the stage gate would be FAIL.
Platform runs lint, tests, dependency review, and the security-trigger heuristic. The gate carries security_review_required: true | false.
{
"stage": "stage-04a",
"workstream": "platform",
"host": "claude-code",
...
"lint_passed": true,
"tests_passed": true,
"dependency_review_passed": true,
"security_review_required": true
}For an SMS opt-in feature that touches auth and PII paths, the heuristic fires. next routes to the conditional security review:
$ devteam next --cwd "$TARGET"
▶️ run-stage — security-review (stage-04b)
stage not started
→ devteam stage security-review
(If the heuristic had not fired — security_review_required: false — next would silently skip stage-04b and advance to peer-review.)
Conditional dispatch via the conditionalOn: { stage: "stage-04a", field: "security_review_required", equals: true } declaration on the stage.
The security subagent reviews; gate carries security_approved and veto flags. veto: true halts the pipeline regardless of any subsequent stage's approvals.
Peer-review has the same multi-workstream shape as build, but the workstreams are areas being reviewed (backend / frontend / platform / qa). All four dispatch to the same reviewer subagent (via subagent: "reviewer" on the stage).
Reviewers write per-area sections in their review files. The PostToolUse hook (approval-derivation.js) fires on every save, parses the file, and upserts the per-area workstream gates.
# Reviewer A writes pipeline/code-review/by-backend.md:# Review by backend
## Review of frontend
Form layout looks good, ARIA labels present, error states clear.
REVIEW: APPROVED
## Review of platform
Deploy steps documented in runbook.
REVIEW: APPROVED# Reviewer B writes pipeline/code-review/by-frontend.md:# Review by frontend
## Review of backend
API surface clean. Validation symmetrical with the frontend.
REVIEW: APPROVED
## Review of platform
Smoke-test runbook references the wrong endpoint.
REVIEW: CHANGES REQUESTED
BLOCKER: pipeline/runbook.md §Smoke test mentions /api/sms but endpoint is /api/v2/smsThe hook runs on each write and reports:
[approval-derivation] dev-backend → APPROVED on frontend (approvals: 1/2, status: FAIL)
[approval-derivation] dev-backend → APPROVED on platform (approvals: 1/2, status: FAIL)
[approval-derivation] dev-frontend → APPROVED on backend (approvals: 1/2, status: FAIL)
[approval-derivation] dev-frontend → CHANGES_REQUESTED on platform (approvals: 1/2, status: FAIL)
Three area gates exist (stage-05.frontend.json, stage-05.platform.json, stage-05.backend.json); the qa area has no review yet, and the platform area has a blocker. next reflects state:
$ devteam next --cwd "$TARGET"
⏳ continue-stage — peer-review (stage-05)
3/4 workstreams complete
completed: frontend, platform, backend
remaining: qa
→ devteam stage peer-review # roles still pending: qa
After a fourth reviewer covers qa and the platform blocker is addressed (a second reviewer flips it from CHANGES_REQUESTED to APPROVED, bumping each area to 2/2 approvals), the area gates all PASS. devteam merge peer-review produces the stage-05 gate.
Same shape as the others; nothing structurally new.
- Stage 6 (QA) writes
pipeline/test-report.mdand a gate carryingall_acceptance_criteria_metand the 1:1 criterion-to-test mapping. - Stage 7 (PM + Platform) is the sign-off stage. The orchestrator can auto-fold this gate when Stage 6 reports
all_acceptance_criteria_met: trueAND a 1:1 mapping — seerules/stage-07.md§Gate. - Stage 8 (Platform) runs the deploy adapter. The adapter (
core/deploy/<name>.md) names the deploy procedure; gate carriesdeploy_adapter,smoke_test_passed,runbook_referenced. Do not auto-rollback on FAIL — the runbook names the rollback and a human decides. - Stage 9 (Principal) synthesizes the retrospective. Harvests
PATTERN:lines from Stage 5 reviews; promotes ≤2 rules intopipeline/lessons-learned.md; auto-ages out rules that haven't been reinforced in 10 runs.
When all stages PASS:
$ devteam next --cwd "$TARGET"
🎉 pipeline-complete
all stages PASS or WARN (track: full)
After a successful full-track run, pipeline/gates/ contains:
pipeline/gates/
├── stage-01.json # PM brief
├── stage-02.json # Principal design
├── stage-03.json # PM clarification
├── stage-04.json # merged build (4 workstreams)
├── stage-04.backend.json
├── stage-04.frontend.json
├── stage-04.platform.json
├── stage-04.qa.json
├── stage-04a.json # Platform pre-review
├── stage-04b.json # (only if security_review_required)
├── stage-05.json # merged peer-review (4 area workstreams)
├── stage-05.backend.json
├── stage-05.frontend.json
├── stage-05.platform.json
├── stage-05.qa.json
├── stage-06.json # QA test report
├── stage-07.json # sign-off
├── stage-08.json # deploy
└── stage-09.json # retrospective
Every gate carries the contract F identity fields. Every multi-role stage has a workstream gate per area plus the merged stage gate. The whole pipeline is auditable from these files alone — the orchestrator never holds state outside of them.
-
Multi-host routing. This example installed only
claude-code. To run backend on Codex and the rest on Claude Code, install both (devteam init --host claude-code,codex) and edit.devteam/config.yml:routing: default_host: claude-code roles: backend: codex
Then
devteam stage buildproduces 4 prompts, with the backend workstream pointing at.codex/prompts/roles/backend.mdand the rest pointing at.claude/agents/dev-*.md. -
Headless invocation.
devteam stage build --headlesswould drive each workstream's host CLI non-interactively (claude --printfor claude-code workstreams,codex execfor codex). Per-workstream exit codes + gate paths get reported in the summary. -
Track shortcuts.
devteam initdefaultspipeline.default_track: full. For a config-only change, edit that toconfig-onlyanddevteam nextwalks a different stage list. -
FAIL / ESCALATE branches. A FAIL gate gives
❌ fix-and-retrywith blockers listed; an ESCALATE gate gives🚨 resolve-escalationwith the reason. Both haltnextuntil the situation is resolved.
The example above ran the full track — every stage, in order. Real teams pick lighter tracks for smaller changes. Let's walk through a hotfix for an outage to show what a different track feels like.
Scenario: Production is alerting. The Stripe webhook handler is returning 500s. You've identified the root cause (a null-check missing after a recent dependency bump), and you need to ship a fix in <30 minutes.
The hotfix track skips the front-of-pipeline work — requirements / design / clarification — because you already know what's broken. It keeps every downstream safety stage (build → pre-review → security-review → peer-review → qa → accessibility-audit → observability-gate → sign-off → deploy → retrospective). That's 10 stages, but most run fast.
# Edit .devteam/config.yml — change default_track to hotfix, OR pass it inline:
devteam stage build --feature "Fix Stripe webhook 500 — add null check on event.data.object" --track hotfixThe CLI's preamble identifies the track:
═══════════════════════════════════════════════════════════════════════
Stage stage-04 (build) — 4 workstreams to dispatch
═══════════════════════════════════════════════════════════════════════
... track: hotfix ...
Only backend is actually touched. The other three workstreams (frontend, platform, qa) run anyway because Stage 4 is a 4-role stage by structure — they each produce a one-line PR summary and a PASS gate explaining they're not in scope. Total wall-clock: ~3 minutes if you're running headless across all four.
The backend workstream adds the null-check, writes the test, and produces:
// pipeline/gates/stage-04.backend.json
{
"stage": "stage-04",
"workstream": "backend",
"host": "claude-code",
"status": "PASS",
"track": "hotfix",
"blockers": [],
"warnings": [],
"files_changed": ["src/backend/webhooks/stripe.ts", "src/backend/webhooks/stripe.test.ts"]
}After all four workstreams: devteam merge build → stage-04 gate, all-PASS.
Platform runs lint, tests, dep review, and the security heuristic against the diff. The diff touches src/backend/webhooks/stripe.ts — that path includes "stripe" but isn't on the explicit security trigger list. Lint passes; tests pass; security heuristic returns security_review_required: false.
// pipeline/gates/stage-04a.json
{
"stage": "stage-04a",
"status": "PASS",
"lint_passed": true,
"tests_passed": true,
"dependency_review_passed": true,
"security_review_required": false
}devteam next skips stage-04b (security review fires only when the prior gate's security_review_required: true) and advances to stage-05.
For a hotfix, you probably don't want multi-model fanout — the cost-benefit tilts toward "ship fast." A single reviewer across all 4 areas is fine.
Reviewer writes pipeline/code-review/by-senior.md:
# Review by senior
## Review of backend
Null check is correct. Test covers the regression. The fix is the smallest possible change.
REVIEW: APPROVED
## Review of frontend
Not in scope for this hotfix.
REVIEW: APPROVED
## Review of platform
Not in scope for this hotfix.
REVIEW: APPROVED
## Review of qa
Not in scope for this hotfix.
REVIEW: APPROVEDThe PostToolUse hook fires four times, upserting four area gates. With 1 reviewer and a required_approvals: 1 policy, each area's gate flips to PASS immediately.
-
Stage 6 (QA): the new test passes; the regression test specifically covers the null-data webhook payload. Gate carries
tests_total: <N+1>,tests_passed: <N+1>,all_acceptance_criteria_met: true. -
Stage 6b (accessibility): backend-only change →
audit_skipped_reason: "backend-only change, no UI affected". Gate writes PASS with the skip reason. -
Stage 6c (observability): the brief said the webhook should emit
webhook.stripe.receivedandwebhook.stripe.errormetrics. Platform greps the code: both are emitted (the existing emitters were untouched by the fix). Gate writes:
{
"stage": "stage-06c",
"status": "PASS",
"metrics": {
"required": ["webhook.stripe.received", "webhook.stripe.error"],
"verified": ["webhook.stripe.received", "webhook.stripe.error"],
"gap": []
},
"verification_method": "code-grep"
}Stage 6 reported all_acceptance_criteria_met: true AND a 1:1 criterion-to-test mapping (the "fix the null case" criterion maps to one specific test). The orchestrator auto-folds Stage 7 — no human action — writing:
{
"stage": "stage-07",
"status": "PASS",
"auto_from_stage_06": true,
"pm_signoff": "auto",
"platform_signoff": "auto"
}devteam next advances directly to stage-08.
Platform follows core/deploy/<adapter>.md (whatever your team's deploy adapter is — Kubernetes, Terraform, custom). The gate carries deploy_adapter, smoke_test_passed, runbook_referenced. Stripe webhooks start returning 200s again; the alert clears.
{
"stage": "stage-08",
"status": "PASS",
"deploy_adapter": "kubernetes",
"smoke_test_passed": true,
"rollback_executed": false
}Even hotfixes get a retro. Principal harvests PATTERN: lines from Stage 5 reviews (none in this case — single reviewer, straightforward fix), considers what to add to pipeline/lessons-learned.md. Likely outcome: add a lesson like **Lesson:** dependency bumps that touch webhook handlers warrant an extra null-check pass before merge.
Total wall-clock: ~25 minutes if running mostly user-driven, ~10 minutes if running mostly headless. The audit trail is complete: you can show stakeholders (or your incident review board) exactly what was changed, who approved it, what tests cover it, and how it was deployed.
- Tracks scale the process to the change size. Full track for cross-area features. Hotfix for production incidents. Quick for single-area work. Nano for typos.
- Auto-fold prevents busy-work. Stage 7 sign-off is automatic when Stage 6 reports complete acceptance criteria with a 1:1 test mapping. You don't sign off on what the system already proved.
- Conditional stages keep the safety net light. Security review fires only when pre-review's heuristic flags it. Accessibility audit can be explicitly skipped for backend-only changes (with a reason).
- Even hotfixes produce a complete audit trail. No corner-cutting on the record, just on the front-of-pipeline planning work that doesn't apply to "fix this specific bug now."
README.md— CLI surface, install layout, First 30 minutes path.docs/concepts.md— vocabulary in one table.docs/user-guide.md— long-form daily-use reference.docs/tracks.md— which track to pick for which kind of change.docs/adoption-guide.md— for team leads deciding whether to adopt.docs/presentation-notes.md— slide deck + speaker notes for pitching this to stakeholders.ARCHITECTURE.md— design model + the 11 locked decisions.CONTRIBUTING.md— extension recipes (adapter, stage, role, skill).docs/walkthroughs/stage-04-split-host.md— the stress-test trace that locked the multi-workstream contract.