Pilot - the build loop
/flow-next:pilot is the in-session autonomous conductor. Each invocation is a tick: it advances exactly one ready spec by one pipeline stage (plan → plan-review → work → make-pr, plus an optional qa stage when pipeline.qa==on) and ends with a terminal PILOT_VERDICT line. Your host’s loop primitive owns iteration. Pilot is the tick, not the runner.
Human judgment lives before pilot: the spec content, its dependencies, and the human-owned ready flag are the consent boundary. Pilot executes the mechanical pipeline and reports ambiguity as NEEDS_HUMAN. It never asks questions.
Chart is outside the build loop. Optional /flow-next:chart is pre-capture discovery and is never a pilot stage. Attended chart decisions (prototype, interview, attended task) terminate CHART_VERDICT=NEEDS_HUMAN under unattended drivers; they never self-resolve. Get the effort briefed, captured, and ready before pilot selects it.
Flags, the verdict table, and the machine contract are on the skill reference. How to run a loop around it is Driving a loop.
What a tick does
Section titled “What a tick does”- Select. The first
open+readyspec whosedepends_on_epicsare all done and that carries no other-actor task claims. - Classify. Derive one stage from flowctl state: no tasks →
plan(orworkon the no-plan route when the spec carriesno_plan: true); plan not shipped →plan-review; ready or in-progress tasks →work; all done →qa(withpipeline.qa==onand no freshqa_verdictreceipt), otherwise probe PR state, thenmake-pr. - Dispatch. Invoke exactly one existing stage skill (
plan,plan-review,work, the optionalqa,make-pr) withmode:autonomous. Pilot never re-implements their logic. - Verify. Re-read flowctl review-status fields and task/spec transitions; for make-pr, a gh-confirmed new OPEN PR URL is the advancement evidence.
- Report. Echo the evidence into the transcript and print the terminal verdict.
Opt-in in-tick chaining. With pipeline.chainStages on (flowctl config set pipeline.chainStages on), a tick whose qa stage produced a fresh terminal verdict runs make-pr before it exits, with its own evidence block and stage line, and the verdict reads stage=qa+make-pr. The chain table is closed to that one row: plan → plan-review is not a pair (the plan dispatch already runs its review loop to SHIP), and nothing chains into work. Off is byte-for-byte today’s tick; the switch is inert unless pipeline.qa is on.
The no-plan route is spec state, never inferred. A ready zero-task spec marked no_plan: true — at capture time (/flow-next:capture … --no-plan) or any time later via flowctl spec set-no-plan fn-N — classifies straight to the work dispatch, which takes the no-plan route and mints its one implicit task. Pilot itself never decides that: the field is an explicit human instruction carried by the spec, and no autonomous path ever sets it. Without the field, a zero-task spec classifies as plan exactly as before; a stale field on a spec that already has tasks is inert (classification matches on task count first). The old per-invocation /flow-next:pilot --no-plan flag is gone — a stray --no-plan gets the standard unknown-flag notice and the tick proceeds.
Sub-skills run autonomously: the mode:autonomous token (plus FLOW_AUTONOMOUS=1 for process-level drivers) suppresses questions and picks safe defaults. Work branches deterministically, and make-pr forces a draft PR and hard-errors instead of prompting. The signal is deliberately distinct from FLOW_RALPH, so none of the Ralph harness hooks activate.
Since 4.1.0 the plan and plan-review stages no longer require the default branch: they probe the current branch for an open PR and plan in place when none exists, so a one-worktree-per-spec setup composes with the full loop.
Don’t-thrash guard
Section titled “Don’t-thrash guard”A spec that fails to advance on two healthy ticks is taken out of selection: pilot clears its ready flag (flowctl spec unready) and the BLOCKED verdict carries the reason. Strikes live in a ledger under .git/, shared across worktrees and never committable. Re-blessing the spec clears its strikes, which is an explicit human reset (flowctl spec ready, or the board move below on tracker-connected repos).
Readiness as the control plane
Section titled “Readiness as the control plane”Pilot consumes exactly one gate: the ready flag. Where that flag comes from depends on your setup, and it changes how you steer a running loop.
Local repos (no tracker, or tracker.readyState unset). flowctl spec ready / unready is authoritative. You bless work on the command line; pilot’s two-strike unready sticks until you re-bless the same way, and a re-blessed spec clears its strikes on the next selection.
Tracker-connected repos (tracker.readyState configured). The board is the control plane. The readiness projection pulls one-way from the tracker: a Linear issue in the configured workflow state (or a GitHub / GitLab issue carrying the configured label, or a Jira issue in the configured workflow status) means ready=true; anything else means ready=false. Move an issue into that state to feed pilot work, out to starve the loop. Local spec ready writes are overwritten on the next pull, so bless on the board.
One interplay to know when driving pilot against a board (corrected in 3.28.0): pilot’s two-strike spec unready is a local write, advisory until the board reflects it, and the next tracker pull re-readies the spec. That projection-set ready never clears the strike. A board echo re-grants readiness with nobody acting, and a deliberate move back into the ready lane is indistinguishable from that echo in every durable artifact, so no board move can serve as the re-bless. When pilot strikes a spec out (BLOCKED … strike 2/2, spec unreadied), move the issue out of the ready state to keep the board honest, fix whatever blocked it, and clear the strike with flowctl pilot strikes clear <spec-id>. The verdict line names the command, and flowctl pilot strikes list shows what is struck. Clearing a strike never changes readiness: strikes are pilot state, the board owns readiness.
This keeps a clean division of labor. The board decides what the loop may touch; pilot decides how far each tick advances it; verdicts report back in the transcript, and the tracker-sync lifecycle events mirror progress onto the issue so the board view stays current while the loop runs.
Backlog mode
Section titled “Backlog mode”By default pilot’s consent boundary sits before the loop: it only picks from the already-ready queue and assumes specs are triaged, dep-clear, and unambiguous. Everything in front of that gate is manual prompting: enumerating the whole open backlog, triaging raw items, sequencing deps, deciding what’s next, and unblocking the things that need a person.
Backlog mode (pilot.autonomy=backlog, default off) makes pilot a standing floor scheduler for the entire open backlog:
flowctl config set pilot.autonomy backlog # or per-run: /flow-next:pilot --backlog (alias --auto)Each tick now:
- Enumerates the full open set: flow specs (
flowctl ready --all) plus normalized tracker issues fromflowctl tracker wire list-open, filtered to the exact configuredtracker.readyState. A pull-before-scan runs first, so a board move is reflected on the next tick. - Orders tracker dependencies: for each tracker candidate,
flowctl tracker wire relation-listreads normalized directed dependency rows from the same durable/display locator and fails closed rather than sorting a truncated graph. - Selects the top dep-ordered actionable item across both sources, skipping anything already parked behind an open question.
- Triages it with an agentic read of the spec, never a deterministic score: workable (ready signal + complete spec) flows into the normal
plan → plan-review → work → [qa] → make-prpipeline; dep-unsatisfied sequences behind the blocker (BLOCKED); ready-but-thin is kicked back with an async question (ASKED). - Advances one stage, or parks behind a question. The single-tick contract is unchanged: one item, one stage or one durable park, one terminal verdict.
This is the one place the consent boundary moves, from before the loop to inside the loop, on block. When pilot cannot safely proceed it surfaces a precise async question rather than stalling, writes it into the spec’s ## Open Questions section, and projects the semantic text through the deterministic tracker facade. It then parks the item (ASKED) and moves on. A human answers in the spec or tracker; the next tick re-triages and proceeds. It never prompts interactively.
The load-bearing boundaries hold unchanged:
- Never authors a spec. A thin or missing spec is surfaced as a “run
/flow-next:captureor/flow-next:interview” gap. Autonomous scope-invention from a one-line ticket is exactly the slop the question valve guards against. It may fill an obvious blank in an existing spec, never write one from nothing. - Never sets the
readyflag. Promotion is the human’s board act; un-promoted backlog items are skipped silently, never worked and never nagged. The agent’s spec-read can only withhold by kicking a promoted-but-thin item back with a question, never force work. - Never merges. Land stays the separate, human-gated ship loop.
Full-auto by default, with an optional force-gate. Workable, dep-clear, unambiguous items advance with no pre-gate. For extra caution you can name classes that must be force-surfaced as a question before action, even when otherwise workable, via the sibling config key pilot.gateClasses (default [], which gates nothing):
flowctl config set pilot.gateClasses risky # a single class# or a JSON array for several: ["risky","prod-config"]Whether a selected item belongs to a gate class is the agent’s read of the item, like triage and never a score.
Backlog mode is a leftward extension of the same single-tick conductor: one /loop or /goal target, one verdict grammar, one mental model. It manages the existing open backlog rather than inventing work the way prospect does. A per-tick decision log (flowctl pilot-log, stored under .flow/pilot-runs/) records each action plus host-reported token cost, which gives you the factory-efficiency readout: percentage moved with no question, with one async answer, or parked, and cost per change. With the gate off, pilot behaves byte-for-byte as documented above.