# Driving a loop

Source: https://flow-next.dev/autonomy/driving-a-loop/

The default one-invocation recipe, the --tick snippets for /goal and /loop on each host, why the verdict grammar is the driver contract, and the wrapper pattern that keeps a bridged CLI from failing silently at 3am.

[`flow --auto`](https://flow-next.dev/autonomy/pilot/) and [land](https://flow-next.dev/autonomy/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](https://flow-next.dev/skills/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:

| Recipe                           | Invocation                                                             | Fits                                                                                                      |
| -------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Default: one invocation per item | `/flow-next:flow --auto`                                               | A 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 `--tick` | A 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](https://flow-next.dev/autonomy/land/#the-land-loop) 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.

## The verdict line is the contract

`/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:

```text
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](https://flow-next.dev/autonomy/pilot/#the-verdict-grammar) and the [land page](https://flow-next.dev/autonomy/land/#the-verdict-grammar). Terminal names are never renamed, because drivers grep them. `ASKED` comes only from backlog mode and `GOAL_MET` only from [goal mode](https://flow-next.dev/autonomy/goal-mode/#verdicts).

## Claude Code

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

```text
/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:

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

Variants worth knowing:

```text
/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:

```text
/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](https://flow-next.dev/autonomy/pilot/#migrating-from-pilot).

## Codex

`$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:

```text
/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.
```

```text
/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.
```

```text
/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.

## Chaining the two loops in one tick

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:

```text
/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](https://flow-next.dev/autonomy/going-autonomous/#running-the-full-pipeline).

## Budgets, caps, and review backends

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.

## Standing changes live in config

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](https://flow-next.dev/guides/model-routing/#two-layers-of-steering-session-vs-machinery).

## The wrapper pattern for bridged CLIs

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`).

## Headless drivers

`/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](https://flow-next.dev/autonomy/unattended-operation/) documents, along with the reasons a fresh wake beats an immortal shell.
