cohorte 1.4.0 → 1.5.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/CHANGELOG.md CHANGED
@@ -3,6 +3,73 @@
3
3
  Entries are shown by `/update-pipeline` ("What's new") after a core refresh. Keep them short,
4
4
  user-facing, most recent first. One `## <version> — <YYYY-MM-DD>` section per release.
5
5
 
6
+ ## 1.5.0 — 2026-08-01
7
+
8
+ > **Re-run `npx cohorte@latest update --global` (or `update`)** to pick up the collector and the
9
+ > `/smoke` removal — the update *deletes* the command and its agent from your install, it does not
10
+ > just stop shipping them. The new dashboard panel comes with `npx cohorte dashboard`.
11
+
12
+ - **New — `/loop <id>`: the review ⇄ fix cycle, run for you.** `/build` → `/review` → `/fix` →
13
+ `/review` … until a review reports **zero blocking findings** (a CRITICAL or a security issue —
14
+ a LOW nit never costs a pass), or the pass ceiling (`--max=N`, default 5), or two consecutive
15
+ reviews returning the *same* blocking findings, which means the fix is treading water and more
16
+ passes won't help. `--no-build` re-runs the loop on an already-built feature; `--rebuild` forces
17
+ a build. Every fix pass is committed (`loop(<id>): fix pass <i>`) — your way back after N
18
+ autonomous passes — and **no fix runs on the last pass**, since fixing without a review behind
19
+ it leaves unaudited code. Exit codes distinguish clean · ceiling · no verdict · non-convergent ·
20
+ usage, so a wrapper can tell "needs more passes" from "needs a human".
21
+ - **The loop does not run in your session — that's the whole design.** Each phase is a separate
22
+ `claude -p` child with its own fresh context, driven by the new shipped `loop.sh`; all child
23
+ output goes to `specs/reports/<id>.loop.log`, which the command is forbidden to read back. Your
24
+ session sees one line per phase and a three-line summary. A slash command cannot `/clear` itself,
25
+ so a conversational loop would pile the diff plus N review reports plus N contracts into a
26
+ history re-sent at input price every turn — it would cost more than the loop saves.
27
+ `disable-model-invocation: true`: an autonomous loop only ever starts because you asked.
28
+ - **`/review` now writes a machine-readable verdict** to `specs/reports/<id>.verdict.json` on every
29
+ run — verdict, finding counts by severity, per-surface breakdown, the normalized blocking items
30
+ and a stable `fingerprint` over them. It is the only contract between the pipeline and any
31
+ driver; no prose is parsed. `blocking` restates the reviewer's existing rule as a number
32
+ (CRITICAL + security, deduplicated), so `blocking == 0` ⟺ `SHIP`. The fingerprint hashes
33
+ *surface + file + problem* with the line number deliberately dropped — a fix that inserts lines
34
+ would otherwise change it every pass and the drift detection would never fire. A red preflight
35
+ writes a degraded `{"aborted":"preflight"}` verdict rather than nothing, so an abort is a
36
+ diagnosis instead of a silence.
37
+ - **BREAKING — `/smoke` and the `smoke` agent are removed.** The end-to-end run phase is gone:
38
+ the command, the agent, its preflight wiring, its telemetry phase and its documentation. The
39
+ loop is now `/brainstorm` → `/spec` → `/build` → `/review` → (`/fix` → `/review`)* → `/ship`,
40
+ with `/clear` safe between each. Nothing else depended on it; a `/smoke` in an old habit will
41
+ report an unknown command.
42
+ - **Nothing in the pipeline runs your app any more — that part is yours.** `/build` now closes by
43
+ telling you to exercise the feature by hand if it's worth it, and `/fix` says the same for
44
+ runtime failures. `/review` follows suit at the SHIP verdict: it ticks only what a stage
45
+ actually verified, and **leaves any DoD criterion that needs the app up open** (runtime flows,
46
+ a visual check against the design) unless you say you exercised it yourself and it held.
47
+ - **The preflight phase gate now gates `review` alone** (`gate.preflight.agents` defaults to
48
+ `[review]`). Existing profiles that list `smoke` keep working — the hook just never sees that
49
+ dispatch. `/doctor` compares against the new default, so re-run it after the update if it
50
+ flags gate drift.
51
+ - **Retired-phase data still renders.** Metrics files and dashboards carrying `phase: "smoke"`
52
+ keep their column, the transcript collector keeps attributing past `/smoke` runs to `/smoke`
53
+ instead of silently reclassifying them, and `telemetry-send.sh` still accepts the phase from a
54
+ stale install. Same treatment `/cycle` got in 1.4.0.
55
+ - **The cockpit now shows what a feature actually cost.** The dashboard's only metrics source
56
+ was `pipeline-metrics.jsonl`, written by the model itself — so it misses any run that ended
57
+ early and can never report tokens. On a real project it had captured 18 phase batches where
58
+ the transcripts hold 53 runs. The new **Cost & runtime** panel reads
59
+ `cohorte metrics` instead: per command, the number of runs, $ per run, $ total, tokens, wall
60
+ and active time, and the median number of subagents dispatched. That last column is the one
61
+ that makes a broken run obvious — a `/build` reporting 0 agents did no fan-out at all.
62
+ - **Both metrics sources are kept, because they answer different questions.** `pipeline-metrics.jsonl`
63
+ carries per-surface verdicts (`ok`, `REVISE:2`, `error`) that only the model knows and the
64
+ transcripts never contain; the collector carries money and time, which the model cannot report
65
+ and the transcripts record exactly. The two panels sit side by side and each says what it is
66
+ for. Neither replaces the other.
67
+ - **Fixed: discussing a command counted as running it.** An inline command mention was treated
68
+ as an invocation regardless of context, so a long message *about* `/review` billed that whole
69
+ conversation to `/review` — in cohorte's own repo it invented five `/cycle` runs out of a
70
+ design discussion. Inline mentions are now length-gated (an instruction is short; a discussion
71
+ is not); an explicit slash-command invocation is always counted.
72
+
6
73
  ## 1.4.0 — 2026-08-01
