From flow-next
Autonomous PR babysitter that checks CI, waits for reviewers, resolves feedback, and merges build-loop-authored PRs. One tick per invocation, never asks questions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/flow-next:flow-next-landThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
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...
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.
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.
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/null
SETUP_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" ]]; then
if [[ ! -L .flow/tmp ]]; then
mkdir -p .flow/tmp 2>/dev/null
rm -f .flow/tmp/setup_stale 2>/dev/null # drop a planted setup_stale symlink before tee follows it
echo "SETUP_STALE: local v${SETUP_VER}, plugin v${PLUGIN_VER}, run /flow-next:setup" | tee .flow/tmp/setup_stale >&2
else
echo "SETUP_STALE: local v${SETUP_VER}, plugin v${PLUGIN_VER}, run /flow-next:setup" >&2
fi
fi
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.
Run these guards before discovery, ledger writes, branch changes, or skill dispatch.
if [[ -n "${FLOW_RALPH:-}" || -n "${REVIEW_RECEIPT_PATH:-}" ]]; then
echo "Ralph and land are alternative drivers — never nest them" >&2
[[ -f .flow/tmp/setup_stale ]] && cat .flow/tmp/setup_stale
echo 'LAND_VERDICT=NEEDS_HUMAN prs=0 pr=- reason="nested under Ralph harness (FLOW_RALPH/REVIEW_RECEIPT_PATH set) — refuse to run"'
exit 1
fi
if git status --porcelain | grep -v '^.. \.flow/' >/dev/null; then
[[ -f .flow/tmp/setup_stale ]] && cat .flow/tmp/setup_stale
echo 'LAND_VERDICT=NEEDS_HUMAN prs=0 pr=- reason="dirty working tree at tick start"'
exit 0
fi
Dirty tree means dirty outside .flow/; land leaves state untouched. No cleanup, no ledger write.
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=0
for ARG in $RAW_ARGS; do
case "$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 ;;
esac
done
export 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.
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
NEEDS_HUMAN.branch_name AND the make-pr breadcrumb in the PR body). Branch-only matches are reported NEEDS_HUMAN, never mutated.gh pr merge --auto, merge-queue enrollment, or any merge without --match-head-commit.BLOCKED.git add -A in the CI-fix path — stage only the files edited for the fix.flow-next-resolve-pr (with mode:autonomous) and flow-next-tracker-sync (opt-in land.merged touchpoint).LAND_VERDICT line.FLOW_RALPH / REVIEW_RECEIPT_PATH).Execute workflow.md in order:
.flow/ start state, resolve the .git land ledger (read-only at this point), read land.* config.gh pr list --head <branch_name> --state all, OPEN-state filter, dual authorship signals, merged-but-unclosed re-entry candidates.land.reviewSignal), stale-approval detection, mergeStateStatus. --dry-run stops here.
silence signal, 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 scans issues/<n>/comments for an automated-reviewer comment matching land.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. Set land.cleanReviewCommentPattern to an explicit empty string "" to disable the comment path (pure reviews-API behavior); leaving it unset uses the built-in default.LAND_VERDICT line (worst-severity rule).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.
npx claudepluginhub gmickel/flow-next --plugin flow-nextMonitors CI status on a PR, auto-rebases when behind, auto-fixes CI failures, delegates review comments to dlc:pr-check, and re-requests review after fixes. Designed for /loop usage with Remote Control.
Monitors an open PR for conflicts, CI failures, review comments, and merge readiness, with auto-fixes and one-shot modes. Use for babysitting a PR, fixing CI, resolving conflicts, or triaging review comments.
Automates the PR review loop: resolves feedback, triggers re-review, and repeats until approval. Stops after 5 cycles to avoid chasing non-blocking findings.