feature-flow-cli 0.8.0__tar.gz → 0.14.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {feature_flow_cli-0.8.0/feature_flow_cli.egg-info → feature_flow_cli-0.14.0}/PKG-INFO +88 -11
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/README.md +87 -10
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/adapters/codex/feature-flow/SKILL.md +18 -9
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/adapters/codex/feature-flow/agents/openai.yaml +1 -1
- feature_flow_cli-0.14.0/agents/flow-architect.md +27 -0
- feature_flow_cli-0.14.0/agents/flow-designer.md +27 -0
- feature_flow_cli-0.14.0/agents/flow-po.md +27 -0
- feature_flow_cli-0.14.0/agents/flow-scout.md +40 -0
- feature_flow_cli-0.14.0/agents/flow-web.md +30 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/__init__.py +1 -1
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/cli.py +31 -8
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/codehash.py +6 -1
- feature_flow_cli-0.14.0/feature_flow/conductor.py +1742 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/git.py +6 -0
- feature_flow_cli-0.14.0/feature_flow/lanes.py +227 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/prompts.py +58 -8
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/proof.py +11 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/report.py +8 -3
- feature_flow_cli-0.14.0/feature_flow/scouts.py +118 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/status.py +12 -4
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/tickets.py +47 -2
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0/feature_flow_cli.egg-info}/PKG-INFO +88 -11
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow_cli.egg-info/SOURCES.txt +9 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/build.md +4 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/plan-review.md +3 -1
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/plan.md +13 -5
- feature_flow_cli-0.14.0/guides/replan.md +35 -0
- feature_flow_cli-0.14.0/guides/review-feature.md +66 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/review-quality.md +8 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/review-single.md +9 -1
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/review.md +8 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/templates/commands.md +7 -1
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/templates/ticket.md +2 -1
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/scripts/flow-trust.py +3 -2
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/skills/feature-flow/SKILL.md +19 -10
- feature_flow_cli-0.8.0/feature_flow/conductor.py +0 -587
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/LICENSE +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/MANIFEST.in +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/agents/feature-planner.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/agents/plan-reviewer.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/agents/ticket-builder.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/agents/ticket-reviewer.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/__main__.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/checks.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/command.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/floorguard.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/gate.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/install.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/proc.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/released.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/state.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/suggest.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/uninstall.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow/view.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow_cli.egg-info/dependency_links.txt +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow_cli.egg-info/entry_points.txt +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/feature_flow_cli.egg-info/top_level.txt +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/brief.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/debug.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/show.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/templates/learnings.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/templates/map.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/templates/spec.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/guides/templates/ui-mockup.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/install.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/install.sh +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/pyproject.toml +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/scripts/floor-guard.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/scripts/flow-status.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/scripts/flow-view.html +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/scripts/flow-view.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/scripts/flow.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/scripts/gate.py +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/setup.cfg +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/skills/architect-review/SKILL.md +0 -0
- {feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/skills/automation-design/SKILL.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feature-flow-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: A ticket-graph workflow for coding agents: installs the feature-flow skill into a repo
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/hamilton-sky/feature-flow
|
|
@@ -71,7 +71,7 @@ Then, in your repo, in the agent:
|
|
|
71
71
|
|
|
72
72
|
The same command plans when `plans/csv-export/` does not exist yet and builds when it does. After your second yes it commits the new plan folder, and only that folder, then goes straight on to building it; say so if you want it to stop after planning.
|
|
73
73
|
|
|
74
|
-
**Planning asks you twice.** First the session shows a short feature brief, written from your conversation (what, why, scope, the bar, and anything it had to assume), and waits for yes, edit or cancel. Then a fresh `feature-planner` subagent, given only that brief, reads the code, looks up outside docs where the code cannot answer, and writes a draft plan into the git-ignored `.feature-flow/state/draft/`. The planner folds changes too small to stand alone into the ticket they belong to, and
|
|
74
|
+
**Planning asks you twice.** First the session shows a short feature brief, written from your conversation (what, why, scope, the bar, and anything it had to assume), and waits for yes, edit or cancel. Then a fresh `feature-planner` subagent, given only that brief, reads the code, looks up outside docs where the code cannot answer, and writes a draft plan into the git-ignored `.feature-flow/state/draft/`. The planner folds changes too small to stand alone into the ticket they belong to, and gives every ticket a level (1 light, 2 standard, 3 boundary) that decides how much review it gets. A fresh `plan-reviewer` checks the draft against the brief. The session shows you the goal, the commands, the ticket graph, what was dropped and the review result, and waits for a second yes. Only then does `flow.py plan-accept` copy the draft into `plans/<feature>/`, and the session commits it and starts the build.
|
|
75
75
|
|
|
76
76
|
Try the graph first, with no setup and no agent: `bash examples/demo.sh --open`.
|
|
77
77
|
|
|
@@ -121,22 +121,34 @@ The Claude Code skill is `skills/feature-flow/`. The Codex skill is written by h
|
|
|
121
121
|
│ PASS │
|
|
122
122
|
▼ │
|
|
123
123
|
quality review: a second fresh reviewer subagent FAIL ─┤
|
|
124
|
-
(a
|
|
125
|
-
|
|
124
|
+
(a level 1 or 2 ticket gets one combined review instead
|
|
125
|
+
of these two; see "Ticket levels" below)
|
|
126
126
|
│ PASS ▼
|
|
127
127
|
▼ findings written into the ticket,
|
|
128
|
-
next ticket reopened, built again (
|
|
128
|
+
next ticket reopened, built again (rounds by level);
|
|
129
129
|
the builder's prompt then carries
|
|
130
130
|
a root-cause debugging guide
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
`scripts/flow.py` is the conductor. The session asks it `next`, and it answers with one line: `BUILD`, `REVIEW`, `DONE`, `STOP` or `HANDOFF`. It prints the full prompt for each subagent from runtime-neutral guides, so the session never writes one itself.
|
|
134
134
|
|
|
135
|
-
**What the script checks and what it trusts.** The conductor verifies the build from the repo: the ticket file must say `resolved`, the tree must be clean and committed, and it runs the gate and the floor guard itself. It also runs the `check` block under each ticket's Done when and compares the exit code and printed text, and for a `Test first: yes` ticket it proves the red test commit itself: it checks out the test-only commit, runs Test, and requires it to fail before the code. A failure of either goes back to the builder before any reviewer is started. It also checks that the reviewer changed no tracked file and made no commit. The review verdicts are different: each reviewer's reply (spec review, then quality review) is relayed by the session, which saves it to a file and hands it to `flow.py verdict`. The script reads `REVIEW: PASS` or `REVIEW: FAIL` from that reply; it cannot prove the session passed it on unedited. A ticket moves on only after two PASS verdicts, or one for a ticket the conductor judged small. The size is measured from the repo (`git diff` against the commit the ticket started from), never taken from the builder.
|
|
135
|
+
**What the script checks and what it trusts.** The conductor verifies the build from the repo: the ticket file must say `resolved`, the tree must be clean and committed, and it runs the gate and the floor guard itself. It also runs the `check` block under each ticket's Done when and compares the exit code and printed text, and for a `Test first: yes` ticket it proves the red test commit itself: it checks out the test-only commit, runs Test, and requires it to fail before the code. A failure of either goes back to the builder before any reviewer is started. It also checks that the reviewer changed no tracked file and made no commit. The review verdicts are different: each reviewer's reply (spec review, then quality review) is relayed by the session, which saves it to a file and hands it to `flow.py verdict`. The script reads `REVIEW: PASS` or `REVIEW: FAIL` from that reply; it cannot prove the session passed it on unedited. A ticket moves on only after two PASS verdicts, or one for a level 1 or 2 ticket (or, with no level, a ticket the conductor judged small). The size is measured from the repo (`git diff` against the commit the ticket started from), never taken from the builder.
|
|
136
136
|
|
|
137
137
|
**The gate: full or quick.** The gate runs Build, Test and Lint after every ticket. With an optional `Quick:` line in `commands.md` (a faster subset of `Test`), it runs Build, Quick and Lint after the tickets in the middle of a plan, and the full Build, Test and Lint after the last ticket, before a handoff, and under `FLOW_GATE_FULL=1`. A plan without `Quick:` runs the full gate every time. The test-first proof still runs the full `Test` command.
|
|
138
138
|
|
|
139
|
-
**
|
|
139
|
+
**Ticket levels.** The planner writes `Level: 1`, `2` or `3` in each ticket, the plan reviewer checks it against written rules, and you approve it with the plan.
|
|
140
|
+
|
|
141
|
+
| | Level 1 (light) | Level 2 (standard) | Level 3 (boundary) |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
| For | tests, docs, prompt text, spikes, the acceptance ticket | ordinary feature code | auth, tokens, permissions, money and run limits, data loss, the flow's own code, anything a hostile caller reaches |
|
|
144
|
+
| Reviews | one combined review | one combined review, spec checklist first | spec review, then a quality review that probes every input as a hostile caller |
|
|
145
|
+
| Rework rounds | 1, then the run stops for you | 2; from round 3 only a `blocker` sends it back, majors go to `plans/<feature>/followups.md` and the ticket passes | `FLOW_MAX_REVIEW_ROUNDS` (3), then the run stops for you |
|
|
146
|
+
|
|
147
|
+
The gate does not depend on the level: it still runs Quick mid-plan and the full Test on the last ticket and before a handoff. Nobody but you can lower a level. The conductor reads `Level:` from the ticket as it was at the base commit, so a builder's edit does nothing (and the floor guard fails it). It raises the level from what it measures, never lowers it: `Review: full`, `Type: convert` and `Floor: allow flow-edit` make a ticket level 3, so does a diff that touches a path on the optional `Sensitive:` line of `commands.md` (frozen by the floor guard), and a level 1 diff over the small limits becomes level 2. A reviewer can raise it with a line `LEVEL: 3` above the verdict; a raise to 3 during a combined review adds a quality review. The log records `LEVEL-1`, `LEVEL-2` or `LEVEL-3` for each review round, and `feature-flow report` shows it.
|
|
148
|
+
|
|
149
|
+
**A rebuild reruns only the review that failed.** When a level 3 ticket fails its quality review, the rebuilt ticket gets the quality review only; the spec review it already passed is not repeated. The conductor's own checks (gate, floor guard, Done when, test first) still run on every rebuild.
|
|
150
|
+
|
|
151
|
+
**One review for a small ticket (no level).** A ticket with no `Level:` line, from a plan written before 0.9.0, keeps the 0.8.0 rules. A ticket whose built diff is 3 files and 60 changed lines at most (the plan folder left out), and whose `check` block ran and passed, gets one combined review instead of the spec and quality reviews. The conductor measures the diff itself. `Review: full` in the ticket (as it was at the start), `Type: convert`, no `check` block, or `FLOW_GATE=off` keep both reviews. Every other conductor check still runs on every ticket.
|
|
140
152
|
|
|
141
153
|
## The skill and the roles
|
|
142
154
|
|
|
@@ -183,6 +195,8 @@ Type: task task | settle | convert
|
|
|
183
195
|
Status: open open | claimed | resolved | parked
|
|
184
196
|
Blocked by: 01, 02 or —
|
|
185
197
|
Test first: yes yes | no
|
|
198
|
+
Level: 2 1 light | 2 standard | 3 boundary
|
|
199
|
+
Touches: src/export/ the paths it changes; a build outside them goes back
|
|
186
200
|
Floor: allow config optional, only when a human decides the guard may let it through
|
|
187
201
|
|
|
188
202
|
...what to do and why...
|
|
@@ -234,6 +248,63 @@ One self contained HTML file: no server, no libraries, no network, light and dar
|
|
|
234
248
|
- **Safe by construction:** ticket text comes from files an agent wrote, so it is escaped when embedded and only ever inserted as text.
|
|
235
249
|
- `flow-status.py <feature> --json` prints the same data for other tools. Mermaid (`--mermaid`) remains the choice for pull requests, because GitHub renders it.
|
|
236
250
|
|
|
251
|
+
## Tickets side by side (lanes)
|
|
252
|
+
|
|
253
|
+
With `FLOW_LANES=2` (up to 4) on `start`, tickets that do not touch the same files are built at the same time, each with its own builder and reviewers in its own git worktree. Only the start and the end of a ticket change: the build, the gate, the floor guard and the reviews are the same as with one lane.
|
|
254
|
+
|
|
255
|
+
- **Plan.** Each ticket lists the paths it changes on a `Touches:` line (a folder ends with `/`, a `*` glob crosses folders). `commands.md` may name `Shared:` paths that many tickets change, and a `Setup:` command a fresh checkout needs. The plan reviewer checks the footprints; you approve them with the plan.
|
|
256
|
+
- **Schedule.** The conductor leases a ready ticket to a free lane only when its `Touches:` (read from the feature branch, never from a builder) does not overlap any running ticket's, it touches nothing on `Shared:`, and, when either ticket is level 3, the two keep to different top-level folders. A ticket with no `Touches:` line runs alone.
|
|
257
|
+
- **Lanes.** The lanes are worktrees in `.feature-flow/lanes/<feature>/` (git ignores the folder), created on first use, with `Setup:` run once in each. Each ticket gets a branch `ff/<feature>/<NN>` from the feature head.
|
|
258
|
+
- **Footprint check.** After every build, with or without lanes, a change to a path outside the ticket's `Touches:` sends the ticket back (`FOOTPRINT-FAIL` in the log).
|
|
259
|
+
- **Merge.** A ticket that passes its review is merged by the conductor, one at a time, in `.feature-flow/lanes/<feature>/integrate` with `git merge --no-ff`, so the reviewed commits land unchanged. The gate runs on the merge (Quick mid-plan, the full Test for the last ticket). Only a green merge fast-forwards the feature branch; then the ticket's branch is deleted with `git branch -d`. When two lanes both appended to `map.md`, `learnings.md` or `followups.md`, the conductor keeps both sides' lines; no agent ever resolves a conflict.
|
|
260
|
+
- **Rebuild.** Any other conflict, or a gate that fails on the merge, sends the ticket back to be built again from the feature branch as it is now, with what went wrong and its earlier commit in the ticket. A second collision stops the run for you: the plan's `Touches:` are likely wrong.
|
|
261
|
+
- **Level 3.** When another ticket merged after a level 3 ticket started, it is merged with the new feature head in its lane, gated, and given its quality review again before it merges.
|
|
262
|
+
- **The end.** After the last merge the lanes are removed. `reset` removes them too, with the ticket branches.
|
|
263
|
+
|
|
264
|
+
The session runs `prompt`, `next` and `verdict` with the lane from the `BUILD` or `REVIEW` line, and `next` answers `WAIT <lanes>` when every lane that can work is busy (see `## Lanes` in the skill).
|
|
265
|
+
|
|
266
|
+
## Scouts
|
|
267
|
+
|
|
268
|
+
Before a planner, builder or reviewer starts, the session can spawn cheap helpers that read ahead for it: `flow-scout` (read only, codebase) and `flow-web` (web only, for the planner). Both run on Haiku in Claude Code (`model: haiku` in their role files); in Codex the skill picks the lightest model `spawn_agent` allows. A scout looks at the code the way its role would (`ROLE: builder` looks for patterns and helpers to reuse, `ROLE: reviewer` for the rules the change must follow) and never decides anything.
|
|
269
|
+
|
|
270
|
+
| Before | Scouts |
|
|
271
|
+
|---|---|
|
|
272
|
+
| planning (`plan-scouts <brief>`) | one codebase scout and one web scout |
|
|
273
|
+
| a level 1 ticket | none |
|
|
274
|
+
| a build below level 3 (first attempt) | one builder scout on the ticket and its `Touches:` |
|
|
275
|
+
| a level 3 build (first attempt) | two builder scouts: the code it changes, and what depends on it |
|
|
276
|
+
| a level 3 ticket's spec review | one reviewer scout on the conventions the diff must follow |
|
|
277
|
+
| the feature review (fast path) | one reviewer scout |
|
|
278
|
+
|
|
279
|
+
`scouts [lane]` lists `SCOUT <k> <agent> <reply file>` lines (or `NONE`) and clears the last ones' replies; `scout-prompt <k> [lane]` prints one scout's prompt. The session saves each reply into its file, and the phase's `prompt` adds them under `## Scout findings (unverified)`, cut to 60 lines each. A reply saved for one phase never reaches another. Retries and later rounds get no scouts. `FLOW_SCOUTS=off` turns them off.
|
|
280
|
+
|
|
281
|
+
## Questions for the architect, designer or product owner
|
|
282
|
+
|
|
283
|
+
A builder, reviewer or planner that hits a question it should not decide alone asks it instead of guessing, and the flow pauses that job until the right role answers.
|
|
284
|
+
|
|
285
|
+
- **Who answers.** `flow-architect` answers technical design questions, `flow-designer` answers UI and UX questions, and `flow-po` answers what the feature must do where the spec is silent. Each is a fresh, read only agent that sees the plan, the ticket as it was planned and the code, never the asker's reasoning or a ticket's Answer. Each gets one scout with its own lens first.
|
|
286
|
+
- **A builder** appends `## Ask: architect|designer|po` with the question to its ticket and stops. `next` hands out `ASK <role> <ticket> <NN> <sha>`. The answer goes into the ticket under `## Consulted (<role>)`, and the ticket is built again. The question does not count as a failed attempt.
|
|
287
|
+
- **A reviewer** ends with a `## Question` and `REVIEW: ASK <role>`. The answer comes back in the next review's notes. On the fast path's feature review, the reviewer writes the question as a finding, and the fix ticket's builder can ask it.
|
|
288
|
+
- **The planner** starts each open question with `architect:`, `designer:`, `po:` or `user:`. The session runs `consult-prompt <role> <question file>` for the first three, adds the answers to the brief under `Consulted:`, kept apart from your `Decided:` lines, and plans again.
|
|
289
|
+
- **What still comes to you.** Any question the asker marks `user`, any answer a consultant ends with `ANSWER: USER` (it changes what you get, so its options come to you), and a third consultant question on one ticket all stop the run with the question, written into the ticket under `## Ask: user`. You answer under `## Consulted (user)` at the end of the ticket, commit, and run `next`. A reviewer's question for you reopens the ticket, so its builder builds again with your answer.
|
|
290
|
+
|
|
291
|
+
## The fast path
|
|
292
|
+
|
|
293
|
+
`/feature-flow <feature> fast` trades per-ticket reviews for speed. The planner gets `Pace: fast` in the brief and writes a short plan of fewer, larger tickets, and the plan reviewer is skipped. With `FLOW_FAST=1` on `start`, a ticket below level 3 (one with no `Level:` line too) that passes the gate, the floor guard and its Done when checks passes at once (`REVIEW-DEFERRED` in the log). With `FLOW_GATE=off` nothing would check it, so every ticket keeps its review. A level 3 ticket, or one that touches a `Sensitive:` path, still gets its own reviews.
|
|
294
|
+
|
|
295
|
+
- **The feature review.** Once every ticket is resolved, `next` hands out `REVIEW <plan folder> all <sha>`: one fresh reviewer reads the deferred tickets as they were planned and the whole diff from the oldest deferred ticket's base to HEAD, with `guides/review-feature.md`. It has no lane, even when lanes run.
|
|
296
|
+
- **A failed feature review** becomes a new ticket, `NN-feature-review-fixes-<round>.md`, with the findings in it. A builder fixes it, and the feature is reviewed again. After `FLOW_MAX_REVIEW_ROUNDS` failed rounds the run stops for you.
|
|
297
|
+
- **Mid-run.** `fast on` and `fast off` (through the runner, like `next`) switch it between phases. Asking the session to go faster runs `fast on`. Asking again while fast is on offers the re-plan below. Tickets already passed without a review still get the feature review, whichever way it is switched.
|
|
298
|
+
- **The cost.** A mistake in an early ticket is found at the end, when later tickets may have built on it. Use it for small or low-risk features. `reset` keeps the list of deferred tickets, so they still get the feature review.
|
|
299
|
+
|
|
300
|
+
## A time budget
|
|
301
|
+
|
|
302
|
+
`FLOW_BUDGET=<minutes>` on `start` gives the feature a budget of active minutes. The conductor counts the time between its steps, at most an hour for one step, so a session left open overnight does not eat the budget, and the time between two sessions does not count. `budget` (through the runner) reports it, and `budget <minutes>`, `budget +<minutes>` or `budget off` change it between phases.
|
|
303
|
+
|
|
304
|
+
- **Over the budget, fast mode first.** Before the next ticket starts, the conductor turns fast mode on by itself (`BUDGET-FAST` in the log).
|
|
305
|
+
- **Still over, a re-plan offer.** Once fast mode has finished a ticket and the run is still over (or at once, if fast mode was already on), the run stops once with the minutes spent, the tickets left, and an offer to re-plan. If one ticket took more than half the time over several attempts, the stop names it as stuck, since that is a stuck ticket, not a slow plan. `next` goes on as planned, and a new `budget` earns a new offer.
|
|
306
|
+
- **The re-plan needs your yes.** On your yes, between tickets, `replan-prompt` hands the `feature-planner` the open tickets with `guides/replan.md`. It folds them into fewer, larger tickets with the same bar: every Done when item and check moves into the ticket that takes over, folded tickets become `Status: parked (folded into NN)`, and resolved tickets stay as they are. The session shows you the change and commits it only on a second yes. The next `next` stops if the re-plan changed a resolved ticket. Nothing is ever cut without you.
|
|
307
|
+
|
|
237
308
|
## Long features and handoff
|
|
238
309
|
|
|
239
310
|
A session's context fills up, so the flow moves to a fresh session every few tickets. After `FLOW_TICKETS_PER_SESSION` tickets (default 4) pass review, `next` prints a `HANDOFF` line instead of the next ticket, for example `HANDOFF /feature-flow csv-export 3f9c0a1b2c3d4e5f.0a1b2c3d4e5f6a7b` (the last word is the runner's digest, which lets the new session check that the flow's files did not change in between). The session stops and tells you to open a new session and type that line. The new session picks up exactly where the last one stopped. Set `FLOW_TICKETS_PER_SESSION=0` to never hand off.
|
|
@@ -250,9 +321,10 @@ The state lives in `.feature-flow/state/` (the state, a log of every step, and t
|
|
|
250
321
|
|
|
251
322
|
```bash
|
|
252
323
|
python3 scripts/flow.py <feature> start|reset [NN] the conductor (the skill runs it; also plan-prompt, plan-review-prompt, plan-accept)
|
|
253
|
-
FLOW_TRUST=<digest|new> FLOW_SESSION=<token> python3 -I scripts/flow-trust.py <feature> next|prompt|verdict <file
|
|
324
|
+
FLOW_TRUST=<digest|new> FLOW_SESSION=<token> python3 -I scripts/flow-trust.py <feature> next|prompt|verdict <file>|fast on|off loop commands by hand, through the runner: prints TRUST <digest> first (no hash check, see below)
|
|
254
325
|
python3 scripts/flow-status.py <feature> table of tickets and which are READY
|
|
255
326
|
python3 scripts/flow-status.py <feature> --next path of the next ready ticket (exit 10 = done, 11 = stuck)
|
|
327
|
+
python3 scripts/flow-status.py <feature> --ready every ready ticket, one per line (the same exit codes)
|
|
256
328
|
python3 scripts/flow-status.py <feature> --counts one line of counts
|
|
257
329
|
python3 scripts/flow-status.py <feature> --check cycles, missing blockers, missing Done when, unordered mentions
|
|
258
330
|
python3 scripts/flow-status.py <feature> --lint --check plus: a check block (or `Check: none <why>`) on every ticket, a Test command
|
|
@@ -274,12 +346,16 @@ The commands in `commands.md` run through `bash -c` on Linux and macOS and throu
|
|
|
274
346
|
|---|---|---|
|
|
275
347
|
| `FLOW_TICKETS_PER_SESSION` | `4` | tickets per session before `HANDOFF`; `0` never hands off |
|
|
276
348
|
| `FLOW_RELAY` | `0` | `1` hands off after every build and every review |
|
|
349
|
+
| `FLOW_LANES` | `1` | read by `start`: `2` to `4` builds tickets with disjoint `Touches:` side by side in worktrees (not with `FLOW_RELAY`) |
|
|
350
|
+
| `FLOW_FAST` | unset | read by `start`: `1` defers the reviews of tickets below level 3 to one feature review at the end, `0` turns it off; unset keeps the run's setting |
|
|
351
|
+
| `FLOW_BUDGET` | unset | read by `start`: minutes of active work before fast mode turns on, then a re-plan offer; `off` drops it |
|
|
352
|
+
| `FLOW_SCOUTS` | `on` | `off` lists no scouts before any phase |
|
|
277
353
|
| `FLOW_TAKEOVER` | unset | `1` lets `start` take a feature over from a session that is gone |
|
|
278
354
|
| `FLOW_MAX_RETRIES` | `2` | builds or reviews per step before the run stops |
|
|
279
|
-
| `FLOW_MAX_REVIEW_ROUNDS` | `3` | times a ticket may be sent back before the run stops |
|
|
355
|
+
| `FLOW_MAX_REVIEW_ROUNDS` | `3` | times a ticket may be sent back before the run stops (a level 1 ticket: once) |
|
|
280
356
|
| `FLOW_GATE` | `on` | `off` skips Build, Test and Lint after each ticket, and also the Done when and test-first checks |
|
|
281
357
|
| `FLOW_GATE_FULL` | unset | `1` runs the full Build, Test and Lint after every ticket, even when `commands.md` has a `Quick:` line |
|
|
282
|
-
| `FLOW_SMALL_FILES` | `3` | most files a ticket may change and still get the single review |
|
|
358
|
+
| `FLOW_SMALL_FILES` | `3` | most files a ticket may change and still get the single review (no level), or stay level 1 |
|
|
283
359
|
| `FLOW_SMALL_LINES` | `60` | most changed lines for the single review; `0` turns the single review off |
|
|
284
360
|
| `FLOW_GATE_TIMEOUT` | `30` | minutes each gate command and the smoke test may run; `0` means no limit. A failed smoke test writes `.feature-flow/state/<feature>.smoke.log` |
|
|
285
361
|
| `FLOW_SMOKE` | the `Smoke:` line of `commands.md` | command run before every ticket; the run stops if it fails |
|
|
@@ -329,7 +405,8 @@ feature-flow's layers are tripwires, not a security boundary. They catch acciden
|
|
|
329
405
|
- **A plan can be wrong and still pass.** The plan lint checks that every ticket has a way to be proven and that `commands.md` has a Test command. It cannot tell whether the plan builds the right thing; the second yes is yours.
|
|
330
406
|
- **The trust runner stops accidents, not the session.** It catches a builder editing the conductor mid-run. The session that runs it is the thing it cannot check; a hand-run `flow-trust.py` is unprotected (see above).
|
|
331
407
|
- **Little real-world use so far.** Almost everything has been built by feature-flow on its own repo. The Codex skill and the Codex planner have not been run by hand, and the Quick gate has only been run by tests. See [docs/field-run.md](docs/field-run.md) for how to try it on your own repo and what to send back.
|
|
332
|
-
- **One ticket at a time**, with a handoff to a new session every few tickets.
|
|
408
|
+
- **One ticket at a time**, with a handoff to a new session every few tickets, unless you turn on lanes.
|
|
409
|
+
- **Lanes share one disk.** A builder in one lane can still write into another lane's folder or the main worktree. The conductor sees that as a dirty tree and stops, but only afterwards. Overlap of `Touches:` is judged from the patterns, so two tickets that could never meet may still run one after the other.
|
|
333
410
|
|
|
334
411
|
## Upgrading
|
|
335
412
|
|
|
@@ -52,7 +52,7 @@ Then, in your repo, in the agent:
|
|
|
52
52
|
|
|
53
53
|
The same command plans when `plans/csv-export/` does not exist yet and builds when it does. After your second yes it commits the new plan folder, and only that folder, then goes straight on to building it; say so if you want it to stop after planning.
|
|
54
54
|
|
|
55
|
-
**Planning asks you twice.** First the session shows a short feature brief, written from your conversation (what, why, scope, the bar, and anything it had to assume), and waits for yes, edit or cancel. Then a fresh `feature-planner` subagent, given only that brief, reads the code, looks up outside docs where the code cannot answer, and writes a draft plan into the git-ignored `.feature-flow/state/draft/`. The planner folds changes too small to stand alone into the ticket they belong to, and
|
|
55
|
+
**Planning asks you twice.** First the session shows a short feature brief, written from your conversation (what, why, scope, the bar, and anything it had to assume), and waits for yes, edit or cancel. Then a fresh `feature-planner` subagent, given only that brief, reads the code, looks up outside docs where the code cannot answer, and writes a draft plan into the git-ignored `.feature-flow/state/draft/`. The planner folds changes too small to stand alone into the ticket they belong to, and gives every ticket a level (1 light, 2 standard, 3 boundary) that decides how much review it gets. A fresh `plan-reviewer` checks the draft against the brief. The session shows you the goal, the commands, the ticket graph, what was dropped and the review result, and waits for a second yes. Only then does `flow.py plan-accept` copy the draft into `plans/<feature>/`, and the session commits it and starts the build.
|
|
56
56
|
|
|
57
57
|
Try the graph first, with no setup and no agent: `bash examples/demo.sh --open`.
|
|
58
58
|
|
|
@@ -102,22 +102,34 @@ The Claude Code skill is `skills/feature-flow/`. The Codex skill is written by h
|
|
|
102
102
|
│ PASS │
|
|
103
103
|
▼ │
|
|
104
104
|
quality review: a second fresh reviewer subagent FAIL ─┤
|
|
105
|
-
(a
|
|
106
|
-
|
|
105
|
+
(a level 1 or 2 ticket gets one combined review instead
|
|
106
|
+
of these two; see "Ticket levels" below)
|
|
107
107
|
│ PASS ▼
|
|
108
108
|
▼ findings written into the ticket,
|
|
109
|
-
next ticket reopened, built again (
|
|
109
|
+
next ticket reopened, built again (rounds by level);
|
|
110
110
|
the builder's prompt then carries
|
|
111
111
|
a root-cause debugging guide
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
`scripts/flow.py` is the conductor. The session asks it `next`, and it answers with one line: `BUILD`, `REVIEW`, `DONE`, `STOP` or `HANDOFF`. It prints the full prompt for each subagent from runtime-neutral guides, so the session never writes one itself.
|
|
115
115
|
|
|
116
|
-
**What the script checks and what it trusts.** The conductor verifies the build from the repo: the ticket file must say `resolved`, the tree must be clean and committed, and it runs the gate and the floor guard itself. It also runs the `check` block under each ticket's Done when and compares the exit code and printed text, and for a `Test first: yes` ticket it proves the red test commit itself: it checks out the test-only commit, runs Test, and requires it to fail before the code. A failure of either goes back to the builder before any reviewer is started. It also checks that the reviewer changed no tracked file and made no commit. The review verdicts are different: each reviewer's reply (spec review, then quality review) is relayed by the session, which saves it to a file and hands it to `flow.py verdict`. The script reads `REVIEW: PASS` or `REVIEW: FAIL` from that reply; it cannot prove the session passed it on unedited. A ticket moves on only after two PASS verdicts, or one for a ticket the conductor judged small. The size is measured from the repo (`git diff` against the commit the ticket started from), never taken from the builder.
|
|
116
|
+
**What the script checks and what it trusts.** The conductor verifies the build from the repo: the ticket file must say `resolved`, the tree must be clean and committed, and it runs the gate and the floor guard itself. It also runs the `check` block under each ticket's Done when and compares the exit code and printed text, and for a `Test first: yes` ticket it proves the red test commit itself: it checks out the test-only commit, runs Test, and requires it to fail before the code. A failure of either goes back to the builder before any reviewer is started. It also checks that the reviewer changed no tracked file and made no commit. The review verdicts are different: each reviewer's reply (spec review, then quality review) is relayed by the session, which saves it to a file and hands it to `flow.py verdict`. The script reads `REVIEW: PASS` or `REVIEW: FAIL` from that reply; it cannot prove the session passed it on unedited. A ticket moves on only after two PASS verdicts, or one for a level 1 or 2 ticket (or, with no level, a ticket the conductor judged small). The size is measured from the repo (`git diff` against the commit the ticket started from), never taken from the builder.
|
|
117
117
|
|
|
118
118
|
**The gate: full or quick.** The gate runs Build, Test and Lint after every ticket. With an optional `Quick:` line in `commands.md` (a faster subset of `Test`), it runs Build, Quick and Lint after the tickets in the middle of a plan, and the full Build, Test and Lint after the last ticket, before a handoff, and under `FLOW_GATE_FULL=1`. A plan without `Quick:` runs the full gate every time. The test-first proof still runs the full `Test` command.
|
|
119
119
|
|
|
120
|
-
**
|
|
120
|
+
**Ticket levels.** The planner writes `Level: 1`, `2` or `3` in each ticket, the plan reviewer checks it against written rules, and you approve it with the plan.
|
|
121
|
+
|
|
122
|
+
| | Level 1 (light) | Level 2 (standard) | Level 3 (boundary) |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| For | tests, docs, prompt text, spikes, the acceptance ticket | ordinary feature code | auth, tokens, permissions, money and run limits, data loss, the flow's own code, anything a hostile caller reaches |
|
|
125
|
+
| Reviews | one combined review | one combined review, spec checklist first | spec review, then a quality review that probes every input as a hostile caller |
|
|
126
|
+
| Rework rounds | 1, then the run stops for you | 2; from round 3 only a `blocker` sends it back, majors go to `plans/<feature>/followups.md` and the ticket passes | `FLOW_MAX_REVIEW_ROUNDS` (3), then the run stops for you |
|
|
127
|
+
|
|
128
|
+
The gate does not depend on the level: it still runs Quick mid-plan and the full Test on the last ticket and before a handoff. Nobody but you can lower a level. The conductor reads `Level:` from the ticket as it was at the base commit, so a builder's edit does nothing (and the floor guard fails it). It raises the level from what it measures, never lowers it: `Review: full`, `Type: convert` and `Floor: allow flow-edit` make a ticket level 3, so does a diff that touches a path on the optional `Sensitive:` line of `commands.md` (frozen by the floor guard), and a level 1 diff over the small limits becomes level 2. A reviewer can raise it with a line `LEVEL: 3` above the verdict; a raise to 3 during a combined review adds a quality review. The log records `LEVEL-1`, `LEVEL-2` or `LEVEL-3` for each review round, and `feature-flow report` shows it.
|
|
129
|
+
|
|
130
|
+
**A rebuild reruns only the review that failed.** When a level 3 ticket fails its quality review, the rebuilt ticket gets the quality review only; the spec review it already passed is not repeated. The conductor's own checks (gate, floor guard, Done when, test first) still run on every rebuild.
|
|
131
|
+
|
|
132
|
+
**One review for a small ticket (no level).** A ticket with no `Level:` line, from a plan written before 0.9.0, keeps the 0.8.0 rules. A ticket whose built diff is 3 files and 60 changed lines at most (the plan folder left out), and whose `check` block ran and passed, gets one combined review instead of the spec and quality reviews. The conductor measures the diff itself. `Review: full` in the ticket (as it was at the start), `Type: convert`, no `check` block, or `FLOW_GATE=off` keep both reviews. Every other conductor check still runs on every ticket.
|
|
121
133
|
|
|
122
134
|
## The skill and the roles
|
|
123
135
|
|
|
@@ -164,6 +176,8 @@ Type: task task | settle | convert
|
|
|
164
176
|
Status: open open | claimed | resolved | parked
|
|
165
177
|
Blocked by: 01, 02 or —
|
|
166
178
|
Test first: yes yes | no
|
|
179
|
+
Level: 2 1 light | 2 standard | 3 boundary
|
|
180
|
+
Touches: src/export/ the paths it changes; a build outside them goes back
|
|
167
181
|
Floor: allow config optional, only when a human decides the guard may let it through
|
|
168
182
|
|
|
169
183
|
...what to do and why...
|
|
@@ -215,6 +229,63 @@ One self contained HTML file: no server, no libraries, no network, light and dar
|
|
|
215
229
|
- **Safe by construction:** ticket text comes from files an agent wrote, so it is escaped when embedded and only ever inserted as text.
|
|
216
230
|
- `flow-status.py <feature> --json` prints the same data for other tools. Mermaid (`--mermaid`) remains the choice for pull requests, because GitHub renders it.
|
|
217
231
|
|
|
232
|
+
## Tickets side by side (lanes)
|
|
233
|
+
|
|
234
|
+
With `FLOW_LANES=2` (up to 4) on `start`, tickets that do not touch the same files are built at the same time, each with its own builder and reviewers in its own git worktree. Only the start and the end of a ticket change: the build, the gate, the floor guard and the reviews are the same as with one lane.
|
|
235
|
+
|
|
236
|
+
- **Plan.** Each ticket lists the paths it changes on a `Touches:` line (a folder ends with `/`, a `*` glob crosses folders). `commands.md` may name `Shared:` paths that many tickets change, and a `Setup:` command a fresh checkout needs. The plan reviewer checks the footprints; you approve them with the plan.
|
|
237
|
+
- **Schedule.** The conductor leases a ready ticket to a free lane only when its `Touches:` (read from the feature branch, never from a builder) does not overlap any running ticket's, it touches nothing on `Shared:`, and, when either ticket is level 3, the two keep to different top-level folders. A ticket with no `Touches:` line runs alone.
|
|
238
|
+
- **Lanes.** The lanes are worktrees in `.feature-flow/lanes/<feature>/` (git ignores the folder), created on first use, with `Setup:` run once in each. Each ticket gets a branch `ff/<feature>/<NN>` from the feature head.
|
|
239
|
+
- **Footprint check.** After every build, with or without lanes, a change to a path outside the ticket's `Touches:` sends the ticket back (`FOOTPRINT-FAIL` in the log).
|
|
240
|
+
- **Merge.** A ticket that passes its review is merged by the conductor, one at a time, in `.feature-flow/lanes/<feature>/integrate` with `git merge --no-ff`, so the reviewed commits land unchanged. The gate runs on the merge (Quick mid-plan, the full Test for the last ticket). Only a green merge fast-forwards the feature branch; then the ticket's branch is deleted with `git branch -d`. When two lanes both appended to `map.md`, `learnings.md` or `followups.md`, the conductor keeps both sides' lines; no agent ever resolves a conflict.
|
|
241
|
+
- **Rebuild.** Any other conflict, or a gate that fails on the merge, sends the ticket back to be built again from the feature branch as it is now, with what went wrong and its earlier commit in the ticket. A second collision stops the run for you: the plan's `Touches:` are likely wrong.
|
|
242
|
+
- **Level 3.** When another ticket merged after a level 3 ticket started, it is merged with the new feature head in its lane, gated, and given its quality review again before it merges.
|
|
243
|
+
- **The end.** After the last merge the lanes are removed. `reset` removes them too, with the ticket branches.
|
|
244
|
+
|
|
245
|
+
The session runs `prompt`, `next` and `verdict` with the lane from the `BUILD` or `REVIEW` line, and `next` answers `WAIT <lanes>` when every lane that can work is busy (see `## Lanes` in the skill).
|
|
246
|
+
|
|
247
|
+
## Scouts
|
|
248
|
+
|
|
249
|
+
Before a planner, builder or reviewer starts, the session can spawn cheap helpers that read ahead for it: `flow-scout` (read only, codebase) and `flow-web` (web only, for the planner). Both run on Haiku in Claude Code (`model: haiku` in their role files); in Codex the skill picks the lightest model `spawn_agent` allows. A scout looks at the code the way its role would (`ROLE: builder` looks for patterns and helpers to reuse, `ROLE: reviewer` for the rules the change must follow) and never decides anything.
|
|
250
|
+
|
|
251
|
+
| Before | Scouts |
|
|
252
|
+
|---|---|
|
|
253
|
+
| planning (`plan-scouts <brief>`) | one codebase scout and one web scout |
|
|
254
|
+
| a level 1 ticket | none |
|
|
255
|
+
| a build below level 3 (first attempt) | one builder scout on the ticket and its `Touches:` |
|
|
256
|
+
| a level 3 build (first attempt) | two builder scouts: the code it changes, and what depends on it |
|
|
257
|
+
| a level 3 ticket's spec review | one reviewer scout on the conventions the diff must follow |
|
|
258
|
+
| the feature review (fast path) | one reviewer scout |
|
|
259
|
+
|
|
260
|
+
`scouts [lane]` lists `SCOUT <k> <agent> <reply file>` lines (or `NONE`) and clears the last ones' replies; `scout-prompt <k> [lane]` prints one scout's prompt. The session saves each reply into its file, and the phase's `prompt` adds them under `## Scout findings (unverified)`, cut to 60 lines each. A reply saved for one phase never reaches another. Retries and later rounds get no scouts. `FLOW_SCOUTS=off` turns them off.
|
|
261
|
+
|
|
262
|
+
## Questions for the architect, designer or product owner
|
|
263
|
+
|
|
264
|
+
A builder, reviewer or planner that hits a question it should not decide alone asks it instead of guessing, and the flow pauses that job until the right role answers.
|
|
265
|
+
|
|
266
|
+
- **Who answers.** `flow-architect` answers technical design questions, `flow-designer` answers UI and UX questions, and `flow-po` answers what the feature must do where the spec is silent. Each is a fresh, read only agent that sees the plan, the ticket as it was planned and the code, never the asker's reasoning or a ticket's Answer. Each gets one scout with its own lens first.
|
|
267
|
+
- **A builder** appends `## Ask: architect|designer|po` with the question to its ticket and stops. `next` hands out `ASK <role> <ticket> <NN> <sha>`. The answer goes into the ticket under `## Consulted (<role>)`, and the ticket is built again. The question does not count as a failed attempt.
|
|
268
|
+
- **A reviewer** ends with a `## Question` and `REVIEW: ASK <role>`. The answer comes back in the next review's notes. On the fast path's feature review, the reviewer writes the question as a finding, and the fix ticket's builder can ask it.
|
|
269
|
+
- **The planner** starts each open question with `architect:`, `designer:`, `po:` or `user:`. The session runs `consult-prompt <role> <question file>` for the first three, adds the answers to the brief under `Consulted:`, kept apart from your `Decided:` lines, and plans again.
|
|
270
|
+
- **What still comes to you.** Any question the asker marks `user`, any answer a consultant ends with `ANSWER: USER` (it changes what you get, so its options come to you), and a third consultant question on one ticket all stop the run with the question, written into the ticket under `## Ask: user`. You answer under `## Consulted (user)` at the end of the ticket, commit, and run `next`. A reviewer's question for you reopens the ticket, so its builder builds again with your answer.
|
|
271
|
+
|
|
272
|
+
## The fast path
|
|
273
|
+
|
|
274
|
+
`/feature-flow <feature> fast` trades per-ticket reviews for speed. The planner gets `Pace: fast` in the brief and writes a short plan of fewer, larger tickets, and the plan reviewer is skipped. With `FLOW_FAST=1` on `start`, a ticket below level 3 (one with no `Level:` line too) that passes the gate, the floor guard and its Done when checks passes at once (`REVIEW-DEFERRED` in the log). With `FLOW_GATE=off` nothing would check it, so every ticket keeps its review. A level 3 ticket, or one that touches a `Sensitive:` path, still gets its own reviews.
|
|
275
|
+
|
|
276
|
+
- **The feature review.** Once every ticket is resolved, `next` hands out `REVIEW <plan folder> all <sha>`: one fresh reviewer reads the deferred tickets as they were planned and the whole diff from the oldest deferred ticket's base to HEAD, with `guides/review-feature.md`. It has no lane, even when lanes run.
|
|
277
|
+
- **A failed feature review** becomes a new ticket, `NN-feature-review-fixes-<round>.md`, with the findings in it. A builder fixes it, and the feature is reviewed again. After `FLOW_MAX_REVIEW_ROUNDS` failed rounds the run stops for you.
|
|
278
|
+
- **Mid-run.** `fast on` and `fast off` (through the runner, like `next`) switch it between phases. Asking the session to go faster runs `fast on`. Asking again while fast is on offers the re-plan below. Tickets already passed without a review still get the feature review, whichever way it is switched.
|
|
279
|
+
- **The cost.** A mistake in an early ticket is found at the end, when later tickets may have built on it. Use it for small or low-risk features. `reset` keeps the list of deferred tickets, so they still get the feature review.
|
|
280
|
+
|
|
281
|
+
## A time budget
|
|
282
|
+
|
|
283
|
+
`FLOW_BUDGET=<minutes>` on `start` gives the feature a budget of active minutes. The conductor counts the time between its steps, at most an hour for one step, so a session left open overnight does not eat the budget, and the time between two sessions does not count. `budget` (through the runner) reports it, and `budget <minutes>`, `budget +<minutes>` or `budget off` change it between phases.
|
|
284
|
+
|
|
285
|
+
- **Over the budget, fast mode first.** Before the next ticket starts, the conductor turns fast mode on by itself (`BUDGET-FAST` in the log).
|
|
286
|
+
- **Still over, a re-plan offer.** Once fast mode has finished a ticket and the run is still over (or at once, if fast mode was already on), the run stops once with the minutes spent, the tickets left, and an offer to re-plan. If one ticket took more than half the time over several attempts, the stop names it as stuck, since that is a stuck ticket, not a slow plan. `next` goes on as planned, and a new `budget` earns a new offer.
|
|
287
|
+
- **The re-plan needs your yes.** On your yes, between tickets, `replan-prompt` hands the `feature-planner` the open tickets with `guides/replan.md`. It folds them into fewer, larger tickets with the same bar: every Done when item and check moves into the ticket that takes over, folded tickets become `Status: parked (folded into NN)`, and resolved tickets stay as they are. The session shows you the change and commits it only on a second yes. The next `next` stops if the re-plan changed a resolved ticket. Nothing is ever cut without you.
|
|
288
|
+
|
|
218
289
|
## Long features and handoff
|
|
219
290
|
|
|
220
291
|
A session's context fills up, so the flow moves to a fresh session every few tickets. After `FLOW_TICKETS_PER_SESSION` tickets (default 4) pass review, `next` prints a `HANDOFF` line instead of the next ticket, for example `HANDOFF /feature-flow csv-export 3f9c0a1b2c3d4e5f.0a1b2c3d4e5f6a7b` (the last word is the runner's digest, which lets the new session check that the flow's files did not change in between). The session stops and tells you to open a new session and type that line. The new session picks up exactly where the last one stopped. Set `FLOW_TICKETS_PER_SESSION=0` to never hand off.
|
|
@@ -231,9 +302,10 @@ The state lives in `.feature-flow/state/` (the state, a log of every step, and t
|
|
|
231
302
|
|
|
232
303
|
```bash
|
|
233
304
|
python3 scripts/flow.py <feature> start|reset [NN] the conductor (the skill runs it; also plan-prompt, plan-review-prompt, plan-accept)
|
|
234
|
-
FLOW_TRUST=<digest|new> FLOW_SESSION=<token> python3 -I scripts/flow-trust.py <feature> next|prompt|verdict <file
|
|
305
|
+
FLOW_TRUST=<digest|new> FLOW_SESSION=<token> python3 -I scripts/flow-trust.py <feature> next|prompt|verdict <file>|fast on|off loop commands by hand, through the runner: prints TRUST <digest> first (no hash check, see below)
|
|
235
306
|
python3 scripts/flow-status.py <feature> table of tickets and which are READY
|
|
236
307
|
python3 scripts/flow-status.py <feature> --next path of the next ready ticket (exit 10 = done, 11 = stuck)
|
|
308
|
+
python3 scripts/flow-status.py <feature> --ready every ready ticket, one per line (the same exit codes)
|
|
237
309
|
python3 scripts/flow-status.py <feature> --counts one line of counts
|
|
238
310
|
python3 scripts/flow-status.py <feature> --check cycles, missing blockers, missing Done when, unordered mentions
|
|
239
311
|
python3 scripts/flow-status.py <feature> --lint --check plus: a check block (or `Check: none <why>`) on every ticket, a Test command
|
|
@@ -255,12 +327,16 @@ The commands in `commands.md` run through `bash -c` on Linux and macOS and throu
|
|
|
255
327
|
|---|---|---|
|
|
256
328
|
| `FLOW_TICKETS_PER_SESSION` | `4` | tickets per session before `HANDOFF`; `0` never hands off |
|
|
257
329
|
| `FLOW_RELAY` | `0` | `1` hands off after every build and every review |
|
|
330
|
+
| `FLOW_LANES` | `1` | read by `start`: `2` to `4` builds tickets with disjoint `Touches:` side by side in worktrees (not with `FLOW_RELAY`) |
|
|
331
|
+
| `FLOW_FAST` | unset | read by `start`: `1` defers the reviews of tickets below level 3 to one feature review at the end, `0` turns it off; unset keeps the run's setting |
|
|
332
|
+
| `FLOW_BUDGET` | unset | read by `start`: minutes of active work before fast mode turns on, then a re-plan offer; `off` drops it |
|
|
333
|
+
| `FLOW_SCOUTS` | `on` | `off` lists no scouts before any phase |
|
|
258
334
|
| `FLOW_TAKEOVER` | unset | `1` lets `start` take a feature over from a session that is gone |
|
|
259
335
|
| `FLOW_MAX_RETRIES` | `2` | builds or reviews per step before the run stops |
|
|
260
|
-
| `FLOW_MAX_REVIEW_ROUNDS` | `3` | times a ticket may be sent back before the run stops |
|
|
336
|
+
| `FLOW_MAX_REVIEW_ROUNDS` | `3` | times a ticket may be sent back before the run stops (a level 1 ticket: once) |
|
|
261
337
|
| `FLOW_GATE` | `on` | `off` skips Build, Test and Lint after each ticket, and also the Done when and test-first checks |
|
|
262
338
|
| `FLOW_GATE_FULL` | unset | `1` runs the full Build, Test and Lint after every ticket, even when `commands.md` has a `Quick:` line |
|
|
263
|
-
| `FLOW_SMALL_FILES` | `3` | most files a ticket may change and still get the single review |
|
|
339
|
+
| `FLOW_SMALL_FILES` | `3` | most files a ticket may change and still get the single review (no level), or stay level 1 |
|
|
264
340
|
| `FLOW_SMALL_LINES` | `60` | most changed lines for the single review; `0` turns the single review off |
|
|
265
341
|
| `FLOW_GATE_TIMEOUT` | `30` | minutes each gate command and the smoke test may run; `0` means no limit. A failed smoke test writes `.feature-flow/state/<feature>.smoke.log` |
|
|
266
342
|
| `FLOW_SMOKE` | the `Smoke:` line of `commands.md` | command run before every ticket; the run stops if it fails |
|
|
@@ -310,7 +386,8 @@ feature-flow's layers are tripwires, not a security boundary. They catch acciden
|
|
|
310
386
|
- **A plan can be wrong and still pass.** The plan lint checks that every ticket has a way to be proven and that `commands.md` has a Test command. It cannot tell whether the plan builds the right thing; the second yes is yours.
|
|
311
387
|
- **The trust runner stops accidents, not the session.** It catches a builder editing the conductor mid-run. The session that runs it is the thing it cannot check; a hand-run `flow-trust.py` is unprotected (see above).
|
|
312
388
|
- **Little real-world use so far.** Almost everything has been built by feature-flow on its own repo. The Codex skill and the Codex planner have not been run by hand, and the Quick gate has only been run by tests. See [docs/field-run.md](docs/field-run.md) for how to try it on your own repo and what to send back.
|
|
313
|
-
- **One ticket at a time**, with a handoff to a new session every few tickets.
|
|
389
|
+
- **One ticket at a time**, with a handoff to a new session every few tickets, unless you turn on lanes.
|
|
390
|
+
- **Lanes share one disk.** A builder in one lane can still write into another lane's folder or the main worktree. The conductor sees that as a dirty tree and stops, but only afterwards. Overlap of `Touches:` is judged from the patterns, so two tickets that could never meet may still run one after the other.
|
|
314
391
|
|
|
315
392
|
## Upgrading
|
|
316
393
|
|
|
@@ -7,23 +7,23 @@ description: "Use to plan a feature (from the conversation so far, with a fresh
|
|
|
7
7
|
|
|
8
8
|
Plan or build the feature in `<arguments>`.
|
|
9
9
|
|
|
10
|
-
The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. If no feature was given and the conversation so far describes work to build, plan it (below) and propose the feature's name in the brief. If no feature was given otherwise, list `plans/*/` and ask which one.
|
|
10
|
+
The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. `fast` means the fast path: run `start` with `FLOW_FAST=1`, and tickets below level 3 that pass their checks skip their own review until one review of the whole feature at the end (a level 3 or `Sensitive:` ticket keeps its reviews). When the user asks mid-run to go faster, run `fast on` (or `fast off`) through `flow-trust.py` between two other conductor commands; if it answers that fast is already on, offer the user the re-plan it names. A time budget (`FLOW_BUDGET=<minutes>` on `start`, `budget <minutes>|+<minutes>|off` mid-run) turns fast on by itself once spent, and later stops to offer a re-plan. A re-plan needs the user's own yes, even with `auto`: then run `replan-prompt` (between tickets), start the planner with it as in planning, with a new `task_name`, show the user its reply and `git diff --stat`, and on a second yes commit the plan folder as `plan(<feature>): re-plan the open tickets` (else `git checkout -- plans/<feature>`), then run `next`. If no feature was given and the conversation so far describes work to build, plan it (below) and propose the feature's name in the brief. If no feature was given otherwise, list `plans/*/` and ask which one.
|
|
11
11
|
|
|
12
12
|
The conductor is `python3 scripts/flow.py <feature> <command>`. If `scripts/flow.py` exists in the repo and the feature-flow package is beside it (`.feature-flow/feature_flow/`, or `feature_flow/` in a feature-flow checkout), use it (the repo's own install wins; an unrelated `scripts/flow.py` does not count); otherwise use `python3 "${FEATURE_FLOW_HOME:-$HOME/.feature-flow}/scripts/flow.py"` (`~/.feature-flow/scripts/flow.py`). Wherever this skill or a message names `scripts/flow.py`, `scripts/flow-status.py` or `scripts/flow-view.py`, it means the same folder, and the guides are then in that folder's sibling `guides/` (`~/.feature-flow/guides/`, else `.feature-flow/guides/` in the repo). Run the conductor with the working directory in the repo: state and plans stay there. It decides the order, runs the gate and the floor guard, and keeps its state in `.feature-flow/state/`, a folder git ignores. You ask it, and you do what it says. The guides it uses are in `.feature-flow/guides/` (in a feature-flow checkout, `guides/`). Read the project's `AGENTS.md` for its conventions.
|
|
13
13
|
|
|
14
14
|
## Calling the conductor
|
|
15
15
|
|
|
16
|
-
Every conductor command (`start`, `next`, `prompt`, `verdict`, `reset`, `plan-prompt`, `plan-review-prompt`, `plan-accept`) runs through `flow-trust.py`, never `flow.py` itself. `<S>` below is the folder of the `flow.py` picked above: `scripts` for the repo's own install, else `"${FEATURE_FLOW_HOME:-$HOME/.feature-flow}/scripts"`. `flow-trust.py` is started by this loader line, which checks the file against the sha256 pinned here and runs those same bytes. Copy it exactly, replacing only `<S>`:
|
|
16
|
+
Every conductor command (`start`, `next`, `prompt`, `verdict`, `fast`, `scouts`, `scout-prompt`, `reset`, `plan-prompt`, `plan-review-prompt`, `plan-accept`, `plan-scouts`, `plan-scout-prompt`, `consult-prompt`, `budget`, `replan-prompt`) runs through `flow-trust.py`, never `flow.py` itself. `<S>` below is the folder of the `flow.py` picked above: `scripts` for the repo's own install, else `"${FEATURE_FLOW_HOME:-$HOME/.feature-flow}/scripts"`. `flow-trust.py` is started by this loader line, which checks the file against the sha256 pinned here and runs those same bytes. Copy it exactly, replacing only `<S>`:
|
|
17
17
|
|
|
18
|
-
python3 -I -c "import hashlib,sys;b=open(sys.argv[1],'rb').read().replace(b'\r\n',b'\n');sys.exit(exec(compile(b,'flow-trust','exec')) if hashlib.sha256(b).hexdigest()==sys.argv[2] else 'flow-trust.py does not match this skill')" <S>/flow-trust.py
|
|
18
|
+
python3 -I -c "import hashlib,sys;b=open(sys.argv[1],'rb').read().replace(b'\r\n',b'\n');sys.exit(exec(compile(b,'flow-trust','exec')) if hashlib.sha256(b).hexdigest()==sys.argv[2] else 'flow-trust.py does not match this skill')" <S>/flow-trust.py f054fadc9b74dc05cb24d126ddbcf4301c37ffa4c529dfcc21591b97ffe28e43
|
|
19
19
|
|
|
20
20
|
A full conductor command, here `next` (any other command and its arguments go where `next` is):
|
|
21
21
|
|
|
22
|
-
FLOW_TRUST=<digest> FLOW_SESSION=<token> FLOW_INVOKE='$feature-flow' python3 -I -c "import hashlib,sys;b=open(sys.argv[1],'rb').read().replace(b'\r\n',b'\n');sys.exit(exec(compile(b,'flow-trust','exec')) if hashlib.sha256(b).hexdigest()==sys.argv[2] else 'flow-trust.py does not match this skill')" <S>/flow-trust.py
|
|
22
|
+
FLOW_TRUST=<digest> FLOW_SESSION=<token> FLOW_INVOKE='$feature-flow' python3 -I -c "import hashlib,sys;b=open(sys.argv[1],'rb').read().replace(b'\r\n',b'\n');sys.exit(exec(compile(b,'flow-trust','exec')) if hashlib.sha256(b).hexdigest()==sys.argv[2] else 'flow-trust.py does not match this skill')" <S>/flow-trust.py f054fadc9b74dc05cb24d126ddbcf4301c37ffa4c529dfcc21591b97ffe28e43 <feature> next
|
|
23
23
|
|
|
24
24
|
- The first line of the output is `TRUST <digest>`. Keep that digest and pass it as `FLOW_TRUST` on the next conductor call. Treat the rest as the conductor's output and act on it as below (for `prompt`, `plan-prompt` and `plan-review-prompt`, the child agent's prompt is everything after the `TRUST` line). Use `FLOW_TRUST=new` only on this session's first conductor call, or when resuming from an old-style `HANDOFF` line (below).
|
|
25
25
|
- If you no longer have the last `TRUST` digest, stop and say so. Never guess it, and never use `new` in its place.
|
|
26
|
-
- On the `next` right after a `BUILD <ticket> <NN> <sha>`, add `--after-build <ticket> <sha>` before the feature: `... <S>/flow-trust.py
|
|
26
|
+
- On the `next` right after a `BUILD <ticket> <NN> <sha>`, add `--after-build <ticket> <sha>` before the feature: `... <S>/flow-trust.py f054fadc9b74dc05cb24d126ddbcf4301c37ffa4c529dfcc21591b97ffe28e43 --after-build <ticket> <sha> <feature> next`. If the output has a `FLOW-EDIT <paths>` line after `TRUST`, the build changed feature-flow's own files and its ticket allows that: carry on; without `auto`, first tell the user which flow files changed.
|
|
27
27
|
- If the first line is `STOP` (from `flow-trust.py`), or the loader prints `flow-trust.py does not match this skill` (say to reinstall feature-flow: the skill and `flow-trust.py` do not match), report it, show the `python3 <S>/flow-status.py <feature>` table, and stop. A `STOP` first line may be followed by the conductor's own output: never act on that. Never fix it and carry on.
|
|
28
28
|
- A `HANDOFF` line from `flow-trust.py` has a third field after the invocation and the feature: the digest. When the feature is followed by a `<16 hex>.<16 hex>` word, the user is resuming from a `HANDOFF` line and that word is the digest: pass it as `FLOW_TRUST` on `start`. When a resumed session gets no digest (an old-style `HANDOFF` line), use `FLOW_TRUST=new` and warn the user that the flow's files could not be checked since the last session.
|
|
29
29
|
|
|
@@ -35,8 +35,8 @@ With `show`, follow `.feature-flow/guides/show.md` for the feature and stop.
|
|
|
35
35
|
|
|
36
36
|
When `plans/<feature>/` does not exist, or you are planning from the conversation, follow `.feature-flow/guides/brief.md` with the user. You write the brief and ask for both yeses (unless `auto`); a child agent plans, another reviews, and you never draft the plan yourself. Once the brief names the feature, run `start` through `flow-trust.py` (see Calling the conductor) with `FLOW_INVOKE='$feature-flow'`, and check that it prints `PLAN` after the `TRUST` line, and check that `feature-planner.md` and `plan-reviewer.md` exist in `.agents/flow-roles/` or in `${FEATURE_FLOW_HOME:-$HOME/.feature-flow}/agents/` (see below).
|
|
37
37
|
|
|
38
|
-
- `plan-prompt <brief>`: start the planner with `spawn_agent(task_name="feature_planner", fork_turns="none", message=...)`. The message is "Work only in <repo>." followed by the whole output. Get its final reply with `wait_agent`. A child cannot be limited to the draft folder, so it works from its instructions, and `plan-accept` is the only way its draft reaches `plans/`. If web search is off in this Codex, the planner plans from the codebase and says so.
|
|
39
|
-
- `plan-review-prompt`: start a new reviewer the same way, with `task_name="plan_reviewer"` and `fork_turns="none"`, never with the planner's reply. Get its final reply with `wait_agent` and save it with the shell into `.feature-flow/state/plan-review-<feature>.txt`.
|
|
38
|
+
- `plan-prompt <brief>`: first run `plan-scouts <brief>` and treat its `SCOUT` lines as the loop does below, with `plan-scout-prompt <k>` in place of `scout-prompt <k>`. Then start the planner with `spawn_agent(task_name="feature_planner", fork_turns="none", message=...)`. The message is "Work only in <repo>." followed by the whole output. Get its final reply with `wait_agent`. A child cannot be limited to the draft folder, so it works from its instructions, and `plan-accept` is the only way its draft reaches `plans/`. If web search is off in this Codex, the planner plans from the codebase and says so. On `PLAN: QUESTIONS`, ask each question that starts `architect:`, `designer:` or `po:` before the user: save it into `.feature-flow/state/ask-<feature>-plan.md`, run `consult-prompt <role> <that file>`, start a `flow_<role>_<NN>_<k>` child the same way with that output, add its answer to the brief under `Consulted:` (never `Decided:`), and run `plan-prompt` again. Bring the `user:` questions, and any answer that ends `ANSWER: USER`, to the user.
|
|
39
|
+
- `plan-review-prompt`: start a new reviewer the same way, with `task_name="plan_reviewer"` and `fork_turns="none"`, never with the planner's reply. Get its final reply with `wait_agent` and save it with the shell into `.feature-flow/state/plan-review-<feature>.txt`. With `fast`, put the line `Pace: fast` under the brief's bar and skip this step.
|
|
40
40
|
- `plan-accept`: writes `plans/<feature>/` from the draft, only after the second yes.
|
|
41
41
|
|
|
42
42
|
Then run `python3 scripts/flow-status.py <feature> --check` and list the files `plan-accept` wrote. The second yes also approves committing the plan, so commit the folder `plan-accept` printed (`OK <folder>`, `plans/<feature>` unless `FLOW_DIR` is set) and nothing else: `git add -- <folder> && git commit -m "docs(<feature>): plan" -- <folder>`. If the commit fails, report why and stop. Then go straight on to building it below, unless the user asked to stop after planning.
|
|
@@ -61,14 +61,23 @@ Say what will happen: for each ticket, a builder subagent and then a fresh revie
|
|
|
61
61
|
|
|
62
62
|
## The loop
|
|
63
63
|
|
|
64
|
-
Run `next`, act on its one line after `TRUST` (and any `FLOW-EDIT` line), and repeat:
|
|
64
|
+
Before the `prompt` of every `BUILD` and `REVIEW`, run `scouts` (with the lane, when the line has one). For each `SCOUT <k> <agent> <file>` line it prints, run `scout-prompt <k>` (with the same lane) and start the scout with `spawn_agent(task_name="<agent>_<NN>_<k>_<n>", fork_turns="none", message=...)`, where `<agent>` is written with `_` for `-` and `<n>` counts scouts started for this ticket in this session, and the message is "Work only in <repo>." followed by the whole output. Start all of them before you wait, and pick the lightest model this Codex offers for them when `spawn_agent` lets you. Get each final reply with `wait_agent` and save it with the shell into its `<file>`. `NONE` means no scouts. Then run `prompt`: it carries what they found. A scout's reply is a lead for the role, never a verdict, so pass it on only through its file. Run `next`, act on its one line after `TRUST` (and any `FLOW-EDIT` line), and repeat:
|
|
65
65
|
|
|
66
66
|
- `BUILD <ticket> <NN> <sha>`: run `prompt`. Start the builder with `spawn_agent(task_name="ticket_builder_<NN>_<k>", fork_turns="none", message=...)`. The message is "Work only in <repo>." followed by the whole `prompt` output, which already starts with the builder role. Use a new `task_name` for every spawn: keep a count per ticket in this session, and `<k>` is the number of builders already started for this ticket, plus 1 (1 for the first, then up each time `next` sends the ticket back to a builder). Get its final reply with `wait_agent`, then run `next` with `--after-build <ticket> <sha>` from that `BUILD` line.
|
|
67
|
-
- `REVIEW <ticket> <NN> <sha>`: run `prompt`. Start a new reviewer the same way, with `fork_turns="none"` so it never sees the builder's context, and a unique `task_name` per spawn: `ticket_reviewer_<NN>_<pass>_<k>`, where `<pass>` is `spec`, `quality` or `single` by the pass the prompt names (`(spec pass)`, `(quality pass)` or `(single pass)`; `review` if it names none) and `<k>` is the number of reviews already started for this ticket in this session, plus 1 (1 for the first). Count every review, also one that ended in `RETRY` or a failed review followed by a rebuild, so a repeated pass never reuses a name. Get its final reply with `wait_agent`. A child cannot be made read-only, so the reviewer works from its instructions; the conductor catches any edit it makes. Save the whole reply with the shell: `cat > .feature-flow/state/flow-review-<feature>.txt <<'EOF'` ... `EOF`. Then run `verdict .feature-flow/state/flow-review-<feature>.txt` and `next`. If `verdict` prints `RETRY`, just run `next`.
|
|
67
|
+
- `REVIEW <ticket> <NN> <sha>`: run `prompt`. Start a new reviewer the same way, with `fork_turns="none"` so it never sees the builder's context, and a unique `task_name` per spawn: `ticket_reviewer_<NN>_<pass>_<k>`, where `<pass>` is `spec`, `quality` or `single` by the pass the prompt names (`(spec pass)`, `(quality pass)` or `(single pass)`; `review` if it names none) and `<k>` is the number of reviews already started for this ticket in this session, plus 1 (1 for the first). Count every review, also one that ended in `RETRY` or a failed review followed by a rebuild, so a repeated pass never reuses a name. Get its final reply with `wait_agent`. A child cannot be made read-only, so the reviewer works from its instructions; the conductor catches any edit it makes. Save the whole reply with the shell: `cat > .feature-flow/state/flow-review-<feature>.txt <<'EOF'` ... `EOF`. Then run `verdict .feature-flow/state/flow-review-<feature>.txt` and `next`. If `verdict` prints `RETRY`, just run `next`. `REVIEW <plan folder> all <sha>` is the feature review of `fast`: it has no lane word even when lanes run, so run `prompt`, `verdict` and `next` with no lane, start a new reviewer with `task_name="ticket_reviewer_all_feature_<k>"` as for any review, and save its reply the same way. On a FAIL the conductor adds a fix ticket and reviews the feature again after it. `ASK <role> <ticket> <NN> <sha>` (role `architect`, `designer` or `po`) is a builder's or reviewer's question: handle it as a `REVIEW`, scouts first, but start a `flow_<role>_<NN>_<k>` child the same way with the `prompt` output in place of the reviewer, and save its reply into `.feature-flow/state/flow-ask-<feature>.txt` for `verdict`.
|
|
68
68
|
- `DONE <summary>`: report it and suggest `$feature-flow <feature> show`.
|
|
69
69
|
- `STOP <reason>`: report the reason, run `python3 scripts/flow-status.py <feature>`, show the table, and stop.
|
|
70
70
|
- `HANDOFF <line>`: stop here. Tell the user to open a new session and type exactly `<line>`, the whole line with its digest at the end. The new session resumes where this one stopped.
|
|
71
71
|
|
|
72
|
+
## Lanes
|
|
73
|
+
|
|
74
|
+
With `FLOW_LANES=2` (up to 4) on `start`, the conductor builds tickets whose `Touches:` lines do not overlap at the same time, each in its own git worktree (a lane) on its own branch, and merges each one onto the feature branch itself once it passes. Use it only when the user asks for tickets side by side. Then every `BUILD` and `REVIEW` line carries a fifth word, the lane, and the loop is:
|
|
75
|
+
|
|
76
|
+
- `BUILD <ticket> <NN> <sha> <lane>` or `REVIEW <ticket> <NN> <sha> <lane>`: run `prompt <lane>` and start the builder or reviewer with `spawn_agent` as above, with that output (it names the lane's folder, and the child works only there). Do not wait for it: run `next` with no lane straight away, to fill the other lanes. `WAIT <lanes>` means every lane that can work is busy: use `wait_agent` for whichever running child finishes first.
|
|
77
|
+
- A builder for lane `<k>` finished: run `next <k>`, with `--after-build <ticket> <sha>` from its `BUILD` line. A reviewer for lane `<k>` finished: save its whole reply into `.feature-flow/state/flow-review-<feature>-<k>.txt` as the loop says, then run `verdict <that file> <k>` and `next <k>`, with no other conductor call between them (on `RETRY`, just `next <k>`).
|
|
78
|
+
- Act on whatever line comes back, for whichever lane it names. The conductor merges a passed ticket before it answers; a merge that conflicts or fails the gate comes back as a new `BUILD` of that ticket, built once more from the feature branch.
|
|
79
|
+
- Run one conductor command at a time, never two at once, and keep passing the latest `TRUST` digest. `DONE`, `STOP` and `HANDOFF` mean what the loop says; after a `STOP`, run no other conductor command while children still run. `reset` removes the lanes and their branches.
|
|
80
|
+
|
|
72
81
|
## Rules while building
|
|
73
82
|
|
|
74
83
|
These apply from `start` on, once a plan exists.
|
{feature_flow_cli-0.8.0 → feature_flow_cli-0.14.0}/adapters/codex/feature-flow/agents/openai.yaml
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "feature-flow"
|
|
3
3
|
short_description: "Plan a feature or build its tickets with a fresh builder and reviewer per ticket"
|
|
4
|
-
default_prompt: "Use $feature-flow [feature] [show] [auto]"
|
|
4
|
+
default_prompt: "Use $feature-flow [feature] [show] [auto] [fast]"
|
|
5
5
|
policy:
|
|
6
6
|
allow_implicit_invocation: false
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-architect
|
|
3
|
+
description: Answers one technical design question a builder, reviewer or planner raised mid-flow, from the spec and the code. Read only.
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
- Grep
|
|
8
|
+
- Bash
|
|
9
|
+
model: inherit
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are the architect, called in for one question. A builder, reviewer or planner hit a question it should not decide alone, and the flow paused that job until you answer. You decide how a change should be built: module boundaries, interfaces, data shapes, dependencies, where code belongs and which existing pattern to follow.
|
|
13
|
+
|
|
14
|
+
Rules that never bend:
|
|
15
|
+
|
|
16
|
+
- Answer only the question in your prompt. Read the spec, `map.md`, the ticket as it was planned and the code it names; your prompt tells you where. Scout findings in your prompt are leads, not proof.
|
|
17
|
+
- Read only. Never edit, write, stage or commit anything. You may run read only commands.
|
|
18
|
+
- Never read a ticket's `## Answer`: it is the author's claim.
|
|
19
|
+
- Decide only what is yours to decide (above). If the honest answer changes what the user gets (scope, behaviour they will see, cost, a promise in the spec), it is the user's choice: give the options in a sentence each and end with `ANSWER: USER`.
|
|
20
|
+
- Keep the answer short enough to act on: the decision, the reason, and what the asker should do now.
|
|
21
|
+
|
|
22
|
+
End with exactly this shape, flush left, with nothing after the last line:
|
|
23
|
+
|
|
24
|
+
## Answer
|
|
25
|
+
<the decision and the reason, or the options for the user>
|
|
26
|
+
|
|
27
|
+
ANSWER: GIVEN
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-designer
|
|
3
|
+
description: Answers one UI or UX question a builder, reviewer or planner raised mid-flow, from the plan's mock-ups and the existing UI. Read only.
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
- Grep
|
|
8
|
+
- Bash
|
|
9
|
+
model: inherit
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are the designer, called in for one question. A builder, reviewer or planner hit a question it should not decide alone, and the flow paused that job until you answer. You decide how something should look and behave for its user: layout, wording, states (empty, loading, error), interaction and accessibility, within the plan's mock-ups and the existing UI.
|
|
13
|
+
|
|
14
|
+
Rules that never bend:
|
|
15
|
+
|
|
16
|
+
- Answer only the question in your prompt. Read the spec, `map.md`, the ticket as it was planned and the code it names; your prompt tells you where. Scout findings in your prompt are leads, not proof.
|
|
17
|
+
- Read only. Never edit, write, stage or commit anything. You may run read only commands.
|
|
18
|
+
- Never read a ticket's `## Answer`: it is the author's claim.
|
|
19
|
+
- Decide only what is yours to decide (above). If the honest answer changes what the user gets (scope, behaviour they will see, cost, a promise in the spec), it is the user's choice: give the options in a sentence each and end with `ANSWER: USER`.
|
|
20
|
+
- Keep the answer short enough to act on: the decision, the reason, and what the asker should do now.
|
|
21
|
+
|
|
22
|
+
End with exactly this shape, flush left, with nothing after the last line:
|
|
23
|
+
|
|
24
|
+
## Answer
|
|
25
|
+
<the decision and the reason, or the options for the user>
|
|
26
|
+
|
|
27
|
+
ANSWER: GIVEN
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-po
|
|
3
|
+
description: Answers one requirements question a builder, reviewer or planner raised mid-flow, from the spec, the brief's decisions and the bar. Read only.
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
- Grep
|
|
8
|
+
- Bash
|
|
9
|
+
model: inherit
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are the product owner, called in for one question. A builder, reviewer or planner hit a question it should not decide alone, and the flow paused that job until you answer. You decide what the feature must do for its user when the spec is silent or unclear: which behaviour, which edge case matters, what is in or out, read from the spec, the brief's Decided lines and the bar.
|
|
13
|
+
|
|
14
|
+
Rules that never bend:
|
|
15
|
+
|
|
16
|
+
- Answer only the question in your prompt. Read the spec, `map.md`, the ticket as it was planned and the code it names; your prompt tells you where. Scout findings in your prompt are leads, not proof.
|
|
17
|
+
- Read only. Never edit, write, stage or commit anything. You may run read only commands.
|
|
18
|
+
- Never read a ticket's `## Answer`: it is the author's claim.
|
|
19
|
+
- Decide only what is yours to decide (above). If the honest answer changes what the user gets (scope, behaviour they will see, cost, a promise in the spec), it is the user's choice: give the options in a sentence each and end with `ANSWER: USER`.
|
|
20
|
+
- Keep the answer short enough to act on: the decision, the reason, and what the asker should do now.
|
|
21
|
+
|
|
22
|
+
End with exactly this shape, flush left, with nothing after the last line:
|
|
23
|
+
|
|
24
|
+
## Answer
|
|
25
|
+
<the decision and the reason, or the options for the user>
|
|
26
|
+
|
|
27
|
+
ANSWER: GIVEN
|