7
74
 
8
75
  > **Re-run `npx cohorte@latest update --global` (or `update`)** — the workflow fixes only apply
package/README.md CHANGED
@@ -20,7 +20,7 @@ then one command per project (`/init-pipeline`) adapts it to that project's stac
20
20
  **stateless agents** that only communicate through a frozen contract:
21
21
 
22
22
  ```
23
- /brainstorm → /spec → (design) → /build <id> → /smoke → /review → (/fix) → /ship
23
+ /brainstorm → /spec → (design) → /build <id> → /review → (/fix) → /ship
24
24
  ```
25
25
 
26
26
  ## How it works — three layers
@@ -217,9 +217,9 @@ it in `.claude/pipeline/VERSION` and bundled repos in their committed `pipeline.
217
217
  | `/brainstorm` | Interactive persona panel that pressure-tests a feature idea. |
218
218
  | `/spec` | Freeze the feature spec + contract into `specs/<id>.md` (UI features also get a standalone design brief at `specs/design/<id>.md`). Also applies review returns. |
219
219
  | `/build <id>` | Lead authors the contract, then dispatches one implementer per surface in parallel. |
220
- | `/smoke <id>` | Run the feature for real: infra up, contract endpoints, UI flows, design conformance. |
221
220
  | `/review <id>` | Read-only review agents (one per touched surface, parallel) audit the diff vs the spec. |
222
- | `/fix <id>` | Apply a review/smoke report: remediation into the spec, re-dispatch only the surfaces with findings. |
221
+ | `/fix <id>` | Apply a review report: remediation into the spec, re-dispatch only the surfaces with findings. |
222
+ | `/loop <id>` | Autonomous `/build → /review → /fix → /review …` until no blocking finding is left (see below). |
223
223
  | `/ship <id>` | Release agent commits, pushes, opens the PR; watches CI; proposes worktree teardown. |
224
224
  | `/audit [path]` | Prioritized refactor backlog for existing code. |
225
225
  | `/refactor <domain>` | Apply the backlog for one surface, TDD-first. |
@@ -230,12 +230,12 @@ it in `.claude/pipeline/VERSION` and bundled repos in their committed `pipeline.
230
230
  ### Run the loop cheaply — `/clear` between stages
231
231
 
232
232
  Every command reloads all the state it needs **from disk** — the frozen spec, the contract, the diff, the
233
- Remediation checkboxes, the freshness stamp, and the last `/review`·`/smoke` report (staged to a gitignored
233
+ Remediation checkboxes, the freshness stamp, and the last `/review` report (staged to a gitignored
234
234
  `specs/reports/<id>.md`). Nothing essential lives in the conversation. So the loop is **`/clear`-safe at
235
235
  every boundary**:
236
236
 
237
237
  ```
238
- /spec → /clear → /build → /clear → /smoke → /clear → /review → /clear → /fix → /clear → /review → /ship
238
+ /spec → /clear → /build → /clear → /review → /clear → /fix → /clear → /review → /ship
239
239
  ```
240
240
 
241
241
  `/clear`-ing between stages sheds the accumulated main-thread context, which is the single biggest token
@@ -243,6 +243,29 @@ lever: long sessions (>150k) are expensive even when cached. Each command tells
243
243
  safe to clear. If you'd rather stay in one session, `/compact` mid-task does the lighter version. (Claude
244
244
  can't fire `/clear` itself — it's a client-side command; the pipeline just makes it always safe to type.)
245
245
 
