cohorte 1.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 +264 -0
- package/LICENSE +661 -0
- package/README.md +269 -0
- package/bin/cli.js +339 -0
- package/core/agents/implementer.template.md +74 -0
- package/core/agents/release.md +51 -0
- package/core/agents/review.md +85 -0
- package/core/commands/align-ds.md +32 -0
- package/core/commands/audit.md +31 -0
- package/core/commands/brainstorm.md +48 -0
- package/core/commands/build.md +91 -0
- package/core/commands/doctor.md +50 -0
- package/core/commands/fix.md +62 -0
- package/core/commands/init-pipeline.md +32 -0
- package/core/commands/refactor.md +38 -0
- package/core/commands/review.md +68 -0
- package/core/commands/ship.md +68 -0
- package/core/commands/smoke.md +55 -0
- package/core/commands/spec.md +67 -0
- package/core/commands/update-pipeline.md +96 -0
- package/core/hooks/__pycache__/gate.cpython-312.pyc +0 -0
- package/core/hooks/gate.py +129 -0
- package/core/templates/agent-handoff.md +34 -0
- package/core/templates/brainstorm-return.md +36 -0
- package/core/templates/design-brief.md +35 -0
- package/core/templates/pr-body.md +29 -0
- package/core/templates/review-feedback.md +36 -0
- package/core/templates/spec.template.md +84 -0
- package/core/templates/steps/init-pipeline/01-detect-stack.md +40 -0
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +41 -0
- package/core/templates/steps/init-pipeline/03-draft-profile.md +10 -0
- package/core/templates/steps/init-pipeline/04-write-render.md +88 -0
- package/core/templates/steps/init-pipeline/05-report.md +12 -0
- package/dashboard/README.md +54 -0
- package/dashboard/dist/apple-touch-icon-180.png +0 -0
- package/dashboard/dist/assets/index-CoBuEdy-.js +42 -0
- package/dashboard/dist/assets/index-DN5OGW9g.css +1 -0
- package/dashboard/dist/favicon-16.png +0 -0
- package/dashboard/dist/favicon-32.png +0 -0
- package/dashboard/dist/favicon-48.png +0 -0
- package/dashboard/dist/icon-192.png +0 -0
- package/dashboard/dist/icon-512.png +0 -0
- package/dashboard/dist/index.html +16 -0
- package/dashboard/server/doctor.js +266 -0
- package/dashboard/server/fleet.js +119 -0
- package/dashboard/server/index.js +306 -0
- package/dashboard/server/kanban.js +158 -0
- package/dashboard/server/versions.js +111 -0
- package/dashboard/server/yaml.js +126 -0
- package/install.ps1 +359 -0
- package/install.sh +301 -0
- package/package.json +40 -0
- package/profile/PIPELINE.template.md +208 -0
- package/profile/SCHEMA.md +303 -0
- package/profile/cohorte.config.template.yaml +43 -0
- package/scripts/new-feature.sh.template +89 -0
- package/scripts/remove-feature.sh.template +53 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Dispatch the read-only review agent to audit the feature against its frozen spec.
|
|
3
|
+
argument-hint: <feature_id>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the **lead**. Dispatch the review for feature **$ARGUMENTS**.
|
|
7
|
+
|
|
8
|
+
> Read `PIPELINE.md` §`vcs.default_branch` (diff base) and the `surfaces`/`contract` fields.
|
|
9
|
+
>
|
|
10
|
+
> Template paths below (`.claude/templates/…`) resolve to `~/.claude/templates/…` when the core is
|
|
11
|
+
> installed globally — read whichever exists.
|
|
12
|
+
>
|
|
13
|
+
> **Kanban** (SCHEMA.md §Kanban): move card `#$ARGUMENTS` → **Review**. No-op silently if no board.
|
|
14
|
+
|
|
15
|
+
## 1. Gather the inputs for stateless reviewers
|
|
16
|
+
|
|
17
|
+
- Confirm `specs/$ARGUMENTS.md` exists.
|
|
18
|
+
- Compute the diff, and let **git do the grouping** (deterministic — don't reason it out file by file):
|
|
19
|
+
per surface, `git diff <default_branch> --name-only -- <surface.path>` is exactly that surface's changed
|
|
20
|
+
files; the **`shared` remainder** (contract file + anything outside every surface) is
|
|
21
|
+
`git diff <default_branch> --name-only -- . $(for p in <each surface.path>; do printf ':(exclude)%s ' "$p"; done)`.
|
|
22
|
+
Attach the remainder to the most relevant surface's reviewer and say so in its dispatch. A surface whose
|
|
23
|
+
`git diff` comes back empty gets no reviewer.
|
|
24
|
+
|
|
25
|
+
## 2. Dispatch review agents — one per touched surface, IN PARALLEL
|
|
26
|
+
|
|
27
|
+
Spawn ONE `review` agent per surface that has changed files, in a **single message** (one Task call
|
|
28
|
+
each, like `/build`) so they run concurrently — NEVER serially: review wall-clock must be the
|
|
29
|
+
slowest surface, not the sum. A diff touching a single surface ⇒ a single reviewer. For each:
|
|
30
|
+
|
|
31
|
+
> `subagent_type: review` — "Review feature `$ARGUMENTS` — **scope: the `<surface.key>` surface
|
|
32
|
+
> only**. Read `PIPELINE.md` first (its flags + §Conventions/§Testing for `<surface.key>`). Spec:
|
|
33
|
+
> `specs/$ARGUMENTS.md` (source of truth). Contract: `<contract.path>/$ARGUMENTS.<ext>`. Changed
|
|
34
|
+
> files in your scope (diff vs `<default_branch>`): <list, with the `--stat` counts>. Review the
|
|
35
|
+
> diff hunks + immediate context — not whole files. Check spec conformance first, then correctness,
|
|
36
|
+
> security, conventions, RBAC/mobile-first _if the profile enables them_, and TDD coverage. Emit
|
|
37
|
+
> the REVIEW REPORT per `.claude/templates/review-feedback.md` — every finding self-sufficient
|
|
38
|
+
> (`file:line` · severity · type · concrete fix)."
|
|
39
|
+
|
|
40
|
+
Note the epoch (`date +%s`) just before dispatching — §3's metrics lines need the wall-clock.
|
|
41
|
+
|
|
42
|
+
## 3. Merge & relay the verdict
|
|
43
|
+
|
|
44
|
+
Merge the returned reports into **one** REVIEW REPORT (same template): findings concatenated and
|
|
45
|
+
re-ordered by severity, counts summed, duplicates collapsed, verdict = the worst returned
|
|
46
|
+
(`BLOCK` > `REVISE` > `SHIP`). Append one line per reviewer to `.claude/pipeline-metrics.jsonl`
|
|
47
|
+
(gitignored): `{"ts":"<ISO>","feature":"$ARGUMENTS","phase":"review","surface":"<key>","seconds":<wall-clock>,"result":"<verdict>:<finding count>"}`.
|
|
48
|
+
Print the report **and stage it to `specs/reports/$ARGUMENTS.md`** (overwrite) — a gitignored buffer so
|
|
49
|
+
a `/fix` after a `/clear` can still read the findings; the `specs/reports/` subfolder is skipped by the
|
|
50
|
+
non-recursive `specs/*.md` glob, so it's never mistaken for a spec (no phantom card, no bogus stage). Then:
|
|
51
|
+
|
|
52
|
+
- **SHIP** → a SHIP verdict *is* the pipeline's statement that the feature meets its Definition of
|
|
53
|
+
Done, so **tick the DoD**: in `specs/$ARGUMENTS.md` §`Acceptance criteria / DoD`, flip each `- [ ]`
|
|
54
|
+
→ `- [x]` for the criteria the pipeline has actually verified — spec conformance + `ui_language`
|
|
55
|
+
copy (this review), tests · lint · typecheck (a green `/build`), mobile-first + runtime flows (a
|
|
56
|
+
prior `/smoke`). **Leave `- [ ]` (and say which) any item whose verifying stage didn't run this
|
|
57
|
+
cycle** — e.g. no `/smoke` ⇒ the mobile-first / runtime item stays open. Ticking is the lead's job
|
|
58
|
+
(the reviewer is read-only). **Then stamp the freshness gate** so `/ship` can refuse to ship code
|
|
59
|
+
edited after this verdict: compute `BASE=$(git merge-base <default_branch> HEAD)` and write into the
|
|
60
|
+
spec front-matter `reviewed_base: $BASE` plus
|
|
61
|
+
`reviewed_digest: $(git diff $BASE -- . ':(exclude)specs/' | sha256sum | cut -c1-16)` — the fingerprint
|
|
62
|
+
of exactly the source you just reviewed (specs excluded, so DoD ticks + the ship status flip don't
|
|
63
|
+
trip it). Then tell the human they can `/ship` — _the handoff is fully on disk, so `/clear` first is safe._
|
|
64
|
+
- **REVISE / BLOCK**, or any finding of any severity → tell the human to run **`/fix $ARGUMENTS`** —
|
|
65
|
+
it appends the report to the spec's `## Remediation` and re-dispatches ONLY the surfaces with
|
|
66
|
+
findings. The full path (`/spec` Mode B then `/build`) remains for findings that change the
|
|
67
|
+
contract in ways that ripple into clean surfaces. _The report is staged to `specs/reports/$ARGUMENTS.md`,
|
|
68
|
+
so you can `/clear` before `/fix` — it reads the findings back from disk._
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: At a SHIP verdict, dispatch the release agent to commit, push, and open the PR (with your confirmation).
|
|
3
|
+
argument-hint: <feature_id>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the **lead**. Ship feature **$ARGUMENTS**. This is the outward-facing gate.
|
|
7
|
+
|
|
8
|
+
> Read `PIPELINE.md` §`vcs` (host, remote, default_branch, feature_branch_prefix).
|
|
9
|
+
>
|
|
10
|
+
> **Kanban** (SCHEMA.md §Kanban) is mirrored in **explicit steps** below, not as an afterthought:
|
|
11
|
+
> §1 moves the card → **Ship**; §4 moves it → **Shipped** and writes the PR number. No-op silently if
|
|
12
|
+
> no board. Do not skip §4's move — a shipped feature whose card is stuck in an earlier column is the
|
|
13
|
+
> bug this ordering prevents.
|
|
14
|
+
|
|
15
|
+
## 1. Pre-flight (confirm before doing anything irreversible)
|
|
16
|
+
|
|
17
|
+
- Confirm the latest `/review` returned **SHIP** (no CRITICAL, no security). If not reviewed, or the
|
|
18
|
+
verdict was REVISE/BLOCK, stop and say so.
|
|
19
|
+
- **Freshness gate** — the reviewed code must be exactly what ships. If the spec front-matter carries
|
|
20
|
+
`reviewed_base` + `reviewed_digest`, recompute
|
|
21
|
+
`git diff <reviewed_base> -- . ':(exclude)specs/' | sha256sum | cut -c1-16` and compare to
|
|
22
|
+
`reviewed_digest`. **Match** ⇒ source unchanged since the SHIP verdict, proceed. **Mismatch** ⇒ source
|
|
23
|
+
(or the contract) was edited after review — the verdict is **stale**: stop and tell the human to re-run
|
|
24
|
+
`/review $ARGUMENTS` before shipping. Missing fields (spec predates the gate) ⇒ skip, don't block.
|
|
25
|
+
- **DoD gate (verify, don't tick — `/review` owns the ticking).** Read `specs/$ARGUMENTS.md`
|
|
26
|
+
§`Acceptance criteria / DoD`; if any item is still `- [ ]`, list the open ones and ask the human to
|
|
27
|
+
confirm shipping anyway (they may be deferred on purpose — e.g. a UI item on a backend-only feature).
|
|
28
|
+
All `- [x]` ⇒ proceed silently.
|
|
29
|
+
- Show `git status` + `git diff --stat`; confirm the branch is `<feature_branch_prefix>$ARGUMENTS`.
|
|
30
|
+
- **Ask the human to confirm** they want to commit, push, and open the PR. Wait for yes.
|
|
31
|
+
- After the yes: **move card `#$ARGUMENTS` → the `ship` column** (SCHEMA.md §Kanban "Move a card"). No-op if no board.
|
|
32
|
+
|
|
33
|
+
## 2. Mark the spec shipped (BEFORE dispatch, so it ships in the same commit)
|
|
34
|
+
|
|
35
|
+
Once the human confirms, edit `specs/$ARGUMENTS.md` front-matter `status: → shipped` — **before**
|
|
36
|
+
dispatching the release agent, so the status flip is part of the tree it commits (otherwise it lands
|
|
37
|
+
uncommitted after the PR opens). Only flip after the human's "yes"; if they decline, leave it.
|
|
38
|
+
|
|
39
|
+
## 3. Dispatch the `release` agent
|
|
40
|
+
|
|
41
|
+
Spawn one agent (`subagent_type: release`): "Release feature `$ARGUMENTS` on branch
|
|
42
|
+
`<feature_branch_prefix>$ARGUMENTS`. Read `PIPELINE.md` §vcs first. Spec: `specs/$ARGUMENTS.md` (already
|
|
43
|
+
`status: shipped` — stage it). Write conventional commit(s), push (no force), open the PR (use `gh` if
|
|
44
|
+
`host: github` + available; else emit the compare URL + drafted PR body from `.claude/templates/pr-body.md`).
|
|
45
|
+
Stage **all** the feature's changes including `specs/$ARGUMENTS.md`. Never edit source, never force-push,
|
|
46
|
+
never run migrations."
|
|
47
|
+
|
|
48
|
+
## 4. Relay + move the card to Shipped (do not skip)
|
|
49
|
+
|
|
50
|
+
Print the release agent's report: commit SHA(s), pushed branch, PR URL (or compare URL + drafted body).
|
|
51
|
+
Confirm `specs/$ARGUMENTS.md` was committed as `status: shipped` (part of the release commit).
|
|
52
|
+
|
|
53
|
+
**Move the card to Shipped — required, and verify it actually moved.** Move card `#$ARGUMENTS` → the
|
|
54
|
+
`shipped` column (SCHEMA.md §Kanban "Move a card") and **append the PR number** so the line reads
|
|
55
|
+
`- [ ] <title> #$ARGUMENTS — PR #<num>`. Take `<num>` from the PR URL (`…/pull/13` ⇒ `13`); **always write
|
|
56
|
+
it when a PR was created** (the `gh` path) — it is what the dashboard turns into a PR link. If only a
|
|
57
|
+
compare URL was emitted (no PR yet), move the card without a number. Then **re-read the board** and confirm
|
|
58
|
+
the card sits under the `shipped` heading with no stale copy left under its previous column. No board ⇒
|
|
59
|
+
skip silently.
|
|
60
|
+
|
|
61
|
+
## 5. After the PR — CI gate + teardown
|
|
62
|
+
|
|
63
|
+
- If `host: github` and `gh` is available, watch the PR's checks (`gh pr checks <url> --watch`) and
|
|
64
|
+
report the result — the human merges only on green. A red check ⇒ back to `/fix $ARGUMENTS`.
|
|
65
|
+
- Once the human confirms the PR is **merged**: if `isolation.enabled`, propose the teardown —
|
|
66
|
+
`scripts/remove-feature.sh $ARGUMENTS` (add `--drop-db` to also drop the feature db; kept by
|
|
67
|
+
default). It removes the worktree, deletes the merged branch, frees the slot. Never run it before
|
|
68
|
+
the merge is confirmed, and only with the human's go-ahead (the gate will ask anyway).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exercise the built feature end-to-end in its worktree — infra up, migrations, contract endpoints, key UI flows, visual check vs design — before /review.
|
|
3
|
+
argument-hint: <feature_id>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the **lead**. Smoke-test feature **$ARGUMENTS** — actually run it. `/review` audits the code
|
|
7
|
+
read-only; nobody has executed it yet. You verify it *works*, you never fix it here (failures go
|
|
8
|
+
through `/fix`). Observe honestly: report what happened, not what should have happened.
|
|
9
|
+
|
|
10
|
+
> Read `PIPELINE.md` §`pipeline-profile`: `commands` (migrate/dev), `isolation` (worktree, slot
|
|
11
|
+
> ports, db), `contract`, `design`, `surfaces`, and the spec `specs/$ARGUMENTS.md` (§5 contract,
|
|
12
|
+
> §9 acceptance).
|
|
13
|
+
>
|
|
14
|
+
> **Kanban** (SCHEMA.md §Kanban): move card `#$ARGUMENTS` → **Review**. No-op silently if no board.
|
|
15
|
+
|
|
16
|
+
## 1. Bring the feature up
|
|
17
|
+
|
|
18
|
+
- Work in the feature's checkout: with `isolation.enabled`, the sibling worktree
|
|
19
|
+
(`../<slug>-$ARGUMENTS`, its slot's ports + db from `.worktrees/slots.tsv`); otherwise the main
|
|
20
|
+
checkout on the feature branch.
|
|
21
|
+
- Infra as needed: the compose stack if one is declared (the gate will ask — that's expected),
|
|
22
|
+
then `commands.migrate`, then `commands.dev` **in the background**. Wait for ready (poll the
|
|
23
|
+
ports), don't assume.
|
|
24
|
+
|
|
25
|
+
## 2. Exercise the contract (the real server, not the tests)
|
|
26
|
+
|
|
27
|
+
- Hit a representative set of spec §5 endpoints with `curl`: every route domain, every auth level,
|
|
28
|
+
at least one error case per class (validation `422`, unauthenticated `401`, wrong-role `403`,
|
|
29
|
+
conflict `409`). Compare status + response envelope against the contract.
|
|
30
|
+
- If `rbac.enabled`: verify at least one denial per role boundary the spec declares.
|
|
31
|
+
- A mismatch is a FAIL entry with the exact command, expected, and actual — precise enough for a
|
|
32
|
+
stateless `/fix` agent.
|
|
33
|
+
|
|
34
|
+
## 3. Exercise the UI (only if a touched surface has `uses_design`)
|
|
35
|
+
|
|
36
|
+
- Drive the spec §8 flows against the running app, **mobile viewport first** (375px), then desktop.
|
|
37
|
+
- If a browser/screenshot tool is available (a project driver, playwright, an agent browser), capture
|
|
38
|
+
each §8 screen and compare against the feature's design pages: each `design_files` entry is a full
|
|
39
|
+
`https://claude.ai/design/p/<projectId>?file=<file>` link — extract its `<projectId>` (the `/p/…`
|
|
40
|
+
segment) + `<file>` (the `?file=` query) and fetch read-only via `DesignSync get_file(<projectId>,
|
|
41
|
+
<file>)`. Compare layout, states (empty/loading/error/suppressed…), copy language. Note deviations.
|
|
42
|
+
- No browser tooling available ⇒ **say so and skip the visual diff** — never claim a visual check
|
|
43
|
+
you didn't perform.
|
|
44
|
+
|
|
45
|
+
## 4. SMOKE REPORT
|
|
46
|
+
|
|
47
|
+
- One line per check: ✅/❌ · what was exercised · (on ❌) command → expected vs actual.
|
|
48
|
+
- **Stage the full report to `specs/reports/$ARGUMENTS.md`** (overwrite) — the same gitignored buffer
|
|
49
|
+
`/review` uses (subfolder ⇒ skipped by the non-recursive `specs/*.md` glob), so a `/fix` after a
|
|
50
|
+
`/clear` still has the failures.
|
|
51
|
+
- Verdict **PASS** (all green) → tell the human to run `/review $ARGUMENTS`.
|
|
52
|
+
Verdict **FAIL** → the failures are findings: feed them to `/fix $ARGUMENTS`, re-run `/smoke`
|
|
53
|
+
after. _Either way the handoff is on disk — `/clear` before the next command is safe._
|
|
54
|
+
- Append one metrics line to `.claude/pipeline-metrics.jsonl` (see `/build` §4, `phase: "smoke"`).
|
|
55
|
+
- Tear down what you started (kill the dev server); leave shared infra as you found it.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Interactively capture a frozen feature spec (or apply a review return) into specs/<id>.md.
|
|
3
|
+
argument-hint: [paste brainstorm return OR review report]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You run the **spec** step in the main thread — interactive, with the human. Pasted input below:
|
|
7
|
+
|
|
8
|
+
**$ARGUMENTS**
|
|
9
|
+
|
|
10
|
+
> Read `PIPELINE.md` first: `contract` (mechanism/path — so §5 names the right schema types),
|
|
11
|
+
> `design.enabled` (whether §8 matters), and §Conventions. Use `specs/_template.md` as the section list.
|
|
12
|
+
>
|
|
13
|
+
> Template paths below (`.claude/templates/…`) resolve to `~/.claude/templates/…` when the core is
|
|
14
|
+
> installed globally — read whichever exists.
|
|
15
|
+
>
|
|
16
|
+
> **Kanban** (SCHEMA.md §Kanban): when the spec opens, move card `#<feature_id>` → **Spec**; on freeze
|
|
17
|
+
> (`status: frozen`, Mode A) → **Ready to build**. No-op silently if no board is configured.
|
|
18
|
+
|
|
19
|
+
Detect the mode from the pasted content:
|
|
20
|
+
|
|
21
|
+
## Mode A — new spec (input is a brainstorm return, or empty)
|
|
22
|
+
|
|
23
|
+
1. If empty, ask the human to paste the brainstorm return (or describe the feature) and wait.
|
|
24
|
+
2. Derive a `feature_id` (kebab-case slug). Confirm it.
|
|
25
|
+
3. Walk the human through each section of `specs/_template.md`, **section by section**, with focused
|
|
26
|
+
questions. The critical one is **§5 API CONTRACT** — pin down every endpoint/interface (method, path,
|
|
27
|
+
auth/role, request fields with types/validation, success envelope + data shape, and every error case),
|
|
28
|
+
and name the exact schema/types that will live in the contract file (`contract.path/<id>.<ext>` in the
|
|
29
|
+
profile's `mechanism`). Don't move on until frontend and backend could each build from it with zero
|
|
30
|
+
further questions. _If `contract.enabled` is false, capture the interface precisely in prose instead._
|
|
31
|
+
4. Fill **§8 Design brief** (only if `design.enabled` / the feature has UI) so it's self-contained for the
|
|
32
|
+
design step: screens, states, components, responsive notes.
|
|
33
|
+
4b. **New-surface heads-up.** If the feature clearly introduces an area no existing `surfaces[].path`
|
|
34
|
+
owns (a new service/app/top-level module), note it in the spec (a line in the relevant task section:
|
|
35
|
+
`> needs new surface: <proposed key/path>`). Don't render agents here — `/build` §1.5 auto-reconciles
|
|
36
|
+
it. This is just so the human isn't surprised when `/build` proposes a new agent.
|
|
37
|
+
5. When the human validates, **freeze**: write `specs/<id>.md` (`status: frozen`, front-matter filled).
|
|
38
|
+
Create the file — do not ask the human to. **Postcondition:** `grep -q '^status: frozen' specs/<id>.md`
|
|
39
|
+
— if it fails the freeze didn't land; fix it before pointing the human at `/build`.
|
|
40
|
+
6. Emit the **spec return** — the §8 design brief rendered via `.claude/templates/design-brief.md`.
|
|
41
|
+
_Only if `design.enabled` / the feature has UI; skip entirely for a backend-only feature._
|
|
42
|
+
- **Write it to `specs/design/<id>.md`** (a standalone `.md`, versioned with the spec). Create the
|
|
43
|
+
file — do not ask the human to. Keep it in the `specs/design/` subfolder, **not** `specs/<id>....md`:
|
|
44
|
+
the `specs/*.md` glob that drives the kanban backfill and `/doctor` is non-recursive, so a brief in
|
|
45
|
+
the subfolder never gets mistaken for a spec (no phantom card, no bogus stage). This is a derived
|
|
46
|
+
artifact that mirrors §8 — overwrite it on every freeze so it never drifts from the spec.
|
|
47
|
+
- Then print the same content in a copy-paste block and tell the human: paste it into the design tool
|
|
48
|
+
(if any — typically a fresh design project for this feature) or just open `specs/design/<id>.md`,
|
|
49
|
+
then run `/build <id>` and hand its design gate the resulting page link(s) — a full
|
|
50
|
+
`https://claude.ai/design/p/<projectId>?file=<file>` link carries its own project + page, no profile
|
|
51
|
+
change needed. (They can also paste the links into the spec's `design_files`
|
|
52
|
+
themselves.) _The frozen spec + `specs/design/<id>.md` are the whole handoff — `/clear` before
|
|
53
|
+
`/build` is safe._
|
|
54
|
+
|
|
55
|
+
## Mode B — review return (input is a REVIEW REPORT)
|
|
56
|
+
|
|
57
|
+
1. Read the report — pasted as input, or (if the context was cleared) read from `specs/reports/<id>.md`,
|
|
58
|
+
where `/review` stages its last report. Identify `feature_id` from its header; open `specs/<id>.md`.
|
|
59
|
+
2. Append each finding to the spec's **`## Remediation`**, one per line:
|
|
60
|
+
`- [ ] <severity> · <file:line> · <spec-violation|quality|security> · <concrete fix>`
|
|
61
|
+
(Keep prior items; add the new round under a dated/numbered subheading.)
|
|
62
|
+
3. If a finding implies the **contract** must change, update §5 and flag it so the lead re-authors the
|
|
63
|
+
contract file.
|
|
64
|
+
4. Set `status: in-review`. Tell the human to run `/build <id>` to re-dispatch fresh agents.
|
|
65
|
+
_The spec is the whole handoff — `/clear` before `/build` is safe._
|
|
66
|
+
|
|
67
|
+
In both modes the spec is the single source of truth; agents are stateless and read only it + the diff.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
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
|
+
argument-hint: [path-to-local-checkout]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the **pipeline updater**. Refresh the installed pipeline core to the latest version of the pipeline
|
|
7
|
+
repo. The installer's `--update` mode never touches generated files: `PIPELINE.md`, rendered surface agents,
|
|
8
|
+
`gate-config.json`, `settings.json`, and the filled `~/.claude/cohorte.config.yaml` are all preserved.
|
|
9
|
+
YOU then bring those generated files up to the new core yourself (§3.5) — additively, never clobbering
|
|
10
|
+
the human's choices — so `/init-pipeline` never needs re-running for an upgrade.
|
|
11
|
+
|
|
12
|
+
## 1. Detect the install scope + current version
|
|
13
|
+
|
|
14
|
+
- **Global** install ⇒ `~/.claude/pipeline/VERSION` exists. **Bundled** ⇒ this repo's
|
|
15
|
+
`.claude/pipeline/VERSION` exists. (Both can exist; prefer the bundled one when running inside such a
|
|
16
|
+
repo, and update both if the human wants.)
|
|
17
|
+
- **Never migrate a repo between bundled and global mode on your own.** Updating means refreshing the
|
|
18
|
+
core *in its current mode*. Only migrate (e.g. delete a bundled core in favor of the global one) if
|
|
19
|
+
the human explicitly asks — and confirm before deleting anything, since it rewrites the repo's
|
|
20
|
+
committed `.claude/` and the `pipeline.json` pointer teammates rely on.
|
|
21
|
+
- Read the VERSION file(s) — a semver like `0.1.0`, possibly suffixed `(abc1234)` for from-main
|
|
22
|
+
installs, or a bare commit hash on old cores. If missing, note "unknown (pre-versioning)".
|
|
23
|
+
|
|
24
|
+
## 2. Run the update
|
|
25
|
+
|
|
26
|
+
- If `$ARGUMENTS` is a path to a local checkout of the pipeline repo (contains `core/` + `install.sh`),
|
|
27
|
+
run from there — useful when iterating on the pipeline itself:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
sh <path>/install.sh --update --global # global core
|
|
31
|
+
sh <path>/install.sh --update # bundled core of the current repo
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- Otherwise use the published npm package (preferred — installs the latest tagged release):
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npx cohorte@latest update --global # global core
|
|
38
|
+
npx cohorte@latest update # bundled core of the current repo
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- If npm/npx is unavailable, fall back to piping the installer from the repo's latest `main`:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
curl -fsSL https://raw.githubusercontent.com/TheBidouilleAgency/cohorte/main/install.sh | sh -s -- --update --global
|
|
45
|
+
# bundled: … | sh -s -- --update
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
(The piped installer clones the repo itself; `-s --` forwards the flags.)
|
|
49
|
+
|
|
50
|
+
## 3. Report old → new
|
|
51
|
+
|
|
52
|
+
Re-read the VERSION file(s) and print `old → new`. If unchanged, say the core was already up to date.
|
|
53
|
+
For a bundled repo, note that `.claude/pipeline.json`'s `core_version` was bumped and should be committed.
|
|
54
|
+
|
|
55
|
+
Then print **What's new**: read the installed `<core>/pipeline/CHANGELOG.md` and show the entries
|
|
56
|
+
between the old and new versions (most recent first). File absent ⇒ the old core predates 0.1.14 —
|
|
57
|
+
skip silently.
|
|
58
|
+
|
|
59
|
+
## 3.5 Reconcile this repo's generated files
|
|
60
|
+
|
|
61
|
+
Only when the current repo has a `PIPELINE.md`: run the **Reconcile procedure** from the installed
|
|
62
|
+
`pipeline/SCHEMA.md` §Reconcile — top up the profile's machine block with new fields at their defaults
|
|
63
|
+
(one batched question set for any genuinely new human decision, e.g. choosing a `retrieval` provider),
|
|
64
|
+
re-render the surface agents from the current `implementer.template.md`, additively patch
|
|
65
|
+
`settings.json`/`gate-config.json`, and run any newly-added capability's wiring (e.g. Serena's
|
|
66
|
+
project-scope `claude mcp add`). Even when no capability is new, **re-run the retrieval provider's
|
|
67
|
+
health check** (SCHEMA.md §Code retrieval: CLI resolvable from PATH, `.mcp.json` entry present —
|
|
68
|
+
upgrading a bare `serena` entry to the PATH-proof launcher form, `.serena/` gitignored, server
|
|
69
|
+
actually connected) and repair whatever fails — wiring that worked at
|
|
70
|
+
init can rot (PATH changes, uninstalls, hand-edits). Report what was reconciled; if nothing was
|
|
71
|
+
missing, say so. This is why `/init-pipeline` never needs re-running for a core upgrade.
|
|
72
|
+
|
|
73
|
+
Two of the §Reconcile steps matter specifically here:
|
|
74
|
+
|
|
75
|
+
- **Global config seed** (§Reconcile step 5): if `~/.claude/cohorte.config.yaml` is absent, seed it
|
|
76
|
+
from the template so the kanban + shared-vault config has a home. Never clobber an existing filled
|
|
77
|
+
file. Report what was seeded.
|
|
78
|
+
- **Kanban sync** (§Reconcile step 6): resolve this project's board from `kanban.boards[<PIPELINE
|
|
79
|
+
name>]`. **Not linked** → offer to link/create a board (confirm the vault + `<folder>/Tasks.md`,
|
|
80
|
+
write the `boards` entry, create the board file per §Kanban). **Linked** → verify the board file
|
|
81
|
+
exists (recreate if the human confirms) and its columns match `kanban.columns` (repair drift). Either
|
|
82
|
+
way, run the §Kanban **full sync/backfill** from `specs/*.md` — this is what adds every
|
|
83
|
+
already-developed feature to the board and repositions cards to match each spec's `status`. Report
|
|
84
|
+
cards added / moved / already-correct. Skip silently if `kanban.enabled` is false and the human
|
|
85
|
+
doesn't want to turn it on.
|
|
86
|
+
|
|
87
|
+
## 4. Tell the human the follow-ups
|
|
88
|
+
|
|
89
|
+
- **Restart / reload the Claude Code session** so it picks up updated commands, agents, and any
|
|
90
|
+
newly-registered MCP server.
|
|
91
|
+
- **Other repos using the global core:** their core is already fresh, but reconcile is per-repo — run
|
|
92
|
+
`/update-pipeline` inside each (it will skip the already-done core update and just reconcile).
|
|
93
|
+
- **Commit** the reconciled files (`PIPELINE.md`, `.claude/`, `.mcp.json` if added) so teammates get them.
|
|
94
|
+
- The kanban config is global and user-scoped
|
|
95
|
+
(`~/.claude/cohorte.config.yaml`) — never committed. The core update never touches it; only the
|
|
96
|
+
reconcile above seeds the file and writes kanban board links (into that global file, not the repo).
|
|
Binary file
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""PreToolUse gate — generic, profile-driven.
|
|
3
|
+
|
|
4
|
+
Forces a confirmation prompt (or hard-denies) on dangerous Bash commands,
|
|
5
|
+
inspecting the FULL command string so chained forms like
|
|
6
|
+
`cd apps/api && node ace migration:run` are caught — something prefix-based
|
|
7
|
+
permission rules in settings.json miss.
|
|
8
|
+
|
|
9
|
+
Patterns come from `.claude/gate-config.json` (generated by /init-pipeline from
|
|
10
|
+
the PIPELINE.md `gate` block):
|
|
11
|
+
|
|
12
|
+
{ "deny": ["node ace migration:fresh", ...],
|
|
13
|
+
"ask": ["git commit", "git push", "node ace migration:run", ...] }
|
|
14
|
+
|
|
15
|
+
A pattern matches a command segment when the segment *contains* the pattern
|
|
16
|
+
(after normalizing whitespace). `deny` wins over `ask`. If the config is missing
|
|
17
|
+
or unreadable, the hook stays silent (exit 0) and lets settings.json decide.
|
|
18
|
+
|
|
19
|
+
Protocol: reads the PreToolUse payload on stdin; emits a JSON permissionDecision
|
|
20
|
+
of "deny" or "ask" on a match; otherwise exits 0 silently.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import re
|
|
26
|
+
import subprocess
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
SPLIT = re.compile(r"&&|\|\||[;|\n]")
|
|
30
|
+
WS = re.compile(r"\s+")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def load_config() -> dict:
|
|
34
|
+
root = os.environ.get("CLAUDE_PROJECT_DIR", ".")
|
|
35
|
+
path = os.path.join(root, ".claude", "gate-config.json")
|
|
36
|
+
empty = {"deny": [], "ask": [], "ask_on_default_branch": [], "default_branch": "main"}
|
|
37
|
+
try:
|
|
38
|
+
with open(path, "r", encoding="utf-8") as fh:
|
|
39
|
+
cfg = json.load(fh)
|
|
40
|
+
except Exception:
|
|
41
|
+
return empty
|
|
42
|
+
return {
|
|
43
|
+
"deny": list(cfg.get("deny", [])),
|
|
44
|
+
"ask": list(cfg.get("ask", [])),
|
|
45
|
+
# Patterns gated ONLY on the default branch — allowed freely on feature branches.
|
|
46
|
+
"ask_on_default_branch": list(cfg.get("ask_on_default_branch", [])),
|
|
47
|
+
"default_branch": cfg.get("default_branch", "main") or "main",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def norm(s: str) -> str:
|
|
52
|
+
return WS.sub(" ", s.strip())
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def current_branch():
|
|
56
|
+
"""The checked-out branch of CLAUDE_PROJECT_DIR, or None (not a repo / detached / git absent)."""
|
|
57
|
+
root = os.environ.get("CLAUDE_PROJECT_DIR", ".")
|
|
58
|
+
try:
|
|
59
|
+
out = subprocess.run(
|
|
60
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
61
|
+
cwd=root, capture_output=True, text=True, timeout=3,
|
|
62
|
+
)
|
|
63
|
+
if out.returncode == 0:
|
|
64
|
+
return out.stdout.strip() or None
|
|
65
|
+
except Exception:
|
|
66
|
+
pass
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def main() -> int:
|
|
71
|
+
try:
|
|
72
|
+
payload = json.load(sys.stdin)
|
|
73
|
+
except Exception:
|
|
74
|
+
return 0 # malformed input → don't block
|
|
75
|
+
|
|
76
|
+
if payload.get("tool_name") != "Bash":
|
|
77
|
+
return 0
|
|
78
|
+
|
|
79
|
+
command = (payload.get("tool_input") or {}).get("command", "") or ""
|
|
80
|
+
cfg = load_config()
|
|
81
|
+
deny, ask, branch_gated = cfg["deny"], cfg["ask"], cfg["ask_on_default_branch"]
|
|
82
|
+
default = cfg["default_branch"]
|
|
83
|
+
if not deny and not ask and not branch_gated:
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
# Branch-conditional patterns (e.g. git/docker) are gated only on the default branch;
|
|
87
|
+
# on a feature branch they run freely. Unknown branch (no repo / detached / no git) ⇒
|
|
88
|
+
# be conservative and gate. Resolve the branch once, lazily.
|
|
89
|
+
on_default = False
|
|
90
|
+
if branch_gated:
|
|
91
|
+
branch = current_branch()
|
|
92
|
+
on_default = branch is None or branch == default
|
|
93
|
+
|
|
94
|
+
for raw in SPLIT.split(command):
|
|
95
|
+
seg = norm(raw)
|
|
96
|
+
if not seg:
|
|
97
|
+
continue
|
|
98
|
+
for pat in deny:
|
|
99
|
+
if norm(pat) in seg:
|
|
100
|
+
return decide("deny", f"`{pat}` is forbidden by the project's PIPELINE.md gate.")
|
|
101
|
+
for pat in ask:
|
|
102
|
+
if norm(pat) in seg:
|
|
103
|
+
return decide("ask", f"`{pat}` is a gated command — confirm first (PIPELINE.md gate).")
|
|
104
|
+
if on_default:
|
|
105
|
+
for pat in branch_gated:
|
|
106
|
+
if norm(pat) in seg:
|
|
107
|
+
return decide("ask", f"`{pat}` is gated on the default branch `{default}` — confirm "
|
|
108
|
+
f"(PIPELINE.md gate). It runs freely on feature branches.")
|
|
109
|
+
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def decide(decision: str, reason: str) -> int:
|
|
114
|
+
print(
|
|
115
|
+
json.dumps(
|
|
116
|
+
{
|
|
117
|
+
"hookSpecificOutput": {
|
|
118
|
+
"hookEventName": "PreToolUse",
|
|
119
|
+
"permissionDecision": decision,
|
|
120
|
+
"permissionDecisionReason": reason,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
return 0
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if __name__ == "__main__":
|
|
129
|
+
sys.exit(main())
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# HANDOFF — <surface> · `<feature_id>`
|
|
2
|
+
|
|
3
|
+
Branch: <feature_branch_prefix><feature_id>
|
|
4
|
+
Commit/diff base: <default_branch...HEAD or "uncommitted working tree">
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
|
|
8
|
+
<2–4 lines: what you built and the approach>
|
|
9
|
+
|
|
10
|
+
## Files touched
|
|
11
|
+
|
|
12
|
+
- `path/to/file` — <what & why>
|
|
13
|
+
|
|
14
|
+
## Migrations / schema (if any)
|
|
15
|
+
|
|
16
|
+
- `<name>` — <additive change> · run with <PIPELINE.md commands.migrate>
|
|
17
|
+
|
|
18
|
+
## Tests
|
|
19
|
+
|
|
20
|
+
- Added: <test files>
|
|
21
|
+
- Run: <this surface's test_cmd> · result: <pass/fail + counts>
|
|
22
|
+
|
|
23
|
+
## Contract adherence
|
|
24
|
+
|
|
25
|
+
- [ ] Implemented exactly to the frozen contract (`<contract.path>/<feature_id>.<ext>`)
|
|
26
|
+
- Mismatches / assumptions: <none, or describe — DO NOT edit the contract; report instead>
|
|
27
|
+
|
|
28
|
+
## Remediation addressed (fix loops only)
|
|
29
|
+
|
|
30
|
+
- <which `## Remediation` items you fixed, by file:line>
|
|
31
|
+
|
|
32
|
+
## TODO / not done
|
|
33
|
+
|
|
34
|
+
- <anything deferred, blocked, or out of scope>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# BRAINSTORM RETURN — <feature title>
|
|
2
|
+
|
|
3
|
+
> Paste this into `/spec`.
|
|
4
|
+
|
|
5
|
+
**One-liner:** <what we're building, in one sentence>
|
|
6
|
+
|
|
7
|
+
## Problem & value
|
|
8
|
+
|
|
9
|
+
<the problem, who has it, why it matters>
|
|
10
|
+
|
|
11
|
+
## Scope
|
|
12
|
+
|
|
13
|
+
- **In:** <…>
|
|
14
|
+
- **Out (non-goals):** <…>
|
|
15
|
+
|
|
16
|
+
## Roles affected
|
|
17
|
+
|
|
18
|
+
> Only if the profile has `rbac.enabled` — list the roles from `PIPELINE.md` §rbac and what each does;
|
|
19
|
+
> explicitly note the lowest-privilege experience. Omit this section if there is no RBAC.
|
|
20
|
+
|
|
21
|
+
<which roles do what>
|
|
22
|
+
|
|
23
|
+
## Rough shape
|
|
24
|
+
|
|
25
|
+
- **Data:** <entities / fields touched>
|
|
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>
|
|
28
|
+
|
|
29
|
+
## Risks & open questions
|
|
30
|
+
|
|
31
|
+
- <risk / unknown>
|
|
32
|
+
- <open question for /spec to resolve>
|
|
33
|
+
|
|
34
|
+
## Panel dissent (what was contested)
|
|
35
|
+
|
|
36
|
+
<the strongest disagreement the panel surfaced — so /spec doesn't paper over it>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# DESIGN BRIEF — <feature title> (`<feature_id>`)
|
|
2
|
+
|
|
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
|
|
5
|
+
> the project has no UI.
|
|
6
|
+
|
|
7
|
+
**Goal:** <one line — what the user accomplishes>
|
|
8
|
+
|
|
9
|
+
**Design system:** use the existing UI kit (`PIPELINE.md` §design → `ui_kit_path`). Mobile-first.
|
|
10
|
+
|
|
11
|
+
## Screens / views
|
|
12
|
+
|
|
13
|
+
For each: purpose, who sees it (role, if RBAC), and the key elements.
|
|
14
|
+
|
|
15
|
+
- **<screen name>** — <purpose> · roles: <…>
|
|
16
|
+
- Elements: <…>
|
|
17
|
+
- States: empty · loading · error · success
|
|
18
|
+
|
|
19
|
+
## Flows
|
|
20
|
+
|
|
21
|
+
<step-by-step of the main user journey, and any role-specific variation>
|
|
22
|
+
|
|
23
|
+
## Responsive
|
|
24
|
+
|
|
25
|
+
- Mobile (base): <layout>
|
|
26
|
+
- Tablet (`md:`): <changes>
|
|
27
|
+
- Desktop (`lg:`): <changes>
|
|
28
|
+
|
|
29
|
+
## Data shown
|
|
30
|
+
|
|
31
|
+
<what fields/values appear on screen — must match the contract in spec §5>
|
|
32
|
+
|
|
33
|
+
## Notes / constraints
|
|
34
|
+
|
|
35
|
+
<accessibility · copy in the profile's `ui_language` · edge cases · theming constraints>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
## <type>(<scope>): <feature title>
|
|
2
|
+
|
|
3
|
+
**feature_id:** `<feature_id>` · closes/relates: <issue if any>
|
|
4
|
+
|
|
5
|
+
### What & why
|
|
6
|
+
|
|
7
|
+
<2–4 lines: what this ships and the value, from the spec goal>
|
|
8
|
+
|
|
9
|
+
### Contract
|
|
10
|
+
|
|
11
|
+
<the interface + contract types added/changed — from spec §5 (omit if contract.enabled is false)>
|
|
12
|
+
|
|
13
|
+
### Changes
|
|
14
|
+
|
|
15
|
+
> One bullet per surface (from `PIPELINE.md` §surfaces), plus the shared contract.
|
|
16
|
+
|
|
17
|
+
- **<surface.key>:** <what changed>
|
|
18
|
+
- **Shared:** `<contract.path>/<feature_id>.<ext>`
|
|
19
|
+
|
|
20
|
+
### Tests
|
|
21
|
+
|
|
22
|
+
- <per surface: test runner · coverage>
|
|
23
|
+
- <PIPELINE.md commands.lint · typecheck · test> — green
|
|
24
|
+
|
|
25
|
+
### Review
|
|
26
|
+
|
|
27
|
+
Verdict: **SHIP** · remediation rounds: <n>
|
|
28
|
+
|
|
29
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|