flow-next-flow ยท diff

git:20260912.7eeadd7 to git:20260912.48855a5

34 added, 6 removed. Audit A to A.

---
name: flow-next-flow
description: Conductor for an idea, spec or task id, tracker issue, branch, bug report, or question; --auto runs a ready spec unattended and emits PILOT_VERDICT. Use when no skill is named or to pilot a spec.
user-invocable: false
allowed-tools: Read, Bash, Grep, Glob, Write, Edit, Task, Skill
---
# /flow-next:flow - the conductor
- Flow chooses the next step so the user does not have to. It reads what it was given, routes from the shared routing reference, runs the routed stage skill, and continues until the next decision that belongs to a human. It re-implements no stage logic: capture, refine, plan, plan-review, work, qa, make-pr, and resolve-pr keep their own contracts, receipts, and gates.
+ Flow chooses the next step so the user does not have to. It reads what it was given, routes from the shared routing reference, runs the routed stage skill, and continues until the next decision that belongs to a human. It re-implements no stage logic: capture, refine, plan, plan-review, work, qa, make-pr, resolve-pr, and land keep their own contracts, receipts, and gates.
**Role:** conductor, inline (no `context: fork`) so `plain-text numbered prompt` stays reachable. On hosts without it, fall back to a plain-text numbered prompt with a final `Other - type your own answer` option.
**Read [workflow.md](workflow.md) for the hop loop.** The routing rules live in `references/`, one file per rule, each opening with its decision record. Read a reference only at the step that names it; a step that does not name one reads nothing.
## Preamble
**CRITICAL: flowctl is BUNDLED - NOT installed globally.** `which flowctl` will fail (expected). Define once; subsequent blocks (here and in `workflow.md`) use `$FLOWCTL`:
```bash
FLOWCTL="${CODEX_HOME:-$HOME/.codex}/scripts/flowctl"
[ -x "$FLOWCTL" ] || FLOWCTL="<plugin-root>/scripts/flowctl" # <plugin-root> = the directory two levels above this skill's SKILL.md file (the harness gave you that file's absolute path when the skill loaded); substitute it literally
[ -x "$FLOWCTL" ] || FLOWCTL=".flow/bin/flowctl"
```
## Mode detection
- Parse `$ARGUMENTS` as exact tokens (never substrings), before any read or write: `--auto` sets `AUTO=1`; `--tick` sets `AUTO_TICK=1` (one hop, then stop; meaningful only with `--auto`); `--explain` sets `EXPLAIN=1`; `--review=<backend>` sets `REVIEW_OVERRIDE` and is passed through unchanged to every stage it dispatches. Everything else is the starting point, verbatim - flow adds no input classifier. A tracker issue id or URL is read through the access the session already has (the sync bridge, an MCP, `gh`, `glab`); flow adds no input adapter.
+ Parse `$ARGUMENTS` as exact tokens (never substrings), before any read or write: `--auto` sets `AUTO=1`; `--tick` sets `AUTO_TICK=1` (one hop, then stop; meaningful only with `--auto`); `--explain` sets `EXPLAIN=1`; `--review=<backend>` sets `REVIEW_OVERRIDE` and is passed through unchanged to every stage it dispatches. The destination parse is shared by both modes:
+ ```bash
+ FLOW_UNTIL=""
+ FLOW_DESTINATION_ERROR=0
+ LAND_AUTHORIZED=0
+ LAND_SCOPE_SPEC=""
+ LAND_SCOPE_PR=""
+ LAND_BASE_ROOT=""
+ AUTO=0
+ for ARG in $ARGUMENTS; do
+ case "$ARG" in
+ --auto) AUTO=1 ;;
+ --until=merge) FLOW_UNTIL=merge ;;
+ --until|--until=*) FLOW_DESTINATION_ERROR=1 ;;
+ esac
+ done
+ if [ "$FLOW_DESTINATION_ERROR" = 1 ]; then
+ if [ "$AUTO" = 1 ]; then
+ echo 'PILOT_VERDICT=NEEDS_HUMAN spec=- stage=- reason="invalid destination; use --until=merge"'
+ else
+ echo 'NEEDS_HUMAN: invalid destination; use --until=merge'
+ fi
+ exit 1
+ fi
+ export FLOW_UNTIL
+ ```
+
+ `--until=merge` authorizes landing the selected item in this invocation, independently of `--auto`. Without it the pre-merge boundary remains; current explicit item-scoped user authorization can also authorize landing. Read `references/tail.md` when reaching that boundary. Consent is host context, never recovered from an environment variable, old transcript, or receipt. A fresh session needs the flag again or current explicit authorization; revocation stops subsequent mutations. Consume destination tokens rather than passing them to a build stage. Everything else is the starting point, verbatim - flow adds no input classifier. A tracker issue id or URL is read through the access the session already has (the sync bridge, an MCP, `gh`, `glab`); flow adds no input adapter.
+
**`AUTO=1`: read [auto.md](auto.md) and follow it.** Attended runs never load it.
## Autonomy refusal - runs right after the token parse
Attended flow, `flow --auto`, and Ralph are three drivers and are never nested. Without `--auto`, flow is attended: under any autonomy marker (scan the marker namespace: `FLOW_RALPH`, `FLOW_AUTONOMOUS`, `REVIEW_RECEIPT_PATH`, `AUTONOMOUS=1`, a `mode:autonomous` token), stop before any read or write:
```
NEEDS_HUMAN: /flow-next:flow is attended - run /flow-next:flow --auto for unattended runs
```
With `--auto`, the refusal is Ralph-only (`FLOW_RALPH`, `REVIEW_RECEIPT_PATH`, in auto.md's hard guards), because `--auto` sets `FLOW_AUTONOMOUS` and `mode:autonomous` for the stages it dispatches. A run that routed, dispatched, or asked under a marker it should have refused has broken this.
## Invariants (every run)
- **Route on content and context, never on input kind.** Read what was given, decide what it is, then match `references/route-matrix.md` at the route step.
- **Ask only on a fork that is material and not observable.** Before any "which approach" or "what should this do" question, classify the fork per `references/prototype-before-ask.md`: an observable answer is settled by running something; only a product or preference call becomes a question, and at most one per hop.
- **Never fabricate a review, QA, or completion verdict** to pass a gate. Every stage flow skips is recorded with its reason (`stage: <name> - skipped(<kind>: <detail>)`), never omitted.
- - **Never merge, never close the spec, never dispatch a second driver.** A run from intent ends when the PR exists; a run on an open PR converges it and stops when merge is the only step left (`references/tail.md`).
+ - **Land owns merge and spec close.** The only driver-composition exception is a currently authorized, item-scoped flow invocation of land per `references/tail.md`. Never dispatch another flow, pilot, Ralph, or a loop from inside a run.
- **`--explain` writes nothing and dispatches nothing.** It prints the route, the positive signal, the safe skip and its kind, and why not the alternatives, in the recommendation shape from `references/route-matrix.md`.
- **Host command form:** print every copy-pasteable flow-next command in the spelling this host invokes - the flat `/flow-next-<name>` form when the resolved plugin root carries `.flow-next-opencode-manifest` (an OpenCode install), otherwise exactly as spelled here.
## Forbidden
- - Running attended under any autonomy marker, or dispatching `/flow-next:land` or a second driver from inside a run.
- - Merging, closing a spec, or force-pushing.
+ - Running attended under any autonomy marker, or dispatching a second driver beyond the scoped land stage.
+ - Executing merge or spec close inline, invoking land without current scoped consent, or force-pushing.
- Re-implementing a stage's logic inline instead of invoking its skill.
- A plain-text numbered prompt whose answer a prototype or experiment could have observed.
- Writing under `.flow/` on an `--explain` run.
- A stage skipped without a recorded reason.
## Report shape (every stop)
```
- Flow stopped at: <the human decision, or "PR exists">
+ Flow stopped at: <the human decision, "PR exists", or the observed landing outcome>
Route taken: <hop 1> -> <hop 2> -> ... (an inline pick reads `prospect [picked: <candidate>]`)
stage: <name> - ran [<start>..<end>] | skipped(<policy|config|empty|error>: <detail>) | failed(<reason>: <detail>) (one line per stage reached)
Next: <natural-language prompt or slash command, or the decision the user must make>
```