cohorte 1.6.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +84 -2
  2. package/README.md +57 -57
  3. package/bin/cli.js +23 -15
  4. package/core/agents/implementer.template.md +3 -3
  5. package/core/agents/release.md +1 -1
  6. package/core/agents/review.md +3 -3
  7. package/core/commands/{audit.md → cohorte-audit.md} +3 -3
  8. package/core/commands/{brainstorm.md → cohorte-brainstorm.md} +4 -4
  9. package/core/commands/{build.md → cohorte-build.md} +15 -15
  10. package/core/commands/{doctor.md → cohorte-doctor.md} +17 -9
  11. package/core/commands/{fix.md → cohorte-fix.md} +15 -13
  12. package/core/commands/{init-pipeline.md → cohorte-init-pipeline.md} +1 -1
  13. package/core/commands/cohorte-loop.md +110 -0
  14. package/core/commands/{refactor.md → cohorte-refactor.md} +3 -3
  15. package/core/commands/{review.md → cohorte-review.md} +20 -19
  16. package/core/commands/{ship.md → cohorte-ship.md} +5 -5
  17. package/core/commands/{spec.md → cohorte-spec.md} +13 -13
  18. package/core/commands/{update-pipeline.md → cohorte-update-pipeline.md} +11 -6
  19. package/core/hooks/gate.py +101 -6
  20. package/core/templates/brainstorm-return.md +4 -4
  21. package/core/templates/decisions.template.md +1 -1
  22. package/core/templates/design-brief.md +1 -1
  23. package/core/templates/spec.template.md +7 -7
  24. package/core/templates/steps/init-pipeline/01-detect-stack.md +1 -1
  25. package/core/templates/steps/init-pipeline/02-interview-gaps.md +6 -6
  26. package/core/templates/steps/init-pipeline/03-draft-profile.md +1 -1
  27. package/core/templates/steps/init-pipeline/04-write-render.md +16 -12
  28. package/core/templates/steps/init-pipeline/05-report.md +5 -5
  29. package/core/workflows/audit.js +6 -6
  30. package/core/workflows/refactor.js +14 -14
  31. package/core/workflows/review.js +22 -22
  32. package/dashboard/README.md +2 -2
  33. package/dashboard/dist/assets/{index-DYyn4p93.js → index-P1I1JGtj.js} +2 -2
  34. package/dashboard/dist/index.html +1 -1
  35. package/dashboard/server/doctor.js +69 -19
  36. package/dashboard/server/index.js +5 -5
  37. package/dashboard/server/metrics.js +1 -1
  38. package/install.ps1 +23 -14
  39. package/install.sh +24 -14
  40. package/package.json +2 -2
  41. package/profile/PIPELINE.template.md +17 -16
  42. package/profile/SCHEMA.md +89 -77
  43. package/profile/cohorte.config.template.yaml +8 -8
  44. package/scripts/loop-detach.sh +153 -0
  45. package/scripts/loop.sh +75 -27
  46. package/scripts/metrics/collect.mjs +17 -8
  47. package/scripts/new-feature.sh.template +3 -3
  48. package/scripts/preflight.sh +40 -4
  49. package/scripts/remove-feature.sh.template +2 -2
  50. package/scripts/test-dashboard.mjs +34 -7
  51. package/scripts/test-gate.mjs +58 -0
  52. package/scripts/test-loop.mjs +49 -7
  53. package/scripts/test-metrics.mjs +23 -11
  54. package/scripts/test-workflows.mjs +7 -7
  55. package/scripts/validate-core.mjs +45 -23
  56. package/core/commands/drive.md +0 -80
  57. /package/core/commands/{align-ds.md → cohorte-align-ds.md} +0 -0
@@ -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,14 @@ 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
- Three of the §Reconcile steps matter specifically here:
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.
88
93
 
89
94
  - **Spec-template top-up** (§Reconcile step 7): `specs/_template.md` was seeded at install and never
90
95
  refreshed since, so add the front-matter fields the current `templates/spec.template.md` has and the
@@ -111,7 +116,7 @@ Three of the §Reconcile steps matter specifically here:
111
116
  - **Restart / reload the Claude Code session** so it picks up updated commands, agents, and any
