cohorte 2.0.2 → 2.2.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 (53) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/README.md +41 -32
  3. package/bin/cli.js +316 -26
  4. package/core/adapter/render.js +389 -0
  5. package/core/agents/implementer.template.md +3 -3
  6. package/core/agents/release.md +7 -4
  7. package/core/agents/review.md +10 -2
  8. package/core/commands/cohorte-audit.md +2 -0
  9. package/core/commands/cohorte-brainstorm.md +3 -6
  10. package/core/commands/cohorte-build.md +14 -17
  11. package/core/commands/cohorte-doctor.md +59 -28
  12. package/core/commands/cohorte-fix.md +2 -3
  13. package/core/commands/cohorte-init-pipeline.md +7 -8
  14. package/core/commands/cohorte-refactor.md +5 -2
  15. package/core/commands/cohorte-review.md +20 -16
  16. package/core/commands/cohorte-ship.md +44 -9
  17. package/core/commands/cohorte-spec.md +3 -7
  18. package/core/commands/cohorte-update-pipeline.md +8 -8
  19. package/core/hooks/gate.py +203 -16
  20. package/core/runtimes/claude.json +73 -0
  21. package/core/runtimes/codex.json +82 -0
  22. package/core/runtimes/cursor.json +75 -0
  23. package/core/runtimes/gemini.json +75 -0
  24. package/core/runtimes/opencode.json +72 -0
  25. package/core/templates/spec.template.md +1 -3
  26. package/core/templates/steps/init-pipeline/01-detect-stack.md +7 -3
  27. package/core/templates/steps/init-pipeline/02-interview-gaps.md +8 -2
  28. package/core/templates/steps/init-pipeline/04-write-render.md +23 -17
  29. package/core/templates/steps/init-pipeline/05-report.md +1 -1
  30. package/dashboard/dist/assets/{index-P1I1JGtj.js → index-D1rsbLat.js} +1 -1
  31. package/dashboard/dist/index.html +1 -1
  32. package/dashboard/server/doctor.js +156 -69
  33. package/dashboard/server/index.js +12 -2
  34. package/dashboard/server/metrics.js +13 -6
  35. package/dashboard/server/runtime.js +115 -0
  36. package/dashboard/server/versions.js +12 -1
  37. package/install.ps1 +23 -2
  38. package/install.sh +22 -4
  39. package/package.json +6 -2
  40. package/profile/PIPELINE.template.md +27 -6
  41. package/profile/SCHEMA.md +88 -49
  42. package/scripts/kanban-move.sh +11 -1
  43. package/scripts/metrics/collect.mjs +5 -3
  44. package/scripts/preflight.sh +27 -8
  45. package/scripts/telemetry-send.sh +10 -3
  46. package/scripts/test-adapter.mjs +368 -0
  47. package/scripts/test-dashboard.mjs +70 -0
  48. package/scripts/test-gate.mjs +62 -0
  49. package/scripts/validate-core.mjs +1 -1
  50. package/core/commands/cohorte-loop.md +0 -110
  51. package/scripts/loop-detach.sh +0 -153
  52. package/scripts/loop.sh +0 -399
  53. package/scripts/test-loop.mjs +0 -330
package/CHANGELOG.md CHANGED
@@ -7,6 +7,154 @@ short, user-facing, most recent first. One `## <version> — <YYYY-MM-DD>` secti
7
7
  > They are history and are deliberately not rewritten — every command gained a `cohorte-` prefix
8
8
  > in 2.0.0.
9
9
 
