Steps
The complete step vocabulary the engine executes: names, order, phase, enforcement, skip rules, artifacts, and the skill each step dispatches. These names are what ai-conductor inline --from <step> accepts.
There are 24 step names: 20 sequential steps in ALL_STEPS and 4 out-of-band steps in OUT_OF_BAND_STEPS. validation and build_verification are step groups wrapping existing steps, not steps themselves — neither can be passed to --from.
Tables on this page are source-ordered (the order of ALL_STEPS), not alphabetized. The order of that array is the flow.
Enforcement levels
Enforcement is a property of the engine’s step definition, not of the skill’s frontmatter. It decides whether the step can be skipped and whether it can be disabled by config.
| Level | Can be skipped | Can be config-disabled | Used by |
|---|---|---|---|
advisory | Yes | Yes | memory, explore, complexity, architecture_diagram, architecture_review, and all four out-of-band steps |
gating | No | Only with configDisableAllowed | prd, stories, conflict_check, plan, coherence_check, acceptance_specs, build_review, test_suite, manual_test, prd_audit, architecture_review_as_built, finish |
structural | No | Never — the flag is ignored entirely | worktree, build, rebase |
mechanical | — | — | Nothing. The level is declared in the type union but no step definition uses it. |
Skippability is exactly enforcement !== 'gating'. Tier and track skips (below) are a separate mechanism and apply to gating steps too. For what a gate is and why it fails closed, see gates.
Sequential steps
The 20 steps of ALL_STEPS, in execution order. “Skips” lists tier and track exclusions; see Tier skips and Track skips.
| # | Step | Phase | Enforcement | Prerequisites | Skips | Dispatches |
|---|---|---|---|---|---|---|
| 0 | worktree | SETUP | structural | — | — | /conduct worktree |
| 1 | memory | UNDERSTAND | advisory | — | — | /memory |
| 2 | explore | DECIDE | advisory | — | — | /explore |
| 3 | complexity | DECIDE | advisory | explore | — | /conduct complexity |
| 4 | prd | DECIDE | gating | explore | track technical | /prd |
| 5 | architecture_diagram | DECIDE | advisory | complexity | tier S | /architecture-diagram |
| 6 | architecture_review | DECIDE | advisory | architecture_diagram | tier S | /architecture-review |
| 7 | stories | DECIDE | gating | architecture_review | — | /stories |
| 8 | conflict_check | DECIDE | gating | stories | tier S | /conflict-check |
| 9 | plan | DECIDE | gating | conflict_check | — | /plan |
| 10 | coherence_check | DECIDE | gating | plan | tier S | /coherence-check |
| 11 | acceptance_specs | BUILD | gating | plan | tier S | /writing-system-tests |
| 12 | build | BUILD | structural | plan | — | /pipeline |
| 13 | test_suite | BUILD | gating | build | — | engine-native |
| 14 | build_review | BUILD | gating | test_suite | — | engine-native |
| 15 | manual_test | SHIP | gating | test_suite | tier S | /manual-test |
| 16 | prd_audit | SHIP | gating | manual_test | — (runs at every tier and on both tracks; trivially passes when a feature has no acceptance criteria to grade) | /prd-audit |
| 17 | architecture_review_as_built | SHIP | gating | prd_audit | — (runs at every tier; individual checks are conditional on tier and artifact presence, see gates) | /architecture-review --as-built |
| 18 | rebase | SHIP | structural | architecture_review_as_built | — | native; /rebase only on conflict |
| 19 | finish | SHIP | gating | rebase | — | /finish |
Per phase: SETUP 1, UNDERSTAND 1, DECIDE 9, BUILD 4, SHIP 5.
test_suite is the sole engine-native BUILD verifier. Static wiring reachability is retired. build_review currently runs only its optional testQuality rubric. The engine freezes the base/HEAD source and active feature artifacts, then derives established test regions and concrete uncertain candidates from changed declarations, current-feature-owned Covers bindings introduced or updated after the review base, and relevant shared setup or helper evidence. An unchanged bare marker remains owned by the feature that landed it; a coincidentally matching active-plan ordinal cannot make it current authority. Only a Covers marker in a real comment binds; Covers: text inside a string or template literal is test data, not a marker. The engine does not make every title in a changed marked file a review target. A candidate’s file can be selected for conservative counterfactual execution without making unchanged sibling tests quality targets.
Enabled testQuality with no established targets or concrete candidates is a valid empty-scope PASS: it does not dispatch its reviewer or counterfactual preflight. An enabled security rubric still reviews the diff and participates in the joined verdict. This preserves production-only refactors and pure moves/renames as non-coverage work; the aggregate suite and CI remain responsible for broad regression execution. For each concrete candidate, the normal reviewer returns one source-bound scope resolution: resolved, out-of-scope, or indeterminate. An indeterminate candidate preserves any otherwise valid findings but creates a derived scope-incomplete fault. It follows the existing bounded mechanical-fault and explicit reduced-coverage recovery path rather than inventing a test-insensitive finding or silently passing. See stalled or stuck feature.
Retiring a step safely
Step retirement has two phases: first remove the step’s machinery while retaining its name as a deprecated no-op; only in a later, separate change may the name be deleted, after no live state or consumer configuration can reference it. This preserves in-flight conduct-state.json, configured step keys, historical artifacts, and downstream prerequisites while operators receive an observable deprecation notice. See adr-2026-08-11-deprecated-no-op-step-retirement.
Two steps are checkpoints (isCheckpoint: true) — the engine pauses for the operator after them in default and interactive mode: build and manual_test.
build is the first loopGate step, which makes index 12 the boundary between the front (DECIDE-ish, one-way) region and the gate loop. Everything from build to finish is a loop gate and can be re-entered when a downstream gate kicks back. The kickback targets — the steps a blocking gate can send work back to — are prd, architecture_review, stories, and plan.
Repository-local self-host tail
This repository adds two configured SHIP gates without changing the static ALL_STEPS index: rebase → maintain-documentation → release-disposition → finish. release-disposition is gating, writes the authoritative structured metadata inside its project-owned region on the retained SHIP draft PR, and records only its completion evidence in .pipeline/release-disposition-pass. The later finish step preserves every project-owned region while supplying the reader-facing PR body.
Each region is owned by one configured custom SHIP step and carries opaque body bytes. The engine prepares a missing region before its owner runs, captures the owner’s non-empty result after success, and restores captured regions after PR-body authoring. See the template-region contract.
Before FINISH begins publication, it also checks every configured custom step that occurs earlier in the resolved flow and declares both enforcement: gating and a completion_artifact. Each such step must be done and have a fresh regular-file marker for the current run. A blocked publication names all unsatisfied custom step keys; advisory and structural custom steps, steps without a completion marker, and steps after FINISH are not FINISH publication prerequisites.
Both configured gates read the retained SHIP PR, which the engine adopts at SHIP-phase entry. If that PR is a reused needs-remediation halt placeholder, the engine makes it presentable whenever that identity is resolved — at adoption, at the pre-finish snapshot, or at the finish-time restore — so a SHIP step scheduled ahead of finish never reads a remediation placeholder. A lighter clear also runs once at the start of every dispatch, regardless of phase, so a resumed BUILD step is not left holding the placeholder either. The draft→ready flip remains finish-only. See running the daemon.
Out-of-band steps
These have full step definitions and are dispatchable, but hold no slot in the sequential loop. --from cannot start at them; the engine invokes them itself when a condition fires.
| Step | Phase | Enforcement | Prerequisites | Dispatches | When it runs |
|---|---|---|---|---|---|
bootstrap | UNDERSTAND | advisory | — | /bootstrap | Prelude, before the loop |
assess | UNDERSTAND | advisory | — | /assess | Prelude; short-circuited when bootstrap_mode is new |
remediate | SHIP | advisory | prd_audit | /remediate | When a SHIP gate blocks (a prd_audit FIXABLE grade or the as-built review’s BLOCKED verdict), or on a build stall |
attribution_verify | SHIP | advisory | — | engine-native | Out-of-band commit-attribution audit |
They exist as definitions because getStepDefinition throws Unknown step: <name> without one, and the daemon turns that throw into a .pipeline/HALT. Config-declared custom steps resolve from a third table that buildStepRegistry populates, consulted after these two — see configuration.
The validation group
validation is a StepGroup over three members already present in ALL_STEPS, in this order: manual_test, prd_audit, architecture_review_as_built. It does not remove, replace, or reorder them, and it does not change their state keys.
The group only fans out when all of these hold:
- The step belongs to a group.
- The run mode is
auto. Interactive and default mode never engage the group. - The entry step’s own prerequisite gate passes.
- More than one member is dispatchable. A width-1 group degrades silently to the serial path.
Fan-out width is capped by the validation_concurrency config key (default 4, which covers the three-member group in a single wave; see configuration). Branches never write conduct-state.json or .pipeline/gates/* — only the loop thread does, after every branch settles.
The build verification group
test_suite is the sole BUILD verifier. It runs after build and before build_review. After a BUILD repair, it reuses a matching content fingerprint or derives a fresh suite result. Reuse does not consume retry or kickback budget.
Tier skips
Tier S skips 6 steps. Tiers M and L skip none.
| Tier | Steps skipped |
|---|---|
| S | architecture_diagram, architecture_review, conflict_check, coherence_check, acceptance_specs, manual_test |
| M | none |
| L | none |
Tier S additionally disengages the land-time coherence gate entirely.
Steps that are not tier-skippable at any tier include the whole BUILD spine — build, build_review, test_suite — plus plan, stories, prd, rebase, finish, prd_audit, and architecture_review_as_built. The latter two used to tier-skip at S; they now run at every tier, with only their individual checks (as-built) or applicability (whether any acceptance criterion changed, prd_audit) conditional — see gates.
A skipped step is marked skipped, which satisfies downstream prerequisites. The chain never breaks because of a skip.
Where the tier comes from
Three separate paths resolve a feature’s tier, and each has its own fallback. They are not reconciled with one another — the path in play decides which fallback you get.
| Path | Where the tier is read | When no tier is found |
|---|---|---|
| Daemon dispatch | .docs/complexity/<slug>.md on the base-branch tree, via the Tier: <S\|M\|L> line; a dated slug falls back once to the date-stripped stem when that stem is unambiguous | M — the daemon’s own fallback for an absent or garbled marker, logged once per slug with the paths tried |
ai-conductor inline --interactive, and the default run mode | The persisted tier, else the complexity step’s assessment, confirmed by the operator | L, when the assessment fails and there is no prompt to fall back on |
The marker file is the only durable carrier. A tier chosen in an interactive run reaches a later daemon build only if the complexity step committed .docs/complexity/<slug>.md under the plan stem — or under its date-stripped form, the one relaxation the daemon allows (undated-stem fallback) — because that file is the only thing the daemon looks at. To pin a tier for a daemon build, commit the marker. See artifacts for the marker’s format.
Track skips
The track split touches exactly one step plus one land-gate layer.
| Difference | product | technical |
|---|---|---|
prd (index 4) | Runs | Skipped — no product requirements to spec |
prd_audit (index 17) | Runs — stories’ acceptance criteria are the audit key on both tracks; PRD FRs are context only when a PRD exists | Runs |
Land-time coherence fr layer | Required | Not required; the layer degrades away |
Everything else is identical on both tracks. The track is decided in explore and recorded in .docs/track/<slug>.md, which the daemon reads with the same undated-stem fallback as the tier marker. A missing track resolves to product, so nothing is track-skipped when the track is unknown.
Other skip mechanisms
| Mechanism | Rule | Steps affected |
|---|---|---|
skipWhenSkipped | Skip when a named upstream step ended skipped, for any reason | The mechanism (shouldSkipForUpstreamSkip) still exists but no current step definition declares it — architecture_review_as_built dropped its use when it stopped mirroring architecture_review’s tier-S skip |
| Bootstrap mode | bootstrap_mode: new skips the step with a mode_skip event | assess only |
configDisableAllowed | Opt-in to steps.<name>.disable: true. Config validation rejects disabling any other gating or structural built-in | manual_test only |
when: | Per-step conditional expression in config. It has the same authority boundary as config disable: advisory steps and the opted-in built-in are allowed; other gating and structural steps are rejected | Advisory steps and manual_test |
Step artifacts and gate behavior
Each step’s completion gate reads evidence from disk. The engine recomputes verdicts from that evidence rather than trusting an agent’s self-report. Committed artifacts live under .docs/; uncommitted run evidence lives under .pipeline/. See artifacts for file-by-file detail.
| Step | Evidence | Committed | What satisfies the gate |
|---|---|---|---|
worktree | — | — | Prerequisites only; no artifact check |
memory | — | — | Prerequisites only |
explore | — | — | Prerequisites only. Notes are ephemeral; the track marker is written but is not a completion glob |
complexity | — | — | Prerequisites only |
prd | .docs/specs/*.md | yes | At least one matching file |
architecture_diagram | .docs/architecture/*.md | yes | At least one matching file |
architecture_review | .docs/decisions/architecture-review-*.md, .docs/decisions/adr-*.md | yes | At least one matching file |
stories | .docs/stories/**/*.md | yes | At least one matching file. The verdict layer additionally requires this feature’s stories doc to carry ### Happy Path and ### Negative Path(s) sections, each with at least one Given/When/Then bullet, and no Status: DRAFT |
conflict_check | .docs/conflicts/*.md | yes | At least one matching file |
plan | .docs/plans/*.md | yes | At least one matching file. The verdict layer additionally requires every story unit in this feature’s plan to be covered by at least one task, and fails when the feature’s plan cannot be resolved among several |
coherence_check | .docs/coherence/*.md | yes | At least one matching file, named with the plan’s filename stem |
acceptance_specs | spec files in the project’s test dirs, plus .pipeline/acceptance-specs-red.json | specs yes, evidence no | At least one spec file and RED evidence proving the feature’s own specs ran and failed. A spec that was skipped, deselected, or hit a collection error does not establish RED |
build | .pipeline/task-status.json | no | No .pipeline/halt-user-input-required marker, every task completed or skipped, and a clean working tree whenever the status probe establishes one. The post-rebase closure applies the same conjunct: a reapplied autostash blocks BUILD until the named paths are committed or discarded. An absent or failed probe fails open to the legacy behavior. Task status is re-seeded and re-derived on each evaluation, so forged rows fail |
build_review | .pipeline/build-review.json | no | A fresh, valid effective PASS from the enabled rubric set (currently only testQuality, off by default — an empty enabled set is a vacuous PASS). An enabled test-quality rubric with a valid empty typed scope also passes without a reviewer or counterfactual dispatch. Missing, prior-session, malformed, unresolved findings, or uncovered scope-incomplete/infrastructure faults all block. Raw outcomes and valid findings remain recorded; effective dispositions control routing. |
test_suite | .pipeline/test-suite-evidence.json | no | A live re-inspection returning CURRENT. File presence alone can never satisfy this gate |
manual_test | .pipeline/manual-test-results.md | no | The latest attempt section has no FAIL rows and is fresh. WARN rows record unavailable browser capability without blocking. After a recorded FAIL, HEAD must have moved before a later FAIL-free attempt is accepted |
prd_audit | .pipeline/prd-audit.md | no | Fresh audit with exactly one graded verdict row — PASS, FIXABLE, PLAN_GAP, or OVER_SCOPE — for every acceptance criterion across the feature’s stories; a FIXABLE row must name its owning plan task. A no-owner finding belongs in ## Findings without an owning criterion as one unique NC.<n> OVER_SCOPE row; an outside-visible finding blocks until the operator decides it, and that decision binds to its evidence summary. A missing, invalid, or duplicate row blocks with a diagnostic. Verdict rows are read only from the ## Verdict Table section when the report carries that heading, so a narrative table elsewhere (e.g. a prior-cycle history table) cannot be read as a current verdict; a report without the heading is scanned whole. An unresolvable or unreadable criterion set blocks fail-closed |
architecture_review_as_built | .pipeline/architecture-review-as-built.md | no | A standalone Verdict: line — optionally decorated as a Markdown heading, such as ### **Verdict: APPROVED** ### — reading APPROVED, APPROVED WITH DRIFT NOTES, or PLAN_GAP with Outcome delivered: yes. A BLOCKED report must carry one ## Blocking Findings table (Finding, Class, Governing clause, Summary): a DESIGN finding halts for a human decision and names its governing clause; an all-REMEDIABLE table may take the enabled bounded remediation route. If that route cannot run or produces no usable plan, its halt names the disabled/non-daemon/planner cause and lists the findings. Malformed or exhausted reports halt needs-human. PLAN_GAP with Outcome delivered: no, missing, or unrecognized evidence also blocks |
rebase | — | — | Computed from live git state, not a file |
finish | .pipeline/finish-choice | no | A fresh final-outcome marker. Interactive intent is acquired by the foreground prompt host before publication; the coordinator writes pr or keep through finish-record only after the corresponding evidence is coherent. Legacy merge-local and discard markers remain readable but are never synthesized by unattended FINISH |
bootstrap, remediate, attribution_verify | — | — | No completion glob. remediate’s output, .pipeline/remediation.json, is read directly by the engine to route |
assess | .docs/decisions/technical-assessment-*.md | yes | At least one matching file |
Every predicate is fail-closed: missing, stale, malformed, or non-passing evidence leaves the gate unsatisfied. Durable verdicts are written to .pipeline/gates/<step>.json.
A step the engine resolves by skipping never runs its predicate, but it still writes a verdict: {"satisfied": true, "reason": "skipped: <cause>"}. The skipped: prefix marks a gate that was deliberately not run, so it is never mistaken for evidence that passed. This covers every skip — tier, track, bootstrap mode, upstream skip, disable: true, a false when:, and an advisory step auto-skipped after a failed completion check (whose reason carries the failure). See gates.
Starting from a step
--from <step> sets the loop’s starting index by a linear name lookup over the resolved step registry:
ai-conductor inline "<feature description>" --from build
Accepted values are the 20 sequential step names above, in underscore form, plus any custom step name inserted through the steps config key. There is no dash normalization in the engine — --from conflict-check is not the same string as conflict_check.
Known limitation.
--fromis unvalidated. An unrecognized name resolves to index-1with no error and no event, and the run proceeds from that index. Check spelling and underscore form before relying on it. Tracked in #1027.
Step-to-skill mapping
Dispatch reads a single map keyed by step name. That map is the authority for what a step invokes; the skillName field on the step definition is not consulted at dispatch time.
Three steps dispatch no skill at all and run entirely in the engine: build_review, test_suite, and attribution_verify. Of these, build_review dispatches its registered rubric set (currently only testQuality) and attribution_verify dispatches its own attribution-audit logic, both from engine code; test_suite is a deterministic aggregate verifier.
Two steps dispatch the conduct skill with an argument rather than a skill of their own name: worktree runs /conduct worktree and complexity runs /conduct complexity.
Known limitation. Two step definitions carry a
skillNamenaming a skill directory that does not exist:worktreedeclaresskillName: 'worktree'andattribution_verifydeclaresskillName: 'attribution-verify'. Neitherskills/worktree/norskills/attribution-verify/is on disk. Nothing breaks, because dispatch uses the invocation map and the model table uses its own skill-to-step map, but the field misleads anyone reading the step definition. The repository’s integrity suite validates/skill-namereferences inside SKILL.md files, notskillNamefields in TypeScript, so these are unguarded. Tracked in #1018.
Related pages
- skills — the catalog of skills these steps dispatch.
- artifacts — every
.docs/artifact and.pipeline/state file. - gates — what a gate is and why it fails closed.
- sdlc-phases — why there are five phases, tracks, and tiers.
- cli — every command and flag, including
--from. - configuration — disabling steps, custom steps,
validation_concurrency.