246
+ ### Let it run itself — `/loop`
247
+
248
+ ```
249
+ /loop feat-x # /build, then /review ⇄ /fix until clean (max 5 passes)
250
+ /loop feat-x --no-build # already built — just re-run the /review ⇄ /fix loop
251
+ /loop feat-x --max=8
252
+ ```
253
+
254
+ It stops when `/review` reports **zero blocking findings** (a CRITICAL or a security issue — a LOW
255
+ nit never costs a pass), at the pass ceiling, or as soon as two consecutive reviews return the same
256
+ blocking findings, which means the fix is treading water and more passes won't help. **Each fix pass
257
+ is committed** (`loop(<id>): fix pass <i>`) — that's your way back after N autonomous passes — and
258
+ **no fix runs on the last pass**, since fixing without a review behind it leaves unaudited code.
259
+
260
+ **The loop does not run in your session.** Each phase is a separate `claude -p` child with its own
261
+ fresh context, driven by `pipeline/scripts/loop.sh`; all of their output goes to
262
+ `specs/reports/<id>.loop.log`, which the command is forbidden to read back. Your session sees one
263
+ line per phase and a three-line summary. That's the whole design: a slash command can't `/clear`
264
+ itself, so a conversational loop would pile the diff plus N review reports plus N contracts into a
265
+ history that is re-sent at input price every turn — it would cost more than the loop saves. The
266
+ machine contract is `specs/reports/<id>.verdict.json`, which `/review` now writes on every run; no
267
+ prose is ever parsed.
268
+
246
269
  ### Run features in parallel — one session per feature
247
270
 
248
271
  With `isolation.enabled`, every feature already gets its own worktree, ports, and database
@@ -256,8 +279,8 @@ The pattern:
256
279
  ```
257
280
  session 1 (main checkout): /spec feat-a → /build feat-a (agents run…)
258
281
  session 2 (main checkout): /spec feat-b → /build feat-b (agents run…)
259
- session 1: /smoke feat-a → /review feat-a → /ship feat-a
260
- session 2: /smoke feat-b → …
282
+ session 1: /review feat-a → /ship feat-a
283
+ session 2: /review feat-b → …
261
284
  ```
262
285
 
263
286
  Rules that make it safe:
@@ -266,7 +289,7 @@ Rules that make it safe:
266
289
  (`specs/<id>.md`, `<contract.path>/<id>.*`, `specs/reports/<id>*`), so sessions never share state —
267
290
  but a single session interleaving two features accumulates both in its context, paying for both.
268
291
  - **Disjoint surfaces per feature are guaranteed** (each worktree is a full checkout), and each
269
- feature's DB/ports come from its slot — `/smoke` runs collide on neither.
292
+ feature's DB/ports come from its slot — two features' dev servers collide on neither.
270
293
  - **The contract package is the one shared tree.** Two features editing
271
294
  `<contract.path>/<their-own-id>.<ext>` never conflict (one file per feature); merge order only
272
295
  matters if a later feature *imports* an earlier one's contract — ship the dependency first.
@@ -331,7 +354,7 @@ bin/cli.js # the npm CLI: install / update / dashboard / version (c
331
354
  install.sh # script installer (fresh + --update) for no-Node environments
332
355
  install.ps1 # same installer for Windows PowerShell (fresh + -Update)
333
356
  core/ # copied verbatim into ~/.claude (global) or <project>/.claude (bundled)
334
- agents/ # implementer.template.md (rendered per surface) + review / release / smoke / profile-reader
357
+ agents/ # implementer.template.md (rendered per surface) + review / release / profile-reader
335
358
  commands/ # init-pipeline + the pipeline commands + /update-pipeline
336
359
  hooks/ # gate.py (destructive-command gate; branch-aware; preflight phase gate)
337
360
  templates/ # handoff / brainstorm-return / design-brief / review-feedback / pr-body / spec
