ai-conductor CLI reference
Every command, subcommand, flag, and exit code of the ai-conductor engine. Commands are grouped operator-first: the ones you type, then the ones the engine invokes on your behalf.
The supported launcher is ai-conductor. The installed conduct alias resolves to that launcher, prints a one-line deprecation warning, and remains available only during the migration window.
Launcher
bin/ai-conductor is a shim. It parses none of its own arguments and passes everything through to the bundled engine at src/conductor/dist/index.js, resolving the dist symlink to a concrete dist-versions/<id>/index.js so a running process is pinned to one engine version. It exports ASDF_NODEJS_VERSION from src/conductor/.tool-versions when asdf is on PATH, then execs node, so the engine’s exit code is the shim’s. The current pin is Node.js 26.7.0, and the engine requires Node >=26.0.0 when the asdf override is unavailable.
| Condition | Message | Exit |
|---|---|---|
dist neither exists nor is a symlink | ai-conductor: missing … plus run 'npm run build' in src/conductor/ | 1 |
dist symlink unresolvable or not a regular file | ai-conductor: dist symlink is broken (…) | 1 |
| Uncaught engine error | Fatal: <message> | 1 |
The bundled skills/intake/scripts/intake-file helper, bin/intake-backfill, and bin/quarantine-engineer-signals are separate entry points, not ai-conductor subcommands.
ai-conductor --version
ai-conductor --version
ai-conductor -V
ai-conductor version
Prints one line naming the installed harness and the engine build actually executing, then exits 0:
ai-conductor 0.104.0 (engine 20260830T172539Z-76db750aa2a8)
The first value is the repo VERSION file; the second is the pinned dist-versions/<id> the launcher resolved — the same build id daemon status prints as version:<id> and a shipped record stamps as engine_version. Both are resolved relative to the running module, not the working directory, so the report is correct when the CLI is reached through the installed ~/.local/bin/ai-conductor symlink.
The command is advisory and never fails: an unreadable VERSION file reports 0.0.0, and an unpublished dev/tsx run — which pins no dist-versions/<id> — reports engine dev. All three spellings must be the whole invocation; ai-conductor daemon --version is a daemon invocation, not a version request. Like every other unsanctioned command it is refused inside a daemon-managed session (see Daemon-session refusal).
ai-conductor update
ai-conductor update [args...]
Runs the owning harness checkout’s bin/update from any working directory. It forwards every argument, inherits the terminal, and returns the updater’s exit status. Use the same options as bin/update, for example:
ai-conductor update --set-channel stable
The command exits 1 without starting an updater when it cannot find the harness checkout, its bin/update, or a checkout .git directory. Its diagnostic names the failed path or root.
bin/install
bin/install --channel <stable|tagged|main>
On a first install, --channel records the selected update channel without prompting. It takes precedence over AI_CONDUCTOR_CHANNEL; with neither explicit value, an interactive installer prompts and a non-interactive installer falls back to stable. Empty environment values are treated as unset; an invalid explicit value exits before installation writes global state. The option is first-run only: change an existing selection with bin/update --set-channel.
bin/update
bin/update
bin/update --auto
bin/update --set-channel <stable|tagged|main>
With no arguments, forces an attended check of the configured harness update channel. --auto honors conductor.auto_check and is the startup form spawned by ai-conductor. --set-channel accepts stable, tagged, or main and changes configuration without moving the checkout.
stable is the fresh-install default. It fetches origin/stable, verifies that its head is an exact semver tag, requires a clean local stable checkout and a fast-forward relationship, then prompts before fast-forwarding to that captured commit and running bin/migrate. An untagged target is rejected before local mutation. If migration fails after the fast-forward, the updater restores the prior stable branch head and recorded version.
A stable checkout left in detached HEAD — as a pre-v1 updater’s git checkout vX.Y.Z did — is recovered rather than skipped. When HEAD is detached and is an ancestor of (or equal to) origin/stable, the updater offers to re-attach the stable branch at the approved release commit, then runs bin/migrate; without a TTY it prints git checkout -B stable <sha> && bin/migrate instead. The offer is made even when HEAD already equals origin/stable, because the detachment is itself the defect. A detached HEAD outside origin/stable’s ancestry is a deliberate checkout and is left untouched, and every other guard — exact semver tag, clean worktree, and applying only the approved SHA — is unchanged. tagged follows semver tag checkouts; main follows every merge to the development branch.
bin/migrate
bin/migrate [--yes|-y] [--dry-run]
Runs bin/install --update, then, from a consumer project, finds runnable bash migration fences in each release through the installed target version. If it cannot read the installed-version configuration, it exits non-zero before selecting migrations. On a project’s first run, the lower bound is the recorded currentVersion (or the target version itself, if currentVersion is an unparsable channel identity such as main@<sha>); that bound is then pinned into the ledger as its candidateBaseline and used on every later run, so it stays fixed even as currentVersion advances. It exports HARNESS_DIR to every block. Blocks are ordered by release version and document position.
--dry-run shows the install action and selected migrations without executing or recording them. Without --yes, an interactive run previews each block and accepts yes, no, all, or stop; skipped and stopped blocks remain pending. Without a TTY and without --yes, it executes no project migrations and leaves every block pending. --yes executes each selected block without prompts.
After each successful block, the runner records its release and content digest in a per-project, human-readable ledger under ~/.ai-conductor/migrations/. A recorded digest is already applied; changed content at the same release is a new candidate. A failed block stops the run immediately: earlier successful blocks remain recorded and later blocks remain pending. Unknown flags exit 2. Every run, including a failure, ends with Migration summary: applied=<n> skipped=<n> failed=<n> already-applied=<n>.
Exit code conventions
| Code | Meaning | Where it appears |
|---|---|---|
| 0 | Success, or an advisory command that never blocks | most commands |
| 1 | Usage error, validation failure, or a refused operation | most commands |
| 2 | Usage guide printed to stderr | task, evidence, derive-feedback |
Commands that are advisory by contract always exit 0 regardless of outcome: overlap-scan, kpi, shipped-record, shipment-evidence audit, and render-diagrams in its default (non---check) mode.
Daemon-session refusal
Every provider session the engine dispatches (daemon builds, reviews, interactive-conductor steps, self-host candidates — both providers, invoke and interactive paths) carries CONDUCT_DAEMON_SESSION=1 in its environment. When that marker is present, ai-conductor refuses to run — printing ai-conductor may not be invoked from inside a daemon-managed session; the engine owns all conductor operations for this run and exiting 1 — before any subcommand parsing. This exists because dispatched maker sessions have recursively invoked the conductor (including daemon park/unpark/restart and reseal), corrupting the run that dispatched them; the engine that started the session owns all conductor operations for it. The only exceptions are the session-sanctioned worker commands the harness’s own skills and hooks require a session to run — scoped-run, overlap-scan, plan-protected-targets, manual-test-record, closeout-event, and derive-feedback, scope-check, and task (start/done) — which stay available under the marker. There is no config off-switch; enforcement lives in src/conductor/src/execution/daemon-session.ts.
ai-conductor build-review
ai-conductor build-review findings --feature <slug> [--json]
ai-conductor build-review accept --feature <slug> --lap <lap> --finding <id> --rationale <text>
ai-conductor build-review record-reduced-coverage --feature <slug> --lap <lap> --rubric <rubric> --rationale <text>
findings is read-only and renders the current feature’s raw and effective build-review findings plus autonomous remediation-case outcomes; --json includes those cases under cases. Human output keeps autonomous outcomes separate from operator dispositions and includes each case’s disposition, resolution, source ids, effect state, rationale, and, for a refutation, its claim and assertion verdicts. A missing case store is a successful listing with no autonomous cases. An unreadable or unsupported case store makes findings name the store and exit 1 rather than silently omitting its cases. accept changes state only from an interactive terminal with a resolved local operator identity. It requires the exact current lap identity, canonical finding ID, and a non-empty rationale. Acceptance works on every registered rubric — currently only testQuality — because the identity the engine prints is the identity the disposition store accepts back. Stale, unknown, unauthorized, or non-interactive requests are refused without changing artifacts and exit 1; successful findings inspection and accepted exact-current findings exit 0. A refusal names the check that failed — unreadable aggregate, non-current lap, unknown finding, or a disposition-store rejection — and records the same reason on the feature’s build_review_disposition_refused event. record-reduced-coverage also requires an interactive terminal, resolved local operator identity, exact current lap, named rubric, and non-empty rationale. It derives the closed infrastructure cause from the current review state; callers cannot supply one. It records a decision only when that rubric currently has an exhausted mechanical infrastructure fault, or a current projection-oversized fault (which halts without consuming that allowance). Judged, skipped, duplicate, unknown-rubric, allowance-remaining for every other cause, and stale-review requests are refused without changing the decision state.
ai-conductor scope-check
ai-conductor scope-check <commit-message-path>
This hook-only command is invoked by the generated commit-msg hook for a task-attributed commit. It reads the active in-progress task row from .pipeline/task-status.json, compares its declared files with the staged paths, and prints every undeclared path with a Scope: <path> — <rationale> trailer that can justify an intentional widening. It is not an operator workflow; worktree provisioning wires it automatically.
| Exit | Meaning | Hook behavior |
|---|---|---|
| 0 | Allowed, including a verified violation in the default report-only mode | Allows the commit; a reported violation remains visible in containment-floor step output and warnings |
| 2 | Positive scope refusal when build_review.scopeContainmentEnforced is true | commit-msg converts it to exit 1 and blocks the commit without changing the working tree or index |
| other | Abstention: no Task trailer, unusable/missing task state, or a checker failure | Logs the abstention and allows the commit |
The report-only default is deliberate. Set build_review.scopeContainmentEnforced: true to enable refusal. A Scope: trailer documents a widening; its path, rationale, task id, and commit SHA are supplied directly to build_review, and it never bypasses the grader’s semantic scope judgment.
ai-conductor inline
Runs the SDLC pipeline in the foreground.
ai-conductor inline [options] "<feature description>"
The inline token is mandatory. A bare ai-conductor "<feature>" is rejected with three lines of guidance and exit 1. Commander declares an inline subcommand so ai-conductor inline --help renders, but it never dispatches it — the engine strips the inline token first and parses the remainder against a subcommand-less base program, which is why a free-text feature description is never mistaken for an unknown command.
| Positional | Arity | Required | Effect |
|---|---|---|---|
[feature] | 0..1 | conditional | Feature description. Drives slug and worktree naming and auto-resume matching. |
A feature description is required unless at least one state flag is present. The state flags are --resume, --status, --cleanup, --reset, --diagnose, --report, and --from. Without one, parsing throws Feature description is required when no state flags are provided and the run exits 1.
| Flag | Type | Default | Constraints | Effect |
|---|---|---|---|---|
--resume | boolean | false | — | With a feature description, honored by the auto-resume gate and clamps the start index. Before dispatch, a resumed run whose derived entry is blocked by an earlier unfinished prerequisite moves back to the earliest dispatchable prerequisite. Without a feature description, cleans up merged worktrees, scans .worktrees/, and shows a selection menu (exit 1 when no features are found), then repoints projectRoot, pipelineDir, and the state file at the selected worktree. |
--fresh | boolean | false | — | Suppresses auto-resume detection so an existing worktree for the same slug is not silently reused. This is its only effect; it is never passed to the engine’s conductor object. |
--auto | boolean | false | deprecated; mutually exclusive with --interactive | Prints Error: --auto is deprecated. Use \ai-conductor daemon start` instead; see docs/guides/running-the-daemon.md.` and exits 1 before starting the inline pipeline. Use the daemon for unattended execution and retained logs. |
--interactive | boolean | false | mutually exclusive with --auto | Run mode interactive: opens a Claude REPL for every conversational step except complexity, conflict_check, architecture_diagram, and rebase. dangerouslySkipPermissions stays off, so a human approves each action. |
--status | boolean | false | — | Prints ## Conductor State and the state file as pretty JSON, then returns. No provider session. |
--from <step> | string | — | must be a step name | Sets the start index to that step. Also suppresses auto-resume. An unrecognized step name exits 1, printing the invalid value and every valid step name (built-ins plus any config-declared custom steps). |
--cleanup | boolean | false | — | Scans resumable features, reads pr_url from each conduct-state.json, checks whether the PR merged, and prompts Remove merged worktree "<name>"? [y/n] per merged feature. Prints a count. |
--reset | boolean | false | — | Writes an empty state object, prints State cleared., and returns. |
--diagnose | boolean | false | — | Non-mutating. Resolves the worktree, then verifies state completeness. SHIP-gating steps recorded as skipped are satisfied without an artifact check; steps that ran still require their normal evidence. State OK prints State OK… and exits 0; gaps print a gap report plus remediation text to stderr and exit 1; an orphaned state exits 1. |
--report | boolean | false | — | Read-only. Renders .pipeline/events.jsonl as step durations, retry hotspots, token spend, and kickbacks by source gate (including BUILD re-entries), then exits 0. A report error exits 1. No provider session. |
--cooldown <seconds> | integer | 10 | no range check | Seconds to pause between steps. A non-numeric value parses to NaN. |
--model <name> | string | — | alias or full model ID | Overrides the model for every step. Beats every configured source. See models. |
--effort <level> | string | — | one of low, medium, high, xhigh, max | Overrides the effort for every step. Beats every configured source. An invalid level exits 1, printing the invalid value and the valid levels. |
--view <mode> | enum | full | full, focus, log | Dashboard layout. Anything other than exactly focus or log silently coerces to full. |
--tail-lines <n> | integer | 20 | 0 disables the pane | Maximum lines of post-step stdout shown in the tail pane. |
-h, --help | boolean | — | — | Prints help and exits 0. |
Passing both --auto and --interactive prints Error: --auto and --interactive are mutually exclusive and exits 1. Passing --auto alone prints its daemon-directed deprecation notice and exits 1.
Inline output includes every event the sink registry declares renderable. Halts, kickbacks, unsatisfied gate verdicts, and gate-loop convergence have dedicated lines; other renderable events are summarized by type.
Auto-resume
When a feature description is given and none of --resume, --fresh, or --from is present, the engine looks for an existing worktree for that slug.
| Detection | Behavior | Exit |
|---|---|---|
| Resumable | Redirects to the worktree and prints Resuming "<desc>" at <n>/<total> (after <step>). Use --fresh to start over. | continues |
| Complete, evidence intact | Prompts Feature "<desc>" is already marked complete (<path>). Start over? [y/N]; anything but y returns | 0 |
| Complete, evidence gaps | Prints the gap report, prompts Roll back feature_status and resume at the first failing step? [Y/n/q]; the default deletes feature_status, flips each failed step to pending, and resumes | continues |
| Orphaned state | Lists the expected worktree locations and two remedies, then refuses | 1 |
Recovery procedures live in worktree and evidence recovery.
Side effects of a run
An inline run creates .pipeline/, ensures .claude/settings.json exists with project-scoped permissions, loads .ai-conductor/config.yml (a non-missing config error exits 1) and the merged user config, reads or creates .pipeline/conduct-session-id, discovers plugins under $HOME/.ai-conductor/plugins and <projectRoot>/.ai-conductor/plugins, appends to .pipeline/events.jsonl and .pipeline/audit-trail/events.jsonl, may run the /bootstrap and /assess prelude, spawns bin/update --auto in the background with all failures swallowed, and creates or cleans git worktrees. OTel export is opt-in and off unless configured.
ai-conductor monitor
ai-conductor monitor all
ai-conductor monitor <project>
Runs the foreground guided-resolution queue for halted features. It does not start, attach to, or dispatch the daemon.
| Selector | Effect |
|---|---|
all | Monitor halted features across every registered project. |
<project> | Monitor halted features in one registered project by name. |
The monitor remains active until interrupted (Ctrl-C). It offers one halted feature at a time in a guided session and re-derives the queue between passes, so resolved or parked work is not offered again. An empty queue prints Monitor queue is empty; staying active. and rechecks every second.
Each offer prints <project>: <slug> — <reason> (<halt class>) [<band>; <basis>]. Queue order:
- Undeferred halts before deferred halts.
- Within each group, the daemon backlog’s priority bands (basis
priority-band). When priority labels cannot be read within 2 seconds, discovery order is kept and the basis isfallback.
When a guided session returns, the monitor prompts [skip/continue] on a TTY. skip records a deferral in the project’s .daemon/deferrals.json; any other answer, or no TTY, keeps the item in the queue. A deferral lasts only while the same HALT marker (same size and modification time) remains; a new or rewritten halt is offered as undeferred.
Each pass also runs halt-issues sweep when the project’s origin remote is on GitHub. Sweep failures are reported once per failure kind and do not stop the queue. Offers, session opens, deferrals, and session ends are recorded on the event spine as monitor_item_offered, monitor_session_opened, monitor_item_deferred, and monitor_session_ended.
| Condition | Output and exit |
|---|---|
| Valid selector, then interrupt | Prints Monitor stopped.; exits 0 unless an inventory error occurred during a pass. |
| Unknown project, unreadable registry, or unreadable selected project | Reports the inventory error; exits 1 after the monitor stops. |
| Configured provider unresolvable or not built in | Prints monitor: unable to resolve provider… or monitor: unregistered provider <name>.; exits 1 before the queue starts. |
Missing selector, extra argument, or selector beginning with - | Prints Usage: ai-conductor monitor all\|<project> to stderr; exits 1. |
| Invoked from a daemon-managed session | Refused by the daemon-session guard before queue enumeration; exits 1. |
monitor is intentionally unavailable to daemon-managed provider sessions. Run it from an operator terminal, not from a dispatched build or review session.
ai-conductor daemon
Runs the background build/ship loop, or manages one. Procedures are in running the daemon; this section is the flag surface.
--help or -h anywhere after daemon short-circuits to daemon help and exits 0. That guard is deliberately checked before every daemon dispatcher — without it, daemon --help would be treated as an unknown flag and launch a daemon run.
A non-flag token after daemon that is not one of status, logs, park, unpark, reclaim-worktree, start, stop, restart, connect, debug, pause, resume prints conduct daemon: unknown subcommand '<token>'. plus the daemon help to stderr and exits 1.
Running the daemon
ai-conductor daemon [--concurrency <n>] [--max-items <n>] [--continuous] [--max-cost <tokens>]
[--max-runtime <seconds>] [--idle-poll <seconds>] [--max-idle-polls <n>]
[--no-watch] [--completed | --all]
| Flag | Type | Default | Effect |
|---|---|---|---|
--concurrency <n> | integer | 1 | Effective feature-executor pool width. An explicit flag wins over daemon_concurrency; otherwise the configured value wins over the default. Values above 1 run up to that many eligible features concurrently. |
--max-items <n> | integer | unset | Stop after this many features. |
--continuous | boolean | false | Idle-poll instead of draining the backlog once. |
--max-cost <tokens> | integer | unset | Total output-token ceiling for the run. |
--max-runtime <seconds> | integer | unset | Wall-clock ceiling for the run. |
--idle-poll <seconds> | integer | 60 | Seconds between polls when the backlog is empty. |
--max-idle-polls <n> | integer | unbounded | Stop after this many consecutive empty polls; dispatching a feature restarts the count. |
--no-watch | boolean | watcher on | Drops the HALT-marker filesystem watcher and relies on polling alone. Not listed in --help. |
--completed, --all | boolean | false | Include already-processed features in the startup dashboard’s console output. The persisted log sink never includes them. Not listed in --help. |
Known limitation.
--idle-poll’s help string reports a default of 5 seconds; the code supplies 60 when the flag is absent, so an unflagged--continuousdaemon polls once a minute, not every five seconds. Pass--idle-pollexplicitly if the interval matters. Tracked in #1012.
Every integer flag reads the next token, and a token beginning with -- is treated as absent. So ai-conductor daemon --max-items --continuous silently uses the default for --max-items rather than reporting an error.
--continuous with no ceiling — no --max-items, --max-cost, --max-runtime, or --max-idle-polls — prints WARNING: --continuous with no ceiling … runs unbounded; Ctrl-C to stop.
When a tmux session for the repo already exists, a bare daemon run wires a self-restart handler so a queued restart respawns the pane at the next idle boundary instead of exiting.
daemon status
ai-conductor daemon status
Takes no flags. Sweeps the project registry and prints one badge line per repo: state, name, path, pid, since, version:<engine-version-id>, pause metadata, the last log line with its mtime, and session:up or session:down. A row with a dead pane or stale pid also shows its matching witnessed exit (killed by <signal> or exited <code>) when available; every row with a recorded sample shows the latest RSS as mem <MB> at <timestamp>. It then prints a GATED: section from .daemon/gated.json, a BLOCKED section from .daemon/blocked.json, and an attribution agreement: N% (n=…) line from .daemon/attribution-accuracy.jsonl; all are skipped for repos whose path is missing.
BLOCKED is the last completed discovery pass: every line contains the slug, machine-readable reason, and remediation text, followed by the snapshot age. An empty valid snapshot prints that no specs are blocked. If the snapshot is missing, malformed, or unreadable, status reports blocked state unknown rather than implying an all-clear. This is a read-only local-file view: it does not invoke Git, GitHub, or the network. The daemon startup dashboard does not yet render this section; that UI work is tracked in #1332.
For every in-progress feature it also prints a PLAN GROWTH [<slug>]: line reading the feature’s kickback ledger and resolved prd_audit cap: the plan’s authored task count, the number of tasks added so far (broken down by the gate that added them, when any), and how many remain under the cap.
Nine rendered states. restart-pending and dead-pane are overlays: they take precedence in the badge, but the underlying liveness and pause facts stay on the row.
| Badge | State | Meaning |
|---|---|---|
● running | running | Live pid owns .daemon/daemon.pid. |
⏸ paused | paused | .daemon/PAUSED present, pid alive or absent. |
⏸ paused (process dead) | paused_dead | Pause marker present and the pidfile owner is dead. |
○ stale | stale | Pidfile present, its owner pid is dead and reclaimable. |
· stopped | stopped | No pidfile, or a corrupt one, and no pause marker. |
✗ path missing | path-missing | The registered path no longer exists. |
✗ unreadable | unreadable | The repo’s daemon state could not be inspected. |
⏳ restart-pending | restart-pending | .daemon/RESTART-PENDING is queued. Renders as ⏳ restart-pending (waiting on <slug>) when it names a blocking feature. |
⚠ session-up/process-dead | dead-pane | The tmux session exists but its pane has died. Never the same as running. |
Exit 0 for any sweep, including one that reports stale or missing entries. Exit 1 only when the registry itself cannot be read. An empty registry prints No projects registered. Use conduct register [path] to add one. and exits 0.
daemon logs
ai-conductor daemon logs [--repo <path>] [--follow] [--all] [--lines <n>]
Reads .daemon/daemon.log.
| Flag | Type | Default | Effect |
|---|---|---|---|
--repo <path>, --repo=<path> | path | cwd | Repo whose log is read. |
--follow, -f | boolean | false | Streams appended lines from the current end of file until SIGINT. -f is not listed in --help. |
--all | boolean | false | Iterates the registry, printing a ==> <path> <== header per repo. |
--lines <n>, -n <n>, --lines=<n> | integer | whole file | Tail length. A non-integer or non-positive value is ignored and the whole file is printed. Not listed in --help. |
--follow with --all prints --follow is not supported with --all; showing a static snapshot. and does not follow. A missing log prints (no daemon log yet for <path>) and contributes 0. Exit 1 when a log is unreadable, or when --all is used and the registry cannot be read.
daemon park and daemon unpark
ai-conductor daemon park <slug>
ai-conductor daemon unpark <slug>
Both act directly on the filesystem before any daemon boot, and both resolve the main repo root via git rev-parse --git-common-dir, so they work from inside any worktree.
park validates the slug — either .docs/plans/<slug>.md or .worktrees/<slug> must exist — and writes .daemon/parked/<slug>. It prints Parked '<slug>' — it will not be dispatched or re-kicked until unparked. plus Marked for park: <path>. An already-parked slug prints '<slug>' is already parked (originally parked at <ts>) — no change. and exits 0. An unknown slug exits 1.
After writing or confirming the marker, park reports the observed work state. A live daemon with a current provider attempt prints `Work for ‘
- ` | comma- or space-separated paths | none | Candidate paths. Consumes every following argument up to the next `-`-prefixed one; each is split on commas, trimmed, empties dropped. | | `--source-ref ` | string | unset | Linked issue reference swept for open blockers. | | `--base ` | string | the origin default branch, else `main` | Base ref used to find each sibling branch's merge base; the scan compares only paths that branch contributed after that point. | | `--cwd
` | Absent paths are still compared against sibling branches. Advisory by contract: it always exits 0. Even an unexpected error prints `overlap-scan: unable to complete scan ()` and still returns 0. Reads git and queries `gh`; writes nothing. ## `ai-conductor plan-protected-targets` ```bash ai-conductor plan-protected-targets .docs/plans/.md ``` Blocking plan-authoring check for tasks that name another feature's artifact under `.docs/architecture/`, `.docs/decisions/`, `.docs/plans/`, `.docs/specs/`, or `.docs/stories/`. It reads the named plan, resolves each task's `**Files:**` set (including `same` inheritance), and writes nothing. Own-feature paths and unsealed `.docs/` paths pass. Every task's prose is also scanned, whether or not it has a `**Files:**` line: a protected artifact cited in a backtick path (e.g. `` `.docs/specs/other-feature.md` ``) is reported alongside any declared `**Files:**` violation, since a `**Files:**` declaration does not resolve a foreign protected reference elsewhere in the task. | Outcome | Output | Exit | | --- | --- | --- | | No violations | `No protected-target violations found.` | 0 | | Violation | One line per offending path: `Task : — return this amendment to DECIDE; BUILD tasks must not target protected artifacts.` | 1 | Run it before committing a plan. Correct the accepted artifact during DECIDE and re-author the task; do not hand the amendment to BUILD. The land gate repeats this check when a spec is landed. ## `ai-conductor kickback-budget` ```bash ai-conductor kickback-budget inspect --feature [--format human|json] ai-conductor kickback-budget raise --feature --gate --by --rationale "" ai-conductor kickback-budget reset --feature --gate --rationale "" ``` Use this operator-only command to inspect or authorize one recovery from a budget-cap halt. `raise` and `reset` require a local interactive terminal, a live HALT, matching cap evidence, and a machine-scoped operator identity resolved from your user config's `spec_owner` and otherwise from the `gh`-authenticated login — no environment variable names the operator. The rationale must be non-empty and at most 2000 bytes. They record an auditable authorization for the daemon to consume. The authorization is durably staged before its event is written, so an interrupted command is reconciled exactly once on the next command entry. The daemon consumes a matching authorization on its next loop iteration; it does not wait for an unrelated base-branch advance. Supported gates are `build_review`, `prd_audit`, and `architecture_review_as_built`. Inspect always lists all three gates; JSON renders an empty `adjustments` array for a valid gate with no adjustment history. Inspect also performs that reconciliation, so it is safe as the first command after an interrupted `raise` or `reset`. Inspect reports each gate's resume authorization against the live halt generation. Human output prints one `Resume authorization:` line per gate: | Line | Meaning | | --- | --- | | `none` | The gate has no recorded authorization. | | `consumed` | The daemon already used this authorization. | | `awaiting daemon sweep` | The authorization and the gate's cap evidence both match the live halt generation; the daemon consumes it on its next loop iteration. | | `stale (bound to halt generation ; live halt generation ); the daemon will not consume it` | The authorization or the gate's cap evidence names a different halt generation, or no halt is live. The daemon logs it as stale and never consumes it. | JSON adds a `resumeAuthorization` object to a gate that has one: `{ state, adjustmentId, boundHaltGeneration, liveHaltGeneration }`, where `state` is `consumed`, `awaiting-sweep`, `stale`, or `pending`. `pending` means the live halt was not read; inspect always reads it. `liveHaltGeneration` is empty when no halt is live. When several gates hold unconsumed authorizations, the daemon consumes only the one bound to the live halt generation. The eligible live halt class is per gate: `build_review`'s cumulative convergence cap halts `needs-human`, while the `prd_audit` and `architecture_review_as_built` remediation-lap caps halt `kickback-cap`. A `raise` or `reset` naming a gate whose live halt carries the other class is refused and changes nothing. `raise` follows the exhausted allowance in the live cap evidence: for a lap halt it increases that gate's effective lap limit by `--by`; for a plan-growth halt it increases the feature's effective plan-growth cap by `--by`. `reset` returns only a gate's lap count to zero without lowering an already authorized effective limit, and refuses plan-growth evidence; use `raise` for that recovery. Inspect shows the effective plan-growth cap and whether it is config-derived or raised. For either mutation, the command temporarily parks an unparked feature while it records the authorization and removes that temporary park after success. It preserves a park that already existed; unpark that feature when ready, otherwise the daemon leaves the authorization unconsumed. ## `ai-conductor decide-grant` ```bash ai-conductor decide-grant --slug --step --reason "" ``` Records one explicit authorization for an autonomous run to enter a named DECIDE step. Run it from any directory inside the repository, including a linked feature worktree; the command resolves the main repository checkout before writing `.daemon/grants/.json`. The daemon never creates this artifact itself. Outside a repository, the command exits non-zero and records no grant. The grant lives in the daemon-owned `.daemon/` directory, **outside every feature worktree**, and that placement is the authorization boundary. It previously lived at `.worktrees//.pipeline/decide-grant.json` — inside the directory a build agent writes all build long — so the subject of the authorization could write its own permission slip. A `decide-grant.json` inside a worktree authorizes nothing. | Flag | Required | Effect | | --- | --- | --- | | `--slug ` | yes | Feature worktree slug. A slash, `.` or `..` is rejected. | | `--step ` | yes | Exact DECIDE step to authorize. The grant does not authorize any other step. `plan` is rejected — see below. | | `--reason ` | yes | Operator's reason for allowing this one DECIDE entry. | **`plan` can never be granted.** The command exits non-zero when `--step plan` is passed, and the entry policy refuses `plan` before it consults a grant at all, so no grant of any provenance admits it. A daemon that re-plans rewrites an approved DECIDE artifact with no human at the gate — the same class of failure the re-entry was meant to repair. Drive the plan revision interactively, then resume the feature. The matching grant is consumed immediately before the provider dispatches the named step. A stale, malformed, mismatched, or already-consumed grant authorizes nothing and the run remains fail-closed. Clearing `.pipeline/HALT` or `.pipeline/HALT.class` is not an authorization; use the [DECIDE-entry recovery procedure](/ai-conductor/runbooks/stalled-or-stuck-feature.html#the-halt-refused-a-decide-entry). ## `ai-conductor halt clear` ```bash ai-conductor halt clear --feature --rationale "" ``` Clears a live halt after recording who authorized it and why — the audited alternative to removing `.pipeline/HALT` and `.pipeline/HALT.class` by hand. Run it from any directory inside the repository; it resolves the main checkout and the worktree `.worktrees/`. | Flag | Required | Effect | | --- | --- | --- | | `--feature ` | yes | Feature worktree slug. A slash, `.`, or `..` is rejected. | | `--rationale ` | yes | Operator rationale. Must be non-empty and at most 2000 bytes. | **Interactive-operator only.** The command requires `process.stdin.isTTY` and a machine-scoped operator identity resolved like [`kickback-budget`](#ai-conductor-kickback-budget): user-config `spec_owner`, otherwise the `gh`-authenticated login. In order, it: 1. Appends a `halt_clear_authorized` event (feature, operator, rationale, halt class, and the recorded `last_step`) to the worktree's `events.jsonl` and an `operator`-origin record to the audit trail. The audit writer is fail-closed: if either write fails, the command refuses and leaves the halt in place. 2. Removes `HALT.class`, then removes `HALT` — except for an `over-scope` halt, whose body is **renamed** to `.pipeline/HALT.cleared` so the next `prd_audit` lap can harvest the edited `over-scope-decisions` block. Edit that block before running the command. 3. Supersedes the feature's halt record with cause `operator`. A record failure prints `halt clear: warning — halt record …` but does not undo the clear. | Exit | Meaning | | --- | --- | | `0` | Halt cleared. | | `1` | Worktree missing, feature not halted, no operator identity, or the authorization event could not be recorded. Nothing is cleared. | | `2` | Non-interactive terminal or invalid rationale. Nothing is cleared. | Any halt class is accepted, including `kickback-cap`. A direct clear changes no budget, so an exhausted allowance halts again on its next cap check; use [`kickback-budget raise` or `reset`](#ai-conductor-kickback-budget) to recover a budget halt. The command grants the daemon no clearing authority. Fix the halt's cause first — see [clear a halt and let the feature resume](/ai-conductor/runbooks/stalled-or-stuck-feature.html#clear-a-halt-and-let-the-feature-resume). ## `ai-conductor rewind` ```bash ai-conductor rewind --to ``` Returns a halted feature to an earlier pipeline step. Run it from that feature's worktree, for example `cd .worktrees/`, after identifying the step that must run again. `--to` is required and must name a step in the resolved registry that is strictly earlier than the feature's recorded `last_step`. The command refuses an unknown, current, or later target without mutating state. It also refuses if the state changed while the rewind was being applied. On success, it marks the target and every later non-skipped step `stale`, clears their gate verdicts, then clears both `.pipeline/HALT` and `.pipeline/HALT.class` atomically. It emits an `operator_rewind` event and prints `Rewound to .` The next daemon dispatch begins at the rewound step. Do not edit `conduct-state.json`, gate files, or halt markers by hand; use this command instead. If clearing the derived records fails, `rewind` exits non-zero and restores the recorded step state, halt markers, and any gate verdicts it staged for removal, so the feature remains retryable. It reports the failure that stopped the rewind first; a subsequent `rewind: rollback failed:` message means that the corrective restoration also failed and needs operator attention. ## `ai-conductor reseal` ```bash ai-conductor reseal --slug --path [--path ...] --reason "" [--clear-halt] ``` Re-fingerprints named protected DECIDE artifacts against the current commit after an operator has reviewed and approved an amendment committed after first BUILD entry — the audited alternative to hand-editing `.pipeline/protected-artifact-seal.json`. Run it from the main repository checkout, not from inside the feature worktree. | Flag | Required | Effect | | --- | --- | --- | | `--slug ` | yes | Feature worktree slug (`.worktrees/`). A slash, `.`, or `..` is rejected. | | `--path ` | yes, repeatable | A protected artifact path to reseal. Every occurrence reseals one more path; at least one is required. | | `--reason ` | yes | Operator rationale for the reseal. A missing or blank rationale is refused and audited. | | `--clear-halt` | no | Also clear a resolved protected-artifact HALT in the worktree after resealing. | **Interactive-operator only.** The command refuses with `requires an interactive terminal` unless `process.stdin.isTTY` is true, so it cannot be reached from a `build`/`SHIP` step subprocess, an autonomous provider session, or a script piping stdin — only a human at a terminal can reseal. **Scoped, not a full rebaseline.** Only the listed paths are re-fingerprinted; every other protected path must still match its existing baseline or the whole reseal is refused — an operator cannot use `--path` to launder unrelated drift into the seal. Each listed path must already be protected and sealed, resolvable at the current commit, not deleted, and free of uncommitted changes (commit the amendment first). Refusal conditions — unknown worktree, missing rationale, non-interactive terminal, missing seal, an unlisted-path violation, or an invalid reseal target — are each written to the audit trail as `protected_artifact_reseal_refused` before the command exits 1; a successful reseal writes `protected_artifact_reseal` with the resealed paths' prior and new fingerprints. Both are audited with an `operator` origin, distinct from step-attributed records. See [artifacts](/ai-conductor/reference/artifacts.html#write-guards) for the seal format and [the stalled-feature runbook](/ai-conductor/runbooks/stalled-or-stuck-feature.html#the-halt-is-a-protected-artifact-violation) for the full recovery procedure. When the changed resealed path is a current coverage-binding DECIDE input, a completed coverage-binding result is invalidated. The next re-kick runs coverage binding before BUILD; its amendment judgement can reopen only completed tasks that the changed DECIDE obligation contradicts. No task is reopened merely because an operator resealed an artifact. The `--reason` rationale is not just an audit-trail entry: the next `build_review` on this feature renders every `operator-reseal`-triggered entry, with its paths, rationale, and commit range, in the grader's prompt as evidence to judge — see [operator-authorized protected-artifact reseals](/ai-conductor/explanation/gates.html#operator-authorized-protected-artifact-reseals). On success the command prints `Resealed protected artifacts: ` and exits 0. With `--clear-halt`, it additionally clears the worktree's `HALT`/`HALT.class` markers when a halt is present and its class is the protected-artifact class, and reports the outcome (`No halt to clear.`, or why a present halt was not cleared) on a second line. ## `ai-conductor evidence` ```bash ai-conductor evidence ``` The `evidence judge` gate was removed — per-task commit stamping is telemetry, not a gate, and citation-quality sampling now runs as a non-blocking spot audit. The command survives only so the token resolves to a clear message instead of an unrecognized-command error. Any argument form prints the retirement notice and exits **2**. The help string does not mention the non-zero exit. ## `ai-conductor closeout-event` ```bash ai-conductor closeout-event ``` Records one pipeline closeout timing event. `` must be one of `evaluator`, `simplify`, `architecture-diagram`, `memory`, `summary`; `` and `` are millisecond epoch timestamps. An unrecognized obligation prints the valid list to stderr and exits 1; otherwise it appends a `pipeline_closeout` event to the pipeline-owned sibling ledger, `.pipeline/pipeline-events.jsonl`, and exits 0. This command is called by a closeout obligation itself (for example the `evaluator` gate in the [pipeline skill](/ai-conductor/reference/skills.html)), not typed by an operator, so it never starts a conductor or daemon and works identically whether `pipeline` is running under the engine or invoked inline in a bare session. The sibling ledger is a separate single-writer file from `.pipeline/events.jsonl` — when a `build` step is running under the engine, a tail reader re-emits each recorded event onto the live bus for the daemon log, terminal UI, and OTel export, but `pipeline_closeout` is never persisted a second time into `.pipeline/events.jsonl`. ## `ai-conductor build-tail` ```bash ai-conductor build-tail [worktree] ``` Renders a read-only task/remediation/closeout timing rollup for one `build` step, decomposing its single opaque provider session into task execution, kickback remediation, and closeout ceremony. `[worktree]` defaults to the current working directory. Always exits 0. The rollup reads and merges the engine ledger (`.pipeline/events.jsonl`) and the pipeline-owned sibling ledger (`.pipeline/pipeline-events.jsonl`) for the given worktree, and reports one of three states: | State | Meaning | | --- | --- | | `measured` | At least one complete `build` window (`step_started`/`step_completed`) was decomposed. Reports, per window, its classification (`first-pass` or `re-entry`), task execution duration, post-resolution tick counts split into remediation and closeout, and closeout duration with a per-obligation breakdown — plus an aggregate across all windows. | | `partial` | The ledgers are readable but a window is unterminated, malformed, or an inline pipeline run (no engine) produced closeout events with no engine window boundaries. When closeout events exist, the closeout-only timeline is still rendered. | | `unavailable` | No `build` window and no closeout events were found. | A window's first tick determines its classification: if it already reads `resolved == total`, the window is a re-entry (the task graph was complete on entry); otherwise its task-execution segment ends at the first tick where `resolved == total`. Post-resolution ticks with `headMoved: true` classify as remediation; closeout classification comes from the merged `pipeline_closeout` timeline. See `.docs/architecture/build-post-task-tail-telemetry.md` for the full model. ## Internal commands, invoked by the engine These are dispatched by skills, hooks, and the daemon rather than typed by an operator. None appear in `--help`. They are listed so a name found in a log or a hook script is findable. | Command | Syntax | Purpose | Exit codes | | --- | --- | --- | --- | | `intake-loop` | `ai-conductor intake-loop --continuous \| --once [--interval-ms ]` | Polls registered repos into the durable inbox, notifies through the status surface, and reconciles closed GitHub issues each tick so a closed issue cannot be re-claimed. Never spawns a provider session and never opens a PR. `--interval-ms` defaults to 300000. Exactly one of `--continuous` and `--once` is required. | 0 on completion; 1 for the usage guide | | `brain start\|stop\|status` | `ai-conductor brain ` | Host-wide singleton that hosts `ai-conductor intake-loop --continuous` in the tmux session `cc-brain-conductor`. `start` is idempotent; `status` prints `brain loop: running\|stopped`, then the current durable `pending`, `claimed`, and `stranded` intake-ledger counts. When available, it separately prints the recorded prior notification batch as `last notification: at `; that batch is not current queue depth. | 0; 1 on a tmux error or when `status` cannot read the intake ledger | | `render-diagrams` | `ai-conductor render-diagrams … [--check]` | Renders the Mermaid blocks in each file using the configured `mermaid_renderer` preset. `--check` parse-checks blocks without opening them. | Default mode always 0. `--check`: 1 when any block fails to parse or a file is unreadable; 0 when everything parses, there are no diagrams, or `mmdc` is unavailable. Zero files: 1 | | `rate-card` | `ai-conductor rate-card refresh [--model ]…` · `ai-conductor rate-card show` | Maintains `/.ai-conductor/rate-card.json`, the committed per-model token price card the harness prices dispatches from for providers that report token counts but no cost (codex, and Pi messages without a provider cost). `refresh` fetches LiteLLM's public `model_prices_and_context_window.json`, prunes it to the models the provider model policies route to and supported opt-in models (plus any `--model`), and rewrites the card with a fresh `as_of`; commit the result. `--model` is repeatable. `show` prints the committed card. The fetch lives here, never on the dispatch path. `.github/workflows/rate-card-refresh.yml` runs `refresh` daily and opens a bot PR on the `automation/rate-card` branch when the published rates actually change — a run that finds identical rates discards its own `as_of`-only diff and opens nothing. A failed fetch, an unparseable payload, or a payload pricing none of the requested models leaves the committed card byte-for-byte unchanged. | 0 on success; 1 for the usage guide, a failed refresh, or a missing/unreadable card under `show` | | `shipment-evidence` | `ai-conductor shipment-evidence --pr [--event ]` · `shipment-evidence reconcile --pr --shipped ` · `shipment-evidence audit [--report ]` | Classifies, repairs, or audits the association between a PR and its shipped record. `audit` is report-only and never writes records. `reconcile` requires `GITHUB_REPOSITORY`. | check: 0 valid or not-applicable association, 1 otherwise. reconcile: 0 unless unresolved. audit: 0. Malformed: 1 | | `finish-record` | `ai-conductor finish-record --choice <pr\|keep> [--pr-url ] --pipeline-dir ` | Records the finish choice. `--choice pr` requires `--pr-url`; `--choice keep` must not carry one; `discard` is not accepted. `--pipeline-dir` must be an absolute path to an existing directory, checked before any spawn or write. The `pr` path is fail-closed across seven checks — PR binding, upstream push, state readability, branch shape (`spec/`, `feature/`, or the daemon's `feat/daemon-` — a bare `feat/` is refused), slug derivability, `git rev-parse HEAD`, and a valid shipment-evidence verdict. | 0; 1 on any guide or failed check | | `github-boundary-audit` | `ai-conductor github-boundary-audit [--root ]` | Read-only static audit of the shipped runtime's GitHub and remote-Git invocation sites. Reports one `file:line:column: message` diagnostic per site that bypasses the guarded operation interface. The integrity suite runs it. | 0 clean; 1 on any finding or unreadable root | | `manual-test-record` | `ai-conductor manual-test-record --skip --reason --pipeline-dir ` · `ai-conductor manual-test-record --results <path\|-> --pipeline-dir ` | Appends a `## Attempt N` section to `/manual-test-results.md`, atomically. `--results -` reads stdin; an exact `WARN` result cell stamps the attempt with ``. `--skip` and `--results` are mutually exclusive and one is required. `--pipeline-dir` must be absolute. | 0; 1 on a usage error, an empty results payload, or any read/write error | | `derive-feedback` | `ai-conductor derive-feedback --sha [--plan ]` | Read-only advisory check for whether commit `` carries `Task: ` evidence, or touches files declared under a task in the given plan. Prints one JSON line. Never writes task status or the evidence sidecar. | **0 evidenced, 1 not evidenced, 2 usage.** Informational only — the calling hook must not propagate them | | `build-auth-status` | `ai-conductor build-auth-status` | Reports the self-host build auth mode and token state as `build-auth-status: mode= state=[ path=][ ()]`. Probes the real dispatch auth path when a token is present. | 0 when the mode is not `daemon-token` (`state=api-key`) or the token is `valid`; 1 for `missing`, `unreadable`, `invalid`, or `unverifiable`, each with a remediation message |