10
+ ## 2.2.0 — 2026-08-12
11
+
12
+ - **The pipeline was Claude Code or nothing.** The doctrine — frozen spec, stateless surfaces, a
13
+ gate you cannot talk your way past — is not Claude-specific, but every artefact that carried it
14
+ was: `.claude/commands/*.md` with Claude frontmatter, `Task` dispatches, a PreToolUse hook. A
15
+ Codex or Cursor user could read the ideas and reimplement nothing.
16
+
17
+ `core/commands/` and `core/agents/` are now **runtime-neutral sources**, and the installer renders
18
+ them per coding agent — as slash commands, TOML commands, or **Codex skills**
19
+ (`.agents/skills/<name>/SKILL.md`, committed with the repo: custom prompts are deprecated *and*
20
+ user-scoped, so a teammate cloning the repo would have got the profile and none of the commands) —
21
+ for **Claude Code, Codex CLI, Cursor, Gemini CLI, OpenCode**
22
+ (`npx cohorte install --runtime=codex,cursor`, or `--all-runtimes`, or nothing and it detects and
23
+ asks). Rendering rewrites the surface — markdown + the frontmatter keys that runtime understands,
24
+ or TOML for Gemini — resolves every path through the tokens `<core>` / `<state>` / `<agents>` /
25
+ `<config>` / `<memory>`, and swaps the argument placeholder (`$ARGUMENTS`, `{{args}}`, or an
26
+ explicit note where the runtime substitutes nothing, as Cursor does).
27
+
28
+ The part that is not cosmetic: the prompts **branch on what the runtime can enforce**, and say so
29
+ in their own text rather than pretending.
30
+
31
+ **The gate really runs on four of the five.** `gate.py` is registered as a blocking hook in each
32
+ runtime's own format — `PreToolUse` in `settings.json` (Claude) and `.codex/hooks.json` (Codex),
33
+ `beforeShellExecution` in `.cursor/hooks.json`, `BeforeTool` in `.gemini/settings.json` — and
34
+ emits that runtime's envelope, selected by `--runtime <id>`. A verdict in the wrong shape is read
35
+ as *allow* by every one of them, so each dialect is covered by `scripts/test-gate.mjs` rather than
36
+ trusted. **Codex and Gemini have no confirmation tier**, so an `ask` verdict is escalated to
37
+ `deny` with the reason attached: the point of that tier is that a human sees the command first,
38
+ and a runtime that cannot ask cannot deliver it — the same rule unattended headless runs already
39
+ used. The phase gate also learned Gemini's shape, where a subagent arrives as a tool of its own
40
+ name rather than as `Task` + `subagent_type`. OpenCode extends via plugins, not hooks, so there
41
+ the commands call `gate.py --check` themselves: same verdicts, but advisory, and
42
+ `/cohorte-doctor` says so instead of reporting ✅.
43
+
44
+ **Subagents are real everywhere**, in four different file formats — markdown + frontmatter for
45
+ Claude, Cursor, Gemini and OpenCode, TOML with the body under `developer_instructions` for Codex.
46
+ Read-only enforcement for the reviewer is derived from the source agent's tool list and re-emitted
47
+ as each runtime spells it (`readonly: true`, `sandbox_mode = "read-only"`); where there is no
48
+ equivalent, the rendered reviewer carries an explicit instruction that read-only is on it, and
49
+ why. Model pins do **not** travel: the profile names Anthropic aliases, which are meaningless
50
+ elsewhere, so agents inherit the runtime's own model selection.
51
+
52
+ **Real subagents are a requirement, not a capability to degrade around.** A sequential-persona
53
+ fallback shipped mid-development and was removed before release: it asked the lead to simulate the
54
+ isolation boundary by discipline — adopt one agent file, do that surface, drop it — which is not
55
+ the same guarantee, and no supported runtime ever took the branch. A runtime declaring
56
+ `subagents: false` is now refused at install with a named error rather than rendered into a
57
+ pipeline whose central promise is silently absent.
58
+
59
+ - **`/cohorte-loop` is removed, on every runtime including Claude Code.** The autonomous
60
+ build→review→fix driver was the one part of the pipeline tied to a single vendor's headless CLI —
61
+ it spawned `claude -p` children through two shell scripts, a detached `screen` session, a
62
+ `caffeinate` assertion and a resume protocol stamped into spec front-matter. That is a lot of
63
+ surface, on the platform-specific end of the codebase, for a phase the human-driven cycle already
64
+ covers at a cost the loop was mostly there to hide.
65
+
66
+ Gone with it: `scripts/loop.sh`, `scripts/loop-detach.sh`, their test suite, and the
67
+ `loop_pass`/`loop_phase` front-matter fields (dropped from the spec template; still *read* by the
68
+ dashboard so a spec left mid-flight by an older core still explains itself). The installer scrubs
69
+ the command and both scripts on upgrade, in every runtime's layout — copy-over never deletes, and
70
+ a surviving command file is a decoy the model can still fire against a core that no longer ships
71
+ its driver.
72
+
73
+ **The file contract stays.** `verdict.json`, `readiness.json` and `build.json` are still written
74
+ on every run, and the `in-progress`/`blocked` spec statuses stay valid: they are what anything
75
+ automating the cycle from outside reads and writes. Removing the built-in driver does not remove
76
+ the ability to drive it — it removes cohorte's opinion about how.
77
+
78
+ Layout: each runtime gets its own rendered core (`.cohorte/<id>/`), because the same template
79
+ resolves differently per capability — but the **project state is shared** (`.cohorte/`:
80
+ gate-config, preflight stamp, metrics), so a repo driven from two agents cannot disagree with
81
+ itself about what is gated or what has been verified. The user config is shared too; the shipped
82
+ scripts probe `~/.claude` then `~/.cohorte`, so one kanban board and one telemetry consent.
83
+
84
+ **Claude Code is unchanged** — same paths, same frontmatter, same hook, and `scripts/test-adapter.mjs`
85
+ asserts that as a regression test alongside the per-runtime output. `install.sh` / `install.ps1`
86
+ now delegate to the Node CLI: there is no shell renderer, and a raw copy would install prompts
87
+ full of unresolved markers that look installed and instruct the model wrongly.
88
+
89
+ - **A config dir with a space in it broke every tool call in the session.** The gate-hook
90
+ registration quoted the script path only on Windows, so a `CLAUDE_CONFIG_DIR` under
91
+ `~/Library/Application Support/…` — where a desktop host naturally puts it — produced
92
+ `python3 /Users/x/Library/Application Support/…/gate.py`. The shell split that, python reported
93
+ `can't open file '/Users/x/Library/Application'`, and *every* Bash and Task call failed —
94
+ including the ones needed to undo it. Quoted on every platform now, in both the global
95
+ registration and the per-runtime one, with a test that installs into a path containing a space
96
+ and asserts the quoting plus that a re-install still reconciles its own entry instead of
97
+ stacking a second.
98
+
99
+ - **`CLAUDE_CONFIG_DIR` was only half honoured.** The runtime registry declares Claude's paths as
100
+ `~/.claude`, and the adapter resolved them from the homedir — so with the variable set, the core
101
+ was written to the real `~/.claude` while the hook was registered in the overridden dir. A
102
+ scratch or CI install silently wrote into the user's actual global core. `resolvePaths` now
103
+ takes a re-rooting override, and the test asserts the core, the commands and the hook all land
104
+ in the override with nothing written to the home default.
105
+
106
+ - **The dashboard reported a healthy non-Claude install as broken.** Every path it checked was
107
+ `.claude/…`, so a repo driven from Cursor came back "no pipeline core installed", "surface with
108
+ no rendered agent", "artifacts not gitignored", "gate.py not registered" — four findings, all
109
+ false. A false red is worse than no check: it sends someone fixing what is not broken. The
110
+ server now resolves paths from `runtimes.json` (`dashboard/server/runtime.js`), reads the gate
111
+ registration in each runtime's own envelope, names artifacts against the right state dir, reads
112
+ the metrics sink from every state dir in play, skips workflows where there is no engine instead
113
+ of reporting them missing, and the reset action backs up every runtime's directory rather than
114
+ leaving the others behind for the fresh install to land beside.
115
+
116
+ - **`SCHEMA.md` is the agents' rulebook, read at run time — and it still hardcoded `.claude`.**
117
+ It ships to `<core>/pipeline/`, so on a non-Claude install it was telling agents to write
118
+ rendered agents and read the gate config in a directory that runtime never looks at. It and
119
+ `PIPELINE.template.md` now use the same `<core>`/`<state>`/`<agents>`/`<config>` tokens as the
120
+ commands, and the installer resolves their capability conditionals like any other prompt.
121
+
122
+ - **The preflight stamp could green code that had changed.** Both sides of the content digest seed
123
+ a throwaway git index from the real one, for its stat cache — but the copy is stamped `now`, and
124
+ git trusts an entry's cached stat data whenever the entry predates the index file. A file edited
125
+ in the same second as the preflight, at an unchanged size, therefore read as clean: the gate let
126
+ a `review` dispatch through onto code the preflight never verified. Both `gate.py` and
127
+ `preflight.sh` now backdate the copy by 5 s, which forces a content check for anything touched
128
+ inside that window and leaves the fast path intact for every older file.
129
+
130
+ ## 2.1.0 — 2026-08-09
131
+
132
+ - **A repo that gates merges on a per-feature release note shipped red PRs, and the flow reported
133
+ success.** Changesets' `changeset` job fails any PR that touches product code without a
134
+ `.changeset/*.md`, but that requirement lives in the project's `CLAUDE.md` — which the ship flow
135
+ never reads. So `/cohorte-ship` committed, pushed, opened the PR and moved the kanban card to
136
+ **Shipped** while the PR was unmergeable, red on a job nobody was watching.
137
+
138
+ The profile gains a `release_notes` block (`enabled`, `tool`, `dir`/`filename`,
139
+ `anchor_package`, `language`, `forbid_levels`, `empty_cmd`, `ci_job`, `guidance` — SCHEMA.md
140
+ §Release notes), and `/cohorte-ship` gains **§2b**: the lead writes the note itself, next to the
141
+ `status: shipped` flip, so it lands *inside* the release commit rather than in a second one after
142
+ the PR is already open. The bump level is project policy, not a git ritual — the release agent is
143
+ now explicitly forbidden from authoring or editing a note, and only stages the one it is handed.
144
+ §4 verifies the note is in the commit; §5 routes a red `ci_job` back to §2b instead of through
145
+ `/cohorte-fix`, which would treat a missing note as a code finding.
146
+
147
+ Two things the block encodes that the tool itself does not: `forbid_levels`, for the `0.x` repo
148
+ where a `major` changeset silently jumps to `1.0.0` with no human deciding it, and the rule to
149
+ **ask** rather than guess between two defensible levels — a wrong bump is a published version
150
+ number, not a fixable draft. `empty_cmd` covers the honest no-op, for a PR that must move no
151
+ version at all.
152
+
153
+ - **Existing projects are asked, not defaulted.** `/cohorte-init-pipeline` detects a versioning tool
154
+ or note-enforcing CI job in Phase 1 and asks the anchor package, language and bump policy in Phase
155
+ 2; `/cohorte-update-pipeline` treats `release_notes` as a genuine human decision during reconcile
156
+ rather than topping it up blind. No tool found ⇒ `enabled: false`, and §2b is a silent no-op.
157
+
10
158
  ## 2.0.2 — 2026-08-08
