cohorte 1.5.0 → 2.0.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 +169 -3
- package/README.md +65 -57
- package/bin/cli.js +31 -15
- package/core/agents/implementer.template.md +3 -3
- package/core/agents/release.md +1 -1
- package/core/agents/review.md +25 -2
- package/core/commands/{audit.md → cohorte-audit.md} +11 -3
- package/core/commands/{brainstorm.md → cohorte-brainstorm.md} +9 -3
- package/core/commands/{build.md → cohorte-build.md} +95 -10
- package/core/commands/{doctor.md → cohorte-doctor.md} +22 -9
- package/core/commands/{fix.md → cohorte-fix.md} +20 -13
- package/core/commands/{init-pipeline.md → cohorte-init-pipeline.md} +1 -1
- package/core/commands/cohorte-loop.md +110 -0
- package/core/commands/{refactor.md → cohorte-refactor.md} +3 -3
- package/core/commands/{review.md → cohorte-review.md} +70 -20
- package/core/commands/{ship.md → cohorte-ship.md} +5 -5
- package/core/commands/{spec.md → cohorte-spec.md} +32 -12
- package/core/commands/{update-pipeline.md → cohorte-update-pipeline.md} +16 -6
- package/core/hooks/gate.py +101 -6
- package/core/templates/brainstorm-return.md +4 -4
- package/core/templates/decisions.template.md +42 -0
- package/core/templates/design-brief.md +1 -1
- package/core/templates/spec.template.md +8 -6
- package/core/templates/steps/init-pipeline/01-detect-stack.md +1 -1
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +6 -6
- package/core/templates/steps/init-pipeline/03-draft-profile.md +1 -1
- package/core/templates/steps/init-pipeline/04-write-render.md +16 -12
- package/core/templates/steps/init-pipeline/05-report.md +5 -5
- package/core/workflows/audit.js +6 -6
- package/core/workflows/refactor.js +14 -14
- package/core/workflows/review.js +62 -20
- package/dashboard/README.md +2 -2
- package/dashboard/dist/assets/{index-dkO8UUVl.css → index-BZ_LQlEj.css} +1 -1
- package/dashboard/dist/assets/{index-8owBnqyv.js → index-P1I1JGtj.js} +11 -11
- package/dashboard/dist/index.html +2 -2
- package/dashboard/server/doctor.js +75 -18
- package/dashboard/server/index.js +5 -5
- package/dashboard/server/metrics.js +1 -1
- package/install.ps1 +31 -14
- package/install.sh +31 -14
- package/package.json +2 -2
- package/profile/PIPELINE.template.md +17 -16
- package/profile/SCHEMA.md +199 -48
- package/profile/cohorte.config.template.yaml +8 -8
- package/scripts/loop-detach.sh +153 -0
- package/scripts/loop.sh +202 -25
- package/scripts/metrics/collect.mjs +17 -8
- package/scripts/new-feature.sh.template +3 -3
- package/scripts/preflight.sh +40 -4
- package/scripts/remove-feature.sh.template +2 -2
- package/scripts/test-dashboard.mjs +34 -7
- package/scripts/test-gate.mjs +58 -0
- package/scripts/test-loop.mjs +269 -0
- package/scripts/test-metrics.mjs +23 -11
- package/scripts/test-workflows.mjs +33 -5
- package/scripts/validate-core.mjs +46 -9
- package/core/commands/loop.md +0 -61
- /package/core/commands/{align-ds.md → cohorte-align-ds.md} +0 -0
|
@@ -13,7 +13,7 @@ You are the **lead**. Dispatch the review for feature **$ARGUMENTS**.
|
|
|
13
13
|
>
|
|
14
14
|
> **Workflow variant** (opt-in — SCHEMA.md §Workflows): on Claude Code ≥ 2.1.154 with workflows
|
|
15
15
|
> enabled, the human can ask to "run the review workflow" (`<core>/workflows/review.js`) instead.
|
|
16
|
-
> This conversational path stays the default and the fallback; `/doctor` shows which is available.
|
|
16
|
+
> This conversational path stays the default and the fallback; `/cohorte-doctor` shows which is available.
|
|
17
17
|
|
|
18
18
|
## 0. Deterministic pre-flight — no agents while red
|
|
19
19
|
|
|
@@ -27,7 +27,7 @@ with `test -x`); note the epoch (`date +%s`) in the same call — §3's metrics
|
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
- **Non-zero exit** ⇒ the script already printed the raw last-40 lines. **STOP: relay them verbatim
|
|
30
|
-
and spawn NO agent** — a compiler/test failure needs `/fix` (or the human), not a review that
|
|
30
|
+
and spawn NO agent** — a compiler/test failure needs `/cohorte-fix` (or the human), not a review that
|
|
31
31
|
rediscovers it at agent prices. This abort is the whole point of the step. Before stopping, write
|
|
32
32
|
the **aborted verdict** (§3's contract, degraded form) so an automated driver gets a diagnosis
|
|
33
33
|
rather than silence:
|
|
@@ -58,7 +58,7 @@ with `test -x`); note the epoch (`date +%s`) in the same call — §3's metrics
|
|
|
58
58
|
## 2. Dispatch review agents — one per touched surface, IN PARALLEL
|
|
59
59
|
|
|
60
60
|
Spawn ONE `review` agent per surface that has changed files, in a **single message** (one Task call
|
|
61
|
-
each, like `/build`) so they run concurrently — NEVER serially: review wall-clock must be the
|
|
61
|
+
each, like `/cohorte-build`) so they run concurrently — NEVER serially: review wall-clock must be the
|
|
62
62
|
slowest surface, not the sum. A diff touching a single surface ⇒ a single reviewer.
|
|
63
63
|
|
|
64
64
|
**Small-diff fast path (re-reviews only):** if a surface's staged diff is tiny (≤2 files and ≤~40
|
|
@@ -86,22 +86,39 @@ metrics line needs it.
|
|
|
86
86
|
|
|
87
87
|
## 3. Merge & relay the verdict
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
**Roll call FIRST — a dead reviewer is not a clean surface.** Every surface you dispatched in §2 must
|
|
90
|
+
come back with a REVIEW REPORT. A reviewer that died (rate limit, transport error, exhausted context)
|
|
91
|
+
returns **nothing**, and zero findings from a dead reviewer is byte-identical to zero findings from a
|
|
92
|
+
genuinely clean one — which is how "every reviewer crashed" reads as the strongest possible verdict
|
|
93
|
+
from no evidence at all (SCHEMA.md §Dead agents). So:
|
|
94
|
+
|
|
95
|
+
- **Retry a silent surface ONCE**, byte-identical dispatch. Most deaths are transient, and the staged
|
|
96
|
+
diff is already on disk — the retry costs one agent, not a re-review.
|
|
97
|
+
- **Silent twice ⇒ that surface is `unreviewed`.** Name it in the report under
|
|
98
|
+
`## NOT reviewed (no verdict on these)`, list it in the verdict JSON's `unreviewed`, and **refuse to
|
|
99
|
+
score `SHIP`** — the merged verdict is at least `REVISE`. Absence of evidence is not evidence of
|
|
100
|
+
absence, and it must never reach `/cohorte-ship` or tick a DoD box.
|
|
101
|
+
- **Never re-review the other surfaces** to compensate: their reports are valid and already on disk.
|
|
102
|
+
|
|
103
|
+
Then merge the returned reports into **one** REVIEW REPORT (same template): findings concatenated and
|
|
90
104
|
re-ordered by severity, counts summed, duplicates collapsed, verdict = the worst returned
|
|
91
|
-
(`BLOCK` > `REVISE` > `SHIP`).
|
|
92
|
-
|
|
93
|
-
|
|
105
|
+
(`BLOCK` > `REVISE` > `SHIP`). The `## Deferred` sections merge the same way (dedupe by
|
|
106
|
+
`file` + problem) and stay **out of the severity table and out of the verdict** — see §3.5, which
|
|
107
|
+
routes them. Append ONE metrics line for the batch to the **main checkout's**
|
|
108
|
+
`$(dirname "$(git rev-parse --git-common-dir)")/.claude/pipeline-metrics.jsonl` (rules in
|
|
109
|
+
`/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>",…}}`.
|
|
110
|
+
In the same Bash call, chain the opt-in usage ping (`/cohorte-build` §4, `phase: "review"`, results = the
|
|
94
111
|
merged verdict + total finding count, e.g. `"REVISE:3"`).
|
|
95
112
|
**Stage the full report to `specs/reports/$ARGUMENTS.md`** (overwrite) — a gitignored buffer so a
|
|
96
|
-
`/fix` after a `/clear` can still read the findings; the `specs/reports/` subfolder is skipped by the
|
|
113
|
+
`/cohorte-fix` after a `/clear` can still read the findings; the `specs/reports/` subfolder is skipped by the
|
|
97
114
|
non-recursive `specs/*.md` glob, so it's never mistaken for a spec (no phantom card, no bogus stage).
|
|
98
115
|
**Write the machine-readable verdict** to `specs/reports/$ARGUMENTS.verdict.json` (overwrite) — on
|
|
99
116
|
**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:
|
|
117
|
+
between the pipeline and an automated driver (`/cohorte-loop`), which parses no prose:
|
|
101
118
|
|
|
102
119
|
```json
|
|
103
120
|
{ "id": "$ARGUMENTS", "phase": "review", "ts": "<ISO>", "verdict": "REVISE",
|
|
104
|
-
"findings": 7, "blocking": 2, "security": 1,
|
|
121
|
+
"findings": 7, "blocking": 2, "security": 1, "deferred": 3, "unreviewed": [],
|
|
105
122
|
"severity": {"CRITICAL": 1, "HIGH": 2, "MEDIUM": 3, "LOW": 1},
|
|
106
123
|
"surfaces": {"backend": {"verdict":"BLOCK","findings":4,"blocking":2}},
|
|
107
124
|
"blocking_items": ["backend|apps/api/src/routes/order.ts|missing authz on post"],
|
|
@@ -121,32 +138,65 @@ between the pipeline and an automated driver (`/loop`), which parses no prose:
|
|
|
121
138
|
`printf '%s\n' "<item>" … | LC_ALL=C sort | sha256sum | cut -c1-16` (`shasum -a 256` where there
|
|
122
139
|
is no `sha256sum`). Empty list ⇒ `""`. A driver comparing two consecutive fingerprints detects a
|
|
123
140
|
fix loop that is treading water.
|
|
141
|
+
- **`deferred`** — the count of merged `## Deferred` items §3.5 parked in the backlog. Informational:
|
|
142
|
+
it never enters `blocking`, so it can never cost a driver an iteration.
|
|
143
|
+
- **`unreviewed`** — the surface keys whose reviewer died twice, `[]` on a complete run. It is
|
|
144
|
+
**separate from `blocking` on purpose**: `blocking` counts real findings (CRITICAL + security), and
|
|
145
|
+
faking a number there to force a driver's hand would corrupt the one field the whole contract rests
|
|
146
|
+
on. A non-empty `unreviewed` means "this run does not cover everything" — a driver treats it as no
|
|
147
|
+
usable verdict, never as clean, whatever `blocking` says.
|
|
148
|
+
|
|
149
|
+
## 3.5 Route the deferred findings — the backlog, not the fix loop
|
|
150
|
+
|
|
151
|
+
Do this on **every** run, before the verdict branch below, and whatever the verdict — a deferred
|
|
152
|
+
finding that is only routed on a `SHIP` is a deferred finding lost on every other verdict, which is
|
|
153
|
+
exactly the leak this step closes. Append each merged `## Deferred` item to
|
|
154
|
+
**`specs/refactor-backlog.md`**, under the `## <domain>` heading of the surface that owns its
|
|
155
|
+
`file:line` (create the file and/or heading if absent — same grouping `/cohorte-audit` writes, so
|
|
156
|
+
`/cohorte-refactor <domain>` picks them up with no extra plumbing):
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
- [ ] <SEVERITY> · <file:line> · <quality|security|rule> · <concrete fix> · deferred:$ARGUMENTS
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- **Never into the spec's `## Remediation`** — that list is what `/cohorte-fix` re-dispatches and what `/cohorte-loop`
|
|
163
|
+
waits on, so a deferred item there would re-trigger the very loop it was deferred out of.
|
|
164
|
+
- **Dedupe before appending:** `grep -F` the backlog for the item's `<file>` + the first words of its
|
|
165
|
+
problem; already there (from a prior round or an `/cohorte-audit`) ⇒ skip it, don't stack duplicates round
|
|
166
|
+
after round.
|
|
167
|
+
- Append with `>>` in ONE Bash call; never read the whole backlog into context to rewrite it (it grows
|
|
168
|
+
with every audit the repo has ever run).
|
|
169
|
+
- Report it as **one line** in chat: `deferred: <n> parked in specs/refactor-backlog.md (<domains>)`.
|
|
124
170
|
|
|
125
171
|
In chat print ONLY: the verdict, the severity-count table, a one-line digest of each CRITICAL/security
|
|
126
172
|
finding, and `Full report: specs/reports/$ARGUMENTS.md` — never echo the findings body into chat (it
|
|
127
173
|
would sit in this session's history, re-sent every turn). Then:
|
|
128
174
|
|
|
129
|
-
- **SHIP** →
|
|
175
|
+
- **SHIP** → only reachable with `unreviewed` empty (the roll call above forbids it otherwise). A SHIP
|
|
176
|
+
verdict *is* the pipeline's statement that the feature meets its Definition of
|
|
130
177
|
Done, so **tick the DoD**: in `specs/$ARGUMENTS.md` §`Acceptance criteria / DoD`, flip each `- [ ]`
|
|
131
178
|
→ `- [x]` for the criteria the pipeline has actually verified — spec conformance + `ui_language`
|
|
132
|
-
copy (this review), tests · lint · typecheck (a green `/build`), mobile-first as far as the code
|
|
179
|
+
copy (this review), tests · lint · typecheck (a green `/cohorte-build`), mobile-first as far as the code
|
|
133
180
|
shows it (this review). **Leave `- [ ]` (and say which) any item no stage actually verified** —
|
|
134
181
|
nothing in the pipeline *runs* the feature, so any criterion that needs the app up (runtime flows,
|
|
135
182
|
a visual check against the design) stays open unless the human says they exercised it by hand and
|
|
136
183
|
it held. Ticking is the lead's job
|
|
137
|
-
(the reviewer is read-only). **Then stamp the freshness gate** so `/ship` can refuse to ship code
|
|
184
|
+
(the reviewer is read-only). **Then stamp the freshness gate** so `/cohorte-ship` can refuse to ship code
|
|
138
185
|
edited after this verdict: compute `BASE=$(git merge-base <default_branch> HEAD)` and write into the
|
|
139
186
|
spec front-matter `reviewed_base: $BASE` plus
|
|
140
187
|
`reviewed_digest: $(git diff $BASE -- . ':(exclude)specs/' | sha256sum | cut -c1-16)` — the fingerprint
|
|
141
188
|
of exactly the source you just reviewed (specs excluded, so DoD ticks + the ship status flip don't
|
|
142
|
-
trip it). Then tell the human they can `/ship` — **recommend a `/clear` first**, the handoff is
|
|
189
|
+
trip it). Then tell the human they can `/cohorte-ship` — **recommend a `/clear` first**, the handoff is
|
|
143
190
|
fully on disk. **SHIP with leftover LOW findings** (or LOW+MEDIUM at the human's call) does NOT
|
|
144
|
-
force a fix cycle for nits: park them
|
|
145
|
-
open `## Remediation` items, which would
|
|
146
|
-
freshness stamp, and let the human ship.
|
|
191
|
+
force a fix cycle for nits: park them through §3.5's exact route (the backlog, under their surface's
|
|
192
|
+
domain heading, tagged `deferred:$ARGUMENTS` — never as open `## Remediation` items, which would
|
|
193
|
+
re-trigger the fix loop), keep the SHIP verdict and the freshness stamp, and let the human ship.
|
|
147
194
|
- **REVISE / BLOCK**, or any CRITICAL/HIGH/security finding → tell the human to run
|
|
148
|
-
**`/fix $ARGUMENTS`** — it appends the report to the spec's `## Remediation` and re-dispatches ONLY
|
|
149
|
-
the surfaces with findings.
|
|
195
|
+
**`/cohorte-fix $ARGUMENTS`** — it appends the report to the spec's `## Remediation` and re-dispatches ONLY
|
|
196
|
+
the surfaces with findings. (If they'd rather automate the rounds, the autonomous driver is
|
|
197
|
+
`disable-model-invocation: true` on purpose: **you cannot start it, they must type it**. Name the
|
|
198
|
+
exact line for them to type rather than attempting it — an attempt that silently fails reads as a
|
|
199
|
+
loop that is running when nothing is.) The full path (`/cohorte-spec` Mode B then `/cohorte-build`) remains for findings that
|
|
150
200
|
change the contract in ways that ripple into clean surfaces. _The report is staged to
|
|
151
|
-
`specs/reports/$ARGUMENTS.md`, so you can `/clear` before `/fix` — it reads the findings back from
|
|
201
|
+
`specs/reports/$ARGUMENTS.md`, so you can `/clear` before `/cohorte-fix` — it reads the findings back from
|
|
152
202
|
disk._
|
|
@@ -15,15 +15,15 @@ You are the **lead**. Ship feature **$ARGUMENTS**. This is the outward-facing ga
|
|
|
15
15
|
|
|
16
16
|
## 1. Pre-flight (confirm before doing anything irreversible)
|
|
17
17
|
|
|
18
|
-
- Confirm the latest `/review` returned **SHIP** (no CRITICAL, no security). If not reviewed, or the
|
|
18
|
+
- Confirm the latest `/cohorte-review` returned **SHIP** (no CRITICAL, no security). If not reviewed, or the
|
|
19
19
|
verdict was REVISE/BLOCK, stop and say so.
|
|
20
20
|
- **Freshness gate** — the reviewed code must be exactly what ships. If the spec front-matter carries
|
|
21
21
|
`reviewed_base` + `reviewed_digest`, recompute
|
|
22
22
|
`git diff <reviewed_base> -- . ':(exclude)specs/' | sha256sum | cut -c1-16` and compare to
|
|
23
23
|
`reviewed_digest`. **Match** ⇒ source unchanged since the SHIP verdict, proceed. **Mismatch** ⇒ source
|
|
24
24
|
(or the contract) was edited after review — the verdict is **stale**: stop and tell the human to re-run
|
|
25
|
-
`/review $ARGUMENTS` before shipping. Missing fields (spec predates the gate) ⇒ skip, don't block.
|
|
26
|
-
- **DoD gate (verify, don't tick — `/review` owns the ticking).** Read `specs/$ARGUMENTS.md`
|
|
25
|
+
`/cohorte-review $ARGUMENTS` before shipping. Missing fields (spec predates the gate) ⇒ skip, don't block.
|
|
26
|
+
- **DoD gate (verify, don't tick — `/cohorte-review` owns the ticking).** Read `specs/$ARGUMENTS.md`
|
|
27
27
|
§`Acceptance criteria / DoD`; if any item is still `- [ ]`, list the open ones and ask the human to
|
|
28
28
|
confirm shipping anyway (they may be deferred on purpose — e.g. a UI item on a backend-only feature).
|
|
29
29
|
All `- [x]` ⇒ proceed silently.
|
|
@@ -61,7 +61,7 @@ or an offset-limited Read around the match): exactly one card, under the `shippe
|
|
|
61
61
|
re-read the whole board into context. No board ⇒ skip silently.
|
|
62
62
|
|
|
63
63
|
**Telemetry — the usage ping that closes the funnel.** Chain it onto the verify call above
|
|
64
|
-
(`/build` §4's shared form, `<phase>` = `ship`, `<seconds>` = `0` — the release agent's duration is
|
|
64
|
+
(`/cohorte-build` §4's shared form, `<phase>` = `ship`, `<seconds>` = `0` — the release agent's duration is
|
|
65
65
|
not the pipeline's, `<results>` = `pr` when a PR was created / `compare` when only a compare URL was
|
|
66
66
|
emitted). Fire it **after** the release agent reports success, never on an aborted ship — a `ship`
|
|
67
67
|
event must mean the feature actually left the pipeline. No board ⇒ still ping, in its own `|| true`
|
|
@@ -70,7 +70,7 @@ call. Silent no-op without consent; never ask about consent here.
|
|
|
70
70
|
## 5. After the PR — CI gate + teardown
|
|
71
71
|
|
|
72
72
|
- If `host: github` and `gh` is available, watch the PR's checks (`gh pr checks <url> --watch`) and
|
|
73
|
-
report the result — the human merges only on green. A red check ⇒ back to `/fix $ARGUMENTS`.
|
|
73
|
+
report the result — the human merges only on green. A red check ⇒ back to `/cohorte-fix $ARGUMENTS`.
|
|
74
74
|
- Once the human confirms the PR is **merged**: if `isolation.enabled`, propose the teardown —
|
|
75
75
|
`scripts/remove-feature.sh $ARGUMENTS` (add `--drop-db` to also drop the feature db; kept by
|
|
76
76
|
default). It removes the worktree, deletes the merged branch, frees the slot. Never run it before
|
|
@@ -13,6 +13,10 @@ You run the **spec** step in the main thread — interactive, with the human. Pa
|
|
|
13
13
|
> Template paths below (`.claude/templates/…`) resolve to `~/.claude/templates/…` when the core is
|
|
14
14
|
> installed globally — read whichever exists.
|
|
15
15
|
>
|
|
16
|
+
> **Decision journal** (SCHEMA.md §Decisions): read `specs/_decisions.md` §Live if it exists — one
|
|
17
|
+
> line per standing decision, so it is cheap. It is the ONLY place the project's transverse rules
|
|
18
|
+
> live; a spec that contradicts one silently un-decides it. Absent ⇒ nothing to honour yet.
|
|
19
|
+
>
|
|
16
20
|
> **Kanban** (SCHEMA.md §Kanban): when the spec opens, move card `#<feature_id>` → **Spec**; on freeze
|
|
17
21
|
> (`status: frozen`, Mode A) → **Ready to build**. No-op silently if no board is configured.
|
|
18
22
|
|
|
@@ -21,7 +25,7 @@ Detect the mode from the pasted content:
|
|
|
21
25
|
## Mode A — new spec (input is a brainstorm return, or empty)
|
|
22
26
|
|
|
23
27
|
1. If empty, look for a staged brainstorm return first — `specs/reports/*-brainstorm.md` (where
|
|
24
|
-
`/brainstorm` stages its output); one match ⇒ read it and confirm, several ⇒ ask which. None ⇒
|
|
28
|
+
`/cohorte-brainstorm` stages its output); one match ⇒ read it and confirm, several ⇒ ask which. None ⇒
|
|
25
29
|
ask the human to paste the return (or describe the feature) and wait.
|
|
26
30
|
2. Derive a `feature_id` (kebab-case slug). Confirm it.
|
|
27
31
|
2b. **Size budget — a spec is a contract, not a novel.** Target ≤ ~300 lines; hard-think at 500.
|
|
@@ -43,44 +47,60 @@ Detect the mode from the pasted content:
|
|
|
43
47
|
non-design surfaces never re-read the full brief on every dispatch).
|
|
44
48
|
4b. **New-surface heads-up.** If the feature clearly introduces an area no existing `surfaces[].path`
|
|
45
49
|
owns (a new service/app/top-level module), note it in the spec (a line in the relevant task section:
|
|
46
|
-
`> needs new surface: <proposed key/path>`). Don't render agents here — `/build` §1.5 auto-reconciles
|
|
47
|
-
it. This is just so the human isn't surprised when `/build` proposes a new agent.
|
|
50
|
+
`> needs new surface: <proposed key/path>`). Don't render agents here — `/cohorte-build` §1.5 auto-reconciles
|
|
51
|
+
it. This is just so the human isn't surprised when `/cohorte-build` proposes a new agent.
|
|
48
52
|
5. When the human validates, **freeze**: write `specs/<id>.md` (`status: frozen`, front-matter filled).
|
|
49
53
|
Create the file — do not ask the human to. **Postcondition:** `grep -q '^status: frozen' specs/<id>.md`
|
|
50
|
-
— if it fails the freeze didn't land; fix it before pointing the human at `/build`. Chain the
|
|
51
|
-
opt-in usage ping onto the postcondition's Bash call (`/build` §4's shared form, `<phase>` =
|
|
54
|
+
— if it fails the freeze didn't land; fix it before pointing the human at `/cohorte-build`. Chain the
|
|
55
|
+
opt-in usage ping onto the postcondition's Bash call (`/cohorte-build` §4's shared form, `<phase>` =
|
|
52
56
|
`spec`, `<seconds>` = `0` — interactive time, not pipeline wall-clock, `<results>` = `frozen`).
|
|
53
57
|
Ping only on a **landed** freeze, so the funnel counts specs that exist, not attempts. Mode B does
|
|
54
|
-
not ping — it re-enters an already-counted spec, and `/fix` covers that loop. Silent no-op without
|
|
58
|
+
not ping — it re-enters an already-counted spec, and `/cohorte-fix` covers that loop. Silent no-op without
|
|
55
59
|
consent; never ask about consent here.
|
|
60
|
+
5b. **Record the transverse decisions — the journal, not a summary.** Walk what the interview settled
|
|
61
|
+
and keep ONLY the decisions that **outlive this feature**: a rule the next spec would otherwise
|
|
62
|
+
re-litigate or contradict (auth model, id/naming scheme, where a kind of state lives, an error
|
|
63
|
+
convention, a deliberate non-goal that binds future features). Typical yield: **0–3 lines**; zero
|
|
64
|
+
is a normal, healthy outcome for a feature that decided nothing new — never invent lines to fill
|
|
65
|
+
the section. Append them to `specs/_decisions.md` §Live (create the file from
|
|
66
|
+
`.claude/templates/decisions.template.md` on first use), each exactly:
|
|
67
|
+
`- <YYYY-MM-DD> · <area> · <decision> — because <reason> · <feature_id>`
|
|
68
|
+
- **Never** duplicate what §5, `PIPELINE.md` §Conventions or the code already states — the journal
|
|
69
|
+
carries the *non-obvious rule*, not the feature's content. A line that restates a spec section is
|
|
70
|
+
a line every future `/cohorte-spec` pays for and learns nothing from.
|
|
71
|
+
- **Contradicting an existing line** is allowed but never silent: tell the human which line this
|
|
72
|
+
feature overrides, get their go-ahead, then append the new line with
|
|
73
|
+
`· supersedes <YYYY-MM-DD> <area>` and move the old one to `## Superseded`.
|
|
74
|
+
- Append with one `>>` Bash call, not a full-file rewrite (the file is append-only, and reading it
|
|
75
|
+
back to re-write it is the one way to make a bounded file expensive).
|
|
56
76
|
6. Author the **design brief** — `specs/design/<id>.md`, rendered via
|
|
57
77
|
`.claude/templates/design-brief.md` (resolves to `~/.claude/templates/…` on a global install).
|
|
58
78
|
_Only if `design.enabled` / the feature has UI; skip entirely for a backend-only feature._
|
|
59
79
|
- **Write it to `specs/design/<id>.md`** (the authored artifact, versioned with the spec; spec §8
|
|
60
80
|
holds the summary + pointer). Create the file — do not ask the human to. Keep it in the
|
|
61
81
|
`specs/design/` subfolder, **not** `specs/<id>....md`: the `specs/*.md` glob that drives the
|
|
62
|
-
kanban backfill and `/doctor` is non-recursive, so a brief in the subfolder never gets mistaken
|
|
82
|
+
kanban backfill and `/cohorte-doctor` is non-recursive, so a brief in the subfolder never gets mistaken
|
|
63
83
|
for a spec (no phantom card, no bogus stage). Overwrite it on every freeze.
|
|
64
84
|
- Print ONLY the path + a one-line summary — never echo the brief into chat (it would sit in this
|
|
65
85
|
session's history; echo it only if the human asks). Tell the human: copy it from
|
|
66
86
|
`specs/design/<id>.md` into the design tool (if any — typically a fresh design project for this
|
|
67
|
-
feature), then run `/build <id>` and hand its design gate the resulting page link(s) — a full
|
|
87
|
+
feature), then run `/cohorte-build <id>` and hand its design gate the resulting page link(s) — a full
|
|
68
88
|
`https://claude.ai/design/p/<projectId>?file=<file>` link carries its own project + page, no
|
|
69
89
|
profile change needed. (They can also paste the links into the spec's `design_files` themselves.)
|
|
70
|
-
**Recommend a `/clear` before `/build`** — the frozen spec + `specs/design/<id>.md` are the whole
|
|
90
|
+
**Recommend a `/clear` before `/cohorte-build`** — the frozen spec + `specs/design/<id>.md` are the whole
|
|
71
91
|
handoff.
|
|
72
92
|
|
|
73
93
|
## Mode B — review return (input is a REVIEW REPORT)
|
|
74
94
|
|
|
75
95
|
1. Read the report — pasted as input, or (whenever nothing is pasted) read from
|
|
76
|
-
`specs/reports/<id>.md`, where `/review` stages its last report. Identify `feature_id` from its
|
|
96
|
+
`specs/reports/<id>.md`, where `/cohorte-review` stages its last report. Identify `feature_id` from its
|
|
77
97
|
header; open `specs/<id>.md`.
|
|
78
98
|
2. Append each finding to the spec's **`## Remediation`**, one per line:
|
|
79
99
|
`- [ ] <severity> · <file:line> · <spec-violation|quality|security> · <concrete fix>`
|
|
80
100
|
(Keep prior items; add the new round under a dated/numbered subheading.)
|
|
81
101
|
3. If a finding implies the **contract** must change, update §5 and flag it so the lead re-authors the
|
|
82
102
|
contract file.
|
|
83
|
-
4. Set `status: in-review`. Tell the human to run `/build <id>` to re-dispatch fresh agents.
|
|
84
|
-
**Recommend a `/clear` before `/build`** — the spec is the whole handoff.
|
|
103
|
+
4. Set `status: in-review`. Tell the human to run `/cohorte-build <id>` to re-dispatch fresh agents.
|
|
104
|
+
**Recommend a `/clear` before `/cohorte-build`** — the spec is the whole handoff.
|
|
85
105
|
|
|
86
106
|
In both modes the spec is the single source of truth; agents are stateless and read only it + the diff.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
model: sonnet
|
|
3
|
-
description: Refresh the pipeline core (global ~/.claude, or a repo's bundled .claude) to the latest published cohorte version, then reconcile this repo's generated files to it — /init-pipeline stays one-time.
|
|
3
|
+
description: Refresh the pipeline core (global ~/.claude, or a repo's bundled .claude) to the latest published cohorte version, then reconcile this repo's generated files to it — /cohorte-init-pipeline stays one-time.
|
|
4
4
|
argument-hint: [path-to-local-checkout]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ You are the **pipeline updater**. Refresh the installed pipeline core to the lat
|
|
|
8
8
|
repo. The installer's `--update` mode never touches generated files: `PIPELINE.md`, rendered surface agents,
|
|
9
9
|
`gate-config.json`, `settings.json`, and the filled `~/.claude/cohorte.config.yaml` are all preserved.
|
|
10
10
|
YOU then bring those generated files up to the new core yourself (§3.5) — additively, never clobbering
|
|
11
|
-
the human's choices — so `/init-pipeline` never needs re-running for an upgrade.
|
|
11
|
+
the human's choices — so `/cohorte-init-pipeline` never needs re-running for an upgrade.
|
|
12
12
|
|
|
13
13
|
## 1. Detect the install scope + current version
|
|
14
14
|
|
|
@@ -57,7 +57,7 @@ differs from the core you just installed, rewrite that one field (leave every ot
|
|
|
57
57
|
and tell the human to commit it. In **bundled** mode the installer already did it; in **global** mode
|
|
58
58
|
**nothing does** — the installer refreshes one shared core and cannot know which repos point at it,
|
|
59
59
|
so before 1.2.5 the field simply drifted forever (a repo on a current core still claiming `1.0.0`).
|
|
60
|
-
`/doctor` check 1 requires the pointer to be coherent with the VERSION file, so a drifted field reads
|
|
60
|
+
`/cohorte-doctor` check 1 requires the pointer to be coherent with the VERSION file, so a drifted field reads
|
|
61
61
|
as a broken install when nothing is broken.
|
|
62
62
|
|
|
63
63
|
Then print **What's new**: read the installed `<core>/pipeline/CHANGELOG.md` and show the entries
|
|
@@ -82,9 +82,19 @@ health check** (SCHEMA.md §Code retrieval: CLI resolvable from PATH, `.mcp.json
|
|
|
82
82
|
upgrading a bare `serena` entry to the PATH-proof launcher form, `.serena/` gitignored, server
|
|
83
83
|
actually connected) and repair whatever fails — wiring that worked at
|
|
84
84
|
init can rot (PATH changes, uninstalls, hand-edits). Report what was reconciled; if nothing was
|
|
85
|
-
missing, say so. This is why `/init-pipeline` never needs re-running for a core upgrade.
|
|
85
|
+
missing, say so. This is why `/cohorte-init-pipeline` never needs re-running for a core upgrade.
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
Four of the §Reconcile steps matter specifically here:
|
|
88
|
+
|
|
89
|
+
- **Local-artifact hygiene** (§Reconcile step 8): gitignore + untrack the pipeline's runtime files
|
|
90
|
+
(`.claude/preflight.ok`, `.claude/pipeline-metrics.jsonl`, `specs/reports/`). A tracked
|
|
91
|
+
`preflight.ok` — what every pre-2.0.0 install ends up with once a release agent stages `.claude/` —
|
|
92
|
+
makes the phase gate ask on every single review dispatch, so fix it here and say so.
|
|
93
|
+
|
|
94
|
+
- **Spec-template top-up** (§Reconcile step 7): `specs/_template.md` was seeded at install and never
|
|
95
|
+
refreshed since, so add the front-matter fields the current `templates/spec.template.md` has and the
|
|
96
|
+
repo's copy lacks (1.6 added `loop_pass`/`loop_phase` and two states to the `status:` comment) —
|
|
97
|
+
front-matter only, never the body.
|
|
88
98
|
|
|
89
99
|
- **Global config seed** (§Reconcile step 5): if `~/.claude/cohorte.config.yaml` is absent, seed it
|
|
90
100
|
from the template so the kanban + shared-vault config has a home. Never clobber an existing filled
|
|
@@ -106,7 +116,7 @@ Two of the §Reconcile steps matter specifically here:
|
|
|
106
116
|
- **Restart / reload the Claude Code session** so it picks up updated commands, agents, and any
|
|
107
117
|
newly-registered MCP server.
|
|
108
118
|
- **Other repos using the global core:** their core is already fresh, but reconcile is per-repo — run
|
|
109
|
-
`/update-pipeline` inside each (it will skip the already-done core update and just reconcile).
|
|
119
|
+
`/cohorte-update-pipeline` inside each (it will skip the already-done core update and just reconcile).
|
|
110
120
|
- **Commit** the reconciled files (`PIPELINE.md`, `.claude/`, `.mcp.json` if added) so teammates get them.
|
|
111
121
|
- The kanban config is global and user-scoped
|
|
112
122
|
(`~/.claude/cohorte.config.yaml`) — never committed. The core update never touches it; only the
|
package/core/hooks/gate.py
CHANGED
|
@@ -6,7 +6,7 @@ inspecting the FULL command string so chained forms like
|
|
|
6
6
|
`cd apps/api && node ace migration:run` are caught — something prefix-based
|
|
7
7
|
permission rules in settings.json miss.
|
|
8
8
|
|
|
9
|
-
Patterns come from `.claude/gate-config.json` (generated by /init-pipeline from
|
|
9
|
+
Patterns come from `.claude/gate-config.json` (generated by /cohorte-init-pipeline from
|
|
10
10
|
the PIPELINE.md `gate` block):
|
|
11
11
|
|
|
12
12
|
{ "deny": ["node ace migration:fresh", ...],
|
|
@@ -16,7 +16,7 @@ A pattern matches a command segment when the segment *contains* the pattern
|
|
|
16
16
|
(after normalizing whitespace). `deny` wins over `ask`. If the config is missing
|
|
17
17
|
or unreadable, the hook stays silent (exit 0) and lets settings.json decide.
|
|
18
18
|
|
|
19
|
-
The hook fires for EVERY agent in the session — the lead, /build's implementers,
|
|
19
|
+
The hook fires for EVERY agent in the session — the lead, /cohorte-build's implementers,
|
|
20
20
|
and subagents spawned by the Workflow runtime alike. Workflow subagents run in
|
|
21
21
|
acceptEdits regardless of the session's permission mode (their Write/Edit calls
|
|
22
22
|
are auto-approved), but acceptEdits does NOT auto-approve Bash or Task, so this
|
|
@@ -31,7 +31,10 @@ Two extra duties beyond Bash patterns:
|
|
|
31
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
|
-
reviewers onto code that doesn't compile burns their whole run.
|
|
34
|
+
reviewers onto code that doesn't compile burns their whole run. Freshness is
|
|
35
|
+
a content digest (see tree_digest), not the HEAD sha: the reviewed tree is
|
|
36
|
+
normally dirty, so a commit must not invalidate a stamp and an uncommitted
|
|
37
|
+
edit must. The stamp is local and gitignored — never versioned.
|
|
35
38
|
|
|
36
39
|
Protocol: reads the PreToolUse payload on stdin; emits a JSON permissionDecision
|
|
37
40
|
of "deny" or "ask" on a match; otherwise exits 0 silently.
|
|
@@ -40,8 +43,10 @@ of "deny" or "ask" on a match; otherwise exits 0 silently.
|
|
|
40
43
|
import json
|
|
41
44
|
import os
|
|
42
45
|
import re
|
|
46
|
+
import shutil
|
|
43
47
|
import subprocess
|
|
44
48
|
import sys
|
|
49
|
+
import tempfile
|
|
45
50
|
import time
|
|
46
51
|
|
|
47
52
|
SPLIT = re.compile(r"&&|\|\||[;|\n]")
|
|
@@ -129,6 +134,84 @@ def known_heads(cwd: str):
|
|
|
129
134
|
return heads
|
|
130
135
|
|
|
131
136
|
|
|
137
|
+
def worktree_dirs(cwd: str):
|
|
138
|
+
"""`cwd` plus every linked worktree path — the checkouts a stamp may describe."""
|
|
139
|
+
dirs = [cwd]
|
|
140
|
+
try:
|
|
141
|
+
out = subprocess.run(
|
|
142
|
+
["git", "worktree", "list", "--porcelain"],
|
|
143
|
+
cwd=cwd, capture_output=True, text=True, timeout=3,
|
|
144
|
+
)
|
|
145
|
+
if out.returncode == 0:
|
|
146
|
+
for line in out.stdout.splitlines():
|
|
147
|
+
if line.startswith("worktree "):
|
|
148
|
+
p = line.split(" ", 1)[1].strip()
|
|
149
|
+
if p and p not in dirs:
|
|
150
|
+
dirs.append(p)
|
|
151
|
+
except Exception:
|
|
152
|
+
pass
|
|
153
|
+
return dirs
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def tree_digest(cwd: str):
|
|
157
|
+
"""The digest pipeline/scripts/preflight.sh stamps for the checkout at `cwd`: the git
|
|
158
|
+
tree id of the working tree, computed in a throwaway index (`.claude` and `specs`
|
|
159
|
+
excluded — the pipeline writes those itself). Content-addressed, so a commit of the
|
|
160
|
+
same code keeps the stamp valid and any real edit invalidates it. Must stay identical
|
|
161
|
+
to the shell side. Returns None when git can't answer, and the caller falls back."""
|
|
162
|
+
tmp = None
|
|
163
|
+
try:
|
|
164
|
+
idx = subprocess.run(
|
|
165
|
+
["git", "rev-parse", "--absolute-git-dir"],
|
|
166
|
+
cwd=cwd, capture_output=True, text=True, timeout=3,
|
|
167
|
+
)
|
|
168
|
+
if idx.returncode != 0:
|
|
169
|
+
return None
|
|
170
|
+
real_index = os.path.join(idx.stdout.strip(), "index")
|
|
171
|
+
fd, tmp = tempfile.mkstemp(prefix="cohorte-idx-")
|
|
172
|
+
os.close(fd)
|
|
173
|
+
# Seed from the real index to keep its stat cache — `add` then re-hashes only
|
|
174
|
+
# what changed instead of the whole tree on every dispatch.
|
|
175
|
+
if os.path.exists(real_index):
|
|
176
|
+
shutil.copyfile(real_index, tmp)
|
|
177
|
+
else:
|
|
178
|
+
os.unlink(tmp) # a 0-byte index is a corrupt index — let git create it
|
|
179
|
+
env = dict(os.environ, GIT_INDEX_FILE=tmp)
|
|
180
|
+
# Drop the excluded paths outright — an `add` exclude only stops them being
|
|
181
|
+
# *updated*, so already-tracked ones would still shift the tree id.
|
|
182
|
+
subprocess.run(
|
|
183
|
+
["git", "rm", "--cached", "-r", "-q", "--ignore-unmatch", "--", ".claude", "specs"],
|
|
184
|
+
cwd=cwd, capture_output=True, timeout=10, env=env,
|
|
185
|
+
)
|
|
186
|
+
add = subprocess.run(
|
|
187
|
+
["git", "add", "-A", "--", ".", ":(exclude).claude", ":(exclude)specs"],
|
|
188
|
+
cwd=cwd, capture_output=True, timeout=30, env=env,
|
|
189
|
+
)
|
|
190
|
+
if add.returncode != 0:
|
|
191
|
+
return None
|
|
192
|
+
out = subprocess.run(
|
|
193
|
+
["git", "write-tree"], cwd=cwd, capture_output=True, text=True, timeout=10, env=env,
|
|
194
|
+
)
|
|
195
|
+
if out.returncode != 0:
|
|
196
|
+
return None
|
|
197
|
+
return out.stdout.strip() or None
|
|
198
|
+
except Exception:
|
|
199
|
+
return None
|
|
200
|
+
finally:
|
|
201
|
+
for p in ((tmp, tmp + ".lock") if tmp else ()):
|
|
202
|
+
try:
|
|
203
|
+
os.unlink(p)
|
|
204
|
+
except Exception:
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def known_digests(cwd: str):
|
|
209
|
+
"""Every checkout's current digest. The preflight may have run in a feature worktree
|
|
210
|
+
while the Task dispatch fires from the main checkout (or vice versa) — same reason
|
|
211
|
+
known_heads() spans worktrees."""
|
|
212
|
+
return {d for d in (tree_digest(p) for p in worktree_dirs(cwd)) if d}
|
|
213
|
+
|
|
214
|
+
|
|
132
215
|
def check_preflight(payload: dict, cfg: dict) -> int:
|
|
133
216
|
"""Phase gate on Task dispatches: review agents need a green preflight stamp."""
|
|
134
217
|
pf = cfg.get("preflight") or {}
|
|
@@ -149,17 +232,29 @@ def check_preflight(payload: dict, cfg: dict) -> int:
|
|
|
149
232
|
why = "no preflight stamp found"
|
|
150
233
|
if raw is not None:
|
|
151
234
|
try:
|
|
152
|
-
|
|
235
|
+
fields = raw.split()
|
|
236
|
+
epoch_s = fields[0]
|
|
237
|
+
sha = fields[1] if len(fields) > 1 else ""
|
|
238
|
+
digest = fields[2] if len(fields) > 2 else ""
|
|
153
239
|
age_min = (time.time() - float(epoch_s)) / 60
|
|
154
240
|
max_age = float(pf.get("max_age_minutes", 30) or 30)
|
|
241
|
+
cwd = session_cwd(payload)
|
|
155
242
|
if age_min > max_age:
|
|
156
243
|
why = f"the preflight stamp is {age_min:.0f} min old (max {max_age:.0f})"
|
|
244
|
+
elif digest not in ("", "none"):
|
|
245
|
+
# 2.0.0+ stamp: key off the verified CONTENT, not HEAD. A commit that
|
|
246
|
+
# doesn't touch code keeps the stamp valid; an edit that never reaches a
|
|
247
|
+
# commit invalidates it — both of which the sha comparison got backwards.
|
|
248
|
+
digests = known_digests(cwd)
|
|
249
|
+
if digests and digest not in digests:
|
|
250
|
+
why = "the code changed since the preflight ran"
|
|
157
251
|
else:
|
|
158
|
-
|
|
252
|
+
# Pre-2.0.0 stamp (no digest) — fall back to the HEAD comparison.
|
|
253
|
+
heads = known_heads(cwd)
|
|
159
254
|
if heads and sha not in ("", "none") and sha not in heads:
|
|
160
255
|
why = "HEAD moved since the preflight ran"
|
|
161
256
|
except Exception:
|
|
162
|
-
why = "the preflight stamp is unreadable (expected `<epoch> <sha>`)"
|
|
257
|
+
why = "the preflight stamp is unreadable (expected `<epoch> <sha> <digest>`)"
|
|
163
258
|
if why is None:
|
|
164
259
|
return 0
|
|
165
260
|
# Same rule as the Bash gate: unattended runs have nobody to answer an "ask".
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BRAINSTORM RETURN — <feature title>
|
|
2
2
|
|
|
3
|
-
> Paste this into `/spec`.
|
|
3
|
+
> Paste this into `/cohorte-spec`.
|
|
4
4
|
|
|
5
5
|
**One-liner:** <what we're building, in one sentence>
|
|
6
6
|
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
|
|
25
25
|
- **Data:** <entities / fields touched>
|
|
26
26
|
- **Screens:** <key screens/flows — mobile-first if the project has UI>
|
|
27
|
-
- **Interface (rough):** <endpoints / methods we'll need — refined into the contract in /spec>
|
|
27
|
+
- **Interface (rough):** <endpoints / methods we'll need — refined into the contract in /cohorte-spec>
|
|
28
28
|
|
|
29
29
|
## Risks & open questions
|
|
30
30
|
|
|
31
31
|
- <risk / unknown>
|
|
32
|
-
- <open question for /spec to resolve>
|
|
32
|
+
- <open question for /cohorte-spec to resolve>
|
|
33
33
|
|
|
34
34
|
## Panel dissent (what was contested)
|
|
35
35
|
|
|
36
|
-
<the strongest disagreement the panel surfaced — so /spec doesn't paper over it>
|
|
36
|
+
<the strongest disagreement the panel surfaced — so /cohorte-spec doesn't paper over it>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Decisions
|
|
2
|
+
|
|
3
|
+
> The project's **transverse decision journal** — the non-obvious rules a stateless agent would
|
|
4
|
+
> otherwise re-discover or contradict, one feature at a time. `PIPELINE.md` says *how this repo is
|
|
5
|
+
> built* (surfaces, commands, conventions); this file says *what was decided and why*.
|
|
6
|
+
>
|
|
7
|
+
> **Append-only. One line per decision. No prose blocks, no code.** The bound is the point: this file
|
|
8
|
+
> is read by `/cohorte-spec`, `/cohorte-brainstorm` and `/cohorte-audit` — the deciding stages — and by **nothing else**.
|
|
9
|
+
> Implementers and reviewers never read it: they work from the frozen contract, which already tells
|
|
10
|
+
> them what to do; handing them the rationale too would cost `surfaces × dispatches` tokens per
|
|
11
|
+
> feature for a fact they cannot act on.
|
|
12
|
+
>
|
|
13
|
+
> Line shape (≤ ~160 chars, `·`-separated, no wrapping):
|
|
14
|
+
>
|
|
15
|
+
> ```
|
|
16
|
+
> - <YYYY-MM-DD> · <area> · <the decision, imperative> — because <the reason> · <feature_id>
|
|
17
|
+
> ```
|
|
18
|
+
>
|
|
19
|
+
> `<area>` is a short tag, not a path: `auth`, `data`, `api`, `ui`, `deploy`, `naming`, `surfaces`…
|
|
20
|
+
>
|
|
21
|
+
> **Reversing a decision** never edits or deletes a line — append a superseding one and move the old
|
|
22
|
+
> one to `## Superseded`:
|
|
23
|
+
>
|
|
24
|
+
> ```
|
|
25
|
+
> - <YYYY-MM-DD> · <area> · <the new decision> — because <reason> · supersedes <YYYY-MM-DD> <area> · <feature_id>
|
|
26
|
+
> ```
|
|
27
|
+
>
|
|
28
|
+
> **Keep it bounded:** when `## Live` passes ~100 lines, move every superseded line into
|
|
29
|
+
> `## Superseded` (the audit trail survives; the section the deciding stages actually read stays
|
|
30
|
+
> short). Never summarize or merge live lines — a decision either holds or has been superseded.
|
|
31
|
+
>
|
|
32
|
+
> **What does NOT belong here:** anything the code, the tests, `PIPELINE.md` §Conventions or a spec
|
|
33
|
+
> already states; a feature-local choice (that lives in its spec); a task, a TODO or a finding (those
|
|
34
|
+
> are `## Remediation` and `specs/refactor-backlog.md`).
|
|
35
|
+
|
|
36
|
+
## Live
|
|
37
|
+
|
|
38
|
+
<!-- newest last -->
|
|
39
|
+
|
|
40
|
+
## Superseded
|
|
41
|
+
|
|
42
|
+
<!-- moved here when a line above supersedes them; never deleted -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# DESIGN BRIEF — <feature title> (`<feature_id>`)
|
|
2
2
|
|
|
3
3
|
> The "spec return". Paste into the design tool (see `PIPELINE.md` §design). This is §8 of the frozen
|
|
4
|
-
> spec, standalone — `/spec` writes it to `specs/design/<feature_id>.md` on freeze. Omit entirely if
|
|
4
|
+
> spec, standalone — `/cohorte-spec` writes it to `specs/design/<feature_id>.md` on freeze. Omit entirely if
|
|
5
5
|
> the project has no UI.
|
|
6
6
|
|
|
7
7
|
**Goal:** <one line — what the user accomplishes>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
feature_id: <slug>
|
|
3
3
|
title: <Feature title>
|
|
4
|
-
status: draft # draft → frozen → in-review → shipped
|
|
4
|
+
status: draft # draft → frozen → in-progress → in-review → shipped · blocked (see SCHEMA.md §Spec status)
|
|
5
5
|
branch: <feature_branch_prefix><slug>
|
|
6
6
|
created: <YYYY-MM-DD>
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
loop_pass: 0 # /cohorte-loop bookkeeping — the review pass it is on; 0 = no loop running. Written by the driver, not by hand
|
|
8
|
+
loop_phase: # /cohorte-loop bookkeeping — build | review | fix | done. With loop_pass, this is what `--resume` reads back
|
|
9
|
+
reviewed_base: # merge-base sha at the last SHIP verdict — freshness-gate anchor (written by /cohorte-review)
|
|
10
|
+
reviewed_digest: # sha256 (16 hex) of the reviewed source diff vs reviewed_base, specs excluded — /cohorte-ship re-checks
|
|
9
11
|
design_files: [] # design page links — full URLs of the form https://claude.ai/design/p/<projectId>?file=<file> (each carries its own project + page); blank until designed; omit if no UI
|
|
10
12
|
---
|
|
11
13
|
|
|
@@ -45,7 +47,7 @@ design_files: [] # design page links — full URLs of the form https://claude.ai
|
|
|
45
47
|
|
|
46
48
|
<repeat per endpoint/interface>
|
|
47
49
|
|
|
48
|
-
### Contract types — lead authors `<contract.path>/<slug>.<ext>` before /build
|
|
50
|
+
### Contract types — lead authors `<contract.path>/<slug>.<ext>` before /cohorte-build
|
|
49
51
|
|
|
50
52
|
- `<slug>...Request` / `<slug>...Response` / `<slug>Base` … (sketch the schemas here)
|
|
51
53
|
|
|
@@ -53,7 +55,7 @@ design_files: [] # design page links — full URLs of the form https://claude.ai
|
|
|
53
55
|
|
|
54
56
|
> One `###` subsection per surface in `PIPELINE.md` §surfaces (e.g. backend, frontend), each TDD.
|
|
55
57
|
> Surfaces are subsections of §6, never top-level sections — §8 and §9 are referenced by number
|
|
56
|
-
> across the whole pipeline (`/build` implements "§8 flows", `/review` checks "§9 acceptance"), so a
|
|
58
|
+
> across the whole pipeline (`/cohorte-build` implements "§8 flows", `/cohorte-review` checks "§9 acceptance"), so a
|
|
57
59
|
> third surface must not become a §8 of its own.
|
|
58
60
|
|
|
59
61
|
### <surface.key>
|
|
@@ -83,5 +85,5 @@ design_files: [] # design page links — full URLs of the form https://claude.ai
|
|
|
83
85
|
|
|
84
86
|
## Remediation
|
|
85
87
|
|
|
86
|
-
> Filled by `/spec` in review-return mode; empty otherwise. Each item:
|
|
88
|
+
> Filled by `/cohorte-spec` in review-return mode; empty otherwise. Each item:
|
|
87
89
|
> `[ ] <SEVERITY> · <file:line> · <spec-violation|quality|security> · <concrete fix>`
|