Skip to content

Driving a loop

flow --auto and land each print one terminal verdict line and stop. Repetition, cadence, budgets, and stop conditions belong to the driver around them: your host’s /goal or /loop primitive, a cron entry, or a scheduler that starts one headless session per wake.

Flow without --auto is never a loop target. It is attended by contract. It asks its picks inline, prints no verdict line a driver could grep, and stops before any read with NEEDS_HUMAN under any autonomy marker. flow --auto is the loop target.

The build driver has two shapes, and the shape decides the recipe:

RecipeInvocationFits
Default: one invocation per item/flow-next:flow --autoA host with stable long sessions; the run hops until its terminal, and you run it again for the next item
Loop: one hop per interval/loop 30m /flow-next:flow --auto --tick or a /goal around --tickA host /loop or /goal that owns repetition, or a host without stable long sessions

Land takes one named pull request and is /loop-shaped: /loop 15m /flow-next:land 123. One invocation is one tick, and the land loop recipes cover one pull request and several. To carry one item through landing in one invocation, use /flow-next:flow --auto fn-12 --until=merge. Add --tick when the outer driver should own the cadence. Each fresh session must carry current scoped authorization; repeat the flag for that spec instead of treating an old receipt as consent.

/goal validators are transcript-blind. They read conversation output and never run tools. That single constraint shapes the whole design. Each hop echoes its verification evidence (flowctl fields, task counts, the gh-confirmed PR URL) into the transcript, and each run ends with exactly one machine-greppable line, nothing after it:

PILOT_VERDICT=<ADVANCED|NO_WORK|DEFERRED_TO_LAND|BLOCKED|NEEDS_HUMAN|ASKED|GOAL_MET> spec=<id> stage=<stage> reason="…"
LAND_VERDICT=<MERGED|RELEASED|FIXING_CI|AWAITING_REVIEW|RESOLVING|BLOCKED|NEEDS_HUMAN|NO_WORK> prs=<n> pr=<url|-> reason="…"

So you phrase stop conditions against the grammar rather than against prose. A long-horizon run names every dispatched stage joined by + (stage=work+qa+make-pr) and carries the last hop’s verdict; a --tick run names one stage. The full verdict tables are on the flow —auto page and the land page. Terminal names are never renamed, because drivers grep them. ASKED comes only from backlog mode and GOAL_MET only from goal mode.

The default shape needs no loop primitive. One invocation drives one ready item to its terminal; run it again for the next item:

/flow-next:flow --auto
/flow-next:flow --auto fn-12 # lock the run to one spec

/goal (v2.1.139+) drains and stops. Its validators are transcript-blind, so phrase the stop condition against the verdict grammar:

/goal keep running /flow-next:flow --auto --tick until it prints PILOT_VERDICT=NO_WORK, or stop after 20 turns

Variants worth knowing:

/goal keep running /flow-next:flow --auto --tick --review=codex until PILOT_VERDICT=NO_WORK or PILOT_VERDICT=NEEDS_HUMAN
/goal keep running /flow-next:flow --auto until it prints PILOT_VERDICT=NO_WORK, or stop after 20 turns # long-horizon: one item per turn
/goal keep running /flow-next:land 123 until it prints LAND_VERDICT=MERGED, or stop on LAND_VERDICT=BLOCKED, LAND_VERDICT=NEEDS_HUMAN or LAND_VERDICT=NO_WORK
/goal keep running /flow-next:flow --goal fn-40 --until=merge until PILOT_VERDICT=GOAL_MET, or stop on NEEDS_HUMAN or BLOCKED # goal mode

PILOT_VERDICT=DEFERRED_TO_LAND is its own terminal (an all-done spec with an open PR); route that pull request to /flow-next:land <PR> rather than re-running the build driver. In backlog mode the grammar also carries PILOT_VERDICT=ASKED, a durable park. The loop continues to the next item on its next turn and the human answers async in the spec or tracker.

/loop (v2.1.72+; loops expire after 7 days) runs on a cadence and keeps watch:

/loop 30m /flow-next:flow --auto --tick
/loop 15m /flow-next:land 123

Every 30 minutes the build driver takes one hop. If a spec is ready it advances one stage; if nothing qualifies it reports NO_WORK and the loop idles until you bless more work. That makes /loop plus the tracker board a standing pipeline. Drag an issue to Todo in Linear, and the next hop picks it up. Land always names its pull request; there is no bare /flow-next:land. RELEASED stays in the grammar for parsers and is never emitted.

A /loop 10m /flow-next:pilot recipe stopped working in 6.1.0, which removed the command. Replace it with /loop 10m /flow-next:flow --auto --tick; the argument mapping is on Migrating from pilot.

