How Flow chooses
/flow-next:flow picks the next step for whatever you hand it. An idea becomes a spec. A ready spec gets implemented. A spec with its tasks done gets a pull request: attended, when you say “open the PR”; under --auto, at once. A bug report, a slow path, a captured profile, a repeated agent mistake, a prompt change, a risky diff, a branch, or a question about the code each take their own route. Flow reads the item’s current state, matches it against the shared route matrix, runs that route, and stops at the next decision that is yours. flow --explain <the same words> prints the route and writes nothing. This page teaches you to read that output and to override it. The rows below are examples derived from the matrix, in this page’s words. The matrix itself lives in the flow skill’s reference files, listed on the Flow page. Flow picks the route in seconds and asks nothing about branches or readiness before a build: it takes a sensible default and says so in one line. A ready spec with one task is then built inline in your conversation.
Explain a route before running it
Section titled “Explain a route before running it”/flow-next:flow --explain rename the config loader without changing behaviourNext: pin the current behaviour of the config loader with a characterization test, then work the rename against it
Route: refactoringSignal: no new behaviour named; callers to migrateSkip/narrow: skip the pin only when existing coverage already asserts the contractSkip kind: signal absentWhy not the alternatives: no open decision to settle, so capture and refine add nothing; new behaviour named anywhere would make this a featureSignal names the fact in your request that selected the route. Skip/narrow names the stage the route leaves out and when leaving it out is safe. Drop --explain and the same words run the route.
Start with the evidence you have
Section titled “Start with the evidence you have”| You have | Flow routes to | What it produces |
|---|---|---|
| A slowdown nobody has measured | Investigation first | A cited answer, no .flow/ write |
| A measured slowness on a surface you can name | Performance: baseline, work, review | The baseline and target as the R-ID; the post-change measurement as evidence, with its limiter and a faster, slower, no measurable difference or inconclusive verdict |
| A captured profile, trace, heap snapshot or crash dump | Diagnosis from the artifact | A diagnosis and the route that would fix it, no .flow/ write |
| A why question about existing code | why-scout over git blame, the PRs behind the commits, the tracker thread, then bug and decision memory | Findings tiered direct, supported, inferred, or unknown |
| A design fork whose answer is observable | Prototype: the smallest discriminating experiment | A decision and its evidence, then capture or work |
Compare fifteen request shapes
Section titled “Compare fifteen request shapes”Each row is a request in ordinary language, the signal that discriminates it, and the evidence or handover it produces.
| You say | Discriminating signal | Route and its evidence |
|---|---|---|
| Build the whole billing overhaul; three of us will implement | A named open decision plus separate owners or staged delivery | capture, refine, plan, plan-review, work, QA per configuration, make-pr; the PR and its receipts |
| Build this from the brief; review the design first | Requirements clear, design risk named | capture the brief, plan-review on zero tasks, work --no-plan, make-pr |
| Work fn-12 | Ready cohesive spec, no positive plan signal | work --no-plan with the configured review; the route recorded on the spec |
| Rename the config key | Tiny, local, low-risk change in one context | a direct change with no spec, run the way a user would, reviewed by risk, and handed back |
This fails: <stack trace> | The unknown is the cause; the risk is regression | check for prior fixes, reproduce and diagnose, bisect when a known-good revision exists, fix, prove on base and head, review; the failing reproduction is the R-ID |
| Extract the parser without changing behaviour | No new behaviour named | pin the contract (characterization test, snapshot, or equivalence check), work, review |
| Export takes four seconds on the demo tenant | A metric and a surface you can name | baseline first, work, review; the post-change measurement is the evidence |
| Get p95 under 300 ms; keep trying | One metric, a target, a harness that reruns cheaply | work proves, then freezes the harness; one change and one measurement per attempt, kept or reverted; the target is never relaxed |
Why does boot take six seconds? boot.cpuprofile attached | A captured artifact and a question, no fix asked for | diagnosis from the artifact, mapped to source; a cause confirmed only by a before-and-after pair; Next: names the route that would fix it; no .flow/ write, no PR |
| Agents keep adding config keys without registering them | Two or more recorded occurrences of one mistake class | capture a spec whose criteria are checks that fail on a past occurrence, fixed at the highest level of the hardening ladder; then work and review |
| Does the new system prompt stop the bot promising refunds? | A behaviour claim about text a model reads, no metric yet | your eval harness, or a small blinded one; a promote, do not promote or inconclusive verdict with its evidence; asks for draws and budget first |
| What could this cache change break? | A diff handed over with doubt about its reach | the one fact the change is safe because of, proven by running code; risks, what was cleared, the cheapest check before merging; read-only |
| Why does this guard exist? | The deliverable is an answer | cited read-only answer; why-scout for why questions; no .flow/ write, no PR |
| Prototype both layouts and tell me which holds at 400 px | A named decision with an observable answer | the smallest discriminating experiment; the decision and its evidence; then capture or work |
| Bump the lockfile | A qualifying triage-skip receipt | direct change, flowctl triage-skip receipt with mode: triage_skip; a PR only when you ask |
Replace fn-12 with your spec id. The boundary each row applies is on the Flow page. Prospect and chart sit upstream. Flow routes through them when the input is a domain to search or one oversized unclear idea, and records the hop on the Route taken line. A bare flow takes the next step for the most recent item it can see, from the spec this conversation just captured down to the next open spec, and asks only when nothing resolves; the ladder is on the Flow page.
Signal: a large intent with a named open decision only you can make, and separate owners or staged delivery.
flowchart LR E([Epic intent]) --> C["/flow-next:capture"] -->|"named decision open"| I["/flow-next:refine"] --> P["/flow-next:plan"] C -->|"nothing open"| P --> PR["/flow-next:plan-review"] --> W["/flow-next:work"] --> M["/flow-next:make-pr"] --> H([PR]) W -.->|"qa on, or auto with UI criteria\n+ drivable surface + startable target"| Q["/flow-next:qa"] -.-> M H -.->|"you merge, or authorize"| L["land"]
Feature, requirements known
Section titled “Feature, requirements known”Signal: requirements are already clear (a brief, a ticket, an argument the team already had) and design risk remains.
flowchart LR B([Brief / ticket]) --> S["Ready spec"] --> PR["/flow-next:plan-review\n(zero tasks)"] --> W["/flow-next:work --no-plan"] --> M["/flow-next:make-pr"] --> H([PR])
No-plan route
Section titled “No-plan route”Signal: a ready, cohesive spec and no positive plan signal.
flowchart LR
S([Zero-task spec]) --> F{plan-vs-no-plan rule} -->|default| W["/flow-next:work --no-plan\nconfigured review inside"] --> PR["/flow-next:make-pr"] --> H([PR])
F -->|positive signal| P["/flow-next:plan"] --> PW["/flow-next:work\nplanned route"] --> PR
Small change
Section titled “Small change”Signal: a tiny, local, low-risk change that fits one implementation context.
flowchart LR T([Say the fix]) --> C["Direct change\nno spec"] --> V["Run it the way\na user would"] --> H([Handed back])
No spec is written and no pull request opens unless you ask for one. Review follows the risk rule: a change that touches persisted or shared state, concurrency, security or data layout is reviewed; a small local fix to output, wording or display is not, and the skip is recorded with its reason.
Bug or defect
Section titled “Bug or defect”Signal: the unknown is the cause; the risk is regression.
flowchart LR R([Bug report]) --> P["Prior-fix check"] --> D["Reproduce reliably,\ndiagnose"] --> B["Bisect"] --> F["Fix, prove on\nbase and head"] --> Rev["Configured review"] --> PR["/flow-next:make-pr"] --> H([PR])
A bug fix runs four steps in order inside work, whether flow routes a pasted report, console output, a screenshot, or failing behaviour, or you run work on a spec that fixes a reported defect. The failing reproduction is the R-ID: the requirement is that the reported behaviour stops happening, provably.
-
Check for prior fixes before writing one. Flow looks for open pull requests and branches that touch the affected area, recent commits and reverts there, the memory bug track, and tracker issues with the same symptom. An existing fix is run against the reproduction and reported; it never gets a competing fix. A fix someone is visibly working on is left to that person. Either one stops the route and leaves the decision to you; an unattended run stops with
NEEDS_HUMAN. Other open pull requests that touch the area are recorded, and the route continues. A reverted or failed earlier attempt counts as a ruled-out cause. A source flow cannot reach, such as the tracker without network access, is recorded as unchecked and the route continues. -
Reproduce reliably, then diagnose. Diagnosis starts from a reproduction that fires reliably; a flaky symptom gets tighter conditions or instrumentation until it does. Flow lists the candidate causes, rules them out one at a time with runtime evidence, and confirms the mechanism before it designs the fix. A failure that appears only after a restart, a reload or a second run makes persisted state (config files, caches, lock files, serialized records, migrations) the first candidate, ruled out with evidence before the code is suspected. Reading source can suggest a cause but never confirms one. A symptom that will not reproduce is reported as not reproduced, with what was tried, and nothing is fixed blind.
Before designing the fix, flow decides whether the state that triggered the symptom may legitimately occur. If it may, the code that broke on it is the cause, and handling the state there is the fix. If it should never exist, a check where it surfaced would hide the fault, so the fix goes where the state was produced. A fix shaped as a guard, retry, swallowed error, clamped value or discarded state says why the state is legitimate.
render_invoicecrashes on a customer with no address; the signup form requires one and an import job is the only writer that can skip it, so the fix makes the import reject such rows and backfills the existing records, and the renderer stays unchanged. In a product where guest checkouts have no address by design, the same crash is fixed in the renderer. When code, docs and history cannot settle legitimacy, it is a product question: an attended run asks, and an unattended run takes the default the evidence supports and records it. -
Bisect when a known-good revision exists. When the report or the history names a revision where the behaviour was correct, flow bisects with the reproduction and cites the commit that introduced the defect and the pull request that carried it. Without a known-good revision, bisectable history, or a reproduction cheap enough to run per revision, the step is skipped and the reason recorded.
-
Prove on base and head, through the surface the report came from. A reproduction that is a cheap test is committed, still failing, before the fix. The same reproduction must fail on the base and pass on the head, run through the surface the report named: the command for a CLI report, the request for an API report, the call for a library report, the app for a UI report. A passing unit test of the changed branch supports the fix and is never the only proof when the report named another surface; a report that names no surface uses the one a user meets first, and the record says which. A report that
tool export --since 2026-01-01drops the first day is proven by running that command against a fixture, 0 rows for January 1 on the base and 42 on the head, with the new date-helper test listed as supporting evidence. When there is a live app, the check repeats on it, starting from the feature map when there is one; if the app cannot start, the test proof stands and the live check is recorded as not run.
The record of all four steps goes into the task’s done summary, and the pull request briefing shows it as proof cells: prior fixes, the cause and its introducing commit, the base and head results, and the live check. A step that did not happen says not done with its reason instead of disappearing. When you hand flow a reproduction, it runs yours rather than writing its own; the other steps still apply. Refine is the wrong instrument for a defect. Capture only when the diagnosis conversation itself carries decisions worth locking down.
Refactoring
Section titled “Refactoring”Signal: a structural change (rename, extract, inline, dedupe, move) with behaviour meant to stay the same.
flowchart LR R([Structural change]) --> Pin["Pin the contract\n= the R-ID"] --> W["/flow-next:work\nconfigured review inside"] --> PR["/flow-next:make-pr"] --> H([PR])
For UI meant to look the same (a restyle, a move to design tokens, a component swap), the pin is a set of screenshots of named states taken before the change and compared by pixel diff, zero unless you name a tolerance. A difference is fixed in the change, never by retaking the baseline. Migrating buttons to design tokens, the checkout state shows a 3-pixel focus-ring offset at 400 px; the migration fixes the offset and the baseline stays as captured.
Performance
Section titled “Performance”Signal: a measured slowness, or a number you want moved once, on a surface you can name.
flowchart LR S([Measured slowness]) --> B["Baseline\n= the R-ID"] --> W["/flow-next:work\nconfigured review inside"] --> Meas["Post-change\nmeasurement"] --> PR["/flow-next:make-pr"] --> H([PR])
When the baseline and the post-change measurement drive a running app and the repo has a feature map, each reads the map index and the matching feature file itself before driving.
A measured number says what bounds it before anyone acts on it. The baseline and the post-change measurement here, the hill climb’s harness and final claim, and a prompt change’s eval verdict all answer the same questions from a real run:
- What bounds it. The limiter (a core, a lock, the disk, the network, an external service, the load generator itself) comes from a profile or system counters taken during a run and mapped to source. A guess from reading code is not a limiter, and because profilers slow the work, the limiter comes from a run that is not reported.
- What else it could be measuring. Errors or early returns that finish fast, a warm cache on one side and a cold one on the other, code that never ran inside the timed region, one side on default or per-item settings, noise, or a piece too small to matter end to end. Each is ruled out with evidence.
- Whether it is possible. Removing a piece saves at most its share of the run, so a 40% gain from a piece that takes 10% of the run measured something else.
- That the work happened. Requests reached the server, rows were written, lazy results were consumed, with errors and units of work counted on every side.
- Repetition and tuning. Each side runs at least five times, alternated, reported as median and range; a gap inside the run-to-run spread is no measurable difference. Every side runs the way production runs it, and with one side untuned there is no winner.
The claim reads faster, slower, no measurable difference or inconclusive, with its run count, range and limiter. It is inconclusive whenever the limiter is unnamed, a side ran untuned, or errors and work done went unchecked. A metric nobody can profile, such as an external service, names its limiter unknown and stays inconclusive unless the gap is far outside the spread and the arithmetic holds. A ballpark you asked for is one run, still checks errors and work done, and says it is one run. The done summary carries one line:
- measured: p50 41 ms -> 33 ms, median of 7 runs per side, range 32-35 ms after; limiter: JSON parsing on one core; errors 0 both sides; work 60,000 rows both sides; verdict: fasterComparing two storage engines for a bulk import, one side is four times faster. The profile shows the slower side waiting on a disk flush after every row, a setting rather than the engine, so the first line reads verdict: inconclusive: untuned side (commit per row). With both sides batching commits, the gap is 6%, inside the spread over seven alternated runs, and the verdict is no measurable difference. Routes that report no measured number never load these checks.
Hill climb
Section titled “Hill climb”Signal: one metric to improve against a target through repeated attempts, with a harness that reruns cheaply.
flowchart LR T(["Set up the\nexperiment"]) --> F["Prove, then\nfreeze the harness"] --> A["/flow-next:work\none change,\none measurement"] -->|"kept: one commit\nor reverted"| L["Ledger row"] L -->|no stop rule met| A L -->|stop| Rev["Configured review"] --> PR["/flow-next:make-pr"] --> H([PR])
A hill climb runs as a loop inside work, within the spec’s one task. The target is the R-ID and each attempt’s comparable measurement is its evidence.
- The agent sets up the experiment. You state the target; the spec needs nothing else. The agent picks a case that reproduces the complaint, the metric and the direction that counts as better, a minimum number of attempts (so one lucky early win does not end the run), a budget, the runs per measurement and the smallest difference that counts, using your numbers wherever you gave them. It writes those choices at the top of the ledger before the first attempt, so they are fixed in advance and visible in the pull request. The only thing that stops the run is a missing target: an attended run asks once, and an unattended run stops with
NEEDS_HUMAN. The target is never invented, relaxed or reinterpreted. - Prove the harness, then freeze it. The agent names the baseline’s limiter per the measured-number checks, then builds the command that measures the metric. The command prints an error count and a count of the work done (requests served, rows written, items produced) next to the metric, so a candidate with fewer units of work or any new error is rejected like a wrong output. It has to rank a known-worse change, the baseline and a known-better change in the right order, with gaps larger than the baseline’s spread, and it has to reject a wrong output, so a change that skips the work cannot score. Contrasting realistic workloads show the chosen case reproduces the complaint while easier cases separate as expected; when no case reproduces it, the agent fixes the reproduction before any attempt or stops and records why. Then its command and files are hashed; a later change to them forces a new baseline and is recorded as a break. The baseline and a green run of your regression tests are recorded before any change. When no harness separates the cases, or the noise is wider than the distance to the target, the run stops before any attempt and says why.
- One change, one measurement, cheapest idea first. Each attempt tests one hypothesis with a named mechanism that a profile or trace supports, measured against the current best with warm-ups discarded and runs interleaved. Families go in cost order, and the loop moves to a later one only when the ledger shows the earlier ones cannot meet the target: do not do the work at all, do not do it again (reuse, cache), do less of it, do it later, do it where nobody waits, do it concurrently, and only then do it cheaper (represent, tune, lighten). It is kept, as exactly one commit, only when it beats the best by more than the noise with no new errors and no less work done, and the regression tests stay green; a simplification that holds the number may be kept too. Anything else is reverted to a clean tree. Independent ideas may be tried in parallel worktrees, each kept only on its own measurement against the current best.
- Keep a ledger. Every attempt writes one row, whatever its verdict: the family, the mechanism, the change, before and after, the delta, the gate, the verdict and why, and the commit when kept. Refuted ideas are not retried. The full ledger goes into the task’s done summary.
- Stop honestly. The run stops when the target is met and the minimum attempts have run, when the budget is spent, when the remaining ideas are marginal, or at once when the harness breaks. Three misses in a row change the approach rather than stopping. A budget spent short of the target leaves the target unverified, never relaxed, and an unattended run opens a pull request that states the gap.
Review runs once over the kept commits. The done summary’s record gives the baseline and final values with their error and work counts and the limiter at each, and the final value passes the measured-number checks before it is claimed. The pull request shows the metric, the target, the baseline and final values with the percent change, the attempt counts (kept, reverted, inconclusive), the kept commits in order, the harness proof, the final gate and the best idea not yet tried. A value that cannot be shown reads unverified. Mechanisms that proved out, or proved useless, go to the memory knowledge track so the next climb starts from them.
A worked example. A spec asks to cut a small CLI’s --version cold start below 40 ms through repeated attempts, without changing what any subcommand prints. It names nothing else.
Before the first attempt the agent writes its setup into the ledger: a median of 11 runs after 3 discarded warm-ups, at least 5 attempts, a budget of 10, a smallest difference of 10 ms, the existing unit suite as the regression gate, and a benchmark script that interleaves two checkouts, prints the error count and the lines printed next to the time, and exits non-zero when the output is wrong. A profile from an unreported run names the limiter, module loading and plugin scanning on one core. The baseline reads 124 ms (118 to 132), errors 0, so the bar is 14 ms, the width of that range. A 30 ms sleep reads 154 ms, stubbing out plugin discovery reads 74 ms, and printing the wrong version fails the output check, so the benchmark is frozen with its hash.
Attempt 1 (reuse): the profile shows plugin discovery running twice. Dropping the redundant rescan reads 96 ms against 119 ms. Kept as one commit. Attempt 2 (defer): --version pays for plugin discovery that only list uses. Discovering inside list reads 73 ms. Kept. Attempt 3 (defer): a 62 ms table computation loads for every subcommand but only stats reads it. Importing it inside stats reads 7 ms. Kept.
The target is met after three attempts, and the floor of five keeps the loop going. Attempt 4 (parallelise) loads the remaining startup modules on threads and reads 19 ms, worse, and is reverted. Attempt 5 (represent) writes the version string at build time instead of reading package metadata and reads 7 ms against 7 ms, inside the noise.
The target is met and five attempts have run, so the loop stops. The pull request shows 124 ms to 8 ms (93% less), 5 attempts with 3 kept, 1 reverted and 1 inconclusive, the three kept commits in order, the harness proof, the green gate on the final head, and the best idea not yet tried: skipping interpreter site initialisation, now the remaining cost.
Diagnosis
Section titled “Diagnosis”Signal: a captured profile, trace, heap snapshot or crash dump, handed over with a why or what question and no request for a fix.
flowchart LR A([Artifact + question]) --> Q["Load it with\ntools you have"] --> L["Name the limiter,\nmap to source"] --> D([Diagnosis + Next route])
Flow loads the artifact with what the repo or your machine already has (the profiler’s own export, jq, a throwaway SQLite table), names what bounds the cost, holds the memory or crashed, and maps it to source. A cause is confirmed only by a before-and-after pair or a reproduction; otherwise the answer calls it the strongest supported hypothesis. When the artifact is older than the checkout, the answer says which findings depend on code that changed since. Nothing is fixed and nothing is written. Hand over boot.cpuprofile with “why does the first window take six seconds?” and the answer names a 2.1 s directory walk in the plugin loader, explains why an over-broad scan is the likely cause, and ends with Next: the hill-climb route on first-window time. Ask for the fix and flow takes the performance or defect route with the artifact as its baseline.
Repeated mistake
Section titled “Repeated mistake”Signal: “agents keep doing X”, or the same correction given twice.
flowchart LR M([Agents keep doing X]) --> E["Collect the class:\nreverts, reviews, memory"] --> C["/flow-next:capture\ncheck fails on a\npast occurrence = R-ID"] --> W["/flow-next:work"] --> PR["/flow-next:make-pr"] --> H([PR])
Flow collects the mistake from commits, reverts, review threads, memory entries and comments that excuse a workaround; one occurrence is a defect, two make a class. The captured spec has one criterion per class: a check that fails on a recorded past occurrence. The fix follows one hardening ladder, highest level first: remove the opening (one owner for each piece of state, one supported way, one list with the others derived from it), make the bad state unwritable with types, add a lint or CI check whose message names the fix, add a test, and write instruction text last, only for a call no check can make. “Agents keep adding config keys without registering them” becomes a typed settings accessor generated from the schema, so the commit that once added retry_jitter unregistered no longer builds. A lesson already in memory whose fix is a lint or CI gate goes to audit’s Harden path, which reads the same ladder; prime recommends this route when history shows a mistake made twice.
Prompt or skill change
Section titled “Prompt or skill change”Signal: a change to text a model reads (a prompt, a skill, an agent instruction) where the question is whether it behaves better, and no metric exists yet.
flowchart LR P([Prompt change]) --> B["Ask: draws\nand budget"] --> H["Your harness, or a\nblinded one"] --> V([promote / do not promote / inconclusive]) V -->|promote| W["/flow-next:work"]
Flow uses your eval harness when the repo has one. Otherwise it builds a small blinded one: an organic task a real user might send, names that do not give the test away, a rubric only the judge sees, the same number of draws for the old and new text, and a judge from another model family that sees outputs only by neutral label. It grades what the draws did, counts the draws on each side that finished the task and judges on those, and trusts a gap only when it holds draw by draw (per message or case) and is larger than the spread inside each side, per the measured-number checks. The verdict carries the completion count. Draws cost money, so it asks for the number of draws and a budget first. A support bot’s prompt rewrite that promised a refund in 1 of 60 replies against 14 of 60 before, with all 60 draws completed on each side, gets promote and then goes through work and review. To push a score up over many attempts, the hill-climb route takes this harness as its frozen measurement.
Diff risk
Section titled “Diff risk”Signal: a diff handed over with “what could this break” or “is this safe to merge”.
flowchart LR D([Diff + doubt]) --> F["Find the one fact\nit is safe because of"] --> R["Prove it by\nrunning code"] --> A([Safety fact, risks, cleared, cheapest check])
Flow reads the change, including what it now does that the diff does not spell out, and finds the one fact it is safe because of. It looks where text search stops: the dependency’s source at the pinned version, stored formats and columns, feature flags, another language reading the same bytes. Then it proves that fact with a script or test that runs the real code, or in the running app when it can reach it, and says how far the proof got: stated, pointed at, walked through, ran, reproduced. A move from time-based to size-based cache eviction is safe because nothing reads an evicted entry without reloading it; a script that fills the real cache past its bound shows every read after eviction reloads. The answer lists each risk with the check that would catch it, what was checked and cleared, and the cheapest check to run before merging. It changes nothing; implementation review stays the gate on the change.
Investigation
Section titled “Investigation”Signal: a read-only question; the deliverable is an answer with citations, no .flow/ write, no PR.
flowchart LR Q([How or why question]) --> S["why-scout, or the repo,\ndocs, and practice scouts"] --> A([Cited answer])
Prototype
Section titled “Prototype”Signal: a design or behaviour fork whose answer is observable, with a named decision the prototype exists to make.
flowchart LR F([Observable fork]) --> X["Smallest discriminating\nexperiment"] --> D([Decision + evidence]) D -->|"spec needed"| C["/flow-next:capture"] --> W["/flow-next:work\nconfigured review inside"] D -->|"tiny, one context"| W W --> PR["/flow-next:make-pr"] --> H([PR])
Docs or chore
Section titled “Docs or chore”Signal: near-zero risk, fully known, with a qualifying triage-skip receipt (recipe).
flowchart LR D([Direct change]) --> TS["flowctl triage-skip\nreceipt: mode triage_skip"] --> H([Handed back]) H -.->|"you ask"| PR([PR])
Continue an existing item
Section titled “Continue an existing item”| The item’s state | Flow routes to |
|---|---|
| A valid spec with a named open product or authority decision | /flow-next:refine; skip kind despite unresolved risk |
| A spec that names a library or API the repo does not use yet | /flow-next:refine --scope=research before work on either route. An existing ## Resolved via Research section or plan findings satisfy it only for the libraries and APIs they cover; a newly named library or API triggers a delta pass that appends under the right scout sub-block; you ratify the section before refine writes it back; --force reruns the whole pass and replaces the section |
| A ready spec with no tasks and no recorded route | work --no-plan by default; plan on a positive signal |
| A spec with planned tasks | /flow-next:work on the planned route with the configured gates |
| A spec with every task done and no PR | QA per pipeline.qa (off, the default, records skipped(config: pipeline.qa=off); on runs on every spec; auto runs only with UI-observable criteria on a drivable surface with a startable target, else skipped(config: pipeline.qa=auto: <reason>)), then /flow-next:make-pr: attended, when you ask for the PR (the handoff ends “Say ‘open the PR’ when you want it”); under --auto, at once |
| A spec with an open PR | Attended: offer land once, which converges the PR and merges it only with your consent (or --until=merge). Unattended without --until=merge: stop with DEFERRED_TO_LAND |
| A tracker issue, a branch, a path, or a prototype directory | Whatever the content and state say; the input form never selects the route |
Understand what changes the route
Section titled “Understand what changes the route”For a ready spec with no tasks and no recorded route, the route is /flow-next:work <spec-id> --no-plan. Plan is chosen only on one of three positive signals:
- You asked for a plan.
- Separate human owners will implement.
- Delivery is staged across several PRs.
Risk, size, and file count never trigger plan on their own. Design risk routes to plan-review, which reviews a spec with zero tasks. Unresolved product or authority choices route to refine, but only when at least one open decision can be named that would change what gets built and that only you can make. Technical detail, performance, and edge cases that implementation, review, and QA will surface do not qualify, and neither do defects, structural cleanups, or requests to move a measured number; the technical pass needs a named costly-to-reverse fork the code does not answer (when to refine). An unfamiliar library or API named in the spec routes to the research pass first. How and when Flow records the route on the spec is on the Flow page.
Read a skip and its consequence
Section titled “Read a skip and its consequence”A recommendation names a skip kind: signal absent means the stage’s work was not needed, and despite unresolved risk means a smaller path was chosen on purpose and the evidence, consent, and review contracts still apply later. A run report names a stage record, skipped(policy|config|empty|error: <detail>); the grammar is on the Flow page.
A skip changes what the PR reviewer inherits:
- Review. The configured backend reviews by risk: three reviewers from another model family for a change that touches persisted or shared state, concurrency, security or data layout, or a multi-file feature; one otherwise. Attended runs get one review and one scoped re-review of the fixes; unattended runs fix and re-review until SHIP. Three skips exist, all recorded: a small local fix to output, wording or display is not reviewed and the report says why;
review=nonerecordsskipped(config: review=none)and is never a verdict; a qualifyingflowctl triage-skipreceipt recordsmode: triage_skipand satisfies the gate without running the backend. On either, the PR reviewer holds the only review. The investigation route has no work stage and no review. - QA.
NEEDS_WORKandBLOCKEDadvance to the PR, and their findings become open items on a draft PR. Neither certifies anything. - Plan-review, refine, research. A skipped design review leaves design risk to the PR reviewer; a skipped refine leaves the
[inferred]source tags as the record of what the agent guessed.
Steer the next run
Section titled “Steer the next run”- Ask for a plan. “Plan fn-12 first; two of us will implement” supplies signal 2.
- Ask for design review. “Review the design before work” routes plan-review on the zero-task spec.
- Set a stopping boundary. “Stop once the spec is written” makes that write the decision that ends the run; the next
flow fn-12resumes from disk. - Invoke a stage directly. Every stage is a skill you can call by name; the cookbook has the recipes.
Choose an attended or unattended driver
Section titled “Choose an attended or unattended driver”Without --auto, Flow is attended. A default run from intent ends with the change committed on a local branch and opens the pull request when you ask; it does not offer landing for a pull request it just opened. A run on an existing PR offers continuation through merge and asks once unless that item is already authorized; --until=merge provides that authorization upfront; its other refusals are on the Flow page. flow --auto is the unattended shape of the same conductor since 5.1.0. It carries one ready spec to a pull request from the same routing references, never asks, and writes every decision it made on your behalf into the PR, so pipeline.qa=auto is judged by the same rule under both, and land ships the result. The /flow-next:pilot command it replaced was removed in 6.1.0. What shipped and what comes next: Flow and the road ahead.
Migrating from 4.x: /flow-next:guide has no alias; /flow-next:flow --explain replaces it. Use /flow-next:refine where you used /flow-next:interview.
Where to go next
Section titled “Where to go next”- One change through review - the evidence chain on a real change, and where Flow’s decisions would appear on it today.
- Flow - what it accepts, the report it prints, and what it refuses to do.
- Compose the pipeline - the doctrine these routes instantiate.
- Cookbook: skip & lighten - recipes for the lightweight routes.