Features
/flow-next:features builds and keeps a committed user-POV drive map at .flow/features/, beside .flow/memory/. Each file records what a user-facing feature is, how a user reaches it, how an agent drives it, and which traps waste a verification run. /flow-next:qa and Drive read it when it exists, so navigation knowledge survives the run that discovered it.
One split keeps every existing contract intact: the map is how a user gets there, the spec is what to prove this time, and the live drive is the proof.
When to use it
Section titled “When to use it”Reach for it when a repo has a real user surface that people keep re-deriving. Every QA run that starts by working out “how do I even get to this screen” is paying the same navigation tax again, and a seeded map turns that into a read. Good moments to seed:
- The app has a stable set of user-facing features somebody drives regularly.
- QA passes keep rediscovering the same login flow, seed data, and empty-state traps.
- A second person (or a cold agent session) needs to drive the app without a walkthrough.
Two modes, resolved by state
Section titled “Two modes, resolved by state”One command, and the state of the repo answers which mode is meant.
| Mode | When | What it does |
|---|---|---|
| seed | .flow/features/ is absent, or you pass explicit init intent (--init, init, --seed, mode:seed) | Interview the repo, prove each route with one live drive, write the index plus one file per proven feature. |
| maintain | .flow/features/ is present and no init intent was given | The audit-shaped pass: index hygiene, source readers, reconcile, one live pass, triage, then ship or stop. |
Seed interviews the repo, not you. It reads the checkout for five facts - surface, run command, drive mechanism, observable evidence, isolation - and asks only for what no file answers. Then it names the top handful of user-facing features, and proves each route by driving it once against a live instance before anything lands.
Nothing undriven enters the map. A partial seed lands the proven features and names the failures, so one broken route never discards the run. A repo with no drivable user surface (a pure library), no usable driver on this host, or a checkout that will not build ends REFUSED with the reason, rather than manufacturing a map that teaches wrong steps.
Maintain
Section titled “Maintain”Maintain is the audit-shaped pass over the map that already exists:
- Index hygiene - reconcile the index against the files on disk, and pull in the drift memos QA filed under the
feature-map-driftmemory tag. - Source wave - one read-only reader per feature file, dispatched concurrently. Readers never drive and never edit; each returns a summary, source entry points, one concrete drift claim or
none, and a live recipe. - Reconcile - merge those recipes into as few app states as practical, spot-check only the drift that was actually cited, and sweep recent churn for unmapped user-facing surfaces. A concrete source path is required before calling one missing.
- Live pass - exercise every feature once against an instance this run started, required even when source looks clean.
- Triage - a wrong user-POV description is doc drift, so fix the map; working behavior the harness cannot drive is a harness gap, so fix the owned harness and re-drive before shipping; broken app behavior is a product bug, so report it and keep it out of the PR.
- Ship or stop - one outcome, no resume state.
Maintain’s edit scope is .flow/features/** plus the harness scripts the map already owns as launch, seed, or drive helpers. Product code is never edited, and a changed pass opens one chore PR and leaves it open for you or land.
Doctor: is this instance worth driving
Section titled “Doctor: is this instance worth driving”Doctor is one read-only check that runs before the first drive, on every fresh session, and again after any failed drive. It observes; it never clicks, types, or mutates.
| Check | Passes when |
|---|---|
| Right build / version | The process this run started is the checkout under test. |
| Port owned by this run | The listening port, socket, or CLI pid is the one this run bound, recorded in the run notes. |
| Auth valid | The disposable profile is signed in as the documented user, or the scenario is the public unauthenticated path. |
All three must pass. One failure is a stop, not a hope. Two ownership rules ride along: never drive an instance this run did not start, and never kill by process name - kill what this run started, by recorded pid, container id, or compose project. An orphaned port from a crashed prior run ends the invocation BLOCKED with the reclaim instruction, even when the wreckage is ours.
Doctor cannot see a wedged UI on a healthy process, so a stuck spinner or an undismissable modal is reset or relaunched rather than hoped through, then re-Doctored before the next drive.
The file shape
Section titled “The file shape”The index at .flow/features/README.md carries the operating rules - baseline preconditions, driving conventions, proof standards, a pointer at the feature-entry contract - plus the inventory grouped by surface. A cold agent reads it first and can drive from the map alone.
Every feature file opens with an H1 title, one paragraph of user-visible behavior, and a required one-line **Surface:** <id> identifier (web, cli, …). The index groups entries by that identifier, and consumers select a feature deterministically by surface plus sub-feature IDs. Then exactly four H2s, in this order:
| Section | Owns |
|---|---|
## Sub-features | Short stable IDs, one line each (notes.list - see owned notes, newest first). |
## How to get to it (user POV) | Every user entry point - fresh session and already-signed-in, URLs, sidebar labels, the CLI a user would type. |
## Driving it | Opens with Preconditions:, then labeled bullets pairing each user action with an exact command and its observable result. |
## Gotchas | Traps that waste or invalidate a run, each with its recovery. |
Commands are literal, handles are stable (roles, accessible names, prompt strings, never pixel coordinates), and implementation details stay out. Proof standards apply to everything that lands: capture the user action and the resulting state, verify side effects beside what is visible, exercise the real user path rather than a test-only endpoint, and report an unreachable route with the attempted route plus the unmet precondition.
Outcomes
Section titled “Outcomes”Every run ends with one terminal line, last, nothing after it:
FEATURES_VERDICT=<SEEDED|CLEAN|CHANGED|BLOCKED|REFUSED> features=<n> reason="<one line>"| Verdict | When |
|---|---|
SEEDED | Seed landed at least one proven feature. A partial seed names its failed routes in reason. |
CLEAN | Maintain covered every feature and found nothing to correct. No branch, no PR. |
CHANGED | Maintain shipped one chore PR of proven map and owned-harness corrections. Never a merge. |
BLOCKED | A named blocker stopped the pass - orphaned port, isolation failure, source-reader collapse, a PR that did not open. Terminal for the invocation; the next run re-enters fresh. |
REFUSED | An autonomy marker, no drivable surface, no usable driver on this host, or a checkout that will not build. |
features=<n> is the count that landed or remains covered, 0 when nothing did. The line is grammar a host loop can read, which is what makes a cadence like /loop 1d /flow-next:features legible.
Entry gates for a maintain pass
Section titled “Entry gates for a maintain pass”Maintain checks two things before it inspects anything, because both are what make its proofs mean something:
- On the default base. The checkout must match
origin/<default>for product code, for the map itself, and for untracked product files. A pass proves routes against the code its PR will ship on, so proof gathered on a diverged feature branch is proof of the wrong base. Diverged endsBLOCKEDwith the instruction to re-run from the default branch; branches are never switched over your working state. - Owned paths clean.
.flow/features/(plus any harness paths the map owns) must have no uncommitted edits. Pre-existing edits could not be told apart from the run’s own later, and theBLOCKEDrestore would discard them. Dirty endsBLOCKEDasking you to commit or stash first.
Explicit re-init over an existing map applies the same clean-tree requirement, then re-proves or removes every pre-existing feature file - no unproven leftovers survive a re-seed.
Where it stops
Section titled “Where it stops”- Never a pipeline stage. Pilot, land, and Ralph do not dispatch it, and no post-merge hook triggers it. Cadence belongs to you or your host loop.
- Never the intent source. Specs and acceptance criteria stay the contract of what to prove; the map is how-to-drive only.
- Never a second QA pass. Maintain proves the map is honest; it does not replace
/flow-next:qaor the human review that follows it. - Never a substitute for live evidence. Consumers read navigation from the map, and a SHIP verdict still rests on evidence captured from the running app.
- Not the code index.
/flow-next:map’s.clawpatch/slices are a code-POV layer for scouts and stay local-only. - No frozen scenarios. This-run test scenarios do not belong in the map; they rot and miss the change under test.
Worked example
Section titled “Worked example”/flow-next:featuresMode: seed (.flow/features/ absent)Interviewed the repo: web surface, `npm run dev`, agent-browser, disposable profile per runDoctor: build matches HEAD, port 8787 owned by this run, dev user signed inProved 3 of 4 routes live (settings/export failed: seed data missing, named in the index)Wrote .flow/features/README.md + notes-list.md, note-editor.md, sign-in.md
FEATURES_VERDICT=SEEDED features=3 reason="settings/export unproven this pass; retry next maintain"A later cadence tick over the same map:
/flow-next:featuresMode: maintain (.flow/features/ present)Entry gate: on origin/main, owned paths cleanSource wave: 3 readers, 1 cited drift (sign-in route moved to /login)Live pass: 3 features driven, settings/export retried and provedTriage: 1 doc drift fixed, 0 harness gaps, 1 product bug filed to memory (kept out of the PR)PR: chore(features): maintain pass
FEATURES_VERDICT=CHANGED features=4 reason="sign-in route corrected; settings/export now proven"Dynamic usage
Section titled “Dynamic usage”Recipes that compose with features in the cookbook:
- Evidence-first - seed the map once, then let QA read navigation instead of re-deriving it.
- Autonomy dial - a host loop can drive the maintain cadence; the pipeline still never dispatches it.
Next step
Section titled “Next step”Seed the map, then run a live pass that consumes it:
/flow-next:features # seed, then maintain on later invocations/flow-next:qa <spec-id> # QA reads navigation from the map when it exists- How it compounds - where the feature map sits among the self-improving surfaces.
- Live-app QA & driving UIs - the consumer, its driver ladder, and what the map does not change about the verdict.
- Glossary: Feature map (user-POV drive map) and Doctor.