11
159
 
12
160
  - **Kanban cards stopped moving mid-pipeline, and every stage still reported success.** The
package/README.md CHANGED
@@ -13,8 +13,9 @@
13
13
 
14
14
  </div>
15
15
 
16
- A **portable, stack-agnostic multi-agent pipeline** for Claude Code. Install it once globally,
17
- then one command per project (`/cohorte-init-pipeline`) adapts it to that project's stack.
16
+ A **portable, stack-agnostic multi-agent pipeline** for your coding agent Claude Code, Codex CLI,
17
+ Cursor, Gemini CLI or OpenCode. Install it once globally, then one command per project
18
+ (`/cohorte-init-pipeline`) adapts it to that project's stack.
18
19
 
19
20
  - **The dev pipeline** — a human **lead** drives feature work through gated commands, dispatching
20
21
  **stateless agents** that only communicate through a frozen contract:
@@ -39,6 +40,44 @@ The core never hardcodes stack facts. Two mechanisms keep it generic:
39
40
  2. **Render-at-init** — things that must be in agent frontmatter (name, `tools:`, surface ownership)
40
41
  are rendered per **surface** by `/cohorte-init-pipeline` from `implementer.template.md`.
41
42
 
43
+ ## Which coding agent — [full matrix →](https://thebidouilleagency.github.io/cohorte/reference/runtimes)
44
+
45
+ The doctrine is one set of source prompts. The installer renders them into whatever your agent
46
+ actually reads, and branches the instructions on what it can actually do:
47
+
48
+ ```sh
49
+ npx cohorte install --runtime=codex,cursor # pick explicitly
50
+ npx cohorte install --all-runtimes # every supported one
51
+ npx cohorte install # detects what you have and asks
52
+ ```
53
+
54
+ | | Commands | Subagents | Gate | Workflows |
55
+ | --- | --- | --- | --- | --- |
56
+ | **Claude Code** | `.claude/commands/*.md` | ✅ `.claude/agents` | ✅ blocking hook, deny + **ask** | ✅ |
57
+ | **Codex CLI** | `.agents/skills/*/SKILL.md` | ✅ `.codex/agents` (TOML) | ✅ blocking hook, deny only | — |
58
+ | **Cursor** | `.cursor/commands/*.md` | ✅ `.cursor/agents` | ✅ blocking hook, deny + **ask** | — |
59
+ | **Gemini CLI** | `.gemini/commands/*.toml` | ✅ `.gemini/agents` | ✅ blocking hook, deny only | — |
60
+ | **OpenCode** | `.opencode/commands/*.md` | ✅ `.opencode/agents` | advisory `--check` | — |
61
+
62
+ The same `gate.py` is registered as a real blocking hook on four of the five — it speaks each
63
+ runtime's envelope (`PreToolUse`, `beforeShellExecution`, `BeforeTool`). Codex and Gemini have **no
64
+ confirmation tier**, so a pattern that would be queried elsewhere is **denied** there rather than
65
+ falling through; the rendered prompts say so. OpenCode extends via plugins rather than hooks, so
66
+ there the commands call `gate.py --check` themselves — same config and verdicts, but advisory.
67
+
68
+ **Real subagents are a requirement, not a capability to degrade around** — the pipeline's isolation
69
+ guarantee is that boundary, so a runtime without them is refused at install rather than rendered
70
+ into a pipeline whose central promise is absent. All five have them.
71
+ `/cohorte-doctor` reports what the runtime you are in can actually enforce.
72
+
73
+ On Codex the commands ship as **skills** (`$cohorte-build`) rather than custom prompts: prompts are
74
+ deprecated and user-scoped, so a teammate cloning the repo would get the profile but none of the
75
+ commands — `.agents/skills/` is committed with it instead.
76
+
77
+ One thing does not travel: model pins. The profile names Anthropic aliases, meaningless elsewhere,
78
+ so agents inherit the runtime's own model. Claude Code remains the reference implementation and its
79
+ install is unchanged.
80
+
42
81
  ## Prerequisites