package/bin/cli.js CHANGED
@@ -213,8 +213,8 @@ function copyFixedAgents() {
213
213
  fs.mkdirSync(path.join(dest, 'agents'), { recursive: true });
214
214
  // Every agent in core/agents/ EXCEPT the *.template.md ones, which /init-pipeline renders
215
215
  // per-surface. Until 1.2.6 this was a hardcoded ['review.md', 'release.md'] that never grew
216
- // the `smoke.md` the shell installers copy, so `npx cohorte install` shipped the /smoke
217
- // command with no `smoke` agent to dispatch — the run reported /smoke as not installed.
216
+ // the agents the shell installers copy, so `npx cohorte install` shipped a command with no
217
+ // agent to dispatch — the run reported the command as not installed.
218
218
  // Reading the directory needs no list to keep in sync with the shell installers.
219
219
  const agentDir = path.join(src, 'core', 'agents');
220
220
  for (const f of fs.readdirSync(agentDir)) {
@@ -224,6 +224,9 @@ function copyFixedAgents() {
224
224
  // 0.1.19 split the bi-mode questionnaire-researcher into research-agent + questionnaire-architect;
225
225
  // copy-over never deletes, so scrub the retired agent lest a dead subagent_type linger.
226
226
  fs.rmSync(path.join(dest, 'agents', 'questionnaire-researcher.md'), { force: true });
227
+ // 1.5.0 removed the /smoke phase; copy-over never deletes, so scrub the orphan agent+command.
228
+ fs.rmSync(path.join(dest, 'agents', 'smoke.md'), { force: true });
229
+ fs.rmSync(path.join(dest, 'commands', 'smoke.md'), { force: true });
227
230
  scrubResearchQuestionnaire();
228
231
  }
229
232
 
@@ -105,10 +105,10 @@ Bash call, chain the opt-in usage ping — **the shared form every phase command
105
105
  `<core>/pipeline/scripts/telemetry-send.sh <phase> "$ARGUMENTS" <seconds> "<results>" || true`
106
106
  (`<core>` = `~/.claude` global / `.claude` bundled; here `<phase>` = `build`, `<results>` =
107
107
  `<ok,ok|error,…>`) — a silent no-op unless the human explicitly consented (SCHEMA.md §Telemetry);
108
- never ask about consent here. `/review`, `/fix` and `/smoke` chain the same line with their own
108
+ never ask about consent here. `/review` and `/fix` chain the same line with their own
109
109
  phase + results. The `|| true` swallows a **missing** script too, so a half-copied core goes
110
110
  silent rather than loud — `/doctor` check 1 is what catches that.
111
- Then tell the human: run `/smoke $ARGUMENTS` to exercise the feature end-to-end (or test by hand),
112
- then `/review $ARGUMENTS`. Do not run the app or migrations yourself here — `/smoke` is the
113
- sanctioned path for that. **Recommend a `/clear` now** — the spec, contract and diff are all on
111
+ Then tell the human: exercise the feature by hand if it's worth it, then run `/review $ARGUMENTS`.
112
+ Do not run the app or migrations yourself here — building is not running.
113
+ **Recommend a `/clear` now** — the spec, contract and diff are all on
114
114
  disk, and the lead's history is re-sent at input price on every turn it survives.
@@ -21,7 +21,7 @@ fix only with the human's go-ahead (or hand them the command).
21
21
  commands' step files are present — `templates/steps/init-pipeline/` non-empty (a router whose
22
22
  `templates/steps/<cmd>/` dir is missing is a partial/stale install ⇒
23
23
  re-run install/update). **Shipped scripts present and executable** in `<core>/pipeline/scripts/`:
24
- `kanban-move.sh`, `telemetry-send.sh`, `preflight.sh`, `new-feature.sh.template`,
24
+ `kanban-move.sh`, `telemetry-send.sh`, `preflight.sh`, `loop.sh`, `new-feature.sh.template`,
25
25
  `remove-feature.sh.template` — ❌ any missing one. Every caller chains these with `|| true`, so an absent script is a **silent**
26
26
  no-op (no kanban card moves, no telemetry ping, no error anywhere) — this check is the only thing
27
27
  that sees it. Also flag ❌ a `VERSION` **newer than** the other `pipeline/` files (compare mtimes):
@@ -34,9 +34,9 @@ fix only with the human's go-ahead (or hand them the command).
34
34
  its `surfaces[].model` — ❌ if missing, mismatched, or a literal `<SURFACE_MODEL>` placeholder
35
35
  (all three silently fall back to inheriting the lead session's model — often Opus — on every
36
36
  dispatch); ⚠️ any `inherit` with the note that it bills at the lead's tier. The generic agents
37
- (`review.md`, `release.md`, `smoke.md`, `profile-reader.md` — repo or `~/.claude/agents/`) must
38
- each carry their `model:` line too (sonnet/haiku/sonnet/haiku). **Command pins:** every mechanical command file
39
- (`build`, `review`, `fix`, `smoke`, `ship`, `audit`, `refactor`, `doctor`, `align-ds`,
37
+ (`review.md`, `release.md`, `profile-reader.md` — repo or `~/.claude/agents/`) must
38
+ each carry their `model:` line too (sonnet/haiku/haiku). **Command pins:** every mechanical command file
39
+ (`build`, `review`, `fix`, `loop`, `ship`, `audit`, `refactor`, `doctor`, `align-ds`,
40
40
  `update-pipeline` — in `.claude/commands/` or `~/.claude/commands/`) carries `model: sonnet` in
41
41
  its frontmatter — ⚠️ if missing (the lead's orchestration turn then bills at the session model,
42
42
  e.g. Opus/Fable). `brainstorm`, `spec`, and `init-pipeline` are intentionally unpinned
@@ -88,7 +88,7 @@ fix only with the human's go-ahead (or hand them the command).
88
88
  `_`-prefixed files and `specs/refactor-backlog.md`, which `/audit` writes as a backlog, not a
89
89
  spec, and which has no front-matter to check; `shipped` specs
90
90
  with a live worktree flagged (see 6). `.claude/pipeline-metrics.jsonl` and `specs/reports/` (the
91
- `/review`·`/smoke` report buffer that lets a `/fix` survive a `/clear`) are gitignored. Metrics
91
+ `/review` report buffer that lets a `/fix` survive a `/clear`) are gitignored. Metrics
92
92
  belong to the **main checkout** — a `pipeline-metrics.jsonl` inside a live feature worktree is a
93
93
  stale-core sign (its lines die at teardown) ⇒ suggest appending its lines to the main checkout's
94
94
  file and deleting the stray.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  model: sonnet
3
- description: Apply a REVIEW REPORT (or SMOKE failures) — append it to the spec's Remediation, then re-dispatch ONLY the surfaces that have findings.
3
+ description: Apply a REVIEW REPORT — append it to the spec's Remediation, then re-dispatch ONLY the surfaces that have findings.
4
4
  argument-hint: <feature_id> [paste REVIEW REPORT]
5
5
  ---
6
6
 
@@ -16,9 +16,9 @@ that change the *contract*; `/fix` is for everything else.
16
16
 
17
17
  ## 1. Ingest the report
18
18
 
19
- - The report is either pasted after the feature id, the REVIEW REPORT / SMOKE failures from this
20
- session's last `/review` / `/smoke`, or — if the context was cleared — read from
21
- `specs/reports/<id>.md`, where `/review` and `/smoke` stage their last report for exactly this reason.
19
+ - The report is either pasted after the feature id (a REVIEW REPORT, or failures the human hit by
20
+ hand), this session's last `/review` REVIEW REPORT, or — if the context was cleared — read from
21
+ `specs/reports/<id>.md`, where `/review` stages its last report for exactly this reason.
22
22
  If you have none of these, ask for it and wait.
23
23
  - Append each finding to `specs/<id>.md` **`## Remediation`** (same format as `/spec` Mode B, under a
24
24
  dated/numbered subheading): `- [ ] <severity> · <file:line> · <type> · <concrete fix>`. Set
@@ -65,7 +65,7 @@ When the agents return:
65
65
  handoff content — and append ONE metrics line for the batch to `pipeline-metrics.jsonl`
66
66
  (see `/build` §4, `phase: "fix"`), chaining the opt-in usage ping in the same Bash call
67
67
  (results = items fixed over items found across surfaces, e.g. `"5/6"`).
68
- - Tell the human: re-run `/smoke` if the failures were runtime ones, and `/review $ARGUMENTS` for the
68
+ - Tell the human: re-test by hand if the failures were runtime ones, and `/review $ARGUMENTS` for the
69
69
  re-verdict — the re-review is what *verifies* the ticked items actually hold (a regression simply
70
70
  reappears as a new finding in the next round). **Recommend a `/clear`** — all state (spec,
71
71
  checkboxes, staged report) is on disk, and the lead's history is re-sent at input price every turn.
@@ -0,0 +1,61 @@
1
+ ---
2
+ model: sonnet
3
+ description: Autonomous /build → /review → /fix → /review loop for one feature, until no blocking finding remains.
4
+ argument-hint: <feature_id> [--max=N] [--no-build] [--rebuild]
5
+ allowed-tools: Bash(bash ~/.claude/pipeline/scripts/loop.sh:*), Bash(bash .claude/pipeline/scripts/loop.sh:*), Bash(test:*), Read(specs/reports/**)
6
+ disable-model-invocation: true
7
+ ---
8
+
9
+ You are the **launcher**, not the loop. Run the driver for **$ARGUMENTS** and relay three lines.
10
+
11
+ > This command exists because a slash command cannot `/clear` itself. Every phase of the loop runs
12
+ > as a **separate `claude -p` child session** with its own fresh context, driven by a bash script —
13
+ > so the diff, the N review reports and the N contracts never accumulate in YOUR history, which is
14
+ > re-sent at input price on every turn. Running the loop conversationally here would cost more than
15
+ > the loop saves.
16
+
17
+ ## 1. Launch
18
+
19
+ Probe the core, then run the script — ONE Bash call, and let it run to completion:
20
+
21
+ ```
22
+ test -f .claude/pipeline/scripts/loop.sh \
23
+ && bash .claude/pipeline/scripts/loop.sh $ARGUMENTS \
24
+ || bash ~/.claude/pipeline/scripts/loop.sh $ARGUMENTS
25
+ ```
26
+
27
+ Pass `$ARGUMENTS` through untouched — the script owns its own flag parsing (`--max=N`,
28
+ `--no-build`, `--rebuild`) and exits 64 on anything it doesn't know. Don't validate flags yourself,
29
+ don't rewrite them, don't add any.
30
+
31
+ **Never read `specs/reports/<id>.loop.log`.** It holds the full transcript of every child session —
32
+ the entire diff, every review report, every fix handoff. Pulling it into this session re-imports
33
+ exactly the context the loop was built to keep out, and it is the one mistake that turns this
34
+ command into the most expensive one in the pipeline. Point the human at the path instead; they can
35
+ open it in an editor for free. The same goes for the per-surface `.diff` and `.preflight.txt` files.
36
+
37
+ ## 2. Report — three lines, from the exit code
38
+
39
+ The script prints one line per phase and one closing line; that is your raw material. For exit
40
+ **1** or **3** only, also Read `specs/reports/<id>.verdict.json` (small, structured, safe) to name
41
+ the remaining findings — never the markdown report, which is the findings body in full.
42
+
43
+ | exit | meaning | what to say |
44
+ | ---- | ------- | ----------- |
45
+ | `0` | clean | no blocking findings left; the human can `/ship <id>` |
46
+ | `1` | ceiling hit | the fix was progressing but ran out of passes ⇒ re-run with a higher `--max` |
47
+ | `2` | no usable verdict | `/review` produced nothing, or aborted on a red preflight — the closing line says which; point at `specs/reports/<id>.preflight.txt` |
48
+ | `3` | non-convergent | the same blocking findings survived a fix pass; a higher `--max` will NOT help — the human needs to look at them (list them from the verdict) |
49
+ | `64` | usage | relay the script's own message verbatim |
50
+
51
+ Then print exactly three lines and nothing else:
52
+
53
+ ```
54
+ outcome: <one clause — clean / ceiling / no verdict / non-convergent / usage>
55
+ iterations: <n> review pass(es)<, m fix pass(es) committed>
56
+ remaining: <blocking count + one short phrase per blocking item, or "none">
57
+ ```
58
+
59
+ Add at most one follow-up sentence: the next command to run. Never restate a finding's fix, never
60
+ summarize the log, never open the diff. Each fix pass is already committed
61
+ (`loop(<id>): fix pass <i>`) — say so on a non-zero exit, since those commits are the way back.
@@ -28,9 +28,13 @@ with `test -x`); note the epoch (`date +%s`) in the same call — §3's metrics
28
28
 
29
29
  - **Non-zero exit** ⇒ the script already printed the raw last-40 lines. **STOP: relay them verbatim
30
30
  and spawn NO agent** — a compiler/test failure needs `/fix` (or the human), not a review that
31
- rediscovers it at agent prices. This abort is the whole point of the step.
31
+ rediscovers it at agent prices. This abort is the whole point of the step. Before stopping, write
32
+ the **aborted verdict** (§3's contract, degraded form) so an automated driver gets a diagnosis
33
+ rather than silence:
34
+ `{"id":"$ARGUMENTS","phase":"review","ts":"<ISO>","aborted":"preflight","verdict":"BLOCK","blocking":null}`
35
+ → `specs/reports/$ARGUMENTS.verdict.json`. One `printf`, in the same Bash call.
32
36
  - **Zero exit** ⇒ it stamped `.claude/preflight.ok`, which the gate hook checks before letting
33
- `review`/`smoke` dispatches through (SCHEMA.md §Preflight). Continue.
37
+ `review` dispatches through (SCHEMA.md §Preflight). Continue.
34
38
  - Script absent (older core) ⇒ run the three commands yourself, each redirected into
35
39
  `specs/reports/$ARGUMENTS.preflight.txt`, aborting on the first failure the same way.
36
40
 
@@ -91,6 +95,33 @@ merged verdict + total finding count, e.g. `"REVISE:3"`).
91
95
  **Stage the full report to `specs/reports/$ARGUMENTS.md`** (overwrite) — a gitignored buffer so a
92
96
  `/fix` after a `/clear` can still read the findings; the `specs/reports/` subfolder is skipped by the
93
97
  non-recursive `specs/*.md` glob, so it's never mistaken for a spec (no phantom card, no bogus stage).
98
+ **Write the machine-readable verdict** to `specs/reports/$ARGUMENTS.verdict.json` (overwrite) — on
99
+ **every** run, including the small-diff fast path of §2 and a `SHIP`. This file is the ONLY contract
100
+ between the pipeline and an automated driver (`/loop`), which parses no prose:
101
+
102
+ ```json
103
+ { "id": "$ARGUMENTS", "phase": "review", "ts": "<ISO>", "verdict": "REVISE",
104
+ "findings": 7, "blocking": 2, "security": 1,
105
+ "severity": {"CRITICAL": 1, "HIGH": 2, "MEDIUM": 3, "LOW": 1},
106
+ "surfaces": {"backend": {"verdict":"BLOCK","findings":4,"blocking":2}},
107
+ "blocking_items": ["backend|apps/api/src/routes/order.ts|missing authz on post"],
108
+ "fingerprint": "b3f1c2a90d4e5f67" }
109
+ ```
110
+
111
+ - **`blocking` = CRITICAL findings + `security` findings, deduplicated** (a finding that is both
112
+ counts once). That is exactly the agent's existing verdict rule restated as a number, so
113
+ `blocking == 0` ⟺ `verdict == SHIP`. HIGH/MEDIUM/LOW quality findings are **not** blocking —
114
+ they follow the `deferred:<id>` backlog route below, and must never cost a driver an iteration.
115
+ - **`blocking_items`** — one normalized string per blocking finding, `<surface>|<file>|<problem>`:
116
+ the file path **without the `:line`** (a fix that inserts lines shifts every line below it — a
117
+ line-bearing identity would change every pass and the drift detection would never fire), and the
118
+ **problem**, not the fix, cut to its first 8 words, lowercased, every run of non-alphanumerics
119
+ collapsed to one space. Identity of a finding, not its wording.
120
+ - **`fingerprint`** — computed in the same Bash call, never by hand:
121
+ `printf '%s\n' "<item>" … | LC_ALL=C sort | sha256sum | cut -c1-16` (`shasum -a 256` where there
122
+ is no `sha256sum`). Empty list ⇒ `""`. A driver comparing two consecutive fingerprints detects a
123
+ fix loop that is treading water.
124
+
94
125
  In chat print ONLY: the verdict, the severity-count table, a one-line digest of each CRITICAL/security
95
126
  finding, and `Full report: specs/reports/$ARGUMENTS.md` — never echo the findings body into chat (it
96
127
  would sit in this session's history, re-sent every turn). Then:
@@ -98,9 +129,11 @@ would sit in this session's history, re-sent every turn). Then:
98
129
  - **SHIP** → a SHIP verdict *is* the pipeline's statement that the feature meets its Definition of
99
130
  Done, so **tick the DoD**: in `specs/$ARGUMENTS.md` §`Acceptance criteria / DoD`, flip each `- [ ]`
100
131
  → `- [x]` for the criteria the pipeline has actually verified — spec conformance + `ui_language`
101
- copy (this review), tests · lint · typecheck (a green `/build`), mobile-first + runtime flows (a
102
- prior `/smoke`). **Leave `- [ ]` (and say which) any item whose verifying stage didn't run this
103
- cycle** e.g. no `/smoke` the mobile-first / runtime item stays open. Ticking is the lead's job
132
+ copy (this review), tests · lint · typecheck (a green `/build`), mobile-first as far as the code
133
+ shows it (this review). **Leave `- [ ]` (and say which) any item no stage actually verified**
134
+ nothing in the pipeline *runs* the feature, so any criterion that needs the app up (runtime flows,
135
+ a visual check against the design) stays open unless the human says they exercised it by hand and
136
+ it held. Ticking is the lead's job
104
137
  (the reviewer is read-only). **Then stamp the freshness gate** so `/ship` can refuse to ship code
105
138
  edited after this verdict: compute `BASE=$(git merge-base <default_branch> HEAD)` and write into the
106
139
  spec front-matter `reviewed_base: $BASE` plus
@@ -28,7 +28,7 @@ prompt that can never be answered.
28
28
  Two extra duties beyond Bash patterns:
29
29
 
30
30
  - Phase gate (`preflight` block in gate-config.json): a Task dispatch of a
31
- listed subagent_type (default review/smoke) requires a fresh
31
+ listed subagent_type (default review) requires a fresh
32
32
  `.claude/preflight.ok` stamp, written by pipeline/scripts/preflight.sh when
33
33
  typecheck+lint+tests are green. Stale/missing stamp => "ask" — dispatching
34
34
  reviewers onto code that doesn't compile burns their whole run.
@@ -70,7 +70,7 @@ def load_config() -> dict:
70
70
  # Patterns gated ONLY on the default branch — allowed freely on feature branches.
71
71
  "ask_on_default_branch": list(cfg.get("ask_on_default_branch", [])),
72
72
  "default_branch": cfg.get("default_branch", "main") or "main",
73
- # Phase gate: {"enabled": true, "agents": ["review","smoke"], "max_age_minutes": 30}
73
+ # Phase gate: {"enabled": true, "agents": ["review"], "max_age_minutes": 30}
74
74
  "preflight": preflight,
75
75
  }
76
76
 
@@ -130,11 +130,11 @@ def known_heads(cwd: str):
130
130
 
131
131
 
132
132
  def check_preflight(payload: dict, cfg: dict) -> int:
133
- """Phase gate on Task dispatches: review/smoke agents need a green preflight stamp."""
133
+ """Phase gate on Task dispatches: review agents need a green preflight stamp."""
134
134
  pf = cfg.get("preflight") or {}
135
135
  if not pf.get("enabled"):
136
136
  return 0
137
- agents = pf.get("agents") or ["review", "smoke"]
137
+ agents = pf.get("agents") or ["review"]
138
138
  subagent = (payload.get("tool_input") or {}).get("subagent_type", "") or ""
139
139
  if subagent not in agents:
140
140
  return 0
@@ -53,7 +53,7 @@ design_files: [] # design page links — full URLs of the form https://claude.ai
53
53
 
54
54
  > One `###` subsection per surface in `PIPELINE.md` §surfaces (e.g. backend, frontend), each TDD.
55
55
  > Surfaces are subsections of §6, never top-level sections — §8 and §9 are referenced by number
56
- > across the whole pipeline (`/smoke` drives "§8 flows", `/review` checks "§9 acceptance"), so a
56
+ > across the whole pipeline (`/build` implements "§8 flows", `/review` checks "§9 acceptance"), so a
57
57
  > third surface must not become a §8 of its own.
58
58
 
59
59
  ### <surface.key>
@@ -20,7 +20,7 @@ Ask ONLY what you couldn't confidently detect. Batch related questions. Cover:
20
20
  Recommended option: dot/failures-only reporter (`--reporter=dot` vitest/playwright, `--silent`
21
21
  jest, `-q` pytest, `--quiet` eslint/ruff — whatever the detected runner supports). These land in
22
22
  `test_quiet_cmd`/`lint_quiet_cmd` + `commands.test_quiet`/`lint_quiet` and are what agents and the
23
- `/review`·`/smoke` pre-flight actually run (SCHEMA.md §Output discipline). If the human declines or
23
+ `/review` pre-flight actually run (SCHEMA.md §Output discipline). If the human declines or
24
24
  the runner has no such flag, leave `""` — consumers then fall back to `<cmd> 2>&1 | tail -40`.
25
25
  - **Contract** — mechanism (`shared-types-zod` / `openapi` / `protobuf` / `json-schema` / `none`) and
26
26
  where feature contracts are authored. If `none`, surfaces sync by the spec prose alone.
@@ -15,7 +15,7 @@
15
15
  (`<SURFACE_EXTRA_NEVER>`, `<SURFACE_DESIGN_INPUT>`, `<SURFACE_TDD_STEP1>` — fill design-related ones
16
16
  only when `uses_design`).
17
17
  Leave the fixed agents as-is (generic, shipped by the installer): `review.md`, `release.md`,
18
- `smoke.md`, `profile-reader.md`.
18
+ `profile-reader.md`.
19
19
  4. **Generate `.claude/gate-config.json`** from the `gate` block — copy all five keys verbatim:
20
20
  `{"deny": [...], "ask": [...], "ask_on_default_branch": [...], "default_branch": "<vcs.default_branch>",
21
21
  "preflight": {"enabled": <gate.preflight.enabled>, "agents": [...], "max_age_minutes": <n>}}`
@@ -27,7 +27,11 @@
27
27
  variants** and repo-wide `commands.*` equivalents as `Bash(<cmd>:*)` rules, plus read-only git —
28
28
  `Bash(git status:*)`, `Bash(git diff:*)`, `Bash(git log:*)`, `Bash(git rev-parse:*)` — plus the
29
29
  shipped pipeline scripts for BOTH cores (`Bash(.claude/pipeline/scripts/:*)` and
30
- `Bash(~/.claude/pipeline/scripts/:*)` — preflight, kanban-move, telemetry-send), and the
30
+ `Bash(~/.claude/pipeline/scripts/:*)` — preflight, kanban-move, telemetry-send) **plus the
31
+ `bash`-prefixed form the `/loop` driver uses** (`Bash(bash .claude/pipeline/scripts/loop.sh:*)`
32
+ and `Bash(bash ~/.claude/pipeline/scripts/loop.sh:*)`) — those prefix rules match a command
33
+ *starting* with the path, so `bash <path>` needs its own entry or `/loop` stalls on a permission
34
+ prompt at every launch, and the
31
35
  retrieval provider's MCP tools when wired (e.g. `mcp__serena`). Never allowlist anything matching
32
36
  a `gate.ask`/`gate.deny` pattern. Mention the human can widen it later with
33
37
  `/fewer-permission-prompts`) + the hooks, **conditioned on the install mode:**
@@ -86,8 +90,8 @@
86
90
  `build_cmd`s that are non-empty). Derive the setup steps from the detected stack — mirror what a
87
91
  sibling workflow does if one exists. `/ship` watches these checks before the merge.
88
92
  11. **Metrics sink & report buffer:** add `.claude/pipeline-metrics.jsonl` to `.gitignore` — `/build`,
89
- `/review`, `/fix` and `/smoke` append per-dispatch evidence there (SCHEMA §Specialization reads it).
90
- Also add `specs/reports/` — `/review` and `/smoke` stage their last report there so a `/fix` (or
93
+ `/review` and `/fix` append per-dispatch evidence there (SCHEMA §Specialization reads it).
94
+ Also add `specs/reports/` — `/review` stages its last report there so a `/fix` (or
91
95
  `/spec` Mode B) survives a `/clear`; it's a derived buffer, not a versioned artifact.
92
96
  12. **Design system:** if `design.enabled` with a snapshot dir, note that `/align-ds` is active; else the
93
97
  `/align-ds` command will no-op with a clear message.