$flow-next-flow --auto in a session is the default shape; one invocation drives one item to its terminal. /goal is opt-in. Add [features] goals = true to your Codex config (CLI >= 0.128.0). Codex has no $skill-in-goal syntax, so write a plain-text objective that names the flow --auto --tick behavior and the grammar:

/goal Run the flow-next flow skill with --auto --tick repeatedly: each run advances one ready
spec by one pipeline stage and ends with a PILOT_VERDICT line. Stop when it prints
PILOT_VERDICT=NO_WORK or PILOT_VERDICT=NEEDS_HUMAN.
/goal Work toward goal fn-40. Run the flow-next flow skill with --goal fn-40 --until=merge
repeatedly: each run measures the goal, builds its next child spec, and ends with a
PILOT_VERDICT line. Stop when it prints PILOT_VERDICT=GOAL_MET, NEEDS_HUMAN or BLOCKED.
/goal Land pull request 123. Run the flow-next land skill on it repeatedly: each run
resolves threads, repairs CI or merges, and ends with a LAND_VERDICT line. Stop when it
prints LAND_VERDICT=MERGED, BLOCKED, NEEDS_HUMAN or NO_WORK.

Codex has no cadence primitive, so the /goal form re-ticks back-to-back rather than sleeping. That is functionally safe, because a tick never blocks; land reports AWAITING_REVIEW and exits. Expect rapid re-checks while waiting on reviewers, and prefer a /loop cadence where your host has one. The parent sandbox must allow the stages’ subprocesses and network.

Both verdict lines are machine-readable, so a driver can compose them into a spec-to-merged-PR pipeline in one prompt. This external-driver recipe remains supported. For a single selected spec, flow --auto fn-12 --until=merge performs the handoff itself:

/loop 30m - one tick: run /flow-next:flow --auto --tick --review=codex --depth=deep.
If PILOT_VERDICT=DEFERRED_TO_LAND, find the open pull request of the spec it names
and run /flow-next:land on that pull request in the same tick.
Send implementation tasks to the implementer tier,
keep UI tasks on the session model, reviews come from codex.
Stop when flow --auto prints NO_WORK and no deferred pull request is left unmerged,
or on any NEEDS_HUMAN.

DEFERRED_TO_LAND exists exactly for this hand-off. The instruction to land the deferred pull request is the merge authorization, so it has to be in the prompt of every fresh session. Running building and landing as separate instances instead is the assembly-line topology.

Landing waits happen at the driver’s cadence and never consume pilot strikes. Iteration caps, budgets, and wall-clock limits belong to the driver (/goal stop clauses, --tokens, /loop cadence). A run has no timeout machinery of its own; the two-strike ledger bounds a spec that advances nothing and the finite stage set bounds one that advances. Under --auto the review loop fixes and re-reviews until SHIP, bounded by flowctl’s round cap and stall check.

Check the reviewer before you walk away. A CLI backend needs its CLI installed and signed in on the machine that runs the loop, and host needs nothing extra. Pick one with --review=codex, --review=copilot, --review=cursor, --review=claude, --review=host, or --review=none. A reviewer whose account hits a usage or credit limit stops the review with the CLI’s own message instead of retrying.

A prompt steers only the session it is typed in. Your routing block and per-run flags are read every turn while you are there, and an unattended run at 3am has no prompt to read. If you want the 3am run to review with a different backend, that is a config change (flowctl config set review.backend …) rather than a sentence. The two steering layers are laid out in Orchestration & model routing.

If a run sends implementation out to a second CLI, one failure class matters more unattended than it ever does in a session. Raw bridge calls fail quietly. Outside a trusted git directory, codex exec refuses in about a second with the error only in its log, and cursor-agent blocks on an interactive workspace-trust prompt, then exits “successfully” with empty output. An interactive host sees the stderr and fixes it. An autonomous loop dies silently.

Wrap the bridge in a thin fast-scout-tier subagent instead of calling it raw. The wrapper composes the self-contained prompt, runs the bridge, verifies the output is non-empty and parseable, repairs the environment if not, and retries once.

Two rules are load-bearing:

  • The wrapper runs the bridge in the foreground, as one blocking call. A backgrounded bridge loses the completion signal, and the wrapper idles forever on a finished or silently dead process.
  • The self-heal license covers environment and flags only, never judgment. In scope: git trust, sandbox flags, stale model ids, empty-output retry. Out of scope: rewriting the task prompt, interpreting review verdicts, or switching models on quality grounds.

This is a documented pattern rather than a shipped agent type; quick interactive calls may stay raw. The bridge recipes themselves ship into the repo you work in and are read on demand with flowctl usage (## Orchestration & model steering).

/goal and /loop need an interactive host. A scheduler can run the same policy with one fresh headless session per wake (claude -p, codex exec, or an equivalent), which is the shape Unattended operation documents, along with the reasons a fresh wake beats an immortal shell.