43
82
 
44
83
  Only one hard requirement — the rest is optional and independent:
@@ -220,7 +259,6 @@ it in `.claude/pipeline/VERSION` and bundled repos in their committed `pipeline.
220
259
  | `/cohorte-build <id>` | Readiness gate on the frozen spec, then the lead authors the contract and dispatches one implementer per surface in parallel. |
221
260
  | `/cohorte-review <id>` | Read-only review agents (one per touched surface, parallel) audit the diff vs the spec; out-of-scope findings go to the refactor backlog. |
222
261
  | `/cohorte-fix <id>` | Apply a review report: remediation into the spec, re-dispatch only the surfaces with findings. |
223
- | `/cohorte-loop <id>` | Autonomous `/cohorte-build → /cohorte-review → /cohorte-fix → /cohorte-review …` until no blocking finding is left (see below). |
224
262
  | `/cohorte-ship <id>` | Release agent commits, pushes, opens the PR; watches CI; proposes worktree teardown. |
225
263
  | `/cohorte-audit [path]` | Prioritized refactor backlog for existing code. |
226
264
  | `/cohorte-refactor <domain>` | Apply the backlog for one surface, TDD-first. |
@@ -244,35 +282,6 @@ lever: long sessions (>150k) are expensive even when cached. Each command tells
244
282
  safe to clear. If you'd rather stay in one session, `/compact` mid-task does the lighter version. (Claude
245
283
  can't fire `/clear` itself — it's a client-side command; the pipeline just makes it always safe to type.)
246
284
 
247
- ### Let it run itself — `/cohorte-loop`
248
-
249
- ```
250
- /cohorte-loop feat-x # /cohorte-build, then /cohorte-review ⇄ /cohorte-fix until clean (max 5 passes)
251
- /cohorte-loop feat-x --no-build # already built — just re-run the /cohorte-review ⇄ /cohorte-fix loop
252
- /cohorte-loop feat-x --max=8
253
- /cohorte-loop feat-x --resume # continue a run that died / hit the ceiling, at the pass it reached
254
- ```
255
-
256
- It stops when `/cohorte-review` reports **zero blocking findings** (a CRITICAL or a security issue — a LOW
257
- nit never costs a pass), at the pass ceiling, as soon as two consecutive reviews return the same
258
- blocking findings (the fix is treading water and more passes won't help), or immediately if `/cohorte-build`'s
259
- readiness gate says the frozen spec **cannot be built** — that one needs `/cohorte-spec`, not passes. **Each
260
- fix pass is committed** (`loop(<id>): fix pass <i>`) — that's your way back after N autonomous passes —
261
- and **no fix runs on the last pass**, since fixing without a review behind it leaves unaudited code.
262
-
263
- **It's resumable.** Before each phase the driver stamps `status: in-progress` + `loop_pass` +
264
- `loop_phase` into the spec's front-matter (plain `awk`, zero tokens), and a terminal `in-review` or
265
- `blocked` on exit. So `--resume` continues at pass 3 instead of re-paying passes 1 and 2 — and the spec
266
- itself tells you, `/cohorte-doctor` and the dashboard where the loop got to.
267
-
268
- **The loop does not run in your session.** Each phase is a separate `claude -p` child with its own
269
- fresh context, driven by `pipeline/scripts/loop.sh`; all of their output goes to
270
- `specs/reports/<id>.loop.log`, which the command is forbidden to read back. Your session sees one
271
- line per phase and a three-line summary. That's the whole design: a slash command can't `/clear`
272
- itself, so a conversational loop would pile the diff plus N review reports plus N contracts into a
273
- history that is re-sent at input price every turn — it would cost more than the loop saves. The
274
- machine contract is `specs/reports/<id>.verdict.json`, which `/cohorte-review` now writes on every run; no
275
- prose is ever parsed.
276
285
 
277
286
  ### Run features in parallel — one session per feature
278
287