flow-workflows 0.65.0 → 0.67.0
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.
- package/README.md +25 -4
- package/adapters/README.md +11 -3
- package/adapters/codex/CORE.md +31 -5
- package/adapters/codex/skills/flow-bug-fix/SKILL.md +5 -0
- package/adapters/codex/skills/flow-bug-review/SKILL.md +19 -6
- package/adapters/codex/skills/flow-bug-ship/SKILL.md +5 -3
- package/adapters/codex/skills/flow-feat-build/SKILL.md +16 -2
- package/adapters/codex/skills/flow-feat-review/SKILL.md +51 -47
- package/adapters/codex/skills/flow-feat-ship/SKILL.md +23 -4
- package/adapters/codex/skills/flow-feat-validate/SKILL.md +5 -1
- package/adapters/codex/skills/flow-init/SKILL.md +4 -3
- package/adapters/codex/skills/flow-work-README/SKILL.md +9 -2
- package/adapters/codex/skills/flow-work-respond/SKILL.md +1 -1
- package/adapters/gemini/CORE.md +31 -5
- package/adapters/gemini/commands/flow/bug/fix.toml +5 -0
- package/adapters/gemini/commands/flow/bug/review.toml +19 -6
- package/adapters/gemini/commands/flow/bug/ship.toml +5 -3
- package/adapters/gemini/commands/flow/feat/build.toml +16 -2
- package/adapters/gemini/commands/flow/feat/review.toml +51 -47
- package/adapters/gemini/commands/flow/feat/ship.toml +23 -4
- package/adapters/gemini/commands/flow/feat/validate.toml +5 -1
- package/adapters/gemini/commands/flow/init.toml +4 -3
- package/adapters/gemini/commands/flow/work/README.toml +9 -2
- package/adapters/gemini/commands/flow/work/respond.toml +1 -1
- package/adapters/hermes/CORE.md +31 -5
- package/adapters/hermes/skills/flow-bug-fix/SKILL.md +5 -0
- package/adapters/hermes/skills/flow-bug-review/SKILL.md +19 -6
- package/adapters/hermes/skills/flow-bug-ship/SKILL.md +5 -3
- package/adapters/hermes/skills/flow-feat-build/SKILL.md +16 -2
- package/adapters/hermes/skills/flow-feat-review/SKILL.md +51 -47
- package/adapters/hermes/skills/flow-feat-ship/SKILL.md +23 -4
- package/adapters/hermes/skills/flow-feat-validate/SKILL.md +5 -1
- package/adapters/hermes/skills/flow-init/SKILL.md +4 -3
- package/adapters/hermes/skills/flow-work-README/SKILL.md +9 -2
- package/adapters/hermes/skills/flow-work-respond/SKILL.md +1 -1
- package/adapters/opencode/CORE.md +31 -5
- package/adapters/opencode/commands/flow-bug-fix.md +5 -0
- package/adapters/opencode/commands/flow-bug-review.md +19 -6
- package/adapters/opencode/commands/flow-bug-ship.md +5 -3
- package/adapters/opencode/commands/flow-feat-build.md +16 -2
- package/adapters/opencode/commands/flow-feat-review.md +51 -47
- package/adapters/opencode/commands/flow-feat-ship.md +23 -4
- package/adapters/opencode/commands/flow-feat-validate.md +5 -1
- package/adapters/opencode/commands/flow-init.md +4 -3
- package/adapters/opencode/commands/flow-work-README.md +9 -2
- package/adapters/opencode/commands/flow-work-respond.md +1 -1
- package/bin/cli.mjs +330 -24
- package/package.json +2 -1
- package/plugins/flow/.claude-plugin/plugin.json +5 -2
- package/plugins/flow/CHANGELOG.md +227 -0
- package/plugins/flow/examples/FLOW.template.md +2 -2
|
@@ -27,10 +27,10 @@ Read `~/.claude/flow/CORE.opencode.md` first (shared rules: `FLOW.md` step 0, mo
|
|
|
27
27
|
|
|
28
28
|
- Read `meta.json` and `00-summary.md`; open in full only `04-fix.md` (the Brief), `06-review.md` (the verdict), `05-validation.md`, the §"Root cause identified" of `03-investigation.md`, and `99-postmortem.md` if it exists. (flow-core §5)
|
|
29
29
|
- Require `review` in `phases_done`; also `validate` if `size` ≥ S, and `postmortem` if `size` is M or L (the same sizes `/flow-bug-review §8` routes to postmortem — one rule). Missing → refuse and redirect to the missing step.
|
|
30
|
-
- **The review has to be about the tree you are about to push
|
|
31
|
-
- **Equal** → continue.
|
|
30
|
+
- **The review has to be about the tree you are about to push — `/flow-feat-ship` §1, in full and unchanged**, including its four newer cases: `reviewed_sha` is the revision the reviewers *read* (`/flow-bug-review` §1.5 froze it), so `fixed_sha` set alongside an equal sha is a mismatch and not a match; a dirty tree at this end is named and asked about rather than passed over; and a `reviewed_sha` that no longer resolves is reported, then treated as absent. Compare `meta.json.reviewed_sha` and `validated_sha` against `git rev-parse HEAD`:
|
|
31
|
+
- **Equal** (and no `fixed_sha`, over a clean tree) → continue.
|
|
32
32
|
- **Absent** (a work from before the shas were recorded) → say so in one line, continue; absence is not a mismatch.
|
|
33
|
-
- **Different** → read the delta (`git log --oneline <sha>..HEAD`, `git diff --stat <sha>..HEAD`) and judge it by **what it touches**. Only test files (`tests/`, `test/`, `spec/`, `__tests__/`, `*_test.*`, `*Test.*`, `*.test.*`, `*.spec.*`) or this work's folder under `.claude/work/` → append both shas and the file list to `06-review.md`, continue without asking. **Anything else** → stop and ask in **every** `autonomy.mode`, no exceptions (`AskUserQuestion`): *re-review the delta* (`/flow-bug-review`
|
|
33
|
+
- **Different** → read the delta (`git log --oneline <sha>..HEAD`, `git diff --stat <sha>..HEAD`) and judge it by **what it touches**. Only test files (`tests/`, `test/`, `spec/`, `__tests__/`, `*_test.*`, `*Test.*`, `*.test.*`, `*.spec.*`) or this work's folder under `.claude/work/` → append both shas and the file list to `06-review.md`, continue without asking. **Anything else** → stop and ask in **every** `autonomy.mode`, no exceptions (`AskUserQuestion`): *re-review the delta* (`/flow-bug-review`, the recommended default — its §1.5 takes `reviewed_sha` as the base, so the delta is both what it reads and what sets its tier) · *ship as it stands*, recording in `06-review.md` the two shas and that the user accepted them unreviewed.
|
|
34
34
|
|
|
35
35
|
## 1. Draft title and description (without sending anything yet)
|
|
36
36
|
|
|
@@ -171,6 +171,7 @@ nothing was measured. A slowness bug is the case where this comment *is* the pro
|
|
|
171
171
|
|
|
172
172
|
## 4. Close
|
|
173
173
|
|
|
174
|
+
- **A worktree does not own the tree its tools wrote into — `/flow-feat-ship` §5.5, in full and unchanged.** First, before anything below: `meta.json.worktree` not null → read the main checkout's `git status --porcelain`, name any paths it holds in one line as what they are (in the main checkout, not in the branch just pushed, and not necessarily put there by this run), and offer once to carry them over. A fix consolidates its knowledge one phase earlier, in `/flow-bug-postmortem` §4, so by the time this runs those writes have already happened and the branch has already been pushed — which is exactly the window this check exists to close.
|
|
174
175
|
- Update `meta.json`: add `ship` to `phases_done`, update `updated_at`. **`phase` becomes `done` only once the MR/PR is confirmed merged** — creating it is not finishing it. Right after §3.1 it is open, so ask once with `AskUserQuestion` whether it has already been merged: **no** (the normal answer) → leave `phase = "ship"`; **yes** → `phase = "done"`. `/flow-work-green` and `/flow-work-respond` work on the open MR/PR; `/flow-work-clean` only sweeps what merged.
|
|
175
176
|
- Refresh `panel.json`: while the MR/PR is open its line is `wait` with the `link`; once merged it is `done`, and only then say there is nothing left here (`mark: "info"`, `style: "ok"`). Drop the `Decision` line either way.
|
|
176
177
|
- Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
|
|
@@ -181,5 +182,6 @@ nothing was measured. A slowness bug is the case where this comment *is* the pro
|
|
|
181
182
|
- **Follow-up triage — `/flow-feat-ship` §6.4, in full and unchanged.** Read `meta.json.followups[]`; nothing `proposed` → skip silently. Otherwise §6.4 step 0 first — **3 or more candidates go to a skeptic** whose job is to refute that each deserves a ticket, and only the strongest `tracker.followup_ask_max` (empty → **2**) reach the user (flow-core §7) — then one batched `AskUserQuestion` (**Do it** / **Not worth it** / **Later**; `decision` entries asked as their question, `tooling` and everything `log`ged never asked), the accepted ones get their tracker issue created (outward-facing → **asks in every mode**), the logged ones nothing but `status: "logged"` in `meta.json`, then one offer to start the first. A bug flow is where the gate earns its keep: the prevention actions `/flow-bug-postmortem` wrote, the neighbouring bugs `/flow-bug-validate` found and the similar-risk patterns `/flow-bug-fix` flagged all land here — half a dozen of them, from one fix, and «similar risk elsewhere» that no skeptic ever read is exactly the entry that turns a closed bug into six open questions. Repeat for anything still `proposed` on the re-run that confirms the merge.
|
|
182
183
|
- **Conventions learned — `/flow-feat-ship` §6.4bis, in full and unchanged.** Read `meta.json.conventions_candidates[]` (flow-core §8); nothing pending → skip silently. Otherwise: drop what `FLOW.md` already says, then one batched `AskUserQuestion` (**Add to FLOW.md** / **Not a rule** / **Later**, or **Replace** when it contradicts a line that is there). Asked in every mode — it edits a file in the tree. A fix is where the corrections land hardest: *«never mock the repository in that layer»*, *«the regression test goes next to the unit test, not in the functional suite»* — said once inside `bug:fix`, and proposed again in the next incident unless they reach the file.
|
|
183
184
|
- **Close the follow-up that started this work.** `meta.json.origin` set and this ship moved `phase` to `done` → open the originating work's `meta.json` (live folder or `_archive/`) and set that `followups[]` entry to `status: "done"`. Best-effort: the originating folder is gone or unreadable → warn in one line and continue, never block. Without this the entry stays `in_progress` forever and `status`/`daily` keep reporting work that is finished.
|
|
185
|
+
- **Only once `phase` is `done`**: delete the candidate anchor `/flow-bug-review §1.5` left behind — `git update-ref -d refs/flow/candidate/<TICKET>`. No question: local, never pushed, and the gate that needed it has run.
|
|
184
186
|
- Ask whether to archive `.claude/work/<TICKET>/` to `.claude/work/_archive/`.
|
|
185
187
|
- If `meta.json.worktree` is not null, offer `git worktree remove <worktree>` once the MR/PR is merged (`--force` only if the user confirms discarding remaining changes). Not before merge, not without confirmation.
|
|
@@ -178,6 +178,17 @@ The step's changes are **always reported before anything is recorded**. Who deci
|
|
|
178
178
|
- **"Something needs to change"** → adjust; the step's commit stays pending until OK.
|
|
179
179
|
- **"Continue without committing, we'll group later"** → next step without committing; changes accumulate in the working tree (fewer clean points if §2.3 cuts).
|
|
180
180
|
|
|
181
|
+
4. **Write the step into the log, committed or not.** Append one line to a `## Steps` section of `05-implementation.md` (create it at the first step):
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
- <short sha> · <step name> · <n files, +a −b>
|
|
185
|
+
- — · <step name> · <n files, +a −b> ← not committed
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
No question, in every autonomy mode — this is flow machinery on the never-ask list of flow-core §2. A step that was **not** committed gets its line too, with `—` where the sha would be: that is the case in `manual`, and the *"continue without committing, we'll group later"* branch above. The log then has no holes, and the absence of a commit is itself what the reader needs to see.
|
|
189
|
+
|
|
190
|
+
The step summary was already written for every step; the sha is the one identifier that makes the log line and the git history the same record. Without it `05-implementation.md` explains *why* each change was made and names not one commit that did it, so §2.3 reads the cut point off `git log --oneline` and matches it by the prose in `WIP <TICKET>: <step>`, and a delta-scoped review (`/flow-feat-review §1.5`) can say which lines are new but not which steps they came from.
|
|
191
|
+
|
|
181
192
|
Rules when a commit does happen:
|
|
182
193
|
- One commit per step. Do not batch steps unless the user explicitly requests it.
|
|
183
194
|
- `--no-verify` **only for WIP commits** (slow hooks run in `/flow-feat-review` and in the final commit of `/flow-feat-ship`).
|
|
@@ -221,13 +232,13 @@ slip under a threshold buys itself the review a smaller change had earned.
|
|
|
221
232
|
**Hot cut mechanics (option 1)**:
|
|
222
233
|
|
|
223
234
|
0. **If there are uncommitted changes** in the working tree: warn the user and ask them to decide before cutting — commit what is done as a WIP for the corresponding step, or stash it (`git stash`) so it does not mix with the next MR/PR.
|
|
224
|
-
1. Identify a cut point: the last
|
|
235
|
+
1. Identify a cut point from the `## Steps` lines of `05-implementation.md` (§2.2): the last committed step where the piece is coherent and mergeable (a closed sub-goal: "endpoint and DTO done", "migration applied", "flow X tests green"). Those lines pair each step with its sha, so the cut is chosen on what the step *was* rather than re-derived from `git log --oneline` and matched by the prose in a commit message. **Name the sha in the proposal** — the user confirms an identifier, not a description. A step with `—` instead of a sha is not a cut point; it never got one. Do not cut in the middle of a change.
|
|
225
236
|
2. Edit `meta.json.mrs`:
|
|
226
237
|
- The current MR/PR keeps `n`, `title`, `wave` and `depends_on`, adjusts `lines_est` and `files_est` to actuals, and stays `in_progress`.
|
|
227
238
|
- Insert a new one with the next `n` (renumbering subsequent ones if any), `title` describing what remains, `status: "pending"`, `phases_done: []` (a fresh MR/PR earns its own review/validate), `depends_on: [n_current]`, `wave` = one after the current one, and new indicative `lines_est` and `files_est`. If you renumber subsequent entries, **update their `depends_on` references accordingly** so no `depends_on` points to a higher `n` than its own.
|
|
228
239
|
3. Edit `04-mr-plan.md`: split the original entry in two, keeping the standalone-mergeable justification for both halves.
|
|
229
240
|
4. Note in `05-implementation.md` under "Hot cut": date, reason, what stays and what moves to the next one.
|
|
230
|
-
5. **Do not rewrite history with `git rebase`**: WIP commits belonging to the next MR/PR stay in the current branch. When building the next one, start from a new branch over the base and transfer them with `git cherry-pick` or equivalent — executed in `/flow-feat-ship` or when starting the next `/flow-feat-build`.
|
|
241
|
+
5. **Do not rewrite history with `git rebase`**: WIP commits belonging to the next MR/PR stay in the current branch. When building the next one, start from a new branch over the base and transfer them with `git cherry-pick` or equivalent — executed in `/flow-feat-ship` or when starting the next `/flow-feat-build`. The shas to transfer are the `## Steps` lines below the cut point, which is what that section is for on this side of the cut too.
|
|
231
242
|
|
|
232
243
|
**If there was already a cut and overrun happens again**: ask the user before cutting again — a second cut on the same MR/PR signals the plan is wrong. The right option is probably **3 (reopen plan)**.
|
|
233
244
|
|
|
@@ -259,6 +270,9 @@ Keep `.claude/work/<TICKET>/05-implementation.md` updated as you work (not at th
|
|
|
259
270
|
## Changes per file
|
|
260
271
|
- <file> — what changed and why (1 line each)
|
|
261
272
|
|
|
273
|
+
## Steps
|
|
274
|
+
<one line per step of §2.2, in order: `- <short sha> · <step name> · <n files, +a −b>`, and `—` in place of the sha where the step was not committed. Written as each step closes, in every autonomy mode. This is what joins the log to the git history: §2.3 reads its cut point here, the transfer after a cut picks its shas here, and a delta-scoped review can say which steps a range came from. A section with no holes is the point — an uncommitted step still gets its line.>
|
|
275
|
+
|
|
262
276
|
## Decisions made during implementation
|
|
263
277
|
- Decision: …
|
|
264
278
|
- Why: …
|
|
@@ -29,6 +29,9 @@ Mandatory review phase. **`/flow-feat-ship` cannot run without passing through h
|
|
|
29
29
|
- **Gather the material once, not once per reviewer.** `npx flow-workflows@<version> bundle --checks --harness opencode`
|
|
30
30
|
prints one context pack for this branch — worklist, the diff (minus `git.diff_exclude`), the changed files
|
|
31
31
|
in full, the raw output of `quality.static_analysis`, and the work's handoff — in a single call.
|
|
32
|
+
**§1.5 runs first and adds `--rev` and `--base` to that call**: the pack is then one frozen
|
|
33
|
+
revision against one resolved base, which is what makes it the same material for every reader
|
|
34
|
+
rather than the same *call* made at different moments.
|
|
32
35
|
Read that instead of rediscovering it, and **pass it to the briefs of §2-§6**: a panel where every
|
|
33
36
|
member runs its own `git diff` and opens the same files pays for that material once per agent, and
|
|
34
37
|
a reviewer that spends its turns exploring has fewer left to review. **Any non-zero exit is a
|
|
@@ -60,60 +63,51 @@ Mandatory review phase. **`/flow-feat-ship` cannot run without passing through h
|
|
|
60
63
|
picks the model, so `models.agents`/`models.workers` do not reach those reviewers. A one-shot sweep
|
|
61
64
|
cannot pull a thread, so an ambiguous finding still goes to a real agent with tools, as below.
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
### 1.5 Freeze what you are about to read, and resolve what to compare it against
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
Two questions, answered here and nowhere later: **which tree** this round reads, and **against which base**. Both used to be answered implicitly — by the checkout as it happened to stand at each moment, and by `git.default_base` always — and both answers were wrong in a way no artifact could show.
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
Read `quality.review_depth` from `FLOW.md` (`proportional` | `full` | `light`; empty → `proportional`) and `meta.json.size`. This decides **what §2.1 launches and at which effort**. Built-in `code-review` effort ladder: **low < medium < high < xhigh < max** (lower = fewer, higher-confidence findings; higher = broader coverage, may surface uncertain ones).
|
|
70
|
+
**The candidate.** A round applies its own fixes; the README's own first-minute example is *"3 reviewers, one finding — fixed"*. So "the working tree" is a different tree for the reviewer that read it first, for the skeptic that read it last, and for the `git rev-parse HEAD` taken at §9 — which by construction contains code no reviewer ever saw, and which `/flow-feat-ship §1` then compares against `HEAD` and finds equal. Resolve one revision before §2 launches anything:
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
- Tree clean → `git rev-parse HEAD`.
|
|
73
|
+
- Tree dirty → `git stash create`. It writes a real, addressable commit object holding the index and the tracked working tree; it does **not** touch the checkout and there is nothing to pop. Empty output (nothing to stash after all) → `git rev-parse HEAD`.
|
|
74
|
+
- **Untracked files are in no revision**, so they are outside the candidate. `git status --porcelain` lists them; name them in `06-review.md` in one line and read them from disk. A new file nobody mentions is the review's blind spot, not a technicality.
|
|
75
|
+
- Anchor it so it cannot be collected: `git update-ref refs/flow/candidate/<TICKET> <sha>`. A `stash create` object is unreferenced, and a candidate the `ship` gate cannot resolve is a gate that reports nothing. The ref is local, never pushed, and `/flow-feat-ship §6.5` deletes it.
|
|
76
|
+
- Write `candidate_sha` to `meta.json` — work-level and in the current `mrs[]` entry — **in this pre-flight**. A candidate recorded after the reading is not a freeze.
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|---|---|
|
|
78
|
-
| ≤ 150 | XS |
|
|
79
|
-
| 151-600 | S |
|
|
80
|
-
| 601-1500 | M |
|
|
81
|
-
| > 1500 | L |
|
|
78
|
+
**The review base.** `reviewed_sha` already records the tree the last passing review read, and `ship` trusts it enough to stop a push — yet the review itself never read it, so a second review re-reads every line the first one cleared, and §2.0 resolves the tier on the accumulated total: a 30-line delta on a 900-line branch reviewed as the 900. Resolve:
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
- The current MR/PR's `reviewed_sha` (work-level when there is no train) when it is **non-empty** and **an ancestor of the candidate** (`git merge-base --is-ancestor <sha> <candidate>`) → that is the base.
|
|
81
|
+
- Otherwise `git.default_base`. A `reviewed_sha` that is not an ancestor means history was rewritten or the branch recut; say so in one line rather than producing a nonsensical delta.
|
|
82
|
+
- **A first review has no `reviewed_sha` and reads the whole branch**, exactly as before. The delta path only exists after a review that passed.
|
|
83
|
+
- `/flow-feat-review --full` ignores `reviewed_sha` and reads the branch on demand.
|
|
84
|
+
- **Fall back to `git.default_base` and say why** when either holds, because a delta cannot see how it interacts with what was cleared before: the delta touches a file the previous review left a finding on, or it changes an external contract copied under `/flow-feat-build §2.0bis`.
|
|
88
85
|
|
|
89
|
-
**
|
|
90
|
-
A diff of 148 lines is reviewed at `medium` with no panel and closes §6's gate; one of 160 gets the
|
|
91
|
-
panel. When the measured diff lands **within 10% under** a threshold that changed the tier (135-150,
|
|
92
|
-
541-600, 1351-1500), say so in one line of `06-review.md`, beside the effective size. It is not an
|
|
93
|
-
accusation and it changes no tier — it is the only place a reader can see that the review they are
|
|
94
|
-
holding was selected by a number, and it is what makes flow-core §9 (the diff reports the change; the
|
|
95
|
-
change is never shaped to fit the number) checkable after the fact rather than merely stated.
|
|
86
|
+
**Then everything reads those two.** `npx flow-workflows@<version> bundle --checks --harness opencode --rev <candidate_sha> --base <review base>` — the pack is that one revision for every reader, and its header says so. The briefs of §2–§6 name the candidate, and the agent that pulls a thread on an ambiguous finding is told to read `git diff <review base>...<candidate_sha>`, never the live tree. `quality.static_analysis` still runs against the checkout, which the pack states: a linter reads the working tree and freezing does not change that.
|
|
96
87
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
## 2. Invoke the code reviews
|
|
89
|
+
|
|
90
|
+
Scope for every reviewer: **the candidate revision of §1.5, against the review base of §1.5** — which is the whole feature work against `git.default_base` on a first review, and the delta since the last passing one otherwise. Committed and uncommitted work travel together inside the candidate; untracked files do not, and §1.5 named them.
|
|
100
91
|
|
|
101
|
-
|
|
102
|
-
|---|---|---|
|
|
103
|
-
| XS | **medium** | do **not** launch |
|
|
104
|
-
| S | **high** | only if the diff touches a sensitive surface; otherwise the built-in alone is the review |
|
|
105
|
-
| M | **high** | full panel |
|
|
106
|
-
| L | **xhigh** | full panel |
|
|
92
|
+
### 2.0 Resolve review depth (scale to *this diff* and the risk)
|
|
107
93
|
|
|
108
|
-
|
|
94
|
+
**The CLI resolves it; you obey the output and record any deviation.** `npx flow-workflows@<version> tier --harness opencode --rev <candidate_sha> --base <review base>` prints, from the config it already parses: changed lines over the §1.5 range, diff size, `meta.json.size`, **effective size** (the lower of the two), the built-in `code-review` effort with and without the sensitive-surface bump, whether the panel runs, the resolved `agents.budget_max`/`fanout_max`, whether the measurement landed **within 10% under** a threshold that changed the tier, and which `quality.sensitive_paths` globs the diff matched, with the path that matched each.
|
|
109
95
|
|
|
110
|
-
|
|
96
|
+
Every step of that is arithmetic, and the cost of leaving it here was never mainly the tokens: `06-review.md` recorded the tier *this agent said it derived*, so a round that eyeballed the diff and a round that measured it wrote a byte-identical artifact — on the one number in this command that carries an incentive on it. Same treatment `Review path` and `Cost:` already got, for the same reason.
|
|
111
97
|
|
|
112
|
-
**
|
|
98
|
+
**Copy the output verbatim** into the `Review tier` and `Effective size` lines of §8, the way `bundle --checks` copies static analysis out. A computed line, not a narrated one.
|
|
113
99
|
|
|
114
|
-
**
|
|
100
|
+
**What the CLI deliberately does not decide, because a command cannot see it:**
|
|
115
101
|
|
|
116
|
-
|
|
102
|
+
- **Whether a generic category applies.** The sensitive surface is the generic list — authentication/authorization, secrets/credentials, payments/billing, personal or otherwise sensitive data, a public API/contract shape, a DB migration/schema change — **plus** every glob in `quality.sensitive_paths`. The CLI reads the key; it carries no idea of its own about what is sensitive (that list is the repo's). You judge the categories against the diff.
|
|
103
|
+
- **Whether a matched path is a sensitive *diff*.** The bump asks what the change *does* there, not where it lives: it applies to **control flow, or the data that reaches a person, a charge or a stored record** — a new guard, a new `catch`, a changed condition, a changed value, a changed contract. It does **not** apply when the diff moves **observability alone**: a log level, a channel, a message's text, a metric, a comment. Mixed diff → not observability-only, so it bumps. **Say which of the two it was in one line**; a diff demoted here is the one place a missed defect is expensive.
|
|
104
|
+
- **Query changes.** §3.6 duels every query the diff adds or modifies, at every size, whatever this section resolved — routing them through the bump buys a second opinion on prose where §3.6 buys a plan.
|
|
105
|
+
|
|
106
|
+
**No CLI, or a non-zero exit → the prose fallback**, and `Review path` names it with the reason (the same contract the panel follows). Measure `git diff --shortstat <review base>...<candidate_sha>`; **≤150 → XS · 151-600 → S · 601-1500 → M · >1500 → L**; effective size = the lower of that and `meta.json.size`; `proportional` runs the built-in at **medium/high/high/xhigh** by size and the panel at M and above (at S only on a sensitive surface); the bump raises the effort one tier (medium→high→xhigh→**max**) and always runs the panel; `full` is **xhigh** plus the panel at any size, `light` is the built-in alone at **medium** with no panel, no §3 and no §6 — and a sensitive surface lifts `light` to the proportional tier for that work. The thresholds and the ladder are documented in `docs/CONFIGURATION.md`, where they can be read without being re-taught to a model on every invocation.
|
|
107
|
+
|
|
108
|
+
**The diff pointing *higher* than `meta.json.size`** is worth one line in `06-review.md` (the work may be misclassified), never a heavier review. **The sensitive-surface bump is never scaled away** — a 12-line change to an authorization check still gets the panel, on a delta exactly as on a first review.
|
|
109
|
+
|
|
110
|
+
Record in `06-review.md` which tier and effort ran and why (e.g. "L + DB migration → built-in at `max` + panel"; "S, `src/**/Payment/**` but log level only → no bump, built-in at `high`"), and **any deviation from what the CLI resolved, with its reason**.
|
|
117
111
|
|
|
118
112
|
### 2.0b Resolve the agent budget (the ceiling is the command's, not the round's)
|
|
119
113
|
This command is the widest fan-out in the plugin: a panel, area reinforcements, a coverage sweep that
|
|
@@ -146,7 +140,7 @@ Launch the reviewers selected in §2.0 and **consolidate their findings into a s
|
|
|
146
140
|
|
|
147
141
|
1. **Built-in `code-review`** (the Claude Code one, no prefix), at the §2.0 effort. Single pass over the local diff: correctness failures + reuse/simplification/efficiency.
|
|
148
142
|
2. **Project panel** (only when §2.0 selected it): read `quality.review_skill` from `FLOW.md`.
|
|
149
|
-
- `review_skill` set → invoke that skill with `03-design.md` as additional context. Scope: `git diff <
|
|
143
|
+
- `review_skill` set → invoke that skill with `03-design.md` as additional context. Scope: `git diff <review base>...<candidate_sha>` (§1.5) — the candidate carries the uncommitted work, so there is no second, later reading to take.
|
|
150
144
|
- `review_skill` empty, `quality.reviewers` has entries → launch each agent in parallel as a panel, same context and scope.
|
|
151
145
|
- Both empty → step 1 already covers this pass; launch nothing more.
|
|
152
146
|
|
|
@@ -154,8 +148,12 @@ Launch the reviewers selected in §2.0 and **consolidate their findings into a s
|
|
|
154
148
|
|
|
155
149
|
**Every brief you write here ends with the report contract of flow-core §6** — `agents.report_max_words` (empty → 250), findings only, one line each as `file:line` + what is wrong + the fix. This file's own prompts (§3.5, §4, §5.5) already carry theirs; the panel briefs are yours to write, and an uncapped one is truncated in transit and reaches you looking exactly like a reviewer that found nothing. The built-in `code-review` takes no brief — bound it with the §2.0 effort tier and hold it to the same fan-out deadline (flow-core §6): over a large diff it is the likeliest member of this round to stall before its first sentence.
|
|
156
150
|
|
|
151
|
+
**Nothing edits the tree while a round is in flight** (flow-core §6). The candidate of §1.5 means every reviewer is handed the same bytes however long it takes to get to them; it does not mean the checkout underneath can be edited freely, because an agent with tools reads that one. The fixes this phase applies are the parent's own edits — applied mid-round they land underneath agents still reading, and a reviewer that reports a line which has moved, or runs `git status` around its own reading to work out what it touched, is the reading this phase exists to trust. Findings are fixed **after** the round is consolidated. The fixes are then a delta the candidate does not contain, which is exactly what §9's `fixed_sha` records and `ship` asks about.
|
|
152
|
+
|
|
157
153
|
When both run, deduplicate overlapping correctness/simplification findings (count each once). The `review_skill` specialists (offensive/defensive security, silent failures, architecture) are not repeated in later phases.
|
|
158
154
|
|
|
155
|
+
**Dedup keeps every source, not the first one.** A finding carries its **origin** from here to §8 — `built-in`, a panel role, or the pass that raised it (`§3.5`, `§3.6`, `§5`, `§5.5`) — and a finding more than one source raised lists all of them. Counted once, credited to each. Beyond the built-in and the panel this command runs four specialised passes, roughly 1 800 of its words, every one added after a real defect got through and not one able to show it still earns that cost: a finding only the idiom audit saw and a finding three sources raised say opposite things about that pass, and until now both were written identically. The `Cost:` line stopped being a headcount when `meta.json.cost[]` arrived; this is the same move for the thing those costs are spent on.
|
|
156
|
+
|
|
159
157
|
### 2.2 Design truth vs design rationale (do not inherit rationalizations)
|
|
160
158
|
`03-design.md` goes to reviewers as context, with unequal authority:
|
|
161
159
|
- **Contracts and acceptance criteria are truth** — verify the code meets them.
|
|
@@ -184,7 +182,7 @@ wall-clock, that is the tell: wait.
|
|
|
184
182
|
|
|
185
183
|
Loop, maximum **2 rounds**:
|
|
186
184
|
|
|
187
|
-
1. **Worklist**: `git diff --stat <
|
|
185
|
+
1. **Worklist**: `git diff --stat <review base>...<candidate_sha>` (§1.5) → list of changed files/areas.
|
|
188
186
|
2. **Coverage map**: from the consolidated §2-§3 findings, mark which files/areas received at least one finding or were explicitly examined.
|
|
189
187
|
3. **Completeness critic (1 agent, blinded and text-only)**: `Agent general-purpose` (takes `models.agents` like every other subagent here), passing **only** the full diff file list (step 1) and, per §2-§3 reviewer, one line on what it covered. **Do not** pass the detailed findings, the design, or the diff. This agent compares two lists and answers in three sentences; it opens no file, runs no `git diff`, reads no code. An agent here that is still working after a few minutes is not being thorough — it was handed the wrong job, and the fix is to kill it and re-launch with the two lists alone. Prompt:
|
|
190
188
|
|
|
@@ -311,8 +309,9 @@ Write `.claude/work/<TICKET>/06-review.md`. The `Cost:` line of `## Summary` is
|
|
|
311
309
|
# Code review <TICKET>
|
|
312
310
|
|
|
313
311
|
## Summary
|
|
312
|
+
- Reviewed: <`<review base>...<candidate_sha>` (§1.5), each as a short sha, plus which base was chosen and why — the previous `reviewed_sha`, or `git.default_base` because this is a first review / the sha was not an ancestor / a fallback rule fired / `--full` was asked for. Any untracked file the candidate could not hold is named here. Without this line a round that read a delta and a round that read the branch are indistinguishable, and so are a frozen round and one that read a moving tree.>
|
|
314
313
|
- Review tier: <full | proportional | light — which reviewers ran, at what built-in effort (medium/high/xhigh/max), and why, per §2.0>
|
|
315
|
-
- Effective size: <diff size (N changed lines) vs `meta.json.size`, which of the two the tier used, and — when the diff pointed higher — that the work may be misclassified. When the diff landed within 10% under a tier threshold, say so here (§2.0).>
|
|
314
|
+
- Effective size: <diff size (N changed lines, measured over the §1.5 range — the branch on a first review, the delta afterwards) vs `meta.json.size`, which of the two the tier used, and — when the diff pointed higher — that the work may be misclassified. When the diff landed within 10% under a tier threshold, say so here (§2.0).>
|
|
316
315
|
- Review path: <the two paths this round actually took: `flow bundle` or git for the material, `flow review` or the agentic panel for the reviewers — each with the reason when it was the fallback (no CLI, non-zero exit, `agents.exec_cmd` empty, every role empty). Without this line a round that skipped the CLI reads exactly like one that used it. When the pack ran, quote its `Config read:` line verbatim: it is the only place that says whether the overlay reached the material, and “no `--harness` was given” there means every key set only in the overlay was missing from this round.>
|
|
317
316
|
- Agent models: <the value of `models.workers`/`models.agents` **with the file each came from** (flow-core §0) when set; otherwise "inherited from this thread", naming the config files you listed — "unset" without them reads the same whether the overlay was empty or never opened — plus, when the panel ran, that agents named in `agents.<role>` kept the model their own definition sets. The values as they are, no judgement on them>
|
|
318
317
|
- Cost: <n>/<budget_max> subagents launched (<k> reviewers · <m> reinforcements · <s> skeptics), tier <light|proportional|full>, effort <medium|high|xhigh|max>
|
|
@@ -356,10 +355,10 @@ Write `.claude/work/<TICKET>/06-review.md`. The `Cost:` line of `## Summary` is
|
|
|
356
355
|
<if §6 did not run, one line saying which condition did not hold — size, diff size, or count>
|
|
357
356
|
|
|
358
357
|
## Blockers (must-fix)
|
|
359
|
-
1. [file:line] description + concrete proposal
|
|
358
|
+
1. [file:line] · <origin> · description + concrete proposal
|
|
360
359
|
|
|
361
360
|
## Suggestions (nice-to-have)
|
|
362
|
-
1. [file:line] description
|
|
361
|
+
1. [file:line] · <origin> · description
|
|
363
362
|
|
|
364
363
|
## Quality gates
|
|
365
364
|
- style_fix: ✅ / ❌
|
|
@@ -375,7 +374,12 @@ Write `.claude/work/<TICKET>/06-review.md`. The `Cost:` line of `## Summary` is
|
|
|
375
374
|
|
|
376
375
|
- Blockers → **do not advance `phase`**. Leave `phase = "build"`; the user resolves them.
|
|
377
376
|
- No blockers → `phase = "review"`, add to `phases_done`. **In a multi-MR/PR work**, also add `review` to the current `in_progress` MR/PR's own `phases_done` (its `mrs[]` entry) — `/flow-feat-ship §1` gates on it per MR/PR.
|
|
378
|
-
- **Record *what* you reviewed**, in the same write
|
|
377
|
+
- **Record *what* you reviewed, and separately what you changed after reading it**, in the same write, work-level and — in a multi-MR/PR work — in this MR/PR's `mrs[]` entry too:
|
|
378
|
+
- `reviewed_sha` = **the candidate of §1.5**, the revision the reviewers actually read. Not `git rev-parse HEAD`, which is what it used to be and which by then holds this round's own fixes — code no reviewer saw, recorded as reviewed, and `ship` finding it equal to `HEAD` every time.
|
|
379
|
+
- `fixed_sha` = `git rev-parse HEAD` after this round's fixes, or **empty** when the round applied none. Two fields because they are two facts, and collapsing them is what hid the delta.
|
|
380
|
+
- `phases_done` says a review *happened*; these say which tree, and `/flow-feat-ship §1` reads both before pushing. Written whenever the phase advances, never when it does not: a review that ended in blockers reviewed nothing that stands.
|
|
381
|
+
- **Record which pass found what**, in the same write: `meta.json.review_findings[]`, one entry per finding **per source** — `{ round, origin, severity, file, discarded_by_skeptic }`, and no finding text, which stays in the artifact. `flow review --record` writes these itself when the CLI ran the panel; when the agentic panel ran, write them here from the origins §2.1 carried. `flow cost` reads them back per origin: how many findings each pass contributed, how many only it raised, and how many of those survived §6. That last number is the only evidence a pass is still earning its words.
|
|
382
|
+
**The retirement rule, written down before the data arrives** so it cannot be argued backwards later: a pass that contributes **no exclusive surviving finding across twenty reviews** is removed from this command. Zero is zero, and the decision is made against the number rather than against whoever is most attached to the pass.
|
|
379
383
|
- **Stage what the review taught** (`knowledge.stage` set; silence by default). Review is where a project teaches the most and, until now, the one phase that never staged — a work with three review rounds and seventeen blockers produced zero knowledge cards because nothing asked for them while they were in context. Candidates are findings about the **domain or the codebase's real behaviour**, not about this diff: a library that does other than its name says, a test disguise that kept the suite green over a live defect, a module rule the reviewers had to cite from its own prose, a measurement. A blocker fixed here is not knowledge; *why it was written wrong* may be. Same evidence rule as `/flow-feat-design` §8 — one line of evidence per finding or it is not staged. One call per finding, one line to the user («Staged N finding(s) for `ship`»). Never `knowledge.save` here.
|
|
380
384
|
- Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
|
|
381
385
|
- **Configuration this phase earned the right to ask about** (flow-core §0, `manual` only, at most one key per work, as an extra option on this same stop — never a question of its own): a role the panel had to improvise on an M/L or sensitive diff → `agents.<role>`; a `Cost:` line that went over `agents.budget_max` → `quality.review_depth`. In `guided`/`auto` neither is asked: the default is taken and recorded in `meta.json.defaults_used[]`.
|
|
@@ -25,12 +25,14 @@ Closes the feature: commit, push, MR/PR (assigned per `git.assignee`, squash per
|
|
|
25
25
|
- Read `meta.json` and `00-summary.md`; open in full only the `Brief MR/PR #N` of `05-implementation.md`, the `## Summary`/blockers of `06-review.md` and `07-validation.md`. (flow-core §5)
|
|
26
26
|
- Require `review` in `phases_done`; for `size` other than `XS`, also require `validate`. **In a multi-MR/PR work** (`meta.json.mrs` has >1 entry) check the **current `in_progress` MR/PR's** own `phases_done` (its `mrs[]` entry), NOT the work-level list — a previous MR/PR's `review`/`validate` does **not** satisfy this gate.
|
|
27
27
|
- Not met → refuse and send the user to the missing step (multi-MR/PR: `/flow-feat-review` or `/flow-feat-validate` **for this MR/PR**).
|
|
28
|
-
- **The review has to be about the tree you are about to push.** Compare `meta.json.reviewed_sha` and `validated_sha` (this MR/PR's `mrs[]` entry in a multi-MR/PR work) against `git rev-parse HEAD`:
|
|
29
|
-
- **Equal** → continue, nothing to report.
|
|
28
|
+
- **The review has to be about the tree you are about to push.** `reviewed_sha` is the revision the reviewers **read** (`/flow-feat-review §1.5` froze it), not the one that phase ended on, so the round's own fixes are a real delta here rather than a match. Compare `meta.json.reviewed_sha` and `validated_sha` (this MR/PR's `mrs[]` entry in a multi-MR/PR work) against `git rev-parse HEAD`:
|
|
29
|
+
- **Equal** → continue, nothing to report — **unless `fixed_sha` is set**, which says the review changed the tree after reading it and cannot then be equal to `HEAD`. That combination is a mismatch, not a match: the shas were recorded by two different mechanisms and one of them is wrong. Say so and treat it as Different below.
|
|
30
|
+
- **The tree is dirty at this end** (`git status --porcelain` non-empty, ignoring untracked) → both shas describe committed history and the bytes about to be pushed are not in either. Name the paths and ask, exactly as Different does; a comparison of two identical shas over two different trees is the case the old gate passed over in silence.
|
|
31
|
+
- **`reviewed_sha` does not resolve** (`git cat-file -e <sha>^{commit}`) — a candidate from a dirty tree whose anchor ref was deleted, or history rewritten since — → say so in one line and treat it as Absent. A gate that cannot read its own input must not report nothing wrong here.
|
|
30
32
|
- **Absent** (work started before the shas were recorded) → say so in one line and continue. Absence is not a mismatch.
|
|
31
|
-
- **Different** → read the delta (`git log --oneline <sha>..HEAD`, `git diff --stat <sha>..HEAD`) and judge it by **what it touches**, never by size.
|
|
33
|
+
- **Different** → read the delta (`git log --oneline <sha>..HEAD`, `git diff --stat <sha>..HEAD`) and judge it by **what it touches**, never by size. When `fixed_sha` is set, say in the same line that part of this delta is the review's own fixes: the user is judging what the reviewers never read, and where it came from is the first thing they need.
|
|
32
34
|
- Only test files (`tests/`, `test/`, `spec/`, `__tests__/`, `*_test.*`, `*Test.*`, `*.test.*`, `*.spec.*`) and this work's own folder under `.claude/work/` → append both shas and the file list to `06-review.md`, continue **without asking** (a test written by `validate` after the review is the normal order).
|
|
33
|
-
- **Anything else** → stop and ask, in **every** `autonomy.mode`, with a single `AskUserQuestion`: *re-review the delta* (`/flow-feat-review`, recommended default — it reads
|
|
35
|
+
- **Anything else** → stop and ask, in **every** `autonomy.mode`, with a single `AskUserQuestion`: *re-review the delta* (`/flow-feat-review`, recommended default — §1.5 resolves `reviewed_sha` as its base, so it reads the delta and nothing else, and resolves the tier on the delta too) · *ship it as it stands*, which first appends to `06-review.md` the two shas, the files, and that the user accepted them unreviewed. Never resolve this one silently.
|
|
34
36
|
- Check for blocking TODO/FIXME added on this branch (`git diff --unified=0 <git.default_base>...HEAD | grep -E '^\+.*(TODO|FIXME)'`). If any, list them and ask whether to continue.
|
|
35
37
|
|
|
36
38
|
## 2. Draft title and description (without sending anything yet)
|
|
@@ -226,6 +228,22 @@ Only if `knowledge.read_staging` or `knowledge.save` is set; neither → skip wi
|
|
|
226
228
|
3. **Combine staging + new findings** into a short list. Empty or only code-derivable → do not insist.
|
|
227
229
|
4. 1+ relevant findings → ask the user whether to consolidate. Yes → run the consolidation of flow-core §0 (`read_staging`, then one decision and one `knowledge.save` per finding). No → do not insist.
|
|
228
230
|
|
|
231
|
+
## 5.5 A worktree does not own the tree its tools wrote into
|
|
232
|
+
|
|
233
|
+
`meta.json.worktree` null → skip silently; there is nothing here that can go wrong.
|
|
234
|
+
|
|
235
|
+
Everything above this line ran in a worktree, and several of the tools it called had no way of knowing that. A configured tool — `knowledge.save` is the first one this caught, and no key is special — is started once per session, resolves the repo from the directory it was started in, and takes no root in its call. A worktree is the same repo with another tree, so its output can land in the **main checkout**, which is standing on another branch, while the branch §4 just pushed goes out without it. Nothing in the invocation could have corrected that: flow is the only party that knows `meta.json.worktree` is set, and this is the only place that knows the push already happened.
|
|
236
|
+
|
|
237
|
+
So, before the Close:
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
git -C "$(git rev-parse --path-format=absolute --git-common-dir)/.." status --porcelain
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Empty → say nothing at all. Non-empty → **one line naming the paths**, and what it means in plain words: they are sitting in the main checkout, not in the branch that was pushed. Say it as a reading, never as an accusation — that checkout may have been dirty before this work started, and only the user can tell their own leftovers from what this run put there.
|
|
244
|
+
|
|
245
|
+
Then offer once, with `AskUserQuestion` **in every autonomy mode** (it edits two trees): **carry them over** — copy the paths the user confirms into this worktree, commit onto the branch, push, and the MR/PR from §4 updates itself — or **leave them**, which is the right answer whenever what is sitting there belongs to the other branch. Either way the Close reports what was decided: a `ship` that quietly repaired two checkouts is worse than one that named the problem and left it standing.
|
|
246
|
+
|
|
229
247
|
## 6. Close
|
|
230
248
|
|
|
231
249
|
### 6.1 Update `meta.json` per scenario
|
|
@@ -405,6 +423,7 @@ paste. Never a list here, and never a question: in `guided`/`auto` not asking is
|
|
|
405
423
|
### 6.5 Archive and cleanup
|
|
406
424
|
|
|
407
425
|
Only if `phase = "done"`:
|
|
426
|
+
- Delete the candidate anchor `/flow-feat-review §1.5` left behind: `git update-ref -d refs/flow/candidate/<TICKET>`. It existed to stop the object being collected while the gate still needed it; the gate has run. No question — it is local, it was never pushed, and nothing reads it after this. Absent already → nothing to do.
|
|
408
427
|
- Ask whether to keep `.claude/work/<TICKET>/` or archive it (move to `.claude/work/_archive/`).
|
|
409
428
|
- If `meta.json.worktree` is not null, offer to remove the worktree (from the main checkout) with `git worktree remove <worktree>` (`--force` only if it still has changes the user confirms discarding). Never without confirmation, never if the MR/PR is not yet merged.
|
|
410
429
|
|
|
@@ -28,10 +28,14 @@ Verify the feature is complete: test coverage, edge cases, performance, regressi
|
|
|
28
28
|
|
|
29
29
|
## 2. Work
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
One of the three below writes and two of them read, so they do not all go at once (flow-core §6, one writer of the working tree at a time). The testing agent's deliverable is **files on disk**; the performance agent and the suite both read the tree it is writing into. Run beside it, the suite reports on a tree that no longer exists by the time you read the result — and a red raised by a half-written test file is a red nobody can act on. Both briefs end with the report contract of flow-core §6 (`agents.report_max_words`, empty → 250; findings only, one line each).
|
|
32
|
+
|
|
33
|
+
**First, alone — the writer.** It is told the **test files are the deliverable**, saved as each one is finished, not at the end:
|
|
32
34
|
|
|
33
35
|
1. **Testing agent**: the `agents.testing` agent from the effective FLOW config; if empty, `Agent general-purpose` with this role. Brief: "Review the branch changes and complete the test suite where coverage is missing. Focus: the acceptance criteria in `03-design.md` marked `test` that no current test demonstrably asserts (see the §3 mapping), plus edge cases from `03-design.md`, error paths, input validations, emitted domain events. Do not rewrite tests that already pass. Read `.claude/work/<TICKET>/03-design.md` and `05-implementation.md`. Follow the project's effective test conventions from `FLOW.md` plus the active harness overlay."
|
|
34
36
|
|
|
37
|
+
**Then, in parallel — the readers**, once the testing agent has reported and its files are on disk. The suite runs here and not earlier for a second reason beyond the collision: run after the writer, it is the first thing that has ever executed the tests this phase just added.
|
|
38
|
+
|
|
35
39
|
2. **Performance agent** if the feature touches persistence, repositories, templates on hot paths, controllers with real traffic, or calls anything outside the process inside a loop: the `agents.performance` agent from `FLOW.md`; if empty, `Agent general-purpose` with this role. Brief: "Detect N+1, missing indexes, unbounded queries, flush in a loop, per-iteration calls that leave the process (external API, HTTP, cache, filesystem), and heavy synchronous work that should go to a queue. For any call inside a loop that can fail, follow what **each failed iteration** sets off downstream — what it publishes, enqueues, disables or logs — and whether N failures multiply it. Report only actionable findings."
|
|
36
40
|
|
|
37
41
|
**A green suite is not a performance result** — a tiny fixture database proves a query's **rows**, never its **plan**. If `/flow-feat-review §3.6` left any query verdict `unresolved`, or an acceptance criterion concerns speed or volume: run the measurement of **`/flow-work-query §4`** with the volumes in `data.volumes` (or a data set shaped like them — the distribution matters more than the total), record plan and timings, and gate on the result. No `data.*` configuration and the criterion cannot be measured → it is `unproven` with the reason, never silently `proven`.
|
|
@@ -18,9 +18,10 @@ description: Assistant that generates FLOW.md and its optional harness overlay (
|
|
|
18
18
|
|
|
19
19
|
Creates or updates the effective FLOW configuration at the repo root: shared values in `FLOW.md`
|
|
20
20
|
and optional harness-owned values in the active `FLOW.<harness>.md`. The active filename is
|
|
21
|
-
`FLOW.claude.md`, `FLOW.codex.md`, `FLOW.opencode.md`, `FLOW.gemini.md`,
|
|
22
|
-
identify it from the
|
|
23
|
-
product executing this command, never from the selected model
|
|
21
|
+
`FLOW.claude.md`, `FLOW.codex.md`, `FLOW.opencode.md`, `FLOW.gemini.md`, `FLOW.hermes.md`, or
|
|
22
|
+
`FLOW.zcode.md`; identify it from the
|
|
23
|
+
product executing this command, never from the selected model (zcode runs the Claude pages too, so
|
|
24
|
+
the page you are reading does not name the harness). Every other `/flow-*` command reads
|
|
24
25
|
the base and then that overlay. The user answers the **minimum**: what the repo can tell is
|
|
25
26
|
auto-detected and only confirmed.
|
|
26
27
|
|
|
@@ -23,7 +23,7 @@ This system **orchestrates** the project's existing sub-agents and skills (it do
|
|
|
23
23
|
- `FLOW.md` at the repo root is the shared base: tracker, branch and MR/PR conventions, quality
|
|
24
24
|
commands, code conventions, and observability profile.
|
|
25
25
|
- The active `FLOW.<harness>.md` is an optional sparse overlay (`claude`, `codex`, `opencode`,
|
|
26
|
-
`gemini`, or `
|
|
26
|
+
`gemini`, `hermes`, or `zcode`) for model names, agents, skills, orchestration, knowledge tools, or any other value that
|
|
27
27
|
differs on that harness. Every command reads the base and then the active overlay in step 0;
|
|
28
28
|
existing repos with only `FLOW.md` behave exactly as before.
|
|
29
29
|
- An overlay key replaces the base even when explicitly empty; an absent key inherits it. Lists
|
|
@@ -85,9 +85,12 @@ This system **orchestrates** the project's existing sub-agents and skills (it do
|
|
|
85
85
|
"phase": "context" | "design" | "plan" | "build" | "review" | "validate" | "ship" | "investigate" | "fix" | "postmortem" | "done" | "abandoned",
|
|
86
86
|
// retired in v0.53.0 and still read from older works: "brainstorm" → treat as "context", "diagnose" → as "investigate"
|
|
87
87
|
"phases_done": ["context", ...],
|
|
88
|
+
"candidate_sha": "40-char sha the current review froze before reading, or ''",
|
|
88
89
|
"reviewed_sha": "40-char sha the last passing review read, or ''",
|
|
90
|
+
"fixed_sha": "40-char sha after that review's own fixes, or '' when it applied none",
|
|
89
91
|
"validated_sha": "40-char sha the suite last went green on, or ''",
|
|
90
92
|
"respond_rounds": 0,
|
|
93
|
+
"review_findings": [{"round": 1, "origin": "security", "severity": "blocker", "file": "src/X.php", "discarded_by_skeptic": false}],
|
|
91
94
|
"mrs": [
|
|
92
95
|
{
|
|
93
96
|
"n": 1,
|
|
@@ -95,7 +98,9 @@ This system **orchestrates** the project's existing sub-agents and skills (it do
|
|
|
95
98
|
"size": "S",
|
|
96
99
|
"status": "pending" | "in_progress" | "merged" | "closed" | "superseded",
|
|
97
100
|
"phases_done": ["build", "review", "validate"],
|
|
101
|
+
"candidate_sha": "",
|
|
98
102
|
"reviewed_sha": "",
|
|
103
|
+
"fixed_sha": "",
|
|
99
104
|
"validated_sha": "",
|
|
100
105
|
"respond_rounds": 0,
|
|
101
106
|
"wave": 1,
|
|
@@ -136,7 +141,9 @@ This system **orchestrates** the project's existing sub-agents and skills (it do
|
|
|
136
141
|
}
|
|
137
142
|
```
|
|
138
143
|
|
|
139
|
-
- **`reviewed_sha` / `validated_sha`**: which code
|
|
144
|
+
- **`candidate_sha` / `reviewed_sha` / `fixed_sha` / `validated_sha`**: which code each verdict is about. `candidate_sha` is written by `review` in its **pre-flight** (§1.5) — the revision it froze before reading anything, `git rev-parse HEAD` over a clean tree and `git stash create` over a dirty one, anchored at `refs/flow/candidate/<TICKET>` so nothing collects it. The other three are written at `## Close`, **only when the phase actually advances**, work-level and in the current `mrs[]` entry: `reviewed_sha` is the candidate that was actually read, `fixed_sha` is `HEAD` after that round's own fixes (empty when it applied none), `validated_sha` is where the suite went green.
|
|
145
|
+
- **`review_findings[]`**: one entry per finding **per source** — `{ round, origin, severity, file, discarded_by_skeptic }`, no finding text (that stays in `06-review.md`). Written at `review`'s Close, and by `flow review --record` when the CLI ran the panel. `origin` is `built-in`, a panel role, or the pass that raised it. It exists so a pass can be retired on evidence: `flow cost` reports per origin how many findings each contributed, how many **only** it raised, and how many of those survived §6 — and a finding three sources raised says nothing about any of them, which is why dedup keeps every source rather than the first. The rule was written down before the data arrived: no exclusive surviving finding across twenty reviews and the pass comes out of the command.
|
|
146
|
+
Two fields for the review because they are two facts. `reviewed_sha` used to be `git rev-parse HEAD` at the end of the phase, which by then held the round's own fixes — so `ship` compared a sha against itself and reported a match over code no reviewer had read. Read by `ship` at pre-flight against `HEAD`: a delta of test files and this work's own folder passes with a note in `06-review.md`; anything else stops and asks in every autonomy mode. Empty is not a mismatch; `fixed_sha` set with `reviewed_sha == HEAD` is one. `review` also reads `reviewed_sha` back as **its own base** on a re-review, so the second round reads the delta instead of the whole branch.
|
|
140
147
|
- **`respond_rounds`**: rounds of `/flow-work-respond` on an MR/PR. `/flow-work-respond §1` reads it against `quality.respond_max_rounds` (empty = 3) and refuses the round that would exceed it, handing the open threads back to the user.
|
|
141
148
|
- **`related_repos`**: the **other repos a task touches**. Captured at `/flow-feat-start` / `/flow-bug-start` §cross-repo, refined at `design`/`plan`, reminded at `ship`, shown by `daily`/`resume`/`status`. flow only notes and reminds — never touches the other repo. `[]` for single-repo.
|
|
142
149
|
- **`contract_handoff`**: whether the sibling knows **what shape to build against** (`scope` is prose; distinct from `status`). `none` — consumes no contract declared here. `pending` — consumes one, not handed over yet. `published → <location>` — published where that side reads it (normally the tracker ticket; `/flow-feat-ship` §6.3), picked up by the sibling's `/flow-feat-start` §3.6.
|
|
@@ -136,7 +136,7 @@ Only the **code-change** bucket, with the flow's building mechanics and conventi
|
|
|
136
136
|
In-review edits are **not** exempt because they are "small": they are where a wrong primitive or over-engineered mechanism slips in, straight into an MR/PR already under human eyes. The round's diff passes the **same gate as `/flow-feat-review`**, scaled to the round:
|
|
137
137
|
|
|
138
138
|
- **Trivial rounds** — only nitpick/style edits, no new classes or wiring → a single built-in `code-review` pass over the round's diff; skip the rest of this subsection.
|
|
139
|
-
- **Non-trivial rounds** — anything beyond nitpicks, or new architectural pieces → run the review machinery of `/flow-feat-review`, **scoped to this round's diff** (not the whole feature),
|
|
139
|
+
- **Non-trivial rounds** — anything beyond nitpicks, or new architectural pieces → run the review machinery of `/flow-feat-review`, **scoped to this round's diff** (not the whole feature). Its **§1.5** is what now delivers that scope rather than leaving it a promise: the previous `reviewed_sha` is the base when it is an ancestor of the candidate, so the round's own diff is both what is read and what sets the tier. Then apply:
|
|
140
140
|
- Its **§2.0 depth ladder** — pick the built-in `code-review` effort from the round's diff size (proxying `meta.json.size` when the round is small) plus the **sensitive-surface bump**; launch the project panel (`quality.review_skill` / `quality.reviewers`) when the ladder selects it, **as defined**. Per its **§2.2**: the stance agreed in §4 goes to the reviewer as context, never as a scope exclusion in the prompt.
|
|
141
141
|
- Its **§4 over-engineering / YAGNI audit** and **§5.5 idiom / primitive audit (blind to the design's rationale)** — they catch extracting a class/interface/mechanism to "answer a comment". **§5.5's trigger — "introduces new architectural pieces" — always runs when that is true, regardless of round size.**
|
|
142
142
|
- Its **§3.6 data-access duel** whenever the round **added or changed a query** — including a one-line change to an order, a bound or a join, and a change made to satisfy a reviewer's comment. If §4.G already measured it this round, reuse that table.
|