cohorte 2.2.0 → 2.4.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 +87 -0
- package/README.md +8 -9
- package/bin/cli.js +8 -2
- package/core/adapter/render.js +2 -2
- package/core/agents/implementer.template.md +27 -0
- package/core/agents/release.md +6 -2
- package/core/agents/review.md +35 -4
- package/core/commands/cohorte-audit.md +8 -3
- package/core/commands/cohorte-brainstorm.md +0 -5
- package/core/commands/cohorte-build.md +24 -7
- package/core/commands/cohorte-doctor.md +4 -10
- package/core/commands/cohorte-fix.md +1 -2
- package/core/commands/cohorte-patch.md +113 -0
- package/core/commands/cohorte-review.md +1 -3
- package/core/commands/cohorte-ship.md +8 -9
- package/core/commands/cohorte-spec.md +1 -6
- package/core/commands/cohorte-update-pipeline.md +5 -4
- package/core/templates/patch.template.md +86 -0
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +0 -10
- package/core/templates/steps/init-pipeline/04-write-render.md +2 -2
- package/core/workflows/review.js +3 -5
- package/install.ps1 +3 -1
- package/install.sh +6 -2
- package/package.json +2 -2
- package/profile/PIPELINE.template.md +1 -0
- package/profile/SCHEMA.md +28 -63
- package/profile/cohorte.config.template.yaml +0 -16
- package/scripts/validate-core.mjs +25 -23
- package/scripts/telemetry-send.sh +0 -84
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,93 @@ short, user-facing, most recent first. One `## <version> — <YYYY-MM-DD>` secti
|
|
|
7
7
|
> They are history and are deliberately not rewritten — every command gained a `cohorte-` prefix
|
|
8
8
|
> in 2.0.0.
|
|
9
9
|
|
|
10
|
+
## 2.4.0 — 2026-08-13
|
|
11
|
+
|
|
12
|
+
- **The pipeline froze *what* to build, never *how much*.** A spec pins the contract and the
|
|
13
|
+
acceptance criteria, so the feature was right — but nothing ever asked an implementer whether the
|
|
14
|
+
helper it was about to write already existed three files over, whether the stdlib shipped it, or
|
|
15
|
+
whether the abstraction it was wrapping had exactly one caller. The diff came out correct and
|
|
16
|
+
fatter than it needed to be, and every extra line was then paid twice: once at build, once again at
|
|
17
|
+
`/cohorte-review`, whose cost is linear in the diff it reads.
|
|
18
|
+
|
|
19
|
+
Implementers now walk a **minimality ladder** before inventing anything: does it need to exist at
|
|
20
|
+
all → already in this repo → stdlib/framework → native platform feature → an already-installed
|
|
21
|
+
dependency → a few inline lines → only then the minimum the contract requires. It governs the
|
|
22
|
+
**how**, never the **what** — a contract field, an acceptance criterion, a test, a validation or an
|
|
23
|
+
authz check is out of its reach, by construction. It is bounded to **one lookup per candidate**, so
|
|
24
|
+
it costs a symbol search and not an exploration, and a shortcut kept on purpose surfaces in the
|
|
25
|
+
handoff's `## TODO / not done` with its ceiling and its upgrade trigger, instead of rotting in a
|
|
26
|
+
comment.
|
|
27
|
+
|
|
28
|
+
The `review` agent gains the matching axis — over-engineering, tagged `delete:` / `stdlib:` /
|
|
29
|
+
`native:` / `yagni:` / `shrink:`, always naming the cheaper replacement. It is deliberately the
|
|
30
|
+
weakest thing in the report: **5 findings max, MEDIUM ceiling, never a CRITICAL, never a REVISE,
|
|
31
|
+
never a BLOCK**. A diff whose only findings are `complexity` ships, and they park in the backlog
|
|
32
|
+
like any other nit. Tests, fixtures and anything the contract mandates are out of bounds — coverage
|
|
33
|
+
is not bloat. In audit mode the axis widens to the whole target (10 per domain, biggest cut first,
|
|
34
|
+
closing with `net: -N lines, -M deps possible.`), and `/cohorte-audit` files them under a new
|
|
35
|
+
`complexity` backlog tag: the cheapest wins in the file, since deleting code needs no new test.
|
|
36
|
+
|
|
37
|
+
Doctrine adapted from [ponytail](https://github.com/dietrichgebert/ponytail) (MIT) — the decision
|
|
38
|
+
ladder and the finding tags are theirs; the subordination to the frozen contract and the
|
|
39
|
+
can't-block severity ceiling are what make them safe inside a gated pipeline.
|
|
40
|
+
|
|
41
|
+
Run `/cohorte-update-pipeline` to re-render your surface agents — the ladder lives in the
|
|
42
|
+
implementer template, so it only reaches your agents through a reconcile.
|
|
43
|
+
|
|
44
|
+
## 2.3.0 — 2026-08-12
|
|
45
|
+
|
|
46
|
+
- **A bug fix had to pretend to be a feature.** The only way into the pipeline was
|
|
47
|
+
`/cohorte-spec` — a section-by-section interview whose critical part is a frozen §5 contract, with
|
|
48
|
+
a ~300-line budget. For a `500` on an empty cart that is more work than the fix, so in practice you
|
|
49
|
+
left the pipeline and patched by hand: no frozen intent, no review, no trace.
|
|
50
|
+
|
|
51
|
+
`/cohorte-patch` is the bug-fix entry point. A **triage, not an interview** — repro, expected
|
|
52
|
+
behaviour, what must not change — it locates the cause itself and freezes
|
|
53
|
+
`specs/patch-<slug>.md` (`kind: patch`, ~60 lines). The **§4 regression test replaces §5 CONTRACT**
|
|
54
|
+
as the thing the diff is checked against.
|
|
55
|
+
|
|
56
|
+
Then nothing downstream is special-cased: a patch spec *is* a spec, so `/cohorte-build` →
|
|
57
|
+
`/cohorte-review` → `/cohorte-fix` → `/cohorte-ship` consume it unchanged, one `/clear` between
|
|
58
|
+
each — four short sessions instead of one thread re-sending its own triage history at input price
|
|
59
|
+
every turn. Only three places read `kind: patch`: build §1.6/§2 (judge the repro + test, author no
|
|
60
|
+
contract when §5 is `none`), the `review` agent (scope creep becomes a first-class finding), and
|
|
61
|
+
ship (branch off `vcs.patch_branch_prefix`, `patch` bump by default, `fix(<scope>)` commit).
|
|
62
|
+
|
|
63
|
+
A patch may span **several surfaces** — one bug, one repro, one spec. The single hard escalation:
|
|
64
|
+
a fix needing **new** contract surface area is a feature wearing a bug's clothes, and it is routed
|
|
65
|
+
to `/cohorte-spec` rather than letting two surfaces invent a shape independently.
|
|
66
|
+
|
|
67
|
+
- **Telemetry is gone — all of it.** The opt-in usage pings that shipped through 2.2.0 are removed
|
|
68
|
+
wholesale: the `telemetry-send.sh` sender, the per-phase pings in the six funnel commands, the
|
|
69
|
+
consent question in `/cohorte-init-pipeline`, the `telemetry:` block in the config template, the
|
|
70
|
+
`/cohorte-doctor` consent check, the collector contract in `SCHEMA.md`. Cohorte now sends nothing,
|
|
71
|
+
anywhere.
|
|
72
|
+
|
|
73
|
+
**Upgrading removes what is already on disk.** Copy-over never deletes, so an existing install
|
|
74
|
+
would otherwise keep an executable that still POSTs to the collector — all three installers
|
|
75
|
+
(`install.sh`, `install.ps1`, `npx cohorte`) now scrub it, and CI asserts its absence. And
|
|
76
|
+
`/cohorte-update-pipeline` deletes the leftover `telemetry:` block from
|
|
77
|
+
`~/.claude/cohorte.config.yaml`: nothing reads it any more, and an `enabled: true` left sitting in
|
|
78
|
+
a file you may open reads as though data were still leaving the machine.
|
|
79
|
+
|
|
80
|
+
The OpenTelemetry tip (Claude Code's own metrics export, a collector you point at yourself) is
|
|
81
|
+
gone from `SCHEMA.md` §Measuring cost and the token-economy guide too. `/cost` and
|
|
82
|
+
`pipeline-metrics.jsonl` remain — both entirely local.
|
|
83
|
+
|
|
84
|
+
`validate-core.mjs` gained a ratchet: any mention of telemetry or a usage ping under
|
|
85
|
+
`core/{commands,agents,templates,workflows}` now fails CI, so it cannot creep back in by copying an
|
|
86
|
+
old command file. The single exemption is `/cohorte-update-pipeline` — the command that *deletes*
|
|
87
|
+
the leftovers has to name them — and it is itself checked for never naming a ping, a sender or a
|
|
88
|
+
consent flow.
|
|
89
|
+
|
|
90
|
+
- **Kanban:** `/cohorte-patch` with no argument offers the **Ideas** column, `[patch]`-titled cards
|
|
91
|
+
first, and titles its own card `[patch] <title>` (join key `#patch-<slug>`). The `[<kind>]` prefix
|
|
92
|
+
stays a human convention — nothing parses it.
|
|
93
|
+
|
|
94
|
+
- **Profile:** new optional `vcs.patch_branch_prefix` (default `fix/`); `/cohorte-update-pipeline`
|
|
95
|
+
tops it up, and a profile without it falls back to the same default.
|
|
96
|
+
|
|
10
97
|
## 2.2.0 — 2026-08-12
|
|
11
98
|
|
|
12
99
|
- **The pipeline was Claude Code or nothing.** The doctrine — frozen spec, stateless surfaces, a
|
package/README.md
CHANGED
|
@@ -256,6 +256,7 @@ it in `.claude/pipeline/VERSION` and bundled repos in their committed `pipeline.
|
|
|
256
256
|
| `/cohorte-init-pipeline` | Detect stack → interview → generate the profile + agents. Run once per project. |
|
|
257
257
|
| `/cohorte-brainstorm` | Interactive persona panel that pressure-tests a feature idea. |
|
|
258
258
|
| `/cohorte-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. |
|
|
259
|
+
| `/cohorte-patch [bug]` | Bug-fix entry: triage a bug and freeze a ~60-line patch spec (`specs/patch-<slug>.md`) whose regression test replaces the contract. Then the normal `/cohorte-build → review → ship`. |
|
|
259
260
|
| `/cohorte-build <id>` | Readiness gate on the frozen spec, then the lead authors the contract and dispatches one implementer per surface in parallel. |
|
|
260
261
|
| `/cohorte-review <id>` | Read-only review agents (one per touched surface, parallel) audit the diff vs the spec; out-of-scope findings go to the refactor backlog. |
|
|
261
262
|
| `/cohorte-fix <id>` | Apply a review report: remediation into the spec, re-dispatch only the surfaces with findings. |
|
|
@@ -344,15 +345,13 @@ The essentials:
|
|
|
344
345
|
|
|
345
346
|
Details: `profile/SCHEMA.md` §Workflows.
|
|
346
347
|
|
|
347
|
-
## Privacy
|
|
348
|
+
## Privacy
|
|
348
349
|
|
|
349
|
-
Cohorte
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
the collector's `DELETE /v1/install/<id>` drops it). Full spec + GDPR details:
|
|
355
|
-
`profile/SCHEMA.md` §Telemetry (including the collector API contract).
|
|
350
|
+
Cohorte sends nothing, anywhere. There is no telemetry, no usage pings, no collector — the
|
|
351
|
+
opt-in stats that shipped through 2.2.0 were removed in 2.3.0, sender included. Everything the
|
|
352
|
+
pipeline records (`pipeline-metrics.jsonl`, `specs/reports/`) stays in your repo, and the only
|
|
353
|
+
network calls are the ones you can see: `git`, `gh`, and whatever MCP providers you wired
|
|
354
|
+
yourself.
|
|
356
355
|
|
|
357
356
|
## License
|
|
358
357
|
|
|
@@ -380,7 +379,7 @@ profile/
|
|
|
380
379
|
PIPELINE.template.md # the profile skeleton /cohorte-init-pipeline fills
|
|
381
380
|
SCHEMA.md # field reference
|
|
382
381
|
cohorte.config.template.yaml # seeds ~/.claude/cohorte.config.yaml (kanban)
|
|
383
|
-
scripts/ # worktree-isolation templates + shipped preflight/kanban
|
|
382
|
+
scripts/ # worktree-isolation templates + the shipped preflight/kanban scripts
|
|
384
383
|
dashboard/ # local web cockpit (npx … dashboard) — see dashboard/README.md
|
|
385
384
|
server/ # dependency-free node runtime (serves the built app + JSON/stream API)
|
|
386
385
|
app/ # Vite + React source (built to dashboard/dist/ at publish time)
|
package/bin/cli.js
CHANGED
|
@@ -237,10 +237,10 @@ function copyCore() {
|
|
|
237
237
|
// write where this runtime does not look.
|
|
238
238
|
resolveTemplateConditionals(pipelineDir);
|
|
239
239
|
// Copy the *.template files AND the shipped executables (kanban-move.sh,
|
|
240
|
-
//
|
|
240
|
+
// preflight.sh). Until 1.2.4 this loop took only `.template`, so every
|
|
241
241
|
// `npx cohorte install/update` produced a core missing both scripts — and since
|
|
242
242
|
// every caller chains them with `|| true`, the result was silent: no kanban card
|
|
243
|
-
// moves, no
|
|
243
|
+
// moves, no error. The shell installers named them explicitly
|
|
244
244
|
// and this port drifted. The rule below needs no list to keep in sync: a `<x>.sh`
|
|
245
245
|
// with an `<x>.sh.template` sibling is rendered per-project by /cohorte-init-pipeline, so
|
|
246
246
|
// only the template ships; every other `.sh` is a shipped executable.
|
|
@@ -255,6 +255,12 @@ function copyCore() {
|
|
|
255
255
|
try { fs.chmodSync(target, 0o755); } catch { /* optional */ }
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
|
+
// 2.3.0 removed telemetry. The copy loop above is by-rule, so it simply stops shipping the
|
|
259
|
+
// sender — but copy-over never deletes, and an existing install would keep an executable
|
|
260
|
+
// that still POSTs to the collector. Scrub it. The dead `telemetry:` block in the user's
|
|
261
|
+
// config is not this installer's to parse: /cohorte-update-pipeline deletes it, where an
|
|
262
|
+
// agent can edit the YAML surgically (SCHEMA.md §Reconcile step 5).
|
|
263
|
+
fs.rmSync(path.join(pipelineDir, 'scripts', 'telemetry-send.sh'), { force: true });
|
|
258
264
|
// The per-surface implementer template is rendered per SURFACE later, by
|
|
259
265
|
// /cohorte-init-pipeline inside the target repo — but its runtime shape (frontmatter keys,
|
|
260
266
|
// capability branches, the preamble) is fixed here, at install time. Run it through the
|
package/core/adapter/render.js
CHANGED
|
@@ -196,7 +196,7 @@ function stateDir(runtime) {
|
|
|
196
196
|
return runtime.id === 'claude' ? '.claude' : '.cohorte';
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
// The user-level config (kanban boards, shared vault
|
|
199
|
+
// The user-level config (kanban boards, shared vault). One file per
|
|
200
200
|
// human, not per project or per runtime; the shipped scripts probe the same two paths.
|
|
201
201
|
function configPath(runtime) {
|
|
202
202
|
return runtime.id === 'claude' ? '~/.claude/cohorte.config.yaml' : '~/.cohorte/cohorte.config.yaml';
|
|
@@ -213,7 +213,7 @@ function preamble(runtime, paths, projectRoot, { kind = 'command' } = {}) {
|
|
|
213
213
|
L.push(`> - \`<core>\` = \`${core}\` — the pipeline's shared assets (\`pipeline/scripts/\`, \`pipeline/SCHEMA.md\`, \`templates/\`). Every \`<core>/…\` path below resolves there, and nowhere else.`);
|
|
214
214
|
L.push(`> - \`<state>\` = \`${stateDir(runtime)}/\` in **this repo** — what the pipeline generates for this project (\`gate-config.json\`, \`preflight.ok\`, \`pipeline-metrics.jsonl\`, \`pipeline.json\`). Always project-relative, even when \`<core>\` is global.`);
|
|
215
215
|
L.push(`> - \`<memory>\` = \`${runtime.memory}\` — this runtime's project-instructions file at the repo root, loaded into every session here. Where the doctrine says to reference or extend it, that is the file.`);
|
|
216
|
-
L.push(`> - \`<config>\` = \`${configPath(runtime)}\` — your user-level config (kanban boards, shared vault
|
|
216
|
+
L.push(`> - \`<config>\` = \`${configPath(runtime)}\` — your user-level config (kanban boards, shared vault). One per human, never committed.`);
|
|
217
217
|
|
|
218
218
|
L.push(`> - \`<agents>\` = \`${agentsDir}\` — real subagents. Dispatch: ${runtime.agent.dispatch}.`);
|
|
219
219
|
|
|
@@ -59,6 +59,33 @@ them over Grep/Glob + whole-file Reads**: locate code by symbol, read only the d
|
|
|
59
59
|
and trace references before changing any shared shape. Fall back to Grep/Read only when the retrieval
|
|
60
60
|
tools are unavailable or come up empty.
|
|
61
61
|
|
|
62
|
+
## How you choose what to write — the minimality ladder
|
|
63
|
+
|
|
64
|
+
The spec froze the **what**; this ladder governs only the **how**. It never licenses you to skip a
|
|
65
|
+
contract field, an acceptance criterion, a test, a validation, an authz check or an accessibility
|
|
66
|
+
attribute — those are the *what*, and they are not yours to trim.
|
|
67
|
+
|
|
68
|
+
Before writing any helper, utility, wrapper, abstraction or new dependency, walk down and stop at the
|
|
69
|
+
first hit:
|
|
70
|
+
|
|
71
|
+
1. **Does it need to exist at all?** An abstraction with one implementation, a config nobody sets, a
|
|
72
|
+
layer with one caller — don't write it. The second caller is when it earns its keep.
|
|
73
|
+
2. **Is it already in this repo?** One retrieval/Grep lookup by symbol name, not a survey — you are
|
|
74
|
+
checking, not exploring. Reuse beats re-implementing, and it keeps the convention.
|
|
75
|
+
3. **Is it in the standard library / framework?** Name it and use it.
|
|
76
|
+
4. **Is it a native platform feature?** (CSS, the HTTP layer, the DB, the runtime.) Prefer it over code.
|
|
77
|
+
5. **Is it in a dependency already installed?** Use that one. Adding a dependency for what tiers 3–5
|
|
78
|
+
already ship is a finding at review.
|
|
79
|
+
6. **Can it be a few lines inline?** Then it doesn't need a file, a class, or a name.
|
|
80
|
+
7. Only then: the **minimum implementation that satisfies the contract** — no speculative options, no
|
|
81
|
+
"we'll probably need" parameters, no premature generalisation.
|
|
82
|
+
|
|
83
|
+
Bound the cost: this is at most **one lookup per candidate**, and it applies to code you are inventing —
|
|
84
|
+
never to code the contract dictates. If a step would cost more searching than writing, write it.
|
|
85
|
+
|
|
86
|
+
Something you deliberately kept simple with a known ceiling goes in your handoff `## TODO / not done`
|
|
87
|
+
with its limit and what would trigger the upgrade — not in a comment, and not silently.
|
|
88
|
+
|
|
62
89
|
## How you work — strict TDD (red → green → refactor)
|
|
63
90
|
|
|
64
91
|
<!-- <SURFACE_TDD_STEP1> is a LEAD-IN paragraph, not a numbered item: it is filled only for a
|
package/core/agents/release.md
CHANGED
|
@@ -28,7 +28,9 @@ committing, pushing, and opening the PR. You do **not** write features.
|
|
|
28
28
|
## Your inputs
|
|
29
29
|
|
|
30
30
|
1. The spec path `specs/<id>.md` (title, goal, contract — for the PR body).
|
|
31
|
-
2. `feature_id` and the branch
|
|
31
|
+
2. `feature_id` and the branch — the lead passes it literally in the dispatch; use that, don't
|
|
32
|
+
re-derive it (a `kind: patch` spec branches off `vcs.patch_branch_prefix`, not
|
|
33
|
+
`feature_branch_prefix`).
|
|
32
34
|
3. If `PIPELINE.md` §`release_notes.enabled`, the already-written note at
|
|
33
35
|
`<release_notes.dir>/<release_notes.filename>` — stage it with everything else.
|
|
34
36
|
|
|
@@ -37,7 +39,9 @@ committing, pushing, and opening the PR. You do **not** write features.
|
|
|
37
39
|
1. Sanity-check: `git status`, `git diff --stat`. Confirm you're on the feature branch (not the default
|
|
38
40
|
branch). Confirm no `.env`/secret files staged.
|
|
39
41
|
2. Stage the feature changes and write **conventional commit(s)**: `feat(<scope>): …` / `fix(<scope>): …`,
|
|
40
|
-
body summarizing what shipped, referencing `feature_id`. Scope from the domain.
|
|
42
|
+
body summarizing what shipped, referencing `feature_id`. Scope from the domain. A spec whose
|
|
43
|
+
front-matter carries `kind: patch` is a bug fix — `fix(<scope>): …`, and the body states the
|
|
44
|
+
symptom it stops, not the code it changed. End the commit body with:
|
|
41
45
|
`Co-Authored-By: Claude <noreply@anthropic.com>`
|
|
42
46
|
3. `git push -u origin <branch>` (plain push, no force).
|
|
43
47
|
4. Open the PR against `vcs.default_branch`:
|
package/core/agents/review.md
CHANGED
|
@@ -47,6 +47,13 @@ be precise and self-contained.
|
|
|
47
47
|
1. **Spec conformance (highest priority).** Does the implementation match the frozen contract exactly —
|
|
48
48
|
every endpoint/interface (method, path, auth, request/response shape, status codes, error cases) and
|
|
49
49
|
every acceptance criterion? Any deviation is a finding. Cross-surface calls must match the contract.
|
|
50
|
+
**On a `kind: patch` spec** (front-matter) the contract is usually `none` and this check reads
|
|
51
|
+
differently: the diff must (a) contain the §4 regression test, asserting what §4 says it asserts,
|
|
52
|
+
(b) actually address the §1 repro through the §3 cause — a fix that suppresses the symptom
|
|
53
|
+
elsewhere is a finding, not a fix — and (c) stay inside §7 Out of scope. **Scope creep is a
|
|
54
|
+
first-class finding on a patch**: a correct, tidy improvement the spec did not ask for still
|
|
55
|
+
widens the blast radius of a change that is shipping fast, so report it rather than waving it
|
|
56
|
+
through. Genuinely-good-but-out-of-scope work is a **deferred** finding, exactly as elsewhere.
|
|
50
57
|
2. **Correctness.** Logic bugs, unhandled errors, validation gaps, auth holes, data exposure.
|
|
51
58
|
3. **Security.** Authz on every entry point, input validation, no secret/PII leakage, no injection.
|
|
52
59
|
A security vulnerability ⇒ verdict **BLOCK**.
|
|
@@ -56,6 +63,20 @@ be precise and self-contained.
|
|
|
56
63
|
additive `sm:/md:/lg:`, no fixed widths that break on mobile. (You can't render; judge from the code.)
|
|
57
64
|
7. **TDD coverage.** Each surface's tests cover its slice of the contract (statuses, validation, auth,
|
|
58
65
|
behavior). Flag untested contract surface.
|
|
66
|
+
8. **Over-engineering (lowest priority, never blocking).** Code the diff *added* that didn't need to
|
|
67
|
+
exist. Tag each one and always name the replacement — a finding with no cheaper alternative is an
|
|
68
|
+
opinion, not a finding:
|
|
69
|
+
- `delete:` dead code, unused flexibility, a speculative feature nothing calls. Replacement: nothing.
|
|
70
|
+
- `stdlib:` hand-rolled thing the standard library or framework ships. Name the function.
|
|
71
|
+
- `native:` a dependency or code doing what the platform already does. Name the feature.
|
|
72
|
+
- `yagni:` abstraction with one implementation, config nobody sets, layer with one caller.
|
|
73
|
+
- `shrink:` same behaviour, materially fewer lines. Name the shorter form.
|
|
74
|
+
|
|
75
|
+
**Hard limits on this axis.** It is capped at **5 findings**, biggest cut first, and its severity
|
|
76
|
+
ceiling is **MEDIUM** — it can never produce CRITICAL, never REVISE, never BLOCK. Test code,
|
|
77
|
+
fixtures and anything the contract or an acceptance criterion mandates are **out of bounds**:
|
|
78
|
+
coverage is not bloat, and "simpler" is never a reason to drop a spec'd behaviour. Deduplication
|
|
79
|
+
that would cross a surface boundary is out of bounds too — that's an architecture call, not a review one.
|
|
59
80
|
|
|
60
81
|
## Language checks (apply only those matching the surfaces under review)
|
|
61
82
|
|
|
@@ -82,10 +103,19 @@ entry point / module with **no test**), and the lint/format/type debt staged at
|
|
|
82
103
|
dispatch names (`specs/reports/audit-gates.txt`). Emit a **prioritized refactor backlog grouped by
|
|
83
104
|
domain** (same finding-line shape) instead of a SHIP/REVISE/BLOCK verdict.
|
|
84
105
|
|
|
106
|
+
In audit mode the over-engineering axis (§8) widens: there is no diff, so the whole target is in
|
|
107
|
+
scope and the 5-finding cap lifts to **10 per domain**, ranked biggest cut first. Hunt the usual
|
|
108
|
+
shapes — deps the stdlib or platform already ships, single-implementation interfaces, factories with
|
|
109
|
+
one product, wrappers that only delegate, dead flags and config, hand-rolled stdlib. Close the
|
|
110
|
+
audit-mode report with one line: `net: -<N> lines, -<M> deps possible.` (`0`/`0` is a valid answer —
|
|
111
|
+
say it rather than inventing cuts).
|
|
112
|
+
|
|
85
113
|
## Severity & verdict
|
|
86
114
|
|
|
87
115
|
- **CRITICAL** — spec violation or correctness bug that must be fixed ⇒ verdict **REVISE**.
|
|
88
116
|
- **HIGH / MEDIUM / LOW** — quality/convention issues; note them.
|
|
117
|
+
- **Over-engineering (§8) caps at MEDIUM** and never drives the verdict — a diff whose only findings
|
|
118
|
+
are `complexity` ships. It is a cleanup signal, not a gate.
|
|
89
119
|
- Any **security vulnerability** ⇒ verdict **BLOCK**.
|
|
90
120
|
- No CRITICAL and no security issue ⇒ verdict **SHIP**.
|
|
91
121
|
|
|
@@ -110,7 +140,7 @@ they are **never lost and never cost a fix loop**.
|
|
|
110
140
|
## Your return — the REVIEW REPORT, exactly this shape
|
|
111
141
|
|
|
112
142
|
Every finding must be **self-sufficient for a stateless agent**: `file:line` · severity ·
|
|
113
|
-
`spec-violation | quality | security` · one concrete suggested fix — it gets appended verbatim to the
|
|
143
|
+
`spec-violation | quality | security | complexity` · one concrete suggested fix — it gets appended verbatim to the
|
|
114
144
|
spec's `## Remediation`. Your final message **is** the report. **The shape is capped:** at most
|
|
115
145
|
**20 findings**, ONE line each, **zero code excerpts** (the diff and the source are on disk — a
|
|
116
146
|
`file:line` is enough for a stateless fixer). More than 20? Keep every CRITICAL/HIGH/security
|
|
@@ -133,12 +163,13 @@ Verdict: <SHIP | REVISE | BLOCK>
|
|
|
133
163
|
|
|
134
164
|
## Findings
|
|
135
165
|
|
|
136
|
-
- **[<SEVERITY>]** `<file>:<line>` · <spec-violation|quality|security> · <problem> → **Fix:** <concrete change>
|
|
137
|
-
(order by severity; "None." if none; max 20 lines, one per finding, no code excerpts
|
|
166
|
+
- **[<SEVERITY>]** `<file>:<line>` · <spec-violation|quality|security|complexity> · <problem> → **Fix:** <concrete change>
|
|
167
|
+
(order by severity; "None." if none; max 20 lines, one per finding, no code excerpts.
|
|
168
|
+
`complexity` lines carry their §8 tag in the problem — `yagni: <what>` — and cap at 5)
|
|
138
169
|
|
|
139
170
|
## Deferred
|
|
140
171
|
|
|
141
|
-
- **[<SEVERITY>]** `<file>:<line>` · <quality|security> · <problem> → **Fix:** <concrete change> · out of scope: <why this feature does not own it>
|
|
172
|
+
- **[<SEVERITY>]** `<file>:<line>` · <quality|security|complexity> · <problem> → **Fix:** <concrete change> · out of scope: <why this feature does not own it>
|
|
142
173
|
(real but out of this feature's scope — see §Deferred; worst first; "None." if none; max 10 lines)
|
|
143
174
|
|
|
144
175
|
## Notes
|
|
@@ -32,8 +32,9 @@ bulk never sits in your history, then grep it for the `file:line` of every failu
|
|
|
32
32
|
|
|
33
33
|
Dispatch `review` (read-only; static prompt first, variable slot last — prompt-cache prefix):
|
|
34
34
|
"Audit a target against `PIPELINE.md` (no spec — **audit mode**). Check conventions (§Conventions
|
|
35
|
-
per surface), TDD coverage (untested entry points / modules per surface),
|
|
36
|
-
|
|
35
|
+
per surface), TDD coverage (untested entry points / modules per surface), over-engineering (§8,
|
|
36
|
+
widened per your audit-mode rules — ranked biggest cut first, closing `net:` line), and — if the
|
|
37
|
+
profile enables them — mobile-first + design-system usage. Mechanical findings from the gates: read
|
|
37
38
|
`specs/reports/audit-gates.txt`. Emit a prioritized refactor backlog (capped finding-line format
|
|
38
39
|
from your instructions), grouped by domain (one group per surface + shared). — Target: `$ARGUMENTS`
|
|
39
40
|
(default: whole repo)."
|
|
@@ -42,7 +43,11 @@ from your instructions), grouped by domain (one group per surface + shared). —
|
|
|
42
43
|
|
|
43
44
|
Merge mechanical + convention findings into one prioritized backlog and **write
|
|
44
45
|
`specs/refactor-backlog.md`**, grouped by domain, each item:
|
|
45
|
-
`- [ ] <SEVERITY> · <file:line> · <rule|tdd|lint|format|type|security> · <concrete fix>`
|
|
46
|
+
`- [ ] <SEVERITY> · <file:line> · <rule|tdd|lint|format|type|security|complexity> · <concrete fix>`
|
|
47
|
+
|
|
48
|
+
`complexity` items are the over-engineering cuts, ranked biggest first inside their domain — they are
|
|
49
|
+
the cheapest wins in the backlog (deleting code needs no new tests) but they never outrank a
|
|
50
|
+
security, correctness or `tdd` item.
|
|
46
51
|
|
|
47
52
|
**Carry over the deferred items** before overwriting: `grep -n 'deferred:' specs/refactor-backlog.md`
|
|
48
53
|
and re-emit every **open** (`- [ ]`) match verbatim under its domain, tag included. Those lines were
|
|
@@ -64,8 +64,3 @@ downstream. Then, in this order:
|
|
|
64
64
|
2. `<core>/pipeline/scripts/kanban-move.sh auto <feature_id> brainstorm --title "<human title>"` —
|
|
65
65
|
which moves the (now tagged) card, or creates one under `--title` if the human typed a fresh
|
|
66
66
|
idea. Read its output: `moved #…` or `kanban: <reason>`. Never assume either.
|
|
67
|
-
|
|
68
|
-
**Telemetry:** chain the opt-in usage ping onto that same Bash call — `/cohorte-build` §4's shared form,
|
|
69
|
-
`<phase>` = `brainstorm`, `<seconds>` = `0` (this phase is human thinking time, not pipeline
|
|
70
|
-
wall-clock — the funnel signal is the event, not its duration), `<results>` = `""`. No board ⇒ still
|
|
71
|
-
ping, in its own `|| true` call. Silent no-op without consent; never ask about consent here.
|
|
@@ -26,6 +26,11 @@ You are the **lead**. Build feature **$ARGUMENTS** from its frozen spec.
|
|
|
26
26
|
`/cohorte-spec` first. Only then read the body, selectively: front-matter, §5 contract, the surface
|
|
27
27
|
task sections, and `## Remediation` (fall back to a full read if the spec doesn't follow the
|
|
28
28
|
template's headings).
|
|
29
|
+
- **`kind: patch` in the front-matter ⇒ this is a bug fix** frozen by `/cohorte-patch`, and it is
|
|
30
|
+
built by this command like any other spec, with two differences called out where they apply: §1.6
|
|
31
|
+
judges it against its regression test instead of a contract, and §2 authors no contract unless §5
|
|
32
|
+
carries a delta. Everything else — surface mapping, parallel dispatch, roll call, metrics — is
|
|
33
|
+
identical, and `kind` absent means feature, so nothing changes for existing specs.
|
|
29
34
|
- **Route check** — if `## Remediation` has open `- [ ]` items and none requires a contract change,
|
|
30
35
|
stop and tell the human to run `/cohorte-fix $ARGUMENTS` instead: it re-dispatches only the surfaces with
|
|
31
36
|
findings. A full build with open items is only right when the contract change ripples into clean
|
|
@@ -79,6 +84,13 @@ good idea (that was `/cohorte-brainstorm`), never by re-reading files you don't
|
|
|
79
84
|
request fields with types + validation, the success shape, and its error cases. A missing
|
|
80
85
|
**request or success shape** ⇒ `NOT-READY` (an implementer would invent it, and the other surface
|
|
81
86
|
would invent a different one). A missing **error case** ⇒ `RESERVATIONS`.
|
|
87
|
+
**On a `kind: patch` spec this check is replaced, not skipped** — a patch has no contract to be
|
|
88
|
+
complete, so judge §1 Symptom & repro + §4 Regression test instead: no stated expected behaviour,
|
|
89
|
+
or a §4 that names no test and no reason the fix would be verifiable ⇒ `NOT-READY` (an implementer
|
|
90
|
+
would fix whatever it guessed the bug was, and nothing would catch a wrong guess). A repro
|
|
91
|
+
explicitly frozen as a hypothesis, or a cause left to the implementer to find, is
|
|
92
|
+
`RESERVATIONS` — normal for a patch, never a blocker. Then judge §5 as above **only** if it
|
|
93
|
+
carries a delta rather than `none`.
|
|
82
94
|
2. **Surface coverage** — every §6 task maps to a surface in the reconciled list, and every contract
|
|
83
95
|
entry has an owner **on each side it names** (producer and consumer). A contract entry no surface
|
|
84
96
|
owns ⇒ `NOT-READY`.
|
|
@@ -103,7 +115,8 @@ this gate and any automated driver, which parses no prose:
|
|
|
103
115
|
```
|
|
104
116
|
|
|
105
117
|
- **`gaps`** — one normalized string per gap, `<check>|<where>|<what is missing>`: `<check>` is
|
|
106
|
-
`contract` · `coverage` · `dependency` · `ambiguity` · `design
|
|
118
|
+
`contract` · `coverage` · `dependency` · `ambiguity` · `design` — plus `repro` on a `kind: patch`
|
|
119
|
+
spec, for a gap check 1 raised against §1/§4; `<where>` is the contract entry,
|
|
107
120
|
surface key or dependency name (no `:line` — it shifts on every edit); `<what>` is the gap, not the
|
|
108
121
|
fix. `READY` ⇒ `[]`.
|
|
109
122
|
- **`NOT-READY` ⇒ STOP: author no contract and spawn NO agent.** Print the gaps and send the human to
|
|
@@ -117,6 +130,15 @@ this gate and any automated driver, which parses no prose:
|
|
|
117
130
|
|
|
118
131
|
## 2. Author the contract (lead-only — the single sync channel)
|
|
119
132
|
|
|
133
|
+
_Skipped entirely on a `kind: patch` spec whose §5 Contract delta is `none`_ — which is the usual
|
|
134
|
+
case: a bug fix corrects behaviour behind a shape that already exists, and re-authoring that shape
|
|
135
|
+
would put the contract file in the diff for nothing. Say you skipped it and why, in one line. A patch
|
|
136
|
+
whose §5 **does** carry a delta is authored exactly as below, from the delta, against the existing
|
|
137
|
+
file — never rewritten from scratch. (A patch needing *new* contract surface area never reaches here:
|
|
138
|
+
`/cohorte-patch` §3 routes it to `/cohorte-spec`.) When you skip, still run `date +%s` on its own —
|
|
139
|
+
the skipped postcondition is where §4's wall-clock start comes from, and a build with no start epoch
|
|
140
|
+
writes a metrics line with no duration.
|
|
141
|
+
|
|
120
142
|
_Only if `contract.enabled`._ From §5 of the spec, write/update the feature's contract file at
|
|
121
143
|
`<contract.path>/$ARGUMENTS.<contract.ext>` in the profile's `mechanism` (e.g. Zod v4 schemas + inferred
|
|
122
144
|
types for `shared-types-zod`). Export it from `contract.index` if set. This is the ONLY file the agents
|
|
@@ -186,12 +208,7 @@ anything went wrong. In the same call write the machine-readable batch result to
|
|
|
186
208
|
`specs/reports/$ARGUMENTS.build.json` (overwrite) — the channel an automated driver reads, since it
|
|
187
209
|
never sees your chat:
|
|
188
210
|
`{"id":"$ARGUMENTS","phase":"build","ts":"<ISO>","surfaces":{"<key>":"ok|error|dead",…},"dead":["<key>",…]}`
|
|
189
|
-
— this is the evidence SCHEMA.md §Specialization asks for before splitting a surface.
|
|
190
|
-
Bash call, chain the opt-in usage ping — **the shared form every phase command reuses**:
|
|
191
|
-
`<core>/pipeline/scripts/telemetry-send.sh <phase> "$ARGUMENTS" <seconds> "<results>" || true` — a silent no-op unless the human explicitly consented (SCHEMA.md §Telemetry);
|
|
192
|
-
never ask about consent here. `/cohorte-review` and `/cohorte-fix` chain the same line with their own
|
|
193
|
-
phase + results. The `|| true` swallows a **missing** script too, so a half-copied core goes
|
|
194
|
-
silent rather than loud — `/cohorte-doctor` check 1 is what catches that.
|
|
211
|
+
— this is the evidence SCHEMA.md §Specialization asks for before splitting a surface.
|
|
195
212
|
Then tell the human: exercise the feature by hand if it's worth it, then run `/cohorte-review $ARGUMENTS` —
|
|
196
213
|
unless a surface is dead, in which case say so first and let them decide whether to re-run `/cohorte-build`
|
|
197
214
|
(a dead surface has no findings, so `/cohorte-fix` has nothing to re-dispatch).
|
|
@@ -30,10 +30,10 @@ fix only with the human's go-ahead (or hand them the command).
|
|
|
30
30
|
commands' step files are present — `templates/steps/init-pipeline/` non-empty (a router whose
|
|
31
31
|
`templates/steps/<cmd>/` dir is missing is a partial/stale install ⇒
|
|
32
32
|
re-run install/update). **Shipped scripts present and executable** in `<core>/pipeline/scripts/`:
|
|
33
|
-
`kanban-move.sh`, `
|
|
33
|
+
`kanban-move.sh`, `preflight.sh`,
|
|
34
34
|
`new-feature.sh.template`, `remove-feature.sh.template` — ❌ any missing one.
|
|
35
35
|
Every caller chains these with `|| true`, so an absent script is a **silent**
|
|
36
|
-
no-op (no kanban card moves, no
|
|
36
|
+
no-op (no kanban card moves, no error anywhere) — this check is the only thing
|
|
37
37
|
that sees it. Also flag ❌ a `VERSION` **newer than** the other `pipeline/` files (compare mtimes):
|
|
38
38
|
a version bumped without a full re-copy is a half-done update ⇒ re-run install/update.
|
|
39
39
|
2. **Profile.** `PIPELINE.md` exists and its `yaml pipeline-profile` block parses. Every
|
|
@@ -51,7 +51,7 @@ fix only with the human's go-ahead (or hand them the command).
|
|
|
51
51
|
(`build`, `review`, `fix`, `ship`, `audit`, `refactor`, `doctor`, `align-ds`,
|
|
52
52
|
`update-pipeline` — in `<commands>/`) carries `model: sonnet` in
|
|
53
53
|
its frontmatter — ⚠️ if missing (the lead's orchestration turn then bills at the session model,
|
|
54
|
-
e.g. Opus/Fable). `brainstorm`, `spec`, and `init-pipeline` are intentionally unpinned
|
|
54
|
+
e.g. Opus/Fable). `brainstorm`, `spec`, `patch`, and `init-pipeline` are intentionally unpinned
|
|
55
55
|
(interactive — they inherit the session model).
|
|
56
56
|
<!-- cohorte:endif -->
|
|
57
57
|
|
|
@@ -96,13 +96,7 @@ fix only with the human's go-ahead (or hand them the command).
|
|
|
96
96
|
When ≥2 slots are live, print the parallel-feature table (feature · worktree · ports · db ·
|
|
97
97
|
branch behind main by N commits) — a worktree far behind main means its next review will diff
|
|
98
98
|
against stale code ⇒ suggest rebasing it.
|
|
99
|
-
7. **
|
|
100
|
-
status in one line: `disabled` / `enabled since <consent_date> · install_id <id> · endpoint <url>`
|
|
101
|
-
(the install_id is the human's GDPR erasure key — see SCHEMA.md §Telemetry). Flag ❌ any
|
|
102
|
-
incoherent state: `enabled: true` with no `install_id` or no `consent_date` (sending without
|
|
103
|
-
recorded consent — fix: set `enabled: false` until the consent question is re-run), or a
|
|
104
|
-
`telemetry:` block missing entirely on a current core (top up via `/cohorte-update-pipeline`).
|
|
105
|
-
7b. **Kanban** (the board mirror — SCHEMA.md §Kanban). Run
|
|
99
|
+
7. **Kanban** (the board mirror — SCHEMA.md §Kanban). Run
|
|
106
100
|
`<core>/pipeline/scripts/kanban-move.sh --check` and report its one line verbatim: the resolved
|
|
107
101
|
board path, or the exact link that is missing. A board mirror is optional, so "not configured" is
|
|
108
102
|
ℹ️, never ❌ — but it must be **stated**, because the whole class of bug here is a card that
|
|
@@ -72,8 +72,7 @@ When the agents return:
|
|
|
72
72
|
handoff content — and append ONE metrics line for the batch to the **main checkout's**
|
|
73
73
|
`$(dirname "$(git rev-parse --git-common-dir)")/<state>/pipeline-metrics.jsonl` (never a bare
|
|
74
74
|
relative path: from a feature worktree that writes a stray sink whose lines die at teardown)
|
|
75
|
-
(rules in `/cohorte-build` §4, `phase: "fix"`)
|
|
76
|
-
(results = items fixed over items found across surfaces, e.g. `"5/6"`).
|
|
75
|
+
(rules in `/cohorte-build` §4, `phase: "fix"`).
|
|
77
76
|
- Tell the human: re-test by hand if the failures were runtime ones, and `/cohorte-review $ARGUMENTS` for the
|
|
78
77
|
re-verdict — the re-review is what *verifies* the ticked items actually hold (a regression simply
|
|
79
78
|
reappears as a new finding in the next round). **Recommend a `/clear`** — all state (spec,
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Triage a bug and freeze a minimal patch spec — the cheap entry into the pipeline for a fix, no brainstorm, no contract.
|
|
3
|
+
argument-hint: [bug description / stack trace / issue text — or empty to pick from the kanban]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You run the **patch triage** in the main thread — interactive, with the human. This is the bug-fix
|
|
7
|
+
entry point: it produces `specs/patch-<slug>.md` and stops. Everything after it is the normal
|
|
8
|
+
pipeline (`/cohorte-build` → `/cohorte-review` → `/cohorte-fix`* → `/cohorte-ship`), unchanged — the
|
|
9
|
+
patch spec is a spec, so those commands consume it as-is. Splitting it this way is the point: each
|
|
10
|
+
phase is a fresh session with the artifact on disk, instead of one long thread paying for the whole
|
|
11
|
+
cycle at input price on every turn.
|
|
12
|
+
|
|
13
|
+
Bug (may be empty): **$ARGUMENTS**
|
|
14
|
+
|
|
15
|
+
> Read `PIPELINE.md` §`pipeline-profile` first: `surfaces` (paths — this is what you map the bug onto)
|
|
16
|
+
> and `vcs`. _Skip the re-read if it's already in your context this session and unmodified since._
|
|
17
|
+
> Do NOT read `specs/_decisions.md`: a bug fix decides nothing transverse, and the journal is for
|
|
18
|
+
> decisions that outlive a feature.
|
|
19
|
+
>
|
|
20
|
+
> **Kanban** (SCHEMA.md §Kanban): two moves, and their **order is load-bearing** — §1 settles the
|
|
21
|
+
> slug and moves the card to `spec`, §4 moves it to `ready` at freeze. Both are one call to
|
|
22
|
+
> `<core>/pipeline/scripts/kanban-move.sh auto patch-<slug> <stage> [--title "[patch] <human title>"]`.
|
|
23
|
+
> The slug is settled first precisely because the move needs it: a card cannot be joined on an id
|
|
24
|
+
> that doesn't exist yet. `auto` resolves the board from the config itself and exits 0
|
|
25
|
+
> with a `kanban: <reason>` line when there is none — so **never decide "no board is configured"
|
|
26
|
+
> without running it**. Reading the Ideas column at §1 still needs the board path: get it from a
|
|
27
|
+
> `kanban-move.sh` run, or grep the config for `boards[<PIPELINE name>]`.
|
|
28
|
+
|
|
29
|
+
## 1. Get the bug
|
|
30
|
+
|
|
31
|
+
If `$ARGUMENTS` is non-empty, restate it in one line and confirm you've got it.
|
|
32
|
+
|
|
33
|
+
If it is empty: when a board is configured and its **Ideas** column has cards, list them (with any
|
|
34
|
+
sub-bullet notes as context) — **cards titled `[patch]` first**, since those are the ones a human
|
|
35
|
+
filed as bugs — and let the human pick one. Otherwise ask **"What's broken?"**. Either way, wait.
|
|
36
|
+
|
|
37
|
+
Then **settle the id, before anything moves.** Derive `<slug>` (kebab-case, from the symptom —
|
|
38
|
+
`500-on-empty-cart`, not `bug-42`) and confirm it. The `feature_id` is **`patch-<slug>`**, prefix
|
|
39
|
+
included: it is the join key for the kanban card, the spec filename, the branch and every later
|
|
40
|
+
command, so the prefix is part of the id itself, not decoration on the card.
|
|
41
|
+
|
|
42
|
+
**Kanban, in this order:**
|
|
43
|
+
|
|
44
|
+
1. **If the human picked an Ideas card, tag it FIRST.** Ideas cards are free text with no
|
|
45
|
+
`#<feature_id>`, and the move script joins on that tag: move before tagging and it finds nothing,
|
|
46
|
+
creates a second card, and strands the untagged original in Ideas forever. One targeted Edit
|
|
47
|
+
appending ` #patch-<slug>` to that line, located by `grep -n` — never a full board read.
|
|
48
|
+
2. `<core>/pipeline/scripts/kanban-move.sh auto patch-<slug> spec --title "[patch] <human title>"` —
|
|
49
|
+
which moves the (now tagged) card, or creates one under `--title` if the human typed a fresh bug.
|
|
50
|
+
Report what it printed — `moved #…` or `kanban: <reason>` — never a guess about which happened.
|
|
51
|
+
|
|
52
|
+
## 2. Triage — three questions, not an interview
|
|
53
|
+
|
|
54
|
+
`/cohorte-spec` walks a template section by section because a feature has to be *designed*. A bug is
|
|
55
|
+
already specified by reality; your job is to pin it down, not to explore it. Ask only what you
|
|
56
|
+
genuinely cannot infer from the input, batched into ONE message:
|
|
57
|
+
|
|
58
|
+
1. **Repro** — the shortest deterministic path to the symptom. No repro ⇒ ask whether they want you
|
|
59
|
+
to go find one first (a diagnosis session, no spec) or to freeze it as a hypothesis and let the
|
|
60
|
+
implementer confirm. Never invent a repro to fill the section.
|
|
61
|
+
2. **Expected behaviour** — often the whole spec. "It should 404, not 500" is a complete contract.
|
|
62
|
+
3. **Blast radius** — what must NOT change. This becomes §7 Out of scope, and it is what stops a fix
|
|
63
|
+
from becoming a refactor.
|
|
64
|
+
|
|
65
|
+
Then locate it yourself — do not make the human do it. Use the retrieval provider if one is wired,
|
|
66
|
+
else grep for the symptom's strings/identifiers. Read only the files the trail actually names.
|
|
67
|
+
Report the suspected `file:line` in one line and let them confirm or correct it.
|
|
68
|
+
|
|
69
|
+
## 3. Map it onto surfaces — as many as it takes
|
|
70
|
+
|
|
71
|
+
Match the suspected cause and the fix's blast radius against `surfaces[].path`. **A patch is not
|
|
72
|
+
capped at one surface**: a bug that spans an API validator and the form feeding it is one bug with
|
|
73
|
+
one repro, and splitting it into two specs would give each half a contract it doesn't have. List the
|
|
74
|
+
surfaces you're claiming, one line each, with why.
|
|
75
|
+
|
|
76
|
+
**The one escalation that is not a judgment call:** if the fix needs **new** contract surface area —
|
|
77
|
+
a new endpoint, a new shared type, a new field crossing surfaces — stop. That is a feature wearing a
|
|
78
|
+
bug's clothes, and §5 is the only channel that keeps two surfaces in agreement about a shape that
|
|
79
|
+
doesn't exist yet. Say so plainly and send the human to `/cohorte-spec`. Changing an **existing**
|
|
80
|
+
contract entry is fine: describe the delta in §5 and continue.
|
|
81
|
+
|
|
82
|
+
## 4. Freeze the patch spec
|
|
83
|
+
|
|
84
|
+
Write `specs/patch-<slug>.md` from `<core>/templates/patch.template.md` with `status: frozen`, filled
|
|
85
|
+
from §§1–3. **Create the file — do not ask the human to.** Budget: **~60 lines**. A patch spec that
|
|
86
|
+
wants 200 is a feature or a refactor — say which and route it (`/cohorte-spec`, or an item on
|
|
87
|
+
`specs/refactor-backlog.md`).
|
|
88
|
+
|
|
89
|
+
Two sections carry the weight, and both are cheap to get wrong:
|
|
90
|
+
|
|
91
|
+
- **§4 Regression test** — name the test file and what it asserts. It replaces §5 as the thing the
|
|
92
|
+
reviewer checks the diff against, so "add a test" is not enough: say what fails today and why. A
|
|
93
|
+
patch whose test can only be written after the cause is found says exactly that.
|
|
94
|
+
- **§5/§9 keep the feature spec's numbers** (contract delta, acceptance). `review.md` and
|
|
95
|
+
`implementer.template.md` name "contract §5, acceptance §9" verbatim — renumbering them here would
|
|
96
|
+
silently point both agents at the wrong section. The template already does this; don't "fix" it.
|
|
97
|
+
|
|
98
|
+
**Postcondition:** `grep -q '^status: frozen' specs/patch-<slug>.md` — if it fails the freeze didn't
|
|
99
|
+
land; fix it before pointing the human at `/cohorte-build`. Chain the second kanban move onto that
|
|
100
|
+
same Bash call — `kanban-move.sh auto patch-<slug> ready` (the card is already tagged and on the
|
|
101
|
+
board from §1, so this one needs no `--title`) — and report what it printed: `moved #…` or
|
|
102
|
+
`kanban: <reason>`, never a guess.
|
|
103
|
+
|
|
104
|
+
## 5. Hand off
|
|
105
|
+
|
|
106
|
+
Print the spec path, the surfaces, and the branch to cut: `<prefix>patch-<slug>`, where `<prefix>` is
|
|
107
|
+
`vcs.patch_branch_prefix` — falling back to `fix/` on a profile that predates the key. If `isolation.enabled`
|
|
108
|
+
and the fix is big enough to want its own worktree, mention `scripts/new-feature.sh patch-<slug>`;
|
|
109
|
+
most patches don't need it.
|
|
110
|
+
|
|
111
|
+
Then: `/cohorte-build patch-<slug>` — and **recommend a `/clear` first**. The spec is on disk; the
|
|
112
|
+
lead's triage history is re-sent at input price on every turn it survives, and `/cohorte-build`
|
|
113
|
+
re-reads everything it needs.
|
|
@@ -118,8 +118,6 @@ re-ordered by severity, counts summed, duplicates collapsed, verdict = the worst
|
|
|
118
118
|
routes them. Append ONE metrics line for the batch to the **main checkout's**
|
|
119
119
|
`$(dirname "$(git rev-parse --git-common-dir)")/<state>/pipeline-metrics.jsonl` (rules in
|
|
120
120
|
`/cohorte-build` §4; never a bare relative path — from a worktree that strands the lines): `{"ts":"<ISO>","feature":"$ARGUMENTS","phase":"review","seconds":<wall-clock>,"surfaces":{"<key>":"<verdict>:<finding count>",…}}`.
|
|
121
|
-
In the same Bash call, chain the opt-in usage ping (`/cohorte-build` §4, `phase: "review"`, results = the
|
|
122
|
-
merged verdict + total finding count, e.g. `"REVISE:3"`).
|
|
123
121
|
**Stage the full report to `specs/reports/$ARGUMENTS.md`** (overwrite) — a gitignored buffer so a
|
|
124
122
|
`/cohorte-fix` after a `/clear` can still read the findings; the `specs/reports/` subfolder is skipped by the
|
|
125
123
|
non-recursive `specs/*.md` glob, so it's never mistaken for a spec (no phantom card, no bogus stage).
|
|
@@ -167,7 +165,7 @@ exactly the leak this step closes. Append each merged `## Deferred` item to
|
|
|
167
165
|
`/cohorte-refactor <domain>` picks them up with no extra plumbing):
|
|
168
166
|
|
|
169
167
|
```
|
|
170
|
-
- [ ] <SEVERITY> · <file:line> · <quality|security|rule> · <concrete fix> · deferred:$ARGUMENTS
|
|
168
|
+
- [ ] <SEVERITY> · <file:line> · <quality|security|rule|complexity> · <concrete fix> · deferred:$ARGUMENTS
|
|
171
169
|
```
|
|
172
170
|
|
|
173
171
|
- **Never into the spec's `## Remediation`** — that list is what `/cohorte-fix` re-dispatches, so a
|