112
117
  newly-registered MCP server.
113
118
  - **Other repos using the global core:** their core is already fresh, but reconcile is per-repo — run
114
- `/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).
115
120
  - **Commit** the reconciled files (`PIPELINE.md`, `.claude/`, `.mcp.json` if added) so teammates get them.
116
121
  - The kanban config is global and user-scoped
117
122
  (`~/.claude/cohorte.config.yaml`) — never committed. The core update never touches it; only the
@@ -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
- epoch_s, _, sha = raw.partition(" ")
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
- heads = known_heads(session_cwd(payload))
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>
@@ -5,7 +5,7 @@
5
5
  > built* (surfaces, commands, conventions); this file says *what was decided and why*.
6
6
  >
7
7
  > **Append-only. One line per decision. No prose blocks, no code.** The bound is the point: this file
8
- > is read by `/spec`, `/brainstorm` and `/audit` — the deciding stages — and by **nothing else**.
8
+ > is read by `/cohorte-spec`, `/cohorte-brainstorm` and `/cohorte-audit` — the deciding stages — and by **nothing else**.
9
9
  > Implementers and reviewers never read it: they work from the frozen contract, which already tells
10
10
  > them what to do; handing them the rationale too would cost `surfaces × dispatches` tokens per
11
11
  > feature for a fact they cannot act on.
@@ -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>
@@ -4,10 +4,10 @@ title: <Feature title>
4
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
- loop_pass: 0 # /drive bookkeeping — the review pass it is on; 0 = no loop running. Written by the driver, not by hand
8
- loop_phase: # /drive 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 /review)
10
- reviewed_digest: # sha256 (16 hex) of the reviewed source diff vs reviewed_base, specs excluded — /ship re-checks
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
11
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
12
12
  ---
13
13
 
@@ -47,7 +47,7 @@ design_files: [] # design page links — full URLs of the form https://claude.ai
47
47
 
48
48
  <repeat per endpoint/interface>
49
49
 
50
- ### Contract types — lead authors `<contract.path>/<slug>.<ext>` before /build
50
+ ### Contract types — lead authors `<contract.path>/<slug>.<ext>` before /cohorte-build
51
51
 
52
52
  - `<slug>...Request` / `<slug>...Response` / `<slug>Base` … (sketch the schemas here)
53
53
 
@@ -55,7 +55,7 @@ design_files: [] # design page links — full URLs of the form https://claude.ai
55
55
 
56
56
  > One `###` subsection per surface in `PIPELINE.md` §surfaces (e.g. backend, frontend), each TDD.
57
57
  > Surfaces are subsections of §6, never top-level sections — §8 and §9 are referenced by number
58
- > 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
59
59
  > third surface must not become a §8 of its own.
60
60
 
61
61
  ### <surface.key>
@@ -85,5 +85,5 @@ design_files: [] # design page links — full URLs of the form https://claude.ai
85
85
 
86
86
  ## Remediation
87
87
 
88
- > Filled by `/spec` in review-return mode; empty otherwise. Each item:
88
+ > Filled by `/cohorte-spec` in review-return mode; empty otherwise. Each item:
89
89
  > `[ ] <SEVERITY> · <file:line> · <spec-violation|quality|security> · <concrete fix>`
@@ -1,4 +1,4 @@
1
- # /init-pipeline · 01 Detect the stack
1
+ # /cohorte-init-pipeline · 01 Detect the stack
2
2
 
3
3
  ### Phase 1 — Detect the stack (read-only, no questions yet)
4
4
 
@@ -1,4 +1,4 @@
1
- # /init-pipeline · 02 Interview the gaps
1
+ # /cohorte-init-pipeline · 02 Interview the gaps
2
2
 
3
3
  ### Phase 2 — Interview the gaps (AskUserQuestion)
4
4
 
@@ -11,7 +11,7 @@ Ask ONLY what you couldn't confidently detect. Batch related questions. Cover:
11
11
  cost), `haiku` for purely mechanical surfaces (scaffolding), `inherit` only for surfaces with real
12
12
  design decisions worth running on the lead's model.
13
13
  - **Specialization (only if Phase 1 flagged a large + cleanly-separable surface)** — offer to split it
14
- into specialized sub-surfaces (e.g. `web-checkout`, `web-billing`) so `/build` runs them in parallel,
14
+ into specialized sub-surfaces (e.g. `web-checkout`, `web-billing`) so `/cohorte-build` runs them in parallel,
15
15
  per SCHEMA.md §Specialization. If the human accepts, apply the rules: **shared code (routing, global
16
16
  state, DS kit/tokens) becomes its own single-owner surface**, and cross-slice shapes go through the
17
17
  contract. Default to NOT splitting when boundaries are tangled or slices are tiny — coarse is fine.
@@ -20,7 +20,7 @@ Ask ONLY what you couldn't confidently detect. Batch related questions. Cover:
20
20
  Recommended option: dot/failures-only reporter (`--reporter=dot` vitest/playwright, `--silent`
21
21
  jest, `-q` pytest, `--quiet` eslint/ruff — whatever the detected runner supports). These land in
22
22
  `test_quiet_cmd`/`lint_quiet_cmd` + `commands.test_quiet`/`lint_quiet` and are what agents and the
23
- `/review` pre-flight actually run (SCHEMA.md §Output discipline). If the human declines or
23
+ `/cohorte-review` pre-flight actually run (SCHEMA.md §Output discipline). If the human declines or
24
24
  the runner has no such flag, leave `""` — consumers then fall back to `<cmd> 2>&1 | tail -40`.
25
25
  - **Contract** — mechanism (`shared-types-zod` / `openapi` / `protobuf` / `json-schema` / `none`) and
26
26
  where feature contracts are authored. If `none`, surfaces sync by the spec prose alone.
@@ -35,11 +35,11 @@ Ask ONLY what you couldn't confidently detect. Batch related questions. Cover:
35
35
  main checkout? If worktrees: DB-per-worktree? port bases? compose file?
36
36
  - **Gate** — confirm the destructive commands to hard-deny and the ones to confirm-first (seed from the
37
37
  detected DB/migration tooling + always git commit/push/merge/rebase/reset).
38
- - **Personas** — keep the default `/brainstorm` panel, or customize members for this domain?
38
+ - **Personas** — keep the default `/cohorte-brainstorm` panel, or customize members for this domain?
39
39
 
40
40
  Prefer sensible defaults from Phase 1 as the first (Recommended) option in each question.
41
41
 
42
- - **Kanban** (optional) — mirror this project's pipeline (`/brainstorm`…`/ship`) onto an Obsidian
42
+ - **Kanban** (optional) — mirror this project's pipeline (`/cohorte-brainstorm`…`/cohorte-ship`) onto an Obsidian
43
43
  Kanban board? If the human says yes: confirm the shared vault path (`obsidian.vault_path` in
44
44
  `~/.claude/cohorte.config.yaml`; ask if empty) and the board's location inside it (default
45
45
  `<ProjectName>/Tasks.md`). Phase 4 creates the board and records the link. Default: no.
@@ -50,7 +50,7 @@ Prefer sensible defaults from Phase 1 as the first (Recommended) option in each
50
50
  - **Telemetry** (optional, machine-scoped — SKIP entirely if `~/.claude/cohorte.config.yaml` already
51
51
  has a `telemetry:` block with a `consent_date`, i.e. the human already answered on this machine).
52
52
  Ask ONE opt-in question, stating exactly: _"Share anonymous usage stats with the cohorte project?
53
- One ping per pipeline phase, `/brainstorm` through `/ship`: core version, OS, phase name, duration,
53
+ One ping per pipeline phase, `/cohorte-brainstorm` through `/cohorte-ship`: core version, OS, phase name, duration,
54
54
  per-surface result counts, and a hash of the feature id — never repo names, paths, code, or IPs.
55
55
  Setup and maintenance commands never ping. Off by default; withdraw anytime
56
56
  (`telemetry.enabled: false`); erase your history anytime (SCHEMA.md §Telemetry). Default: No."_
@@ -1,4 +1,4 @@
1
- # /init-pipeline · 03 Draft the profile
1
+ # /cohorte-init-pipeline · 03 Draft the profile
2
2
 
3
3
  ### Phase 3 — Draft the profile (show, don't write yet)
4
4
 
@@ -1,4 +1,4 @@
1
- # /init-pipeline · 04 Write & render
1
+ # /cohorte-init-pipeline · 04 Write & render
2
2
 
3
3
  ### Phase 4 — Write & render (after go-ahead)
4
4
 
@@ -28,9 +28,9 @@
28
28
  `Bash(git status:*)`, `Bash(git diff:*)`, `Bash(git log:*)`, `Bash(git rev-parse:*)` — plus the
29
29
  shipped pipeline scripts for BOTH cores (`Bash(.claude/pipeline/scripts/:*)` and
30
30
  `Bash(~/.claude/pipeline/scripts/:*)` — preflight, kanban-move, telemetry-send) **plus the
31
- `bash`-prefixed form the `/drive` driver uses** (`Bash(bash .claude/pipeline/scripts/loop.sh:*)`
31
+ `bash`-prefixed form the `/cohorte-loop` driver uses** (`Bash(bash .claude/pipeline/scripts/loop.sh:*)`
32
32
  and `Bash(bash ~/.claude/pipeline/scripts/loop.sh:*)`) — those prefix rules match a command
33
- *starting* with the path, so `bash <path>` needs its own entry or `/drive` stalls on a permission
33
+ *starting* with the path, so `bash <path>` needs its own entry or `/cohorte-loop` stalls on a permission
34
34
  prompt at every launch, and the
35
35
  retrieval provider's MCP tools when wired (e.g. `mcp__serena`). Never allowlist anything matching
36
36
  a `gate.ask`/`gate.deny` pattern. Mention the human can widen it later with
@@ -49,7 +49,7 @@
49
49
  Preserve any existing custom keys.
50
50
  6. **Wire the retrieval provider** (skip if `retrieval.provider: none`):
51
51
  - **serena:** if the `serena` CLI is missing, have the human install it (`uv tool install -p 3.13
52
- serena-agent`) — or set the provider to `none` if they decline, and say `/update-pipeline` can wire
52
+ serena-agent`) — or set the provider to `none` if they decline, and say `/cohorte-update-pipeline` can wire
53
53
  it later. If the binary exists (e.g. `~/.local/bin/serena`) but `command -v serena` fails,
54
54
  recommend the PATH fix (`uv tool update-shell`, or add `~/.local/bin` to the shell profile) for
55
55
  CLI use. Then register at **project scope** (committed `.mcp.json`, portable —
@@ -82,19 +82,23 @@
82
82
  (or, without npm: curl -fsSL https://raw.githubusercontent.com/TheBidouilleAgency/cohorte/main/install.sh | sh -s -- --global;
83
83
  Windows: install.ps1 -Global from the same repo)> " }`.
84
84
  In **global** mode also add, near the top of `CLAUDE.md`, a one-liner:
85
- `> Pipeline: global core — run the installer above if /brainstorm etc. are missing.`
85
+ `> Pipeline: global core — run the installer above if /cohorte-brainstorm etc. are missing.`
86
86
  10. **CI workflow** (if `vcs.host: github` and no existing workflow already runs the profile's
87
87
  checks): with the human's go-ahead, generate `.github/workflows/pipeline-ci.yml` — on
88
88
  `pull_request` to `<default_branch>`: checkout, set up the `package_manager` toolchain,
89
89
  `commands.install`, then `commands.lint` · `commands.typecheck` · `commands.test` (+ per-surface
90
90
  `build_cmd`s that are non-empty). Derive the setup steps from the detected stack — mirror what a
91
- sibling workflow does if one exists. `/ship` watches these checks before the merge.
92
- 11. **Metrics sink & report buffer:** add `.claude/pipeline-metrics.jsonl` to `.gitignore` — `/build`,
93
- `/review` and `/fix` append per-dispatch evidence there (SCHEMA §Specialization reads it).
94
- Also add `specs/reports/` — `/review` stages its last report there so a `/fix` (or
95
- `/spec` Mode B) survives a `/clear`; it's a derived buffer, not a versioned artifact.
96
- 12. **Design system:** if `design.enabled` with a snapshot dir, note that `/align-ds` is active; else the
97
- `/align-ds` command will no-op with a clear message.
91
+ sibling workflow does if one exists. `/cohorte-ship` watches these checks before the merge.
92
+ 11. **Metrics sink, report buffer & preflight stamp:** add `.claude/pipeline-metrics.jsonl` to
93
+ `.gitignore` — `/cohorte-build`, `/cohorte-review` and `/cohorte-fix` append per-dispatch evidence
94
+ there (SCHEMA §Specialization reads it).
95
+ Also add `specs/reports/` `/cohorte-review` stages its last report there so a `/cohorte-fix` (or
96
+ `/cohorte-spec` Mode B) survives a `/clear`; it's a derived buffer, not a versioned artifact.
97
+ And `.claude/preflight.ok` a local, per-checkout freshness stamp. Versioning it breaks the phase
98
+ gate for good (a committed stamp describes the tree *before* its own commit, and it rides into every
99
+ new worktree as a green nobody earned). If it is already tracked: `git rm --cached .claude/preflight.ok`.
100
+ 12. **Design system:** if `design.enabled` with a snapshot dir, note that `/cohorte-align-ds` is active; else the
101
+ `/cohorte-align-ds` command will no-op with a clear message.
98
102
  13. **Kanban** (only if the human opted in at Phase 2): wire it per SCHEMA.md §Kanban, writing into the
99
103
  **global** `~/.claude/cohorte.config.yaml` (never into this repo — the board points at a
100
104
  personal vault). Create the file from `pipeline/cohorte.config.template.yaml` if absent; set
@@ -1,12 +1,12 @@
1
- # /init-pipeline · 05 Report
1
+ # /cohorte-init-pipeline · 05 Report
2
2
 
3
3
  ### Phase 5 — Report
4
4
 
5
5
  Print: the install mode (bundled core under `.claude/` vs global core in `~/.claude/` + the committed
6
6
  `.claude/pipeline.json` pointer), the files written/rendered, the surface→agent mapping, and the
7
7
  tailored workflow line, e.g.
8
- `/brainstorm → /spec → (design) → /build <id> → test → /review → /ship`. Tell the human to sanity-check
9
- `PIPELINE.md`, commit it, and run `/brainstorm` to start a feature. Note that this was the one-time
10
- setup: from now on `/update-pipeline` both refreshes the core AND reconciles the generated files
11
- (SCHEMA.md §Reconcile), and `/build` auto-grows surfaces — re-running `/init-pipeline` is only for deep
8
+ `/cohorte-brainstorm → /cohorte-spec → (design) → /cohorte-build <id> → test → /cohorte-review → /cohorte-ship`. Tell the human to sanity-check
9
+ `PIPELINE.md`, commit it, and run `/cohorte-brainstorm` to start a feature. Note that this was the one-time
10
+ setup: from now on `/cohorte-update-pipeline` both refreshes the core AND reconciles the generated files
11
+ (SCHEMA.md §Reconcile), and `/cohorte-build` auto-grows surfaces — re-running `/cohorte-init-pipeline` is only for deep
12
12
  stack changes (package manager, contract mechanism, surface overhaul).
@@ -1,5 +1,5 @@
1
- // cohorte — /audit as a deterministic workflow (opt-in; the conversational
2
- // /audit command remains the default path and the fallback).
1
+ // cohorte — /cohorte-audit as a deterministic workflow (opt-in; the conversational
2
+ // /cohorte-audit command remains the default path and the fallback).
3
3
  //
4
4
  // Invoke with args = {target: "<path or domain>"} (optional — default whole repo).
5
5
  //
@@ -135,7 +135,7 @@ const surfaces = Array.isArray(profile.surfaces) ? profile.surfaces : []
135
135
  // guard compares against `surfaces` — an empty list makes them all vacuously
136
136
  // pass. Fail loudly here instead of finishing with nothing done.
137
137
  if (!surfaces.length) {
138
- return { error: 'profile has no surfaces — nothing would be audited. the `yaml pipeline-profile` block in PIPELINE.md is empty or unparseable, or the profile-reader mis-returned; run /doctor' }
138
+ return { error: 'profile has no surfaces — nothing would be audited. the `yaml pipeline-profile` block in PIPELINE.md is empty or unparseable, or the profile-reader mis-returned; run /cohorte-doctor' }
139
139
  }
140
140
  const quiet = (q, full) => (q && !String(q).startsWith('<') ? q : full ? `${full} 2>&1 | tail -40` : '')
141
141
  const scope = target || 'the whole repo'
@@ -204,7 +204,7 @@ const written = await agent(
204
204
  `Write EXACTLY this content to specs/refactor-backlog.md (overwrite), then return the single word done:\n<<<BACKLOG\n${body.join('\n')}\nBACKLOG`,
205
205
  { model: 'haiku', label: 'write-backlog', effort: 'low' },
206
206
  )
207
- // Returning `backlog: <path>` when the writer died points /refactor at a file
207
+ // Returning `backlog: <path>` when the writer died points /cohorte-refactor at a file
208
208
  // that does not exist (or, worse, at the PREVIOUS run's stale backlog).
209
209
  const backlogOk = written != null && /done/i.test(String(written))
210
210
 
@@ -220,6 +220,6 @@ return {
220
220
  next: !backlogOk
221
221
  ? 'the backlog was NEVER written (writer died) — the counts above are real but nothing is on disk; re-run the audit'
222
222
  : deadDomains.length
223
- ? `re-audit ${deadDomains.join(', ')} (auditor died — not covered), then /refactor <domain>`
224
- : 'refactor a domain with /refactor <domain> (or the refactor workflow for big domains)',
223
+ ? `re-audit ${deadDomains.join(', ')} (auditor died — not covered), then /cohorte-refactor <domain>`
224
+ : 'refactor a domain with /cohorte-refactor <domain> (or the refactor workflow for big domains)',
225
225
  }
@@ -1,8 +1,8 @@
1
- // cohorte — /refactor as a deterministic workflow (opt-in; the conversational
2
- // /refactor command remains the default path and the fallback).
1
+ // cohorte — /cohorte-refactor as a deterministic workflow (opt-in; the conversational
2
+ // /cohorte-refactor command remains the default path and the fallback).
3
3
  //
4
4
  // BIG domains only: a domain with just a handful of open backlog items is
5
- // cheaper through the conversational /refactor — this script skips it and says
5
+ // cheaper through the conversational /cohorte-refactor — this script skips it and says
6
6
  // so. Invoke with args = {domains: ["backend", …]} or {domains: "all"}.
7
7
  //
8
8
  // Shape (SCHEMA.md §Workflows): profile via profile-reader (phase 0), the open
@@ -13,7 +13,7 @@
13
13
 
14
14
  export const meta = {
15
15
  name: 'cohorte-refactor',
16
- description: 'Apply the /audit refactor backlog for big domains: shared first, then parallel surface implementers, per-domain verify + one retry',
16
+ description: 'Apply the /cohorte-audit refactor backlog for big domains: shared first, then parallel surface implementers, per-domain verify + one retry',
17
17
  whenToUse: 'Only when the human explicitly asks for the refactor workflow on big domains. args = {domains: ["<surface key>", …] | "all"}.',
18
18
  phases: [
19
19
  { title: 'Profile', detail: 'PIPELINE.md → JSON via profile-reader', model: 'haiku' },
@@ -26,7 +26,7 @@ export const meta = {
26
26
  }
27
27
 
28
28
  // A domain below this many open items is not "big" — the conversational
29
- // /refactor handles it with less overhead than a workflow run.
29
+ // /cohorte-refactor handles it with less overhead than a workflow run.
30
30
  const MIN_ITEMS = 5
31
31
 
32
32
  // The Workflow runtime hands `args` to a script verbatim, so a caller that passes a
@@ -136,7 +136,7 @@ const surfaces = Array.isArray(profile.surfaces) ? profile.surfaces : []
136
136
  // guard compares against `surfaces` — an empty list makes them all vacuously
137
137
  // pass. Fail loudly here instead of finishing with nothing done.
138
138
  if (!surfaces.length) {
139
- return { error: 'profile has no surfaces — nothing would be refactored. the `yaml pipeline-profile` block in PIPELINE.md is empty or unparseable, or the profile-reader mis-returned; run /doctor' }
139
+ return { error: 'profile has no surfaces — nothing would be refactored. the `yaml pipeline-profile` block in PIPELINE.md is empty or unparseable, or the profile-reader mis-returned; run /cohorte-doctor' }
140
140
  }
141
141
  const byKey = Object.fromEntries(surfaces.map(s => [s.key, s]))
142
142
  const contractPath = (profile.contract && profile.contract.path) || ''
@@ -153,15 +153,15 @@ const backlog = await agent(
153
153
  { model: 'haiku', label: 'read-backlog', schema: OPEN, effort: 'low' },
154
154
  )
155
155
  // A dead reader is not "the backlog is empty" — reporting it as such sends the
156
- // human to re-run /audit on a backlog that is already there.
156
+ // human to re-run /cohorte-audit on a backlog that is already there.
157
157
  if (!backlog) return { error: 'the backlog-reading agent died — nothing was read; re-run the refactor workflow' }
158
158
  const open = (backlog.domains || []).filter(d => d.items.length)
159
- if (!open.length) return { error: 'no open backlog items for the requested domains — run /audit (or the audit workflow) first' }
159
+ if (!open.length) return { error: 'no open backlog items for the requested domains — run /cohorte-audit (or the audit workflow) first' }
160
160
 
161
161
  const big = open.filter(d => d.items.length >= MIN_ITEMS)
162
162
  const small = open.filter(d => d.items.length < MIN_ITEMS)
163
- for (const d of small) log(`Skipping ${d.key} (${d.items.length} open item(s) < ${MIN_ITEMS}) — use the conversational /refactor ${d.key}, it's cheaper`)
164
- if (!big.length) return { skipped: Object.fromEntries(small.map(d => [d.key, d.items.length])), reason: `every requested domain is below the ${MIN_ITEMS}-item workflow threshold — use /refactor` }
163
+ for (const d of small) log(`Skipping ${d.key} (${d.items.length} open item(s) < ${MIN_ITEMS}) — use the conversational /cohorte-refactor ${d.key}, it's cheaper`)
164
+ if (!big.length) return { skipped: Object.fromEntries(small.map(d => [d.key, d.items.length])), reason: `every requested domain is below the ${MIN_ITEMS}-item workflow threshold — use /cohorte-refactor` }
165
165
 
166
166
  const implementPrompt = d =>
167
167
  'Refactor pass on your surface (no feature spec). Read PIPELINE.md first. Add the missing tests ' +
@@ -243,7 +243,7 @@ if (clearedAll.length) {
243
243
  { model: 'haiku', label: 'tick-backlog', effort: 'low' },
244
244
  )
245
245
  // Reporting items as cleared while the backlog still shows them open means the
246
- // next /refactor re-dispatches work that is already done.
246
+ // next /cohorte-refactor re-dispatches work that is already done.
247
247
  tickedOk = ticked != null && /done/i.test(String(ticked))
248
248
  }
249
249
 
@@ -256,8 +256,8 @@ return {
256
256
  backlogTicked: tickedOk,
257
257
  next: !tickedOk
258
258
  ? `${clearedAll.length} item(s) were cleared in code but NOT ticked off specs/refactor-backlog.md ` +
259
- '(the ticking agent died) — tick them by hand, or the next /refactor re-dispatches finished work'
259
+ '(the ticking agent died) — tick them by hand, or the next /cohorte-refactor re-dispatches finished work'
260
260
  : results.some(r => r.remaining.length || !r.gatesGreen)
261
- ? 'items remain — finish them with the conversational /refactor <domain>'
262
- : 'all dispatched domains clean — optionally close with one final /audit',
261
+ ? 'items remain — finish them with the conversational /cohorte-refactor <domain>'
262
+ : 'all dispatched domains clean — optionally close with one final /cohorte-audit',
263
263
  }