Skip to content

Land — the Ship Loop

/flow-next:land is the third flow-next loop: where pilot and Ralph deliberately stop at a draft PR, land babysits those PRs the rest of the way — CI green, automated reviews converged, merged, and (if your project defines one) released. Fittingly, it was the first spec pilot drove end-to-end.

It is /loop-shaped, not /goal-shaped: babysitting waits on external events — CI runs, reviewer bots — over hours, so the loop wakes on a cadence, acts on every PR it owns, and sleeps.

/loop 30m /flow-next:land
Terminal window
/flow-next:land # one tick over all build-loop-authored PRs
/flow-next:land --dry-run # full gate classification, zero mutations

Land is fully autonomous by design — there is no interactive mode. It never asks questions; ambiguity maps to NEEDS_HUMAN. Requires gh and jq on PATH and gh auth status passing (gate surfaces verified against gh 2.93.0 — re-verify gh pr checks --json bucket, --match-head-commit, and mergeStateStatus on major gh bumps).

For each open PR the build loop authored, land takes at most one action class per PR per tick:

  1. Discover — open specs with all tasks done, probed by branch: a PR qualifies only with both authorship signals — its head branch matches a spec’s branch_name AND the make-pr breadcrumb is in the PR body. Branch-only matches are reported NEEDS_HUMAN, never acted on; your hand-opened PRs are never touched. Specs with in-flight tasks stay pilot’s — the pilot-concurrency interlock.
  2. CI — tri-state over all checks (gh pr checks --json bucket): any fail → diagnose from the failing run’s logs, scope a fix, push, report FIXING_CI; any pending (or an empty check list right after a push) → wait, never treated as green. Fix attempts are bounded (land.ciFixBudget, default 3, strikes ledgered under .git/); on exhaustion the PR gets a durable flow-next:needs-human label, is reported NEEDS_HUMAN, and is skipped on later ticks until a human removes the label. Unrelated infra flakes get one gh run rerun instead of an edit.
  3. Reviews — wait for automated reviewers inside a patience window (land.patienceMinutes, default 30), anchored to the last push (a CI-fix push invalidates prior reviews and restarts the wait). Report AWAITING_REVIEW until reviews land or the window elapses.
  4. Resolve — unresolved threads route through /flow-next:resolve-pr dispatched with mode:autonomous — fix, verify, reply, resolve — and land gates on its machine-readable terminal line (RESOLVE_PR_VERDICT=<RESOLVED|PENDING|NEEDS_HUMAN> …), looping tick over tick until convergence.
  5. Merge — CI green + threads addressed + the review signal satisfied → flip the draft to ready (gh pr ready) and merge explicitly: gh pr merge --squash --delete-branch --match-head-commit <head-sha>. Never gh pr merge --auto — land itself is the gate, and --auto insta-merges on unprotected repos. Conflicts get a mechanical rebase only; any conflict hunk aborts to an honest BLOCKED — land never hand-resolves hunks.
  6. Close + release — after the merge: flowctl spec close (so the build loop never re-selects merged work), the tracker touchpoint (tracker.perEvent.land.merged, active by default when the bridge is active — the only path that flips the issue to Done, gated on the GitHub MERGED probe; 2.1.2+) + a release/verdict comment, then discover and follow your project’s own release instructions (docs/RELEASING.md, RELEASING.md, agent_docs/releasing.md, or docs referenced from CLAUDE.md/AGENTS.md) if present — deterministic, non-interactive commands from those docs only, with an idempotency probe so a re-entry tick never re-tags. No release docs → stop at merge. Land never invents versioning or publish steps.

A merged-but-unclosed spec re-enters idempotently: a later tick resumes close → tracker → release, never a second merge.

Per-PR verdicts are MERGED | RELEASED | FIXING_CI | AWAITING_REVIEW | RESOLVING | BLOCKED | NEEDS_HUMAN, echoed as evidence blocks. Every tick ends with exactly one machine-greppable line — the last line of output, nothing after it (the PILOT_VERDICT precedent):

LAND_VERDICT=<verdict|NO_WORK> prs=<n> pr=<deciding-pr-url|-> reason="<one line>"

The tick-level verdict is the worst severity across PRs (NEEDS_HUMAN > BLOCKED > FIXING_CI > RESOLVING > AWAITING_REVIEW > RELEASED > MERGED); NO_WORK when discovery finds zero authored PRs.

Land is /loop-shaped, not /goal-shaped: babysitting waits on external events — CI runs, reviewer bots — over hours, so the loop wakes on a cadence, acts on every PR it owns, and sleeps. A 30-minute cadence comfortably brackets the default patience window (land.patienceMinutes, 30).

Claude Code /loop (v2.1.72+; loops expire after 7 days) — the default shape:

/loop 30m /flow-next:land

Claude Code /goal (v2.1.139+) — if you’d rather stop on a terminal verdict than run on a fixed cadence:

/goal keep running /flow-next:land until it prints LAND_VERDICT=NO_WORK or LAND_VERDICT=NEEDS_HUMAN

Codex /goal — opt-in: add [features] goals = true to your Codex config (CLI ≥ 0.128.0). There is no $skill-in-goal syntax on Codex — write a plain-text objective that names the behavior and the grammar:

/goal Run the flow-next land skill repeatedly: each run takes one tick over the build
loop's open PRs — CI, reviews, merge, release — and ends with a LAND_VERDICT line. Stop
when it prints LAND_VERDICT=NO_WORK or LAND_VERDICT=NEEDS_HUMAN.

Codex has no cadence primitive, so the /goal form re-ticks back-to-back rather than sleeping — functionally safe (a tick never blocks; it reports AWAITING_REVIEW and exits), but expect rapid re-checks while waiting on reviewers. Prefer a /loop cadence where your host has one. Either way, cadence and stop conditions belong to the driver — a tick has no timeout machinery, and the patience window is gate state, not a sleep.

Land is the only place in flow-next licensed to auto-merge — an explicit, confined override of the repo-wide “no gh pr merge from skills” rule. The license is bounded by the gate:

  • CI green — every check’s bucket ∈ pass/skipping, fixed by the loop if needed.
  • Every review thread addressed — resolve-pr convergence, bounded at its existing fix-verify cycles.
  • The configured review signal (land.reviewSignal) satisfied:
SignalSatisfied when
silence (default)≥1 review by an automated reviewer of the current head + zero unresolved threads + the patience window elapsed since the last push. Built for bot reviewers (Codex et al.) that comment but never file formal APPROVEs — including the case where the bot signals a clean pass via a comment naming the reviewed commit (see below) rather than a formal review.
approveFormal reviewDecision == APPROVED
<github-login>That reviewer’s latest review is APPROVED, or COMMENTED with zero unresolved threads

An automated reviewer is a review author whose login ends in [bot], plus any login you list in land.automatedReviewers. No automated review ever + no signal configured → land never merges unreviewed — it reports NEEDS_HUMAN instead. A repo that dismisses stale approvals on push is detected (approve → re-push → re-required ping-pong) and reported NEEDS_HUMAN rather than re-looped.

Clean-review comments count too (2.1.1+). Some bots — notably Codex — only file a formal review when they have findings; on a clean pass they post an issue comment (“Didn’t find any major issues. Reviewed commit abc1234”) that never reaches the reviews API. Under silence, land also scans the PR comments: a comment from an automated reviewer that matches land.cleanReviewCommentPattern and names the current head SHA counts as a head-current review (it only ever adds evidence — it never overrides a formal review, an open thread, or a red check). The SHA must be the current head, so a land-authored fix push correctly forces a fresh clean comment before merge. Set land.cleanReviewCommentPattern to an empty string to turn the comment path off.

All keys ship with seeded defaults — flowctl config get land.reviewSignal returns "silence" on a fresh repo, not null:

KeyDefaultMeaning
land.releasetrueRun the release-follow step after merge (also no-ops when no release docs are discovered)
land.patienceMinutes30Reviewer patience window, anchored to the last push
land.reviewSignalsilenceMerge signal: silence, approve, or a GitHub login
land.automatedReviewers""CSV allowlist of reviewer logins counted as automated, supplementing the [bot]-suffix rule
land.reviewTrigger""One-shot comment posted to summon a reviewer bot on a draft PR with zero automated reviews (e.g. "@codex review" — bots don’t auto-review drafts, and the build loop’s PRs are born draft). Empty = never post
land.cleanReviewCommentPatternbuilt-in EREUnder silence, a regex matching a bot’s clean-review comment (default matches Codex’s “no issues … Reviewed commit <sha>”); a match that names the current head SHA counts as a head-current review. Set to an empty string "" to disable the comment path
land.ciFixBudget3CI-fix attempts per PR before the durable needs-human label
Terminal window
flowctl config set land.reviewSignal approve
flowctl config set tracker.perEvent.land.merged push # opt-in tracker touchpoint
  • A separate skill — projects that never run it carry zero risk and zero new surface.
  • Touches only PRs the build loop authored — both authorship signals required before any mutation.
  • The release step has its own off-switch (land.release), independent of the rest of the loop.
  • Refuses to nest under the Ralph harness (FLOW_RALPH / REVIEW_RECEIPT_PATH), same as pilot; refuses a dirty working tree at tick start; restores your branch and asserts a clean tree between PRs and at tick end.

With land in place the three loops close the lifecycle end to end:

board / flowctl pilot land
bless a spec ──▶ plan → review → work ──▶ CI → reviews → merge → release
(your judgment) (draft PR out) (shipped)

Run both loops concurrently for the full assembly line — one instance each, separate clones (both mutate the working tree). See Running the full pipeline.

See Going Autonomous for how the loops fit together, and the changelog for the release notes.