Skill v1.0.1
currentAutomated scan100/100+3 new
version: "1.0.1" name: flow-next-land description: Cadence-tick autonomous babysitter for build-loop-authored PRs (/loop-shaped ship loop). Each invocation is one tick over all open PRs the build loop authored — keep CI green (bounded fix budget), wait out the reviewer patience window, resolve feedback via resolve-pr, and once converged merge + close the spec + follow the project's release instructions. Ends with a terminal LAND_VERDICT line for the driver to read. Triggers on /flow-next:land, optionally with --dry-run. Autonomous by design — never asks the user questions; reports NEEDS_HUMAN instead. user-invocable: false allowed-tools: Read, Bash, Grep, Glob, Write, Edit, Skill
/flow-next:land — cadence-tick autonomous PR babysitter
A tick is one invocation of /flow-next:land: discover the open PRs the build loop authored, walk each through the gate tree (CI tri-state → patience window → review-thread resolution → review signal → merge gates), take at most ONE action class per PR, and end with one terminal LAND_VERDICT line. It is intentionally not a runner; /loop in Claude Code owns the cadence (babysitting waits on external events — CI, reviewers — over hours).
Land is the ship loop to pilot's build loop: pilot (/goal-shaped) drains ready specs into draft PRs; land (/loop-shaped) wakes on a cadence, acts on those PRs, sleeps. Land never authors PRs and never touches in-flight specs — it only babysits PRs whose authoring spec has ALL tasks done (the pilot-concurrency interlock).
Land and Ralph are alternative autonomous drivers. Never nest them, and never reuse Ralph harness state inside land.
Auto-merge override (confined). Land intentionally overrides the standing "no gh pr merge from skills" rule — confined to this one opt-in skill. Land itself is the gate: it merges explicitly (--squash --delete-branch --match-head-commit) only after every gate passes in-tick, and NEVER uses gh pr merge --auto (on a repo with no branch protection --auto merges instantly; server-side gating adds nothing). Every other skill keeps the no-auto-merge rule.
Preamble
CRITICAL: flowctl is BUNDLED — NOT installed globally. which flowctl will fail (expected). Define once; subsequent blocks (here and in workflow.md) use $FLOWCTL:
FLOWCTL="${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/scripts/flowctl"[ -x "$FLOWCTL" ] || FLOWCTL=".flow/bin/flowctl"
gh (verified against gh 2.93.0 — re-verify gh pr checks --json bucket/exit-8, --match-head-commit, and mergeStateStatus on major gh bumps) and jq must be on PATH; gh auth status must pass.
Pre-check: Local setup version
Non-blocking, never asks (autonomous). On mismatch, stash a SETUP_STALE line so the verdict contract can co-locate it with the terminal LAND_VERDICT (a stderr echo alone gets buried); version_ack never suppresses it. Detection logic is unchanged:
[[ -d .flow/tmp && ! -L .flow/tmp ]] && rm -f .flow/tmp/setup_stale 2>/dev/nullSETUP_VER=$(jq -r '.setup_version // empty' .flow/meta.json 2>/dev/null)PLUGIN_JSON="${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/.claude-plugin/plugin.json"PLUGIN_VER=$(jq -r '.version' "$PLUGIN_JSON" 2>/dev/null || echo "unknown")if [[ -n "$SETUP_VER" && "$PLUGIN_VER" != "unknown" && "$SETUP_VER" != "$PLUGIN_VER" ]]; thenif [[ ! -L .flow/tmp ]]; thenmkdir -p .flow/tmp 2>/dev/nullrm -f .flow/tmp/setup_stale 2>/dev/null # drop a planted setup_stale symlink before tee follows itecho "SETUP_STALE: local v${SETUP_VER}, plugin v${PLUGIN_VER}, run /flow-next:setup" | tee .flow/tmp/setup_stale >&2elseecho "SETUP_STALE: local v${SETUP_VER}, plugin v${PLUGIN_VER}, run /flow-next:setup" >&2fifi
Continue regardless (never blocks; fail-open - silent when setup was never run, versions match, or any read fails). Symlink guard (autonomous-safety): a repo-committed symlink at .flow/tmp (or at the setup_stale file) would redirect the tee write outside the workspace during an unattended tick. When .flow/tmp is a symlink land refuses to stash - stderr echo only, no file write - so the verdict-time cat simply finds nothing (acceptable degradation, never an out-of-tree write); the cleanup guard is likewise symlink-aware (! -L), and the pre-tee rm -f drops a planted setup_stale symlink so tee creates a fresh regular file. See the verdict contract for how the stashed line is emitted before the terminal verdict.
Hard guards (before anything else)
Run these guards before discovery, ledger writes, branch changes, or skill dispatch.
if [[ -n "${FLOW_RALPH:-}" || -n "${REVIEW_RECEIPT_PATH:-}" ]]; thenecho "Ralph and land are alternative drivers — never nest them" >&2[[ -f .flow/tmp/setup_stale ]] && cat .flow/tmp/setup_staleecho 'LAND_VERDICT=NEEDS_HUMAN prs=0 pr=- reason="nested under Ralph harness (FLOW_RALPH/REVIEW_RECEIPT_PATH set) — refuse to run"'exit 1fiif git status --porcelain | grep -v '^.. \.flow/' >/dev/null; then[[ -f .flow/tmp/setup_stale ]] && cat .flow/tmp/setup_staleecho 'LAND_VERDICT=NEEDS_HUMAN prs=0 pr=- reason="dirty working tree at tick start"'exit 0fi
Dirty tree means dirty outside .flow/; land leaves state untouched. No cleanup, no ledger write.
Mode Detection
Parse $ARGUMENTS for the dry-run switch. Unknown flags warn to stderr and are ignored. The loop avoids bash positional parameters — the host's argument interpolation rewrites positional tokens inside skill code blocks (pilot dogfood finding, 1.13.0).
RAW_ARGS="$ARGUMENTS"LAND_DRY_RUN=0for ARG in $RAW_ARGS; docase "$ARG" in--dry-run) LAND_DRY_RUN=1 ;;-*) echo "Unknown flag: $ARG (ignored by /flow-next:land)" >&2 ;;*) echo "Unknown argument: $ARG (ignored by /flow-next:land)" >&2 ;;esacdoneexport LAND_DRY_RUN
--dry-run stops after GATE: full discovery + per-PR classification report (CI tri-state read, review-signal state, would-be action) and the aggregated terminal line, with ZERO mutations — no checkout, no push, no label, no merge, no resolve-pr dispatch, no ledger write.
The verdict contract (read this before the workflow)
Cadence drivers are transcript-blind: they read conversation output only and never run tools. Every tick therefore echoes its per-PR evidence (gate reads, action taken, verdict) into the output, one block per PR.
Per-PR verdicts are exactly: MERGED | RELEASED | FIXING_CI | AWAITING_REVIEW | RESOLVING | BLOCKED | NEEDS_HUMAN.
Every tick ends with exactly one terminal line, the last line of the response, with nothing after it:
LAND_VERDICT=<verdict|NO_WORK> prs=<n> pr=<deciding-pr-url|-> reason="<one line>"
The tick-level verdict is the worst severity across PRs by priority NEEDS_HUMAN > BLOCKED > FIXING_CI > RESOLVING > AWAITING_REVIEW > RELEASED > MERGED; pr= is the URL of the PR that decided it (- when none). NO_WORK when discovery finds zero authored PRs. prs= is the number of PRs processed this tick.
SETUP_STALE line. Whenever the pre-check detected a setup-version mismatch it wrote .flow/tmp/setup_stale. At EVERY terminal LAND_VERDICT emission - the report line, each hard-guard exit, and the Preamble gh-auth failure - first print that file's SETUP_STALE: local v<X>, plugin v<Y>, run /flow-next:setup line, so it lands in the same output block immediately before the verdict and survives into driver logs. Emit it verbatim (cat .flow/tmp/setup_stale in bash blocks; a plain preceding line when the verdict is printed as text). It never blocks, is never suppressed, and fail-opens to nothing when the file is absent.
Driver condition examples:
/loop 30m /flow-next:land/goal keep running /flow-next:land until it prints LAND_VERDICT=NO_WORK or LAND_VERDICT=NEEDS_HUMAN
Forbidden
- Asking the user anything in the tick path. Land is autonomous; ambiguity maps to
NEEDS_HUMAN. - Authoring PRs, choosing/planning/implementing specs — that is the build loop (pilot). Land only babysits existing PRs.
- Acting on a PR without BOTH authorship signals (branch matches a spec's
branch_nameAND the make-pr breadcrumb in the PR body). Branch-only matches are reportedNEEDS_HUMAN, never mutated. gh pr merge --auto, merge-queue enrollment, or any merge without--match-head-commit.- Hand-resolving merge-conflict hunks. The conflict path is mechanical rebase only; any conflict hunk aborts →
BLOCKED. - Inventing release steps. Release-follow runs deterministic, non-interactive commands from the project's discovered release docs ONLY, or stops at merge.
git add -Ain the CI-fix path — stage only the files edited for the fix.- Dispatching any skill other than
flow-next-resolve-pr(withmode:autonomous) andflow-next-tracker-sync(opt-inland.mergedtouchpoint). - Printing anything after the
LAND_VERDICTline. - Running under Ralph (
FLOW_RALPH/REVIEW_RECEIPT_PATH).
Workflow
Execute workflow.md in order:
- guards — refuse Ralph nesting, refuse dirty non-
.flow/start state, resolve the.gitland ledger (read-only at this point), readland.*config. - discover — open specs with all tasks done →
gh pr list --head <branch_name> --state all, OPEN-state filter, dual authorship signals, merged-but-unclosed re-entry candidates. - gate — per-PR read-only classification: durable-label skip, CI tri-state over ALL checks, patience window anchored to last push, unresolved review threads, review signal (
land.reviewSignal), stale-approval detection,mergeStateStatus.--dry-runstops here.
- Under the default
silencesignal, a review bot that posts a no-findings issue comment instead of a formal APPROVE (e.g. Codex's "Didn't find any major issues. Reviewed commit:<sha>") also satisfies the gate — land scansissues/<n>/commentsfor an automated-reviewer comment matchingland.cleanReviewCommentPattern(a structured built-in default) that names the current head SHA. It only ever adds this evidence; CI, unresolved-thread, and window gates are unchanged, and a stale-SHA or non-automated comment is ignored. Setland.cleanReviewCommentPatternto an explicit empty string""to disable the comment path (pure reviews-API behavior); leaving it unset uses the built-in default.
- act — at most one action class per PR: CI fix, resolve-pr dispatch, mechanical rebase/update, or ready→merge→post-merge tail (spec close → tracker touchpoint → release-follow).
- report — per-PR verdict evidence, ledger writes, and the terminal
LAND_VERDICTline (worst-severity rule).
Unattended runs
Land is fully autonomous by design — there is no interactive mode. Wall-clock limits and cadence belong to the driver (/loop <interval>, /goal stop clauses). A land tick has no timeout machinery; the patience window (land.patienceMinutes, default 30) is gate state, not a sleep — a tick never blocks waiting for reviewers, it reports AWAITING_REVIEW and exits.