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.
Files changed (51) hide show
  1. package/README.md +25 -4
  2. package/adapters/README.md +11 -3
  3. package/adapters/codex/CORE.md +31 -5
  4. package/adapters/codex/skills/flow-bug-fix/SKILL.md +5 -0
  5. package/adapters/codex/skills/flow-bug-review/SKILL.md +19 -6
  6. package/adapters/codex/skills/flow-bug-ship/SKILL.md +5 -3
  7. package/adapters/codex/skills/flow-feat-build/SKILL.md +16 -2
  8. package/adapters/codex/skills/flow-feat-review/SKILL.md +51 -47
  9. package/adapters/codex/skills/flow-feat-ship/SKILL.md +23 -4
  10. package/adapters/codex/skills/flow-feat-validate/SKILL.md +5 -1
  11. package/adapters/codex/skills/flow-init/SKILL.md +4 -3
  12. package/adapters/codex/skills/flow-work-README/SKILL.md +9 -2
  13. package/adapters/codex/skills/flow-work-respond/SKILL.md +1 -1
  14. package/adapters/gemini/CORE.md +31 -5
  15. package/adapters/gemini/commands/flow/bug/fix.toml +5 -0
  16. package/adapters/gemini/commands/flow/bug/review.toml +19 -6
  17. package/adapters/gemini/commands/flow/bug/ship.toml +5 -3
  18. package/adapters/gemini/commands/flow/feat/build.toml +16 -2
  19. package/adapters/gemini/commands/flow/feat/review.toml +51 -47
  20. package/adapters/gemini/commands/flow/feat/ship.toml +23 -4
  21. package/adapters/gemini/commands/flow/feat/validate.toml +5 -1
  22. package/adapters/gemini/commands/flow/init.toml +4 -3
  23. package/adapters/gemini/commands/flow/work/README.toml +9 -2
  24. package/adapters/gemini/commands/flow/work/respond.toml +1 -1
  25. package/adapters/hermes/CORE.md +31 -5
  26. package/adapters/hermes/skills/flow-bug-fix/SKILL.md +5 -0
  27. package/adapters/hermes/skills/flow-bug-review/SKILL.md +19 -6
  28. package/adapters/hermes/skills/flow-bug-ship/SKILL.md +5 -3
  29. package/adapters/hermes/skills/flow-feat-build/SKILL.md +16 -2
  30. package/adapters/hermes/skills/flow-feat-review/SKILL.md +51 -47
  31. package/adapters/hermes/skills/flow-feat-ship/SKILL.md +23 -4
  32. package/adapters/hermes/skills/flow-feat-validate/SKILL.md +5 -1
  33. package/adapters/hermes/skills/flow-init/SKILL.md +4 -3
  34. package/adapters/hermes/skills/flow-work-README/SKILL.md +9 -2
  35. package/adapters/hermes/skills/flow-work-respond/SKILL.md +1 -1
  36. package/adapters/opencode/CORE.md +31 -5
  37. package/adapters/opencode/commands/flow-bug-fix.md +5 -0
  38. package/adapters/opencode/commands/flow-bug-review.md +19 -6
  39. package/adapters/opencode/commands/flow-bug-ship.md +5 -3
  40. package/adapters/opencode/commands/flow-feat-build.md +16 -2
  41. package/adapters/opencode/commands/flow-feat-review.md +51 -47
  42. package/adapters/opencode/commands/flow-feat-ship.md +23 -4
  43. package/adapters/opencode/commands/flow-feat-validate.md +5 -1
  44. package/adapters/opencode/commands/flow-init.md +4 -3
  45. package/adapters/opencode/commands/flow-work-README.md +9 -2
  46. package/adapters/opencode/commands/flow-work-respond.md +1 -1
  47. package/bin/cli.mjs +330 -24
  48. package/package.json +2 -1
  49. package/plugins/flow/.claude-plugin/plugin.json +5 -2
  50. package/plugins/flow/CHANGELOG.md +227 -0
  51. package/plugins/flow/examples/FLOW.template.md +2 -2
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # flow-workflows
2
2
 
3
3
  Guided `feat` / `bug` workflows for terminal coding agents: a ticket reaches an open MR/PR through explicit, reviewable phases, not one big prompt.
4
- For developers running Claude Code (or opencode, Gemini CLI, Codex CLI, Hermes Agent) on real repos with tickets, reviewers and a deploy.
4
+ For developers running Claude Code (or zcode, opencode, Gemini CLI, Codex CLI, Hermes Agent) on real repos with tickets, reviewers and a deploy.
5
5
  You get named phases, an artifact on disk after each one, hard gates the agent never crosses alone, and an autonomy dial from "ask me everything" to "run it and record what you decided".
6
6
 
7
7
  ## Quickstart
@@ -23,6 +23,14 @@ codex plugin add flow@flow-plugins
23
23
  # then, in a new session: $flow:init · $flow:next
24
24
  ```
25
25
 
26
+ **zcode** — same plugin, from its own marketplace
27
+
28
+ ```bash
29
+ zcode plugins marketplace add mashware/flow-workflows
30
+ zcode plugins install flow@flow-plugins
31
+ # then, in a new session: /flow:init · /flow:next
32
+ ```
33
+
26
34
  **opencode, Gemini CLI, Hermes Agent** — one command, nothing to clone
27
35
 
28
36
  ```bash
@@ -194,7 +202,7 @@ Artifacts are **hand-editable**: rewrite `03-design.md` and the next phase respe
194
202
  ## Configuration: base plus harness overlay
195
203
 
196
204
  `FLOW.md` at the repo root is the shared base. `FLOW.claude.md`, `FLOW.codex.md`,
197
- `FLOW.opencode.md`, `FLOW.gemini.md`, or `FLOW.hermes.md` can sparsely override values for the product executing the
205
+ `FLOW.opencode.md`, `FLOW.gemini.md`, `FLOW.hermes.md`, or `FLOW.zcode.md` can sparsely override values for the product executing the
198
206
  command. Existing repos with only `FLOW.md` behave exactly as before; anything effectively empty
199
207
  is auto-detected or asked for.
200
208
 
@@ -269,7 +277,12 @@ compaction point — selective, on disk and reversible.
269
277
  → [PHILOSOPHY](docs/PHILOSOPHY.md#nothing-here-asks-you-to-compact)
270
278
 
271
279
  Cost figures in these docs are estimates, not measurements: the flow reports how many agents a
272
- command launched, not how many tokens they spent.
280
+ command launched, not how many tokens they spent. **Quality used to be in the same position and no
281
+ longer is, for one command.** `/flow:feat:review` has a bench —
282
+ [`plugins/flow/evals/`](plugins/flow/evals/README.md) — that runs it over fixtures whose outcome is
283
+ known: a defect seeded at a known `file:line` the review has to raise, and a clean diff where any
284
+ blocker it reports is a false positive. Every other phase is still argued from judgement, and the
285
+ design decisions inside `review` itself were too, until there was something to check them against.
273
286
 
274
287
  ## Other harnesses
275
288
 
@@ -279,6 +292,7 @@ The same commands, in each harness's own shape. Only the invocation syntax diffe
279
292
  |---|---|---|
280
293
  | **Codex CLI** | `codex plugin add flow@flow-plugins` (see [Quickstart](#quickstart)) | `$flow:feat-start` |
281
294
  | **Codex CLI**, one repo only | `npx flow-workflows install codex project` → `.agents/skills/` | `$flow-feat-start` |
295
+ | **zcode** | `zcode plugins install flow@flow-plugins` (see [Quickstart](#quickstart)) | `/flow:feat:start` |
282
296
  | **opencode** | `npx flow-workflows install opencode` | `/flow-feat-start` |
283
297
  | **Gemini CLI** | `npx flow-workflows install gemini` | `/flow:feat:start` |
284
298
  | **Hermes Agent** | `npx flow-workflows install hermes` | `/flow-feat-start` |
@@ -288,6 +302,11 @@ dropped upstream does not linger. `npx flow-workflows check` compares what your
288
302
  against the newest release. From a clone, `adapters/install.sh <harness> [project]` does the same
289
303
  thing without Node; the preflight installs both ways and fails if the results differ by one byte.
290
304
 
305
+ **zcode** reads this plugin package directly and names a command after its path, with no plugin
306
+ prefix — so the `/flow:`-prefixed set it installs is a generated mirror living beside the Claude
307
+ one, and the plugin's own pages also show up there unprefixed (`/feat:start`, `/init`, …). Those
308
+ name Claude's overlay, so type the `/flow:` names.
309
+
291
310
  The mirrors are **generated** from the plugin commands by `script/adapter-build.py` and checked mechanically on every preflight (format, prefix, cited paths, install location).
292
311
  They have **not** been executed end to end in those harnesses — validate as you use them. → [adapters/README](adapters/README.md)
293
312
 
@@ -314,6 +333,7 @@ flow-workflows/
314
333
  ├── .github/ preflight CI · issue forms · PR template
315
334
  ├── plugins/flow/
316
335
  │ ├── commands/ feat/ bug/ work/ + next, init, doctor, news
336
+ │ ├── codex-skills/ · zcode-commands/ # generated mirrors for the two other plugin hosts
317
337
  │ ├── skills/flow-core/ # shared rules, loaded once per session
318
338
  │ ├── hooks/ push guard · update notice · where-you-left-off notice
319
339
  │ └── examples/ FLOW.template.md · symfony/ (a worked example, never loaded)
@@ -335,9 +355,10 @@ bash script/tests/push-guard.sh # the push guard's cases
335
355
  bash script/tests/notify-update.sh # the update-notice hook's cases
336
356
  bash script/tests/session-start.sh # the where-you-left-off hook's cases
337
357
  bash script/tests/review-unwrap.sh # the answer shapes `flow review` has to unwrap
358
+ bash script/tests/bench-compare.sh # the reader that compares two runs of the review bench
338
359
  ```
339
360
 
340
- CI runs the same six on every PR. The preflight refuses what has shipped broken before: an empty tracked file, unparsable JSON or TOML, a manifest version out of step with `CHANGELOG.md`, a hook without its executable bit, a stale or unusable mirror. → [RELEASING](RELEASING.md)
361
+ CI runs the same seven on every PR. The preflight refuses what has shipped broken before: an empty tracked file, unparsable JSON or TOML, a manifest version out of step with `CHANGELOG.md`, a hook without its executable bit, a stale or unusable mirror. → [RELEASING](RELEASING.md)
341
362
 
342
363
  ## What it does not ship (on purpose)
343
364
 
@@ -6,8 +6,9 @@ The `flow` plugin (in `../plugins/flow`) is for **Claude Code**. These adapters
6
6
  rules in `../plugins/flow/skills/flow-core/SKILL.md`, and writes every file under
7
7
  `opencode/commands/`, `codex/skills/`, `gemini/commands/flow/`, `hermes/skills/` plus one
8
8
  `<harness>/CORE.md` each —
9
- and `../plugins/flow/codex-skills/`, the same Codex skills packaged inside the plugin so
10
- `codex plugin add` installs them (see `codex/README.md`).
9
+ and the two mirrors that live inside the plugin package itself: `../plugins/flow/codex-skills/`,
10
+ the same Codex skills so `codex plugin add` installs them (see `codex/README.md`), and
11
+ `../plugins/flow/zcode-commands/`, the `/flow:`-prefixed set zcode reads (see the zcode row below).
11
12
  The **logic and prose are the plugin's, verbatim** — only the wrapper changes.
12
13
 
13
14
  | Harness | Commands | Subagents | MCP | Autopilot watch |
@@ -17,6 +18,7 @@ The **logic and prose are the plugin's, verbatim** — only the wrapper changes.
17
18
  | **Codex CLI** | `skills/*/SKILL.md` (`$flow-feat-start`) | `[agents.*]` in `config.toml` | `[mcp_servers.*]` | cron + `codex exec` |
18
19
  | **Codex CLI, packaged** | `plugins/flow/codex-skills/` (`$flow:feat-start`) | same | same | same |
19
20
  | **Hermes Agent** | `skills/*/SKILL.md` (`/flow-feat-start`) | `delegate_task`, built from the prompt | `config.yaml` `mcp_servers` | **native** `/cron add` |
21
+ | **zcode, packaged** | `plugins/flow/zcode-commands/flow/**` (`/flow:feat:start`) | `Agent` + `subagent_type`, as in Claude Code | `mcp.servers` in `~/.zcode/cli/config.json` | **native** `ScheduleWakeup` |
20
22
 
21
23
  ## What the generator changes per harness
22
24
 
@@ -30,7 +32,13 @@ Only the mechanics, and only these:
30
32
  Codex plugin package (Codex namespaces a plugin's skills), `/flow:feat:build` for Gemini. Hermes
31
33
  invokes its skills with a slash, so it shares opencode's `/flow-feat-build`: the file is shaped
32
34
  like Codex's, the prefix is opencode's.
33
- - **`$ARGUMENTS`** → `{{args}}` for Gemini.
35
+ - **`$ARGUMENTS`** → `{{args}}` for Gemini. zcode reads `$ARGUMENTS`, like Claude Code.
36
+ - **Nothing at all, for zcode** — it reads this plugin's own format and exposes the same
37
+ primitives under the same names, so its mirror is the Claude page with the overlay flag changed
38
+ to `--harness zcode` and one `flow/` folder above the command tree. That folder is the whole
39
+ trick: zcode names a command after its path under the root it was found in and adds no plugin
40
+ prefix, so `feat/start.md` on its own would be `/feat:start` — colliding with zcode's built-ins
41
+ (`/init`, `/doctor`) and with the user's own commands.
34
42
  - **The CORE pointer** — the plugin's `flow:flow-core` skill and `${CLAUDE_PLUGIN_ROOT}` become
35
43
  `~/.claude/flow/CORE.<tool>.md`, the file `install.sh` places there. In the Codex package they stay
36
44
  a skill (`$flow:flow-core`) and a relative root (`../..`), because there both actually exist.
@@ -17,7 +17,7 @@ Every `$flow-*` command assumes these rules. They are stated once, here, so a co
17
17
  carries what is specific to its phase. Read this once per session; a command that says "load
18
18
  `flow-core`" means this file.
19
19
 
20
- **This file belongs to flow `0.65.0`.** Compare it once, at the start of the session, against
20
+ **This file belongs to flow `0.67.0`.** Compare it once, at the start of the session, against
21
21
  `version` in `~/.claude/flow/.claude-plugin/plugin.json`. The two differing means the session
22
22
  is running a **mixture** — the commands from one copy of the plugin, these shared rules from another
23
23
  — which is exactly what happens when a branch or a release candidate is loaded over an installed
@@ -38,10 +38,14 @@ a command relies on is simply absent in that session, and nothing else will repo
38
38
  them. **Name the files this listing found the first time a phase reports any configured value**,
39
39
  so a missed overlay is visible in the artifact rather than invisible everywhere.
40
40
  - Read `FLOW.md` at the repo root as the base, then the optional overlay for the harness running
41
- this command: `FLOW.claude.md`, `FLOW.codex.md`, `FLOW.opencode.md`, `FLOW.gemini.md`, or
42
- `FLOW.hermes.md`. The
41
+ this command: `FLOW.claude.md`, `FLOW.codex.md`, `FLOW.opencode.md`, `FLOW.gemini.md`,
42
+ `FLOW.hermes.md`, or `FLOW.zcode.md`. The
43
43
  harness is the product executing the command, never guessed from a model name. An overlay is
44
- valid without a base file; unknown `FLOW.*.md` names are ignored. Throughout every command,
44
+ valid without a base file; unknown `FLOW.*.md` names are ignored. **A page can outlive the product it
45
+ was written for**: zcode reads the plugin's Claude pages as well as its own `/flow:`-prefixed
46
+ mirror of them, so a `claude` in a path or in a `--harness` flag records where the page was
47
+ written, never who is running it. On zcode the overlay is `FLOW.zcode.md` and the flag is
48
+ `--harness zcode`, whichever of the two pages you were handed. Throughout every command,
45
49
  **"FLOW.md" means this effective merged configuration** unless the command explicitly names a
46
50
  file it will edit.
47
51
  - **"The repo root" is the main checkout's, not the worktree's.** With `git.worktree` set, every
@@ -55,6 +59,12 @@ a command relies on is simply absent in that session, and nothing else will repo
55
59
  differ — and otherwise read both files from the main checkout, which
56
60
  `git rev-parse --path-format=absolute --git-common-dir` locates from anywhere (its parent
57
61
  directory). Name the file you actually read when a stop reports configuration.
62
+ **This is a rule about reading a git-ignored config, and it inverts for anything a phase
63
+ writes into the repo.** A tracked file belongs to the branch, and the branch is the worktree:
64
+ written to the main checkout it lands on whatever that checkout is standing on — usually the
65
+ base branch — and goes out in no MR/PR at all. So a phase writing a tracked file writes it
66
+ **here**, in the directory it is running in, and `$flow-feat-ship §5.5` is what catches the
67
+ tools that could not be told.
58
68
  - Merge by section and key. A key present in the active overlay replaces the base value; a key
59
69
  absent there inherits the base. **A present-but-empty overlay key masks the base** and resolves
60
70
  to the normal empty-key fallback — this is how Codex can inherit its session model while a legacy
@@ -115,7 +125,8 @@ worth a command of its own, which is why there is no longer one:
115
125
  3. **Per finding**: `knowledge.search` set → query it with the topic and the finding's paths, then
116
126
  decide *create* · *update* · *enrich with a new angle* · *conflict*. A conflict is asked
117
127
  immediately (`AskUserQuestion`) and nothing is saved until it is resolved. Then `knowledge.save`,
118
- one call per finding; the role empty → append to **`KNOWLEDGE.md` at the repo root** under a
128
+ one call per finding; the role empty → append to **`KNOWLEDGE.md` at the root of the checkout
129
+ you are in** (a tracked file belongs to the branch — the inversion above) under a
119
130
  `## <topic>` heading — the finding, its evidence line, the date and the branch. Creating that file
120
131
  the first time edits the tree, so it is asked once, in every mode; appending to an existing one is
121
132
  recorded, not asked.
@@ -315,6 +326,21 @@ test suite, a migration, a batch of edits — name the **path it writes** and re
315
326
  after each finished piece, not at the end**. Then verify the artifact instead of believing the
316
327
  report: half a chunk on disk is recoverable, what lives only in the agent's context is not.
317
328
 
329
+ **One writer of the working tree at a time.** An agent whose deliverable is files on disk is not
330
+ another reader with a longer report: it is break, run, restore, and it composes with nothing else
331
+ looking at the same tree. Two of them at once — or one of them beside a reader — and every reading
332
+ taken in that window is of a tree somebody else was halfway through changing. That is how a suite
333
+ comes back red for an edit belonging to another agent, how a reviewer ends up running `git status`
334
+ around each of its own findings to work out which changes are its doing, and how a commit that
335
+ looks clean is clean by luck. **The parent thread is a writer too**: applying a round's fixes while
336
+ its agents are still reading is the same collision, from the side nobody counts.
337
+
338
+ So a round that contains a writer is not one round. Readers fan out freely, as they always did;
339
+ writers go one at a time, each either alone or in a worktree of its own, and the readers sit
340
+ entirely before them or entirely after them. The parent's own edits wait until the round is
341
+ consolidated. Ordering costs wall clock and the phase says so in one line — a reading nobody can
342
+ trust costs the phase twice, which is the trade this rule is making.
343
+
318
344
  **A parallel round names a path too, however short the answer.** Not for volume — for arrival. A
319
345
  round of four is four chances for a result to reach you as nothing: truncated in transit, delivered
320
346
  as a notification while you were mid-turn, or an agent that went idle without replying at all. From
@@ -74,6 +74,8 @@ Save the brief at the top of `04-fix.md`. Any "also fix X while we're at it" dur
74
74
  - **`guided`** — ask **once**, at the first step; apply that answer for the rest of the fix; record it in `04-fix.md`.
75
75
  - **`auto`** — commit the WIP yourself (`git add <files> && git commit -m "WIP <TICKET>: <step>" --no-verify`) without asking. Invoking the command in `auto` is the authorization, and it covers **only** WIP commits on the work branch — push and MR/PR creation stay hard gates in every mode.
76
76
 
77
+ **Then write the step into the log, committed or not** — `$flow-feat-build §2.2`, unchanged, into a `## Steps` section of `04-fix.md`: `- <short sha> · <step name> · <n files, +a −b>`, with `—` in place of the sha for a step that was not committed. No question, in every mode. A fix is usually one step and one commit, which is exactly when the missing line costs least to add and the `06-review.md` delta is most likely to be that one sha.
78
+
77
79
  ## 2.2 The premise this fix rests on (any size)
78
80
 
79
81
  Same instrument as `$flow-feat-build §2.1bis`: the four conditions, one round per fix, the same
@@ -122,6 +124,9 @@ A **product decision** the fix turns out to need is neither of these — not sco
122
124
  ## Changes by file
123
125
  - <file> — what changed and why (1 line)
124
126
 
127
+ ## Steps
128
+ <one line per step of §2.1, in order: `- <short sha> · <step name> · <n files, +a −b>`, and `—` in place of the sha where the step was not committed. This is what joins the log to the git history; a section with no holes is the point, so an uncommitted step still gets its line.>
129
+
125
130
  ## Premises the fix depends on
126
131
  <one row per premise put through §2.2, plus any belief recorded as an assumption without a round: premise · what it rests on · verdict (holds / refuted / unsettled) · the evidence, or what would settle it. Omit the section when the fix depends on nothing outside its own diff.>
127
132
 
@@ -62,14 +62,24 @@ Read `~/.claude/flow/CORE.codex.md` first (shared rules: `FLOW.md` step 0, model
62
62
  picks the model, so `models.agents`/`models.workers` do not reach those reviewers. A one-shot sweep
63
63
  cannot pull a thread, so an ambiguous finding still goes to a real agent with tools, as below.
64
64
 
65
+ ### 1.5 Freeze what you are about to read, and resolve what to compare it against
66
+
67
+ **`$flow-feat-review` §1.5, in full and unchanged.** In short, before §2 launches anything:
68
+
69
+ - **Candidate**: `git rev-parse HEAD` over a clean tree, `git stash create` over a dirty one (a real commit object, the checkout untouched, nothing to pop), anchored with `git update-ref refs/flow/candidate/<TICKET> <sha>` so nothing collects it and `$flow-bug-ship §0` can still resolve it. Untracked files are in no revision — name them and read them from disk. Written to `meta.json.candidate_sha` here, not later: a candidate recorded after the reading is not a freeze.
70
+ - **Review base**: the previous `reviewed_sha` when it is non-empty and an ancestor of the candidate, otherwise `git.default_base`, with the reason in one line. `--full` forces the whole branch.
71
+ - **Then**: `bundle --rev <candidate_sha> --base <review base>`, and every brief names that range rather than the live tree.
72
+
73
+ A fix is usually small enough that the delta and the branch are the same thing — which is exactly why the freeze matters more here than the scoping: a one-file fix whose round applies its own correction is the case where `reviewed_sha` used to equal `HEAD` while describing a tree nobody read.
74
+
65
75
  ## 2. Run the code reviews
66
76
 
67
- Scope for every reviewer: the fix against the base (committed + uncommitted working tree).
77
+ Scope for every reviewer: **the candidate revision of §1.5, against the review base of §1.5** — the fix against `git.default_base` on a first review, the delta since the last passing one afterwards. The candidate carries the uncommitted work; untracked files stay outside it and §1.5 named them.
68
78
 
69
79
  ### 2.0 Resolve review depth (scale to *this diff* and the risk)
70
80
  Read `quality.review_depth` from `FLOW.md` (`light` | `proportional` | `full`; empty → `proportional`) and `meta.json.size`. Built-in `code-review` effort ladder: **low < medium < high < xhigh < max** (lower = fewer, higher-confidence findings; higher = broader coverage).
71
81
 
72
- **The tier is resolved against the diff in front of you**, per `$flow-feat-review §2.0`: measure the real diff (`git diff --shortstat <git.default_base>...HEAD` plus the working tree), derive its size (≤150 lines → XS · 151-600 → S · 601-1500 → M · >1500 → L), and run on the **lower** of that and `meta.json.size` — a one-file fix inside a bug classified M is an XS review. Every size-gated step here (§4.5, §5) reads that effective size. The sensitive-surface bump applies on top and is never scaled away. **When the measured diff lands within 10% under a threshold that changed the tier** (135-150, 541-600, 1351-1500), say so in one line beside the effective size in §7 — the tier does not change, and it is what makes flow-core §9 checkable after the fact.
82
+ **The tier is resolved by the CLI, against the diff in front of you** — `$flow-feat-review §2.0`, in full and unchanged: `npx flow-workflows@<version> tier --harness codex --rev <candidate_sha> --base <review base>` prints changed lines, diff size, `meta.json.size`, effective size (the lower of the two — a one-file fix inside a bug classified M is an XS review), the effort with and without the bump, the resolved ceilings, the within-10% flag and which `quality.sensitive_paths` globs matched. **Copy it verbatim** into §7 and record any deviation with its reason; no CLI or a non-zero exit falls back to the prose there (≤150 → XS · 151-600 → S · 601-1500 → M · >1500 → L) and `Review path` names the fallback. What stays yours is what a command cannot see: whether a generic category applies, and whether a matched path is a control-flow change or observability only. Every size-gated step here (§4.5, §5) reads the effective size; the bump is never scaled away.
73
83
 
74
84
  **Say what the round will run on** (flow-core §1): the skeptics and the completeness check have no agent of their own, so with the fan-out key empty they inherit this thread's model — one line before the first round of 4 or more naming the count and the inheritance, and the same fact on the artifact's `Agent models` line. Name the key (`models.workers`, then `models.agents`), never a model.
75
85
 
@@ -97,6 +107,8 @@ Launch the reviewers selected in §2.0 and **consolidate their findings into a s
97
107
  1. **Built-in `code-review`** (the Claude Code one, no prefix), at the effort of §2.0. Single pass over the local diff: correctness + simplification/efficiency.
98
108
  2. **Project panel** (only when §2.0 selected it): skill `quality.review_skill` from FLOW.md, invoked as `<review_skill> branch`. `review_skill` empty and `quality.reviewers` set → launch those agents in parallel as a panel. Both empty → the built-in `code-review` is the whole review. Launch the panel **as defined** — whole roster, no subset, no substitutions; an agent that cannot run goes to §7 `Agents launched` with the reason. **Every brief you write for it — and for the §3 reinforcements — ends with the report contract of flow-core §6** (`agents.report_max_words`, empty → 250; findings only, one line each): an uncapped report is truncated in transit and reads exactly like a reviewer with nothing to say. The built-in `code-review` takes no brief; bound it with the §2.0 tier and hold it to the same fan-out deadline.
99
109
 
110
+ **Nothing edits the tree while a round is in flight** — `$flow-feat-review` §2.1, unchanged: the reviewers all read the same working tree, and this phase's own fixes wait until the round is consolidated, so §3 onward read a tree that stopped moving (flow-core §6).
111
+
100
112
  Deduplicate overlaps (count once). Fix-specific focus beyond generic analysis:
101
113
  - The change genuinely resolves the problem from `03-investigation.md`.
102
114
  - No expanded scope (hidden refactor) — list any.
@@ -136,7 +148,7 @@ Review the diff for new defensive mechanisms smuggled in "just in case" (validat
136
148
 
137
149
  ## 4.5. Completeness check (M/L, no loop)
138
150
 
139
- **M/L by effective size (§2.0) only**, **one** check, no loop, and only while the budget covers it: after consolidating §2-§3 — never in parallel with them, since the check's input is what they covered — contrast `git diff --stat <git.default_base>...HEAD` against what was reviewed. Any changed file no reviewer looked at → targeted pass with the applicable reviewer, merge. A small diff (normal for a fix) resolves this in seconds or makes it not apply.
151
+ **M/L by effective size (§2.0) only**, **one** check, no loop, and only while the budget covers it: after consolidating §2-§3 — never in parallel with them, since the check's input is what they covered — contrast `git diff --stat <review base>...<candidate_sha>` (§1.5) against what was reviewed. Any changed file no reviewer looked at → targeted pass with the applicable reviewer, merge. A small diff (normal for a fix) resolves this in seconds or makes it not apply.
140
152
 
141
153
  ## 5. Adversarial finding verification (parallel fan-out, optional)
142
154
 
@@ -208,17 +220,18 @@ Cost line: count every subagent this command launched — reviewers = §2.1 buil
208
220
  <only if §5 was run; refuted findings with their reason, or "not applicable">
209
221
 
210
222
  ## Blockers
211
- 1. [file:line] …
223
+ 1. [file:line] · <origin> · …
212
224
 
213
225
  ## Suggestions
214
- 1. …
226
+ 1. [file:line] · <origin> · …
215
227
  ```
216
228
 
217
229
  ## 8. Close
218
230
 
219
231
  - With blockers: `phase` stays where it was (`validate` for size ≥ S, `fix` on XS, where validate never ran). Iterate.
220
232
  - Without blockers: `phase = "review"`, add to `phases_done`. Suggest `$flow-bug-postmortem` (M/L) or `$flow-bug-ship` (XS/S).
221
- - **Record *what* you reviewed**, in the same write: `reviewed_sha` = `git rev-parse HEAD` — `phases_done` says a review happened, the sha says on which tree, and `$flow-bug-ship §0` compares it against what is being pushed. Only when the phase advances: a review that ended in blockers reviewed nothing that stands.
233
+ - **Record *what* you reviewed, and separately what you changed after reading it** — `$flow-feat-review` §9, unchanged: `reviewed_sha` = the **candidate** of §1.5 (the revision the reviewers read, never `git rev-parse HEAD`, which by now holds this round's own fixes), and `fixed_sha` = `git rev-parse HEAD` after those fixes, empty when the round applied none. `phases_done` says a review happened; these two say on which tree and what moved afterwards, and `$flow-bug-ship §0` reads both against what is being pushed. Only when the phase advances: a review that ended in blockers reviewed nothing that stands.
234
+ - **Record which pass found what** — `$flow-feat-review §9`, unchanged: one `meta.json.review_findings[]` entry per finding **per source** (`{ round, origin, severity, file, discarded_by_skeptic }`, no finding text), written by `flow review --record` when the CLI ran the panel and by this Close when the agentic panel did. `flow cost` reads them back per origin, and the retirement rule is the same: a pass with no exclusive surviving finding across twenty reviews comes out of the command.
222
235
  - **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 `postmortem`/`ship`»). Never `knowledge.save` here.
223
236
  - Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
224
237
  - Stop body (after the flow-core §3 header): the findings that survived and what you did with each, plus the same Cost line as the Summary: "- Cost: <n>/<budget_max> subagents launched (<k> reviewers · <m> reinforcements · <s> skeptics), tier <light|proportional|full>".
@@ -29,10 +29,10 @@ Read `~/.claude/flow/CORE.codex.md` first (shared rules: `FLOW.md` step 0, model
29
29
 
30
30
  - 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)
31
31
  - 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.
32
- - **The review has to be about the tree you are about to push.** Compare `meta.json.reviewed_sha` and `validated_sha` against `git rev-parse HEAD`:
33
- - **Equal** → continue.
32
+ - **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`:
33
+ - **Equal** (and no `fixed_sha`, over a clean tree) → continue.
34
34
  - **Absent** (a work from before the shas were recorded) → say so in one line, continue; absence is not a mismatch.
35
- - **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` over `<sha>..HEAD`, the recommended default) · *ship as it stands*, recording in `06-review.md` the two shas and that the user accepted them unreviewed.
35
+ - **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.
36
36
 
37
37
  ## 1. Draft title and description (without sending anything yet)
38
38
 
@@ -173,6 +173,7 @@ nothing was measured. A slowness bug is the case where this comment *is* the pro
173
173
 
174
174
  ## 4. Close
175
175
 
176
+ - **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.
176
177
  - 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.
177
178
  - 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.
178
179
  - Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
@@ -183,5 +184,6 @@ nothing was measured. A slowness bug is the case where this comment *is* the pro
183
184
  - **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.
184
185
  - **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.
185
186
  - **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.
187
+ - **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.
186
188
  - Ask whether to archive `.claude/work/<TICKET>/` to `.claude/work/_archive/`.
187
189
  - 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.
@@ -180,6 +180,17 @@ The step's changes are **always reported before anything is recorded**. Who deci
180
180
  - **"Something needs to change"** → adjust; the step's commit stays pending until OK.
181
181
  - **"Continue without committing, we'll group later"** → next step without committing; changes accumulate in the working tree (fewer clean points if §2.3 cuts).
182
182
 
183
+ 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):
184
+
185
+ ```
186
+ - <short sha> · <step name> · <n files, +a −b>
187
+ - — · <step name> · <n files, +a −b> ← not committed
188
+ ```
189
+
190
+ 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.
191
+
192
+ 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.
193
+
183
194
  Rules when a commit does happen:
184
195
  - One commit per step. Do not batch steps unless the user explicitly requests it.
185
196
  - `--no-verify` **only for WIP commits** (slow hooks run in `$flow-feat-review` and in the final commit of `$flow-feat-ship`).
@@ -223,13 +234,13 @@ slip under a threshold buys itself the review a smaller change had earned.
223
234
  **Hot cut mechanics (option 1)**:
224
235
 
225
236
  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.
226
- 1. Identify a cut point: the last WIP commit where the piece is coherent and mergeable (a closed sub-goal: "endpoint and DTO done", "migration applied", "flow X tests green"). Do not cut in the middle of a change.
237
+ 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.
227
238
  2. Edit `meta.json.mrs`:
228
239
  - The current MR/PR keeps `n`, `title`, `wave` and `depends_on`, adjusts `lines_est` and `files_est` to actuals, and stays `in_progress`.
229
240
  - 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.
230
241
  3. Edit `04-mr-plan.md`: split the original entry in two, keeping the standalone-mergeable justification for both halves.
231
242
  4. Note in `05-implementation.md` under "Hot cut": date, reason, what stays and what moves to the next one.
232
- 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`.
243
+ 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.
233
244
 
234
245
  **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)**.
235
246
 
@@ -261,6 +272,9 @@ Keep `.claude/work/<TICKET>/05-implementation.md` updated as you work (not at th
261
272
  ## Changes per file
262
273
  - <file> — what changed and why (1 line each)
263
274
 
275
+ ## Steps
276
+ <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.>
277
+
264
278
  ## Decisions made during implementation
265
279
  - Decision: …
266
280
  - Why: …
@@ -31,6 +31,9 @@ Mandatory review phase. **`$flow-feat-ship` cannot run without passing through h
31
31
  - **Gather the material once, not once per reviewer.** `npx flow-workflows@<version> bundle --checks --harness codex`
32
32
  prints one context pack for this branch — worklist, the diff (minus `git.diff_exclude`), the changed files
33
33
  in full, the raw output of `quality.static_analysis`, and the work's handoff — in a single call.
34
+ **§1.5 runs first and adds `--rev` and `--base` to that call**: the pack is then one frozen
35
+ revision against one resolved base, which is what makes it the same material for every reader
36
+ rather than the same *call* made at different moments.
34
37
  Read that instead of rediscovering it, and **pass it to the briefs of §2-§6**: a panel where every
35
38
  member runs its own `git diff` and opens the same files pays for that material once per agent, and
36
39
  a reviewer that spends its turns exploring has fewer left to review. **Any non-zero exit is a
@@ -62,60 +65,51 @@ Mandatory review phase. **`$flow-feat-ship` cannot run without passing through h
62
65
  picks the model, so `models.agents`/`models.workers` do not reach those reviewers. A one-shot sweep
63
66
  cannot pull a thread, so an ambiguous finding still goes to a real agent with tools, as below.
64
67
 
65
- ## 2. Invoke the code reviews
68
+ ### 1.5 Freeze what you are about to read, and resolve what to compare it against
66
69
 
67
- Scope for every reviewer: the full feature work against the base branch (committed + working tree — commits are opt-in).
70
+ 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.
68
71
 
69
- ### 2.0 Resolve review depth (scale to *this diff* and the risk)
70
- 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).
72
+ **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:
71
73
 
72
- **The tier is resolved against the diff in front of you, not against the size of the feature.**
73
- `meta.json.size` describes the whole work; in a multi-MR/PR train every MR/PR inherits it, so a
74
- 41-line third MR/PR gets reviewed as the L its feature is — the single largest source of wasted
75
- review in this plugin. Measure the real diff (`git diff --shortstat <git.default_base>...HEAD`, plus
76
- the working tree) and derive its own size:
74
+ - Tree clean → `git rev-parse HEAD`.
75
+ - 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`.
76
+ - **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.
77
+ - 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.
78
+ - 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.
77
79
 
78
- | Changed lines in this diff | Diff size |
79
- |---|---|
80
- | ≤ 150 | XS |
81
- | 151-600 | S |
82
- | 601-1500 | M |
83
- | > 1500 | L |
80
+ **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:
84
81
 
85
- **Effective size = the lower of `meta.json.size` and the diff size**, and every size-gated step in
86
- this command (§3.5, §5.5, §6) reads the effective size, not the recorded one. The diff pointing
87
- *higher* than the recorded size is worth one line in `06-review.md` (the work may be misclassified),
88
- not a heavier review. The **sensitive-surface bump below applies on top of the effective size and is
89
- never scaled away** — a 12-line change to an authorization check still gets the panel.
82
+ - 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.
83
+ - 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.
84
+ - **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.
85
+ - `$flow-feat-review --full` ignores `reviewed_sha` and reads the branch on demand.
86
+ - **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`.
90
87
 
91
- **The threshold is visible in the artifact, because it is the one number with an incentive on it.**
92
- A diff of 148 lines is reviewed at `medium` with no panel and closes §6's gate; one of 160 gets the
93
- panel. When the measured diff lands **within 10% under** a threshold that changed the tier (135-150,
94
- 541-600, 1351-1500), say so in one line of `06-review.md`, beside the effective size. It is not an
95
- accusation and it changes no tier — it is the only place a reader can see that the review they are
96
- holding was selected by a number, and it is what makes flow-core §9 (the diff reports the change; the
97
- change is never shaped to fit the number) checkable after the fact rather than merely stated.
88
+ **Then everything reads those two.** `npx flow-workflows@<version> bundle --checks --harness codex --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.
98
89
 
99
- - **`full`** (any size): built-in `code-review` at **xhigh** + the project panel. Skip the tiering below.
100
- - **`light`** (any size): built-in `code-review` (or `quality.review_skill`) **only**, at **medium** effort. No project panel, no §3 reinforcements, no §6 skeptic fan-out. The sensitive-surface bump still applies: a sensitive surface upgrades `light` to the `proportional` tier for that work.
101
- - **`proportional`** (default), base effort by size:
90
+ ## 2. Invoke the code reviews
91
+
92
+ 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.
102
93
 
103
- | Size | Built-in `code-review` | Project panel (`review_skill`/`reviewers`) |
104
- |---|---|---|
105
- | XS | **medium** | do **not** launch |
106
- | S | **high** | only if the diff touches a sensitive surface; otherwise the built-in alone is the review |
107
- | M | **high** | full panel |
108
- | L | **xhigh** | full panel |
94
+ ### 2.0 Resolve review depth (scale to *this diff* and the risk)
109
95
 
110
- - **Sensitive-surface bump** (proportional, any size): the diff touches a sensitive surface → **raise the built-in effort one tier** (medium→high→xhigh→**max**) and always run the panel. S/M on a sensitive surface → **xhigh**; **L on a sensitive surface → `max`**.
96
+ **The CLI resolves it; you obey the output and record any deviation.** `npx flow-workflows@<version> tier --harness codex --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.
111
97
 
112
- **Sensitive surface** = the generic categories — authentication/authorization, secrets/credentials, payments/billing, personal or otherwise sensitive data, a public API/contract shape, or a DB migration/schema change — **plus** every glob in `quality.sensitive_paths` from `FLOW.md`. The key is **additive**: it names what is sensitive in *this* repo and nowhere in that list (an internal event or message contract consumers depend on, a pricing or entitlement table, a consent flow, a feature-flag resolver), and a repo that declares its own surfaces never loses the ones it never thought about.
98
+ 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.
113
99
 
114
- **A sensitive path is not yet a sensitive diff, and this is where the precision comes from** — not from a shorter list. The bump asks what the change *does* there, not where it lives. It applies when the diff touches **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 log channel, the text of a message, a metric, a comment. That diff gets the review its size already earned. 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, so the line is not optional.
100
+ **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.
115
101
 
116
- **Query changes do not belong here.** §3.6 already duels every query the diff adds or modifies, at every size, whatever this section resolved — routing them through the bump would buy a second opinion on prose where §3.6 buys a plan.
102
+ **What the CLI deliberately does not decide, because a command cannot see it:**
117
103
 
118
- 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`").
104
+ - **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.
105
+ - **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.
106
+ - **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.
107
+
108
+ **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.
109
+
110
+ **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.
111
+
112
+ 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**.
119
113
 
120
114
  ### 2.0b Resolve the agent budget (the ceiling is the command's, not the round's)
121
115
  This command is the widest fan-out in the plugin: a panel, area reinforcements, a coverage sweep that
@@ -148,7 +142,7 @@ Launch the reviewers selected in §2.0 and **consolidate their findings into a s
148
142
 
149
143
  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.
150
144
  2. **Project panel** (only when §2.0 selected it): read `quality.review_skill` from `FLOW.md`.
151
- - `review_skill` set → invoke that skill with `03-design.md` as additional context. Scope: `git diff <git.default_base>...HEAD`, plus uncommitted working-tree changes.
145
+ - `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.
152
146
  - `review_skill` empty, `quality.reviewers` has entries → launch each agent in parallel as a panel, same context and scope.
153
147
  - Both empty → step 1 already covers this pass; launch nothing more.
154
148
 
@@ -156,8 +150,12 @@ Launch the reviewers selected in §2.0 and **consolidate their findings into a s
156
150
 
157
151
  **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.
158
152
 
153
+ **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.
154
+
159
155
  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.
160
156
 
157
+ **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.
158
+
161
159
  ### 2.2 Design truth vs design rationale (do not inherit rationalizations)
162
160
  `03-design.md` goes to reviewers as context, with unequal authority:
163
161
  - **Contracts and acceptance criteria are truth** — verify the code meets them.
@@ -186,7 +184,7 @@ wall-clock, that is the tell: wait.
186
184
 
187
185
  Loop, maximum **2 rounds**:
188
186
 
189
- 1. **Worklist**: `git diff --stat <git.default_base>...HEAD` → list of changed files/areas.
187
+ 1. **Worklist**: `git diff --stat <review base>...<candidate_sha>` (§1.5) → list of changed files/areas.
190
188
  2. **Coverage map**: from the consolidated §2-§3 findings, mark which files/areas received at least one finding or were explicitly examined.
191
189
  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:
192
190
 
@@ -313,8 +311,9 @@ Write `.claude/work/<TICKET>/06-review.md`. The `Cost:` line of `## Summary` is
313
311
  # Code review <TICKET>
314
312
 
315
313
  ## Summary
314
+ - 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.>
316
315
  - Review tier: <full | proportional | light — which reviewers ran, at what built-in effort (medium/high/xhigh/max), and why, per §2.0>
317
- - 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).>
316
+ - 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).>
318
317
  - 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.>
319
318
  - 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>
320
319
  - Cost: <n>/<budget_max> subagents launched (<k> reviewers · <m> reinforcements · <s> skeptics), tier <light|proportional|full>, effort <medium|high|xhigh|max>
@@ -358,10 +357,10 @@ Write `.claude/work/<TICKET>/06-review.md`. The `Cost:` line of `## Summary` is
358
357
  <if §6 did not run, one line saying which condition did not hold — size, diff size, or count>
359
358
 
360
359
  ## Blockers (must-fix)
361
- 1. [file:line] description + concrete proposal
360
+ 1. [file:line] · <origin> · description + concrete proposal
362
361
 
363
362
  ## Suggestions (nice-to-have)
364
- 1. [file:line] description
363
+ 1. [file:line] · <origin> · description
365
364
 
366
365
  ## Quality gates
367
366
  - style_fix: ✅ / ❌
@@ -377,7 +376,12 @@ Write `.claude/work/<TICKET>/06-review.md`. The `Cost:` line of `## Summary` is
377
376
 
378
377
  - Blockers → **do not advance `phase`**. Leave `phase = "build"`; the user resolves them.
379
378
  - 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.
380
- - **Record *what* you reviewed**, in the same write: `reviewed_sha` = `git rev-parse HEAD`, work-level and — in a multi-MR/PR work — in this MR/PR's `mrs[]` entry too. `phases_done` says a review *happened*; the sha says which tree, and `$flow-feat-ship §1` compares it before pushing. Write it whenever the phase advances, never when it does not: a review that ended in blockers reviewed nothing that stands.
379
+ - **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:
380
+ - `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.
381
+ - `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.
382
+ - `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.
383
+ - **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.
384
+ **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.
381
385
  - **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.
382
386
  - Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
383
387
  - **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[]`.