cohorte 2.10.0 → 3.0.0-dev.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 (211) hide show
  1. package/README.md +7 -0
  2. package/assets/manifest.json +193 -0
  3. package/assets/migrations/config/README.md +12 -0
  4. package/assets/migrations/state/0001_init.sql +154 -0
  5. package/assets/prompts/README.md +11 -0
  6. package/assets/prompts/agents/fixer.md +7 -0
  7. package/assets/prompts/agents/implementer.md +12 -0
  8. package/assets/prompts/agents/reviewer.md +8 -0
  9. package/assets/prompts/agents/security-reviewer.md +7 -0
  10. package/assets/prompts/phases/build.md +5 -0
  11. package/assets/prompts/phases/review.md +5 -0
  12. package/assets/prompts/phases/test.md +5 -0
  13. package/assets/prompts/system.md +11 -0
  14. package/assets/schemas/agent-output.schema.json +225 -0
  15. package/assets/schemas/auth-status.schema.json +87 -0
  16. package/assets/schemas/command-result.schema.json +192 -0
  17. package/assets/schemas/commands.schema.json +952 -0
  18. package/assets/schemas/config.schema.json +2259 -0
  19. package/assets/schemas/doctor-report.schema.json +104 -0
  20. package/assets/schemas/events.schema.json +8514 -0
  21. package/assets/schemas/fake-script.schema.json +549 -0
  22. package/assets/schemas/inspect.schema.json +3133 -0
  23. package/assets/schemas/manifest.schema.json +79 -0
  24. package/assets/schemas/ownership.schema.json +52 -0
  25. package/assets/schemas/policy-verdict.schema.json +282 -0
  26. package/assets/schemas/project-model.schema.json +783 -0
  27. package/assets/schemas/project-status.schema.json +217 -0
  28. package/assets/schemas/reconcile-plan.schema.json +155 -0
  29. package/assets/schemas/run-diff.schema.json +177 -0
  30. package/assets/schemas/run-snapshot-manifest.schema.json +2104 -0
  31. package/assets/schemas/run-state.schema.json +1755 -0
  32. package/assets/schemas/runtime-capabilities.schema.json +1545 -0
  33. package/assets/schemas/sandbox-capabilities.schema.json +116 -0
  34. package/assets/schemas/skill.schema.json +87 -0
  35. package/assets/schemas/spec.schema.json +202 -0
  36. package/assets/schemas/tool-catalogue.schema.json +655 -0
  37. package/assets/schemas/trust-record.schema.json +38 -0
  38. package/assets/skills/README.md +9 -0
  39. package/assets/skills/inspect-surface.md +4 -0
  40. package/dist/agent-host.mjs +50 -0
  41. package/dist/agent-host.mjs.map +1 -0
  42. package/dist/bundle-manifest.json +102 -0
  43. package/dist/chunks/__host-DgGPyco9.mjs +18 -0
  44. package/dist/chunks/__host-DgGPyco9.mjs.map +1 -0
  45. package/dist/chunks/approve-Cv135UqI.mjs +19 -0
  46. package/dist/chunks/approve-Cv135UqI.mjs.map +1 -0
  47. package/dist/chunks/auth-BT5oVp76.mjs +132 -0
  48. package/dist/chunks/auth-BT5oVp76.mjs.map +1 -0
  49. package/dist/chunks/auth-D8rCVY33.mjs +84 -0
  50. package/dist/chunks/auth-D8rCVY33.mjs.map +1 -0
  51. package/dist/chunks/brainstorm-CU10T1tm.mjs +7 -0
  52. package/dist/chunks/brainstorm-CU10T1tm.mjs.map +1 -0
  53. package/dist/chunks/cancel-KV_cpsqS.mjs +15 -0
  54. package/dist/chunks/cancel-KV_cpsqS.mjs.map +1 -0
  55. package/dist/chunks/canonical-C8UxeEmB.mjs +57 -0
  56. package/dist/chunks/canonical-C8UxeEmB.mjs.map +1 -0
  57. package/dist/chunks/catalogue-BvwSrM_j.mjs +1549 -0
  58. package/dist/chunks/catalogue-BvwSrM_j.mjs.map +1 -0
  59. package/dist/chunks/compose-Cr0hP0qm.mjs +7303 -0
  60. package/dist/chunks/compose-Cr0hP0qm.mjs.map +1 -0
  61. package/dist/chunks/config-DefHA2Lw.mjs +96 -0
  62. package/dist/chunks/config-DefHA2Lw.mjs.map +1 -0
  63. package/dist/chunks/contract-B0ra2-bm.mjs +295 -0
  64. package/dist/chunks/contract-B0ra2-bm.mjs.map +1 -0
  65. package/dist/chunks/contract-vYbwYr0Y.mjs +123 -0
  66. package/dist/chunks/contract-vYbwYr0Y.mjs.map +1 -0
  67. package/dist/chunks/deny-ClTxSvHJ.mjs +15 -0
  68. package/dist/chunks/deny-ClTxSvHJ.mjs.map +1 -0
  69. package/dist/chunks/diff-Qw0II0sj.mjs +101 -0
  70. package/dist/chunks/diff-Qw0II0sj.mjs.map +1 -0
  71. package/dist/chunks/discover-30Ciya_0.mjs +20 -0
  72. package/dist/chunks/discover-30Ciya_0.mjs.map +1 -0
  73. package/dist/chunks/doctor-DfCrKpfT.mjs +222 -0
  74. package/dist/chunks/doctor-DfCrKpfT.mjs.map +1 -0
  75. package/dist/chunks/fix-D4Tj8RVA.mjs +18 -0
  76. package/dist/chunks/fix-D4Tj8RVA.mjs.map +1 -0
  77. package/dist/chunks/gc-BBXlBF5w.mjs +161 -0
  78. package/dist/chunks/gc-BBXlBF5w.mjs.map +1 -0
  79. package/dist/chunks/init-Okz4mlhW.mjs +30 -0
  80. package/dist/chunks/init-Okz4mlhW.mjs.map +1 -0
  81. package/dist/chunks/inspect-Buby4cQD.mjs +22 -0
  82. package/dist/chunks/inspect-Buby4cQD.mjs.map +1 -0
  83. package/dist/chunks/logs-Giq5sUau.mjs +25 -0
  84. package/dist/chunks/logs-Giq5sUau.mjs.map +1 -0
  85. package/dist/chunks/migrate-DRoFUg53.mjs +19 -0
  86. package/dist/chunks/migrate-DRoFUg53.mjs.map +1 -0
  87. package/dist/chunks/models-Btp6X2YZ.mjs +24 -0
  88. package/dist/chunks/models-Btp6X2YZ.mjs.map +1 -0
  89. package/dist/chunks/pause-BASBfKLO.mjs +16 -0
  90. package/dist/chunks/pause-BASBfKLO.mjs.map +1 -0
  91. package/dist/chunks/policy-DUIgzF3W.mjs +139 -0
  92. package/dist/chunks/policy-DUIgzF3W.mjs.map +1 -0
  93. package/dist/chunks/providers-Dx78imKj.mjs +19 -0
  94. package/dist/chunks/providers-Dx78imKj.mjs.map +1 -0
  95. package/dist/chunks/reconcile-BfD785gd.mjs +26 -0
  96. package/dist/chunks/reconcile-BfD785gd.mjs.map +1 -0
  97. package/dist/chunks/render-DyZr5Lgi.mjs +53 -0
  98. package/dist/chunks/render-DyZr5Lgi.mjs.map +1 -0
  99. package/dist/chunks/resume-GJQRb2Eb.mjs +15 -0
  100. package/dist/chunks/resume-GJQRb2Eb.mjs.map +1 -0
  101. package/dist/chunks/retry-CLrUem1H.mjs +15 -0
  102. package/dist/chunks/retry-CLrUem1H.mjs.map +1 -0
  103. package/dist/chunks/review-BDdjb6kB.mjs +17 -0
  104. package/dist/chunks/review-BDdjb6kB.mjs.map +1 -0
  105. package/dist/chunks/run-CgTWYlcd.mjs +82 -0
  106. package/dist/chunks/run-CgTWYlcd.mjs.map +1 -0
  107. package/dist/chunks/run-tool-C3rAv4XF.mjs +26 -0
  108. package/dist/chunks/run-tool-C3rAv4XF.mjs.map +1 -0
  109. package/dist/chunks/schema-C9QbUW4H.mjs +578 -0
  110. package/dist/chunks/schema-C9QbUW4H.mjs.map +1 -0
  111. package/dist/chunks/send-BdnJ85Nz.mjs +19 -0
  112. package/dist/chunks/send-BdnJ85Nz.mjs.map +1 -0
  113. package/dist/chunks/ship-DTuLGoaJ.mjs +19 -0
  114. package/dist/chunks/ship-DTuLGoaJ.mjs.map +1 -0
  115. package/dist/chunks/shutdown-BM92Iyv2.mjs +15 -0
  116. package/dist/chunks/shutdown-BM92Iyv2.mjs.map +1 -0
  117. package/dist/chunks/skip-DZG5kpcH.mjs +18 -0
  118. package/dist/chunks/skip-DZG5kpcH.mjs.map +1 -0
  119. package/dist/chunks/spec-Bq0P3jVh.mjs +20 -0
  120. package/dist/chunks/spec-Bq0P3jVh.mjs.map +1 -0
  121. package/dist/chunks/src-CId6kMsY.mjs +1689 -0
  122. package/dist/chunks/src-CId6kMsY.mjs.map +1 -0
  123. package/dist/chunks/src-CfAOde7C.mjs +5403 -0
  124. package/dist/chunks/src-CfAOde7C.mjs.map +1 -0
  125. package/dist/chunks/src-Cu3TxNob.mjs +783 -0
  126. package/dist/chunks/src-Cu3TxNob.mjs.map +1 -0
  127. package/dist/chunks/src-DcU4IXMe.mjs +117 -0
  128. package/dist/chunks/src-DcU4IXMe.mjs.map +1 -0
  129. package/dist/chunks/src-JxawBQBn.mjs +446 -0
  130. package/dist/chunks/src-JxawBQBn.mjs.map +1 -0
  131. package/dist/chunks/src-s7YRvk0t.mjs +2116 -0
  132. package/dist/chunks/src-s7YRvk0t.mjs.map +1 -0
  133. package/dist/chunks/status-SMjiOMYy.mjs +29 -0
  134. package/dist/chunks/status-SMjiOMYy.mjs.map +1 -0
  135. package/dist/chunks/tail-BuhT-2PH.mjs +25 -0
  136. package/dist/chunks/tail-BuhT-2PH.mjs.map +1 -0
  137. package/dist/chunks/update-BXIppsbi.mjs +16 -0
  138. package/dist/chunks/update-BXIppsbi.mjs.map +1 -0
  139. package/dist/cli.mjs +175 -0
  140. package/dist/cli.mjs.map +1 -0
  141. package/package.json +17 -32
  142. package/CHANGELOG.md +0 -1453
  143. package/bin/cli.js +0 -812
  144. package/bin/report.js +0 -141
  145. package/core/adapter/render.js +0 -389
  146. package/core/agents/implementer.template.md +0 -142
  147. package/core/agents/profile-reader.md +0 -50
  148. package/core/agents/release.md +0 -58
  149. package/core/agents/review.md +0 -179
  150. package/core/commands/cohorte-align-ds.md +0 -33
  151. package/core/commands/cohorte-audit.md +0 -58
  152. package/core/commands/cohorte-brainstorm.md +0 -70
  153. package/core/commands/cohorte-build.md +0 -219
  154. package/core/commands/cohorte-doctor.md +0 -170
  155. package/core/commands/cohorte-fix.md +0 -80
  156. package/core/commands/cohorte-fleet.md +0 -103
  157. package/core/commands/cohorte-init-pipeline.md +0 -31
  158. package/core/commands/cohorte-intake.md +0 -92
  159. package/core/commands/cohorte-patch.md +0 -118
  160. package/core/commands/cohorte-refactor.md +0 -59
  161. package/core/commands/cohorte-retro.md +0 -85
  162. package/core/commands/cohorte-review.md +0 -270
  163. package/core/commands/cohorte-ship.md +0 -122
  164. package/core/commands/cohorte-spec.md +0 -101
  165. package/core/commands/cohorte-update-pipeline.md +0 -134
  166. package/core/hooks/gate.py +0 -580
  167. package/core/runtimes/claude.json +0 -74
  168. package/core/runtimes/codex.json +0 -83
  169. package/core/runtimes/cursor.json +0 -76
  170. package/core/runtimes/gemini.json +0 -76
  171. package/core/runtimes/opencode.json +0 -73
  172. package/core/templates/agent-handoff.md +0 -34
  173. package/core/templates/brainstorm-return.md +0 -36
  174. package/core/templates/decisions.template.md +0 -42
  175. package/core/templates/design-brief.md +0 -44
  176. package/core/templates/patch.template.md +0 -86
  177. package/core/templates/pr-body.md +0 -25
  178. package/core/templates/review-feedback.md +0 -38
  179. package/core/templates/spec.template.md +0 -87
  180. package/core/templates/steps/init-pipeline/01-detect-stack.md +0 -44
  181. package/core/templates/steps/init-pipeline/02-interview-gaps.md +0 -55
  182. package/core/templates/steps/init-pipeline/03-draft-profile.md +0 -10
  183. package/core/templates/steps/init-pipeline/04-write-render.md +0 -115
  184. package/core/templates/steps/init-pipeline/05-report.md +0 -12
  185. package/core/workflows/audit.js +0 -240
  186. package/core/workflows/loop.js +0 -641
  187. package/core/workflows/refactor.js +0 -276
  188. package/core/workflows/review.js +0 -456
  189. package/install.ps1 +0 -103
  190. package/install.sh +0 -114
  191. package/lib/doctor.js +0 -504
  192. package/lib/runtime.js +0 -134
  193. package/lib/versions.js +0 -149
  194. package/lib/yaml.js +0 -129
  195. package/profile/PIPELINE.template.md +0 -251
  196. package/profile/SCHEMA.md +0 -698
  197. package/profile/cohorte.config.template.yaml +0 -43
  198. package/scripts/assert-gate-hook.mjs +0 -75
  199. package/scripts/kanban-move.sh +0 -313
  200. package/scripts/metrics/collect.mjs +0 -531
  201. package/scripts/metrics/prices.json +0 -42
  202. package/scripts/new-feature.sh.template +0 -98
  203. package/scripts/preflight.sh +0 -124
  204. package/scripts/remove-feature.sh.template +0 -56
  205. package/scripts/test-adapter.mjs +0 -368
  206. package/scripts/test-gate.mjs +0 -428
  207. package/scripts/test-kanban.mjs +0 -304
  208. package/scripts/test-lib.mjs +0 -310
  209. package/scripts/test-metrics.mjs +0 -156
  210. package/scripts/test-workflows.mjs +0 -758
  211. package/scripts/validate-core.mjs +0 -320
package/profile/SCHEMA.md DELETED
@@ -1,698 +0,0 @@
1
- # PIPELINE.md profile — field reference
2
-
3
- `/cohorte-init-pipeline` fills the `yaml pipeline-profile` block in `PIPELINE.md` (from
4
- `PIPELINE.template.md`) plus the prose sections. This documents every field and how the
5
- generic pipeline uses it, so a stateless agent can read/regenerate the profile correctly.
6
-
7
- ## `yaml pipeline-profile` block
8
-
9
- | Field | Type | Used by | Meaning |
10
- | ------------------------------------ | ------------ | --------------------------------- | ------------------------------------------------------------- |
11
- | `name` | string | all | Project name, used in agent prose + commit scopes. |
12
- | `one_liner` | string | brainstorm/spec | One-sentence product description. |
13
- | `ui_language` | string | implementer, review | Language of ALL user-facing copy. |
14
- | `package_manager` | enum | all | `pnpm`/`npm`/`yarn`/`bun`/`pip`/`cargo`/`go`. |
15
- | `vcs.host` | enum | release | `github`→use `gh`; else emit compare URL. |
16
- | `vcs.remote` | string | release | `owner/repo` for the PR/compare URL. |
17
- | `vcs.default_branch` | string | build, review, release | Base branch for diffs + PRs. |
18
- | `vcs.feature_branch_prefix` | string | ship, isolation script | `feature/` → branch `feature/<id>`. |
19
- | `vcs.patch_branch_prefix` | string | ship | Same, for a `kind: patch` spec: `fix/` → branch `fix/patch-<slug>`. Optional — a profile that predates it falls back to `fix/`. |
20
- | `repo.layout` | enum | build, audit | `monorepo` (many surfaces) or `single`. |
21
- | `repo.workspace_tool` | enum | audit | `turborepo`/`nx`/`none`. |
22
- | `retrieval.provider` | enum | init, update-pipeline, implementer | `serena` (default) / `graphify` / `none` — see §Code retrieval. |
23
- | **`surfaces[]`** | list | **build, review, refactor, init** | One per independently-built area. Grows via reconcile (below). |
24
- | `surfaces[].key` | string | build | Short id + review scope. |
25
- | `surfaces[].path` | string | implementer | The ONLY tree that surface's agent may touch. |
26
- | `surfaces[].label` | string | build, init (`<SURFACE_LABEL>`) | Human label + framework, e.g. `frontend (React)`. |
27
- | `surfaces[].agent` | string | build (`subagent_type`) | Rendered agent file name. |
28
- | `surfaces[].tools` | list | init | Frontmatter `tools:` for the rendered agent. |
29
- | `surfaces[].model` | enum | init (`<SURFACE_MODEL>`) | Frontmatter `model:` tier — `sonnet`/`haiku`/`inherit`. Default `sonnet` (implementers mostly apply a frozen contract — far cheaper than the Opus lead the dispatcher runs on, and Sonnet handles it well); `haiku` for purely mechanical surfaces (scaffolding); `inherit` only for surfaces with real design decisions worth the lead's model. |
30
- | `surfaces[].*_cmd` | string | implementer | test/lint/format/typecheck/build commands. |
31
- | `surfaces[].test_quiet_cmd` `.lint_quiet_cmd` | string | implementer, preflight, workflows | Bridled variants agents actually run (dot reporter / `--quiet` / failures-only). `""` ⇒ `<cmd> 2>&1 \| tail -40`. See §Output discipline. |
32
- | `surfaces[].uses_design` | bool | build, frontend | Whether this surface consumes designs. |
33
- | `contract.enabled` | bool | build | `false` ⇒ skip contract authoring (§2 of /cohorte-build). |
34
- | `contract.mechanism` | enum | build, lead | `shared-types-zod`/`openapi`/`protobuf`/`json-schema`/`none`. |
35
- | `contract.path` `.ext` `.index` | string | build | Where `<feature_id>` contract is authored + barrel. |
36
- | `contract.authored_by` | const `lead` | build | Implementers import it read-only, never edit. |
37
- | `release_notes.enabled` | bool | ship | `false` ⇒ skip note authoring (§2b of /cohorte-ship). See §Release notes. |
38
- | `release_notes.tool` | enum | ship | `changesets`/`none` — what consumes the file. |
39
- | `release_notes.dir` `.filename` | string | ship | Where the per-feature note is authored, e.g. `.changeset/<feature_id>.md`. |
40
- | `release_notes.anchor_package` | string | ship | Sole key in the note's front-matter; a lockstep/`fixed` group propagates the bump. |
41
- | `release_notes.language` | string | ship | Language of the note's prose — usually `ui_language`. |
42
- | `release_notes.forbid_levels` | list | ship | Bump levels project policy refuses, e.g. `[major]` while `0.x`. |
43
- | `release_notes.empty_cmd` | string | ship | Escape hatch when no version should move; `""` if none. |
44
- | `release_notes.ci_job` | string | ship | CI job that fails on a missing note — lets §5 name the red check. |
45
- | `release_notes.guidance` | string | ship | Project policy the lead follows when picking the bump + writing the prose. |
46
- | `commands.*` | string | all | Repo-wide install/dev/lint/format/typecheck/test + migrate. |
47
- | `commands.test_quiet` `.lint_quiet` | string | review, audit, workflows | Repo-wide bridled variants — what the `/cohorte-review` pre-flight runs. Same fallback as the per-surface ones. |
48
- | `rbac.enabled` | bool | brainstorm, review | Toggle RBAC personas + authz audit. |
49
- | `rbac.hierarchy` | list | review | Highest→lowest role list. |
50
- | `design.enabled` | bool | build, frontend, align-ds | `false` ⇒ design steps are no-ops. |
51
- | `design.provider` | enum | frontend, align-ds | `claude-design`/`figma`/`none`. |
52
- | `design.inline` | bool | spec, build, doctor | Default `false`. `true` ⇒ `/cohorte-spec` offers the `/design` artboard step after freezing the brief, instead of ending at "paste this into the design tool". Requires `provider: claude-design`, the `inline_design` runtime capability, and Claude Code ≥ 2.1.234. **A research preview: artboards are not persisted for you.** The brief on disk stays the source of truth either way, so `false` loses nothing but the round trip. |
53
- | `design.design_system_project` | id | align-ds, frontend | UI-kit source of truth. |
54
- | `design.design_project` | id | build, frontend | Legacy fallback for bare-filename `design_files` only; default `none`. New specs use full `…/design/p/<projectId>?file=<file>` links that carry their own project + page (nothing to go stale on a DS rebuild). |
55
- | `design.snapshot_dir` | path | align-ds | Committed DS snapshot for diffing. |
56
- | `design.ui_kit_path` `.tokens_path` | path | align-ds, frontend | Where the kit + tokens live in code. |
57
- | `isolation.enabled` | bool | new-feature script | `false` ⇒ build in main checkout. |
58
- | `isolation.db_per_worktree` | bool | new-feature script | Create `<name>_<id>` DB per worktree. |
59
- | `isolation.db_name_pattern` | string | new-feature script | `<name>_<id>`. |
60
- | `isolation.port_base` | map | new-feature script | `api`/`web` base ports; +slot per worktree. |
61
- | `isolation.compose_file` `.registry` | path | new-feature script | Docker stack + slot registry. |
62
- | `gate.deny[]` | list | hooks/gate.py, settings | Command substrings hard-denied, on any branch. |
63
- | `gate.ask[]` | list | hooks/gate.py, settings | Command substrings that require confirm, on any branch. |
64
- | `gate.ask_on_default_branch[]` | list | hooks/gate.py | Confirm ONLY on `default_branch`; free on feature branches. |
65
- | `gate.default_branch` | string | hooks/gate.py | Protected branch (default `main`); gate resolves via git. |
66
- | `gate.preflight.enabled` | bool | hooks/gate.py, review | Phase gate: review dispatches need a fresh preflight stamp. See §Preflight. |
67
- | `gate.preflight.agents[]` | list | hooks/gate.py | `subagent_type`s the stamp gates (default `[review]`). |
68
- | `gate.preflight.max_age_minutes` | number | hooks/gate.py | Stamp freshness window (default 30). |
69
-
70
- ## Prose sections
71
-
72
- - **Conventions** — per-surface rules the implementer follows and review audits.
73
- - **Testing** — the TDD contract per surface (what a test must cover, DB isolation).
74
- - **Design brief note** — feeds `/cohorte-spec` §8 and the Claude Design step.
75
- - **Personas** — the `/cohorte-brainstorm` panel; include one per RBAC role when `rbac.enabled`.
76
-
77
- ## How the pieces reference this file
78
-
79
- - **Agents** (`implementer`, `review`, `release`) are told at dispatch: _read `PIPELINE.md`
80
- §Commands / §Conventions / §Surfaces first._ They have `Read`, so they load it live.
81
- - **Commands** (`/cohorte-build`, `/cohorte-review`, …) parse the `yaml pipeline-profile` block to know how
82
- many surfaces to dispatch, the contract mechanism, the commands, and the capability flags.
83
- - **Hook** (`gate.py`) reads `gate.deny`/`gate.ask`/`gate.ask_on_default_branch`/`gate.default_branch`
84
- from a generated `<state>/gate-config.json`. The last two make git + docker free on feature branches
85
- but confirm-gated on the default branch (branch resolved at run time via `git rev-parse`).
86
- - **Scripts** (`new-feature.sh`) read the `isolation` block (rendered in at init).
87
-
88
- ## Code retrieval — `retrieval.provider`
89
-
90
- Agents spend most of their wall-clock reading the repo; a retrieval provider replaces grep-and-read
91
- with symbol/graph queries. The flag is a **value, not a boolean**, so switching provider later is a
92
- one-line profile change + re-running the wiring (no agent re-render needed — the guidance agents
93
- follow is provider-agnostic: _"prefer the retrieval MCP tools over Grep/Glob + whole-file Reads"_).
94
-
95
- | Provider | Mechanism | Freshness | Cost |
96
- | --- | --- | --- | --- |
97
- | `serena` (default) | live LSP symbol navigation (find symbol, references, semantic edits) | always current | none — no index |
98
- | `graphify` | persistent tree-sitter knowledge graph over code + docs | as fresh as the last rescan | index step + re-index discipline |
99
- | `none` | agents fall back to Grep/Glob/Read | — | — |
100
-
101
- **Wiring (done by `/cohorte-init-pipeline`, or `/cohorte-update-pipeline` retroactively):**
102
-
103
- - `serena` — requires the `serena` CLI (`uv tool install -p 3.13 serena-agent`). For day-to-day CLI
104
- use it should also be on PATH (`uv tool update-shell`; uv installs to `~/.local/bin`). Register at
105
- **project scope** so the registration is committed and portable (`--project-from-cwd` resolves the
106
- project at server start, so the committed entry works on every machine) — and register the
107
- **PATH-proof launcher**, not the bare command: Claude Code spawns MCP servers with whatever
108
- environment it was launched from (a stale terminal, a GUI/IDE launch that never sourced a shell
109
- profile), where `~/.local/bin` may be missing from PATH — a bare `serena` entry then dies with
110
- ENOENT and agents silently fall back to Grep/Read:
111
-
112
- ```sh
113
- claude mcp add --scope project serena -- sh -c 'exec "$(command -v serena || echo "$HOME/.local/bin/serena")" start-mcp-server --context claude-code --project-from-cwd --open-web-dashboard False'
114
- ```
115
-
116
- (Windows-native teams: no `sh` — register the bare `serena` form instead and ensure the uv tools
117
- dir is on PATH; keep the `--open-web-dashboard False` flag.) `--open-web-dashboard False` keeps the
118
- dashboard available (reachable at `http://localhost:24282/dashboard/`) but stops it popping a browser
119
- tab on every server start — the flag overrides the machine's `serena_config.yml`, so the behaviour is
120
- the same for everyone on the repo. Gitignore `.serena/` (per-machine cache/config). Optionally
121
- pre-index large repos once: `serena project index`.
122
- - `graphify` — requires `uv tool install graphify` + `graphify install`; build the initial graph
123
- (`/graphify .`) and rescan incrementally after big changes (`--update`). See graphify.net.
124
- - Rendered agents get the provider's MCP tools appended to their `tools:` list (e.g. `mcp__serena`
125
- grants the whole server); `none` ⇒ nothing appended.
126
-
127
- **Serena health check** — run after wiring in `/cohorte-init-pipeline` AND on every `/cohorte-update-pipeline`
128
- reconcile (wiring that worked once can rot: PATH changes, tool uninstalled, entry hand-edited):
129
-
130
- 1. **CLI resolves:** `command -v serena`. Fails but `~/.local/bin/serena` exists ⇒ PATH repair
131
- above; missing entirely ⇒ reinstall.
132
- 2. **Registered:** this repo's `.mcp.json` has the `serena` entry ⇒ else re-run the `claude mcp add`.
133
- If the entry is the bare `serena` form on a POSIX machine, upgrade it to the PATH-proof launcher
134
- above (immune to launch-environment PATH gaps). If a launcher entry predates the
135
- `--open-web-dashboard False` flag, append it so the dashboard no longer auto-opens a browser tab.
136
- 3. **Gitignored:** `.serena/` is in `.gitignore` ⇒ else append it.
137
- 4. **Actually connected:** the `mcp__serena` tools are exposed in the session (or `claude mcp list`
138
- shows serena connected). If 1–3 pass but this fails, a session restart is needed — say so
139
- explicitly instead of reporting success.
140
-
141
- Report each check's result; never report Serena "wired" on registration alone.
142
-
143
- Teammates cloning the repo get the committed `.mcp.json` and only need the provider CLI installed
144
- and on PATH — if either is missing, the MCP server fails to start and agents silently fall back to
145
- Grep/Read; the health check above is the diagnostic.
146
-
147
- ## Specialization — when to split one surface into more agents
148
-
149
- `/cohorte-build` dispatches ONE agent per surface, in parallel, so build wall-clock ≈ the **slowest single
150
- surface**. More agents only build faster when they let the *slowest* surface's work run concurrently —
151
- and only if the split is safe. The invariant that keeps parallelism safe is **one owner per tree, and
152
- the frozen contract as the only cross-surface channel**. So specialization means carving a surface into
153
- **smaller non-overlapping surfaces**, never pointing two agents at the same tree.
154
-
155
- **Split a surface into specialized sub-surfaces only when BOTH hold:**
156
-
157
- 1. **It's a bottleneck** — the surface is large (many modules / high LOC) and dominates build time.
158
- 2. **The boundary is clean** — its work partitions into trees that don't share files, e.g. feature
159
- modules (`src/features/*`, `src/modules/*`), route groups, or independent services (`services/*`).
160
-
161
- **Rules when splitting (non-negotiable — they preserve the invariant):**
162
-
163
- - **Shared code gets its own surface with a single owner.** Anything two slices both touch — routing,
164
- global state/store, the design-system kit + tokens, shared utils — becomes its OWN surface (e.g.
165
- `web-shared`), owned by exactly one agent. Never let two feature-slice agents both edit shared trees.
166
- - **Cross-slice references go through the contract**, not direct imports between slice trees. If
167
- `web-checkout` needs a shape produced by `api-billing`, that shape lives in the frozen contract.
168
- - **Don't over-split.** A slice too small to hold ≥1 real task, or one with tangled boundaries, is worse
169
- than not splitting — the coordination + token cost (each stateless agent re-reads `PIPELINE.md` + spec)
170
- outweighs the parallelism. When boundaries aren't clean, keep one surface.
171
-
172
- Coarse first, specialize on evidence: start with one `frontend` / `backend` surface each; split only a
173
- surface that's proven slow and cleanly separable. The evidence lives in
174
- the **main checkout's** `<state>/pipeline-metrics.jsonl` (gitignored) — one JSONL line per phase batch
175
- (`ts`/`feature`/`phase`/`seconds`/`surfaces:{key: result}`), appended by `/cohorte-build`, `/cohorte-review`
176
- and `/cohorte-fix`.
177
- **`surfaces` keys are surface keys, nothing else** — run-level facts go in their own top-level
178
- fields. Anything put inside `surfaces` is read
179
- as a surface: `cohorte doctor` renders it as a row in the per-surface table and scores a non-`ok`
180
- value as that surface failing. Always the main checkout, never the feature worktree (which dies at teardown while
181
- metrics must accumulate across features) — resolve from anywhere with
182
- `$(dirname "$(git rev-parse --git-common-dir)")/<state>/pipeline-metrics.jsonl`. Read it before
183
- proposing a split: split the surface that actually dominates wall-clock, not the one that feels big.
184
-
185
- ## Measuring cost — what's slow vs what's expensive
186
-
187
- `pipeline-metrics.jsonl` records **wall-clock seconds** per phase batch (§Specialization) — it tells you
188
- what's SLOW. Tokens are recorded only where they can be read honestly: the **workflow paths**
189
- (`loop.js`, `review.js`) stamp an approximate `tokens` field per batch from the runtime's own
190
- counter (`budget.spent()` deltas), and the loop's return carries a per-round breakdown in its
191
- `history`. The **conversational** commands still record none — a lead cannot reliably read a
192
- subagent's token count, and a guessed number is worse than a missing one. `cohorte metrics` sums
193
- whatever is stamped (a token-less line aggregates as 0, rendered as absent, never as "free").
194
- For exact spend, use Claude Code's own accounting:
195
-
196
- - **`/cost`** (built-in, zero setup) — reports per-**subagent** and per-**slash-command** share of your usage
197
- over the last 24 h / 7 d (e.g. _"Top subagents: frontend 7 %, backend 4 % · Top skills: /cohorte-build 1 %,
198
- /cohorte-review 1 %"_). That IS the per-phase ledger — approximate (share-of-total, machine-local, not exact
199
- tokens). Read it to see which surface/command actually dominates the bill before you tune a `model` tier.
200
-
201
- **Lead context discipline — the silent bill.** The lead session's conversation history is re-sent as
202
- input on EVERY turn; a session that spans spec→build→review→fix without clearing re-pays the
203
- accumulated spec walk-through, handoffs, and reports on each turn. The pipeline is built so this is
204
- never necessary: every phase handoff (spec, contract, diff, staged reports) lives on disk, so `/clear`
205
- at each phase boundary is always safe — each command's closing line recommends it. Corollaries the
206
- commands enforce: never paste a diff into a dispatch (agents compute their own, scoped); never echo a
207
- staged report or design brief into chat; redirect bulky command output to a file and grep it.
208
-
209
- ## Output discipline — quiet commands
210
-
211
- A test runner's default output is written for a human watching a terminal: one line per test, banners,
212
- timing tables. An agent pays input price for every one of those lines, on every turn they survive in its
213
- context. The profile therefore stores **two forms of each noisy command**:
214
-
215
- - `test_cmd` / `lint_cmd` — the full form, for a human running it by hand.
216
- - `test_quiet_cmd` / `lint_quiet_cmd` (per surface) and `commands.test_quiet` / `commands.lint_quiet`
217
- (repo-wide) — the **bridled** form agents actually execute: dot/failures-only reporter
218
- (`--reporter=dot`, `--quiet`, `-q`, `--silent`, framework equivalent) so a green run costs lines,
219
- not pages, and a red run prints only the failures.
220
-
221
- Rules for every consumer (implementers, preflight, `/cohorte-audit` gates, workflow agents):
222
-
223
- 1. Run the quiet variant when set.
224
- 2. Quiet variant empty/absent (older profile) ⇒ run `<full cmd> 2>&1 | tail -40` — never the bare
225
- command into your context.
226
- 3. Need the full log? Redirect it to a file and grep it; never print it.
227
-
228
- `/cohorte-init-pipeline` **asks** for these variants (detected defaults offered first) instead of silently
229
- storing a bare `pnpm test` as the thing agents execute; `/cohorte-update-pipeline` tops up older profiles.
230
-
231
- ## Spec status — the lifecycle state machine
232
-
233
- A spec's front-matter `status` is not a label, it is the pipeline's **state**: every command routes on
234
- it, `cohorte specs` boards on it, and the kanban backfill maps it to a column. Six states:
235
-
236
- | status | meaning | written by | who may build it |
237
- | --- | --- | --- | --- |
238
- | `draft` | the interview is open, nothing is frozen | `/cohorte-spec` Mode A | no |
239
- | `frozen` | the contract is frozen — the handoff to `/cohorte-build` | `/cohorte-spec` Mode A freeze | yes |
240
- | `in-progress` | a round is under way on this spec (or died mid-way) | an automated driver, if any | yes |
241
- | `in-review` | reviewed / awaiting the next round or `/cohorte-ship` | `/cohorte-spec` Mode B, `/cohorte-fix` | yes |
242
- | `blocked` | a round gave up here (non-convergent, no verdict, not implementable) | an automated driver, if any | yes, with the reason named |
243
- | `shipped` | the PR is open; the status flip is part of the release commit | `/cohorte-ship` | no |
244
-
245
- **`in-progress` and `blocked` are driver states.** No conversational command writes them — the
246
- human-driven cycle moves `frozen` → `in-review` → `shipped`. Their producer is the **loop
247
- workflow** (`core/workflows/loop.js`, `/cohorte-loop` — the successor of the 2.2.0-retired
248
- conversational driver): it stamps `in-progress` at each round, `in-review` when a run ends at
249
- zero blocking findings, and `blocked` when a round gives up (non-convergent, unreviewed
250
- surfaces, dead implementers, a contract-change finding), with the reason in
251
- `specs/reports/<id>.loop.json`. External drivers may write them too. Every reader routes on
252
- them either way, and a stamp on a spec with no front-matter stays a silent no-op — no driver
253
- dies over a status line.
254
-
255
- **`kind` — feature (default) or `patch`.** Orthogonal to `status`, and the only other front-matter
256
- field commands route on. `/cohorte-patch` freezes `specs/patch-<slug>.md` with `kind: patch` from
257
- `templates/patch.template.md`: a ~60-line bug spec whose §4 **regression test** replaces §5 CONTRACT
258
- as the thing the diff is checked against. It moves through the same states and the same commands —
259
- `/cohorte-build` → `/cohorte-review` → `/cohorte-fix`* → `/cohorte-ship` — which is the whole design:
260
- a patch is a spec, so nothing downstream is special-cased beyond three lines.
261
-
262
- | what reads `kind: patch` | what it does differently |
263
- | --- | --- |
264
- | `/cohorte-build` §1.6 | judges §1 repro + §4 regression test instead of contract completeness; gap check `repro` |
265
- | `/cohorte-build` §2 | authors no contract when §5 Contract delta is `none` (the usual case) |
266
- | `/cohorte-ship` §1/§2b/§3 | branches off `vcs.patch_branch_prefix`; a `patch` bump by default; `fix(<scope>)` commit |
267
-
268
- A patch may span **several surfaces** — one bug, one repro, one spec. What it may never do is add
269
- **new** contract surface area: two surfaces agreeing on a shape that doesn't exist yet is what §5 is
270
- for, so `/cohorte-patch` routes that to `/cohorte-spec` instead. Changing an *existing* contract entry
271
- is a legitimate delta. The patch template keeps contract on **§5** and acceptance on **§9** — the two
272
- numbers `review.md` and `implementer.template.md` name verbatim — and simply has no §8.
273
-
274
- Corollaries worth knowing:
275
-
276
- - A spec with no front-matter makes every stamp a **silent no-op** — the state is bookkeeping, and the
277
- loop must never die over a status line.
278
- - Child commands write `status` too (`/cohorte-fix` sets `in-review`); re-stamping before each phase is what
279
- keeps `in-progress` true for the duration of the run rather than for its first phase.
280
- - `blocked` is not a failure to hide: it is the resumable state. `/cohorte-build` accepts it, names it, and
281
- routes by the spec's `## Remediation` (open items ⇒ `/cohorte-fix`).
282
-
283
- ## Dead agents — silence is not a green light
284
-
285
- A subagent can die mid-run: a rate limit, a transport error that outlived its retries, its own context
286
- exhausted on a big surface. When it does it returns **nothing** — and nothing is byte-identical to
287
- "finished, nothing to report". Every phase that fans out therefore does a **roll call** before it
288
- integrates anything, because the default reading of silence is the most dangerous one available:
289
-
290
- | phase | what a dead agent looks like | what the phase must do |
291
- | --- | --- | --- |
292
- | `/cohorte-build` | a surface with no handoff | retry it **once** alone (byte-identical prompt), then mark it `dead`, verify the tree with that surface's own quiet commands, never call the batch ok |
293
- | `/cohorte-review` | a reviewer with no report ⇒ **zero findings** | retry once, then list the surface in `unreviewed` and refuse to score `SHIP` |
294
- | `/cohorte-fix` | a re-dispatched agent with no handoff | retry once, then leave **every** one of its items `- [ ]` — a dead agent never ticks a box |
295
- | workflows | `agent()` resolves to `null` | already enforced (`review.js` `unreviewedSurfaces`) — the doctrine started here |
296
-
297
- Non-negotiables, in every phase:
298
-
299
- - **Retry once, alone, byte-identical.** Most deaths are transient, and the other surfaces' work is
300
- already on disk — so recovery costs one agent, never a rebuild. Never retry an agent that answered.
301
- - **Never speak for a dead agent.** You did not see its work: report what the *tree* says (quiet
302
- commands, redirected to a file, grepped), not what a handoff would have said.
303
- - **Never let it reach a driver as clean.** `/cohorte-build` writes `dead[]` into
304
- `specs/reports/<id>.build.json`, `/cohorte-review` writes `unreviewed[]` into the verdict; a driver
305
- must abort on either *before* it reads `blocking`, since a dead reviewer makes
306
- `blocking == 0` a statement about code nobody read.
307
- - **`unreviewed` is separate from `blocking` on purpose.** Faking a count in `blocking` to force a
308
- driver's hand would corrupt the one field the whole contract rests on; a driver reads them as two
309
- different facts — "what was found" and "what was covered".
310
- - **Write the metrics line anyway** (`"<key>":"dead"`). An incomplete batch is exactly the batch worth
311
- recording; holding the append back "until it's complete" deletes the evidence that anything failed.
312
-
313
- ## Readiness — the gate between a frozen spec and N implementers
314
-
315
- `/cohorte-build` §1.6 scores the frozen spec on **implementability** before authoring the contract and before
316
- dispatching anything, and writes `specs/reports/<id>.readiness.json`
317
- (`verdict`: `READY` · `RESERVATIONS` · `NOT-READY`, plus `gaps[]`). It costs **zero extra agents** — the
318
- lead already holds the spec, the profile and the reconciled surface list — which is the whole economics
319
- of the step: a spec that cannot be built does not get cheaper by being built on N surfaces in parallel.
320
-
321
- - Five checks: contract completeness · surface coverage · dependencies exist · residual ambiguity ·
322
- the design gate. Each maps to `NOT-READY` (a surface would have to invent the answer) or
323
- `RESERVATIONS` (a surface can proceed on a stated assumption).
324
- - **`NOT-READY` aborts the build with no agent spawned** and sends the human to `/cohorte-spec`.
325
- A driver reads the same file and must stop rather than retry — it is the one outcome more passes
326
- cannot fix.
327
- - **`RESERVATIONS` never blocks.** Each gap is inlined verbatim into the dispatch of the surface it
328
- affects, as an assumption the implementer must apply *and* flag in its handoff. A gate that stalled a
329
- sound build on a missing error case would cost more human round-trips than it saves.
330
-
331
- ## Deferred findings — real, but not this feature's problem
332
-
333
- `/cohorte-review` ends on "zero blocking findings", so everything non-blocking used to be discarded with the
334
- report. A **deferred** finding is one the reviewer judges true and **out of this feature's scope**
335
- (pre-existing code the staged diff never touched, adjacent debt the spec never claims to fix). The
336
- review agent returns them in their own `## Deferred` section — never in `findings` — each carrying its
337
- own out-of-scope reason.
338
-
339
- - They count in **no** severity row, enter **no** verdict, and are **never** cross-checked: a deferred
340
- item cannot cost a fix loop an iteration, and refuting one would spend an agent arguing about
341
- something that cannot change the outcome.
342
- - **Not deferrable, ever:** anything the diff touched or introduced, any spec violation, any security
343
- issue on a path this feature adds, calls or modifies.
344
- - `/cohorte-review` §3.5 routes them, **on every verdict**, into `specs/refactor-backlog.md` under the
345
- `## <domain>` heading of the owning surface, tagged `deferred:<feature_id>` — the same grouping
346
- `/cohorte-audit` writes, so `/cohorte-refactor <domain>` picks them up with no extra plumbing. Never into the spec's
347
- `## Remediation`, which is what `/cohorte-fix` re-dispatches.
348
- - `/cohorte-audit` **carries open `deferred:` items over** when it rewrites the backlog; overwriting them away
349
- is the one way they silently vanish.
350
- - The verdict JSON carries `deferred: <n>` (informational, outside `blocking`), so a driver can name
351
- them in its closing line without reading a report.
352
-
353
- ## Decisions — the transverse decision journal
354
-
355
- `PIPELINE.md` is a **stack profile** (surfaces, commands, conventions); it says nothing about what this
356
- project has *decided*. Without somewhere for those, every `/cohorte-spec` re-discovers or contradicts them.
357
- `specs/_decisions.md` (from `core/templates/decisions.template.md`) is that place, deliberately small:
358
-
359
- - **Append-only, one line per decision, ≤ ~160 chars:**
360
- `- <YYYY-MM-DD> · <area> · <decision> — because <reason> · <origin>`, where `<origin>` is the
361
- `feature_id` that decided it — or the originating command (`retro`) when no single feature owns
362
- it. Reversal never edits a line:
363
- append a superseding one (`· supersedes <date> <area>`) and move the old one to `## Superseded`. When
364
- `## Live` passes ~100 lines, sweep the superseded ones down.
365
- - **Written by** `/cohorte-spec` at freeze (the decisions that outlive the feature — typically 0–3 lines, and
366
- zero is a normal outcome), `/cohorte-build` §1.5 when it adds or splits a surface, and
367
- `/cohorte-retro` §4 when the human ratifies a convention rule (one line per adopted rule).
368
- - **Read by the deciding stages only** — `/cohorte-brainstorm` (so the panel argues about the idea, not about
369
- settled ground), `/cohorte-spec` (so a new spec does not silently un-decide something), `/cohorte-audit` (standing
370
- decisions are part of the rulebook it audits against).
371
- - **Never read by implementers or reviewers.** They work from the frozen contract, which already tells
372
- them what to do; shipping them the rationale would cost `surfaces × dispatches` tokens per feature
373
- for a fact they cannot act on. This is what keeps the journal cheap enough to be worth having.
374
- - The `_` prefix is load-bearing: `/cohorte-doctor`, the `cohorte specs` scanner and the kanban backfill all skip
375
- `specs/_*.md`, so the journal is never mistaken for a spec (no phantom card, no bogus stage).
376
-
377
- ## Preflight — the deterministic phase gate
378
-
379
- `/cohorte-review` starts by running `pipeline/scripts/preflight.sh` — a plain shell script (no
380
- agent) that executes the profile's mechanical checks in order (typecheck → lint → tests, quiet
381
- variants) with all output redirected to `specs/reports/<id>.preflight.txt`:
382
-
383
- - **Any check red** ⇒ the script prints the last 40 lines raw and exits 1. The command **aborts
384
- there: zero agents are spawned.** A reviewer dispatched onto code that doesn't compile burns its
385
- whole run rediscovering what `tsc` already printed for free — the failure goes straight to the
386
- human (or `/cohorte-fix`) instead.
387
- - **All green** ⇒ the script stamps `<state>/preflight.ok` (`<epoch> <HEAD sha> <tree digest>` —
388
- local and **gitignored**; a versioned stamp describes the tree *before* its own commit and rides
389
- into every clone and worktree, which breaks the gate both ways).
390
-
391
- `hooks/gate.py` enforces the stamp as a **phase gate** (the `preflight` block of `gate-config.json`,
392
- generated from `gate.preflight`): a Task dispatch of a listed `subagent_type` (default
393
- `review`) with a missing/stale stamp — older than `max_age_minutes`, or the digest no longer
394
- matches the working tree (`.claude`, `.cohorte` and `specs` excluded, so the pipeline's own writes and a
395
- commit of already-verified code do not invalidate it) — gets an
396
- "ask", so a lead can't accidentally skip the gate but a human can consciously override it. The gate
397
- hook fires for **every** agent in the session, including subagents spawned by the Workflow runtime
398
- (they run in `acceptEdits` whatever the session mode — Write/Edit auto-approved — but Bash and Task
399
- still pass through hooks). In `bypassPermissions` (headless runs) every gate "ask" is escalated to a
400
- hard deny, because nobody is there to answer a prompt.
401
-
402
- ## Rendering / reconciling a surface agent (shared procedure)
403
-
404
- Both `/cohorte-init-pipeline` (initial render) and `/cohorte-build` (auto-reconcile when a spec needs a new agent) use
405
- this exact procedure so a surface is always defined the same way. To add surface `S`:
406
-
407
- 1. **Add the `surfaces[]` entry** to `PIPELINE.md`: `key`, `path` (the disjoint tree it exclusively
408
- owns), `label`, `agent` (rendered file name), `tools` (add `DesignSync` only if `uses_design: true`;
409
- append the retrieval provider's MCP tools when `retrieval.provider` ≠ `none` — e.g. `mcp__serena`),
410
- `model` (tier for the rendered agent: `sonnet` (default) — the implementer mostly applies a frozen
411
- contract, which Sonnet does well at a fraction of the Opus-lead cost; `haiku` for purely mechanical
412
- scaffolding; `inherit` only when the surface makes real design decisions worth the lead's model),
413
- the five `*_cmd`s (derive from the surface's `package.json` / workspace
414
- filter, mirroring a sibling surface), and `uses_design`.
415
- 2. **Render the agent file** `<agents>/<agent>.md` from `<core>/pipeline/implementer.template.md`
416
- — the template is already rendered for this runtime, so only the placeholders are yours to fill —
417
- substituting `<SURFACE_AGENT>`, `<SURFACE_LABEL>`,
418
- `<SURFACE_PATH>`, `<SURFACE_TOOLS>`, `<SURFACE_MODEL>`, `<PROJECT_NAME>`, and the surface-specific
419
- blocks (`<SURFACE_EXTRA_NEVER>`, `<SURFACE_DESIGN_INPUT>`, `<SURFACE_TDD_STEP1>` — leave the design
420
- ones empty unless `uses_design`). Fill `<SURFACE_CONVENTIONS>` with the surface's convention slice
421
- **baked at render time**: `PIPELINE.md` §Conventions `### Shared` + this surface's
422
- `### Surface: <key>` stanza + its §Testing lines, verbatim. At runtime the agent then reads only
423
- the profile's machine block (the fenced `yaml pipeline-profile`) — never the prose sections. The
424
- bake stays honest because §Conventions edits go through `/cohorte-update-pipeline`, whose reconcile
425
- re-renders every agent (step 2 below); hand-edit the prose without re-rendering and the baked
426
- slice goes stale — that's the trade for not re-reading the prose on every dispatch. For a `uses_design` surface, fill them **link-based** (never with a
427
- stored `design_project` id — that goes stale on a DS rebuild):
428
- - `<SURFACE_DESIGN_INPUT>` — a 4th input bullet: _"The **feature design** — the pages this feature
429
- touches, listed in your dispatch's design slot as full links
430
- (`https://claude.ai/design/p/<projectId>?file=<file>`); a slot saying `none` means a fix loop with
431
- no visual work — skip DesignSync entirely. For each link, extract the `<projectId>` (the
432
- `/p/…` segment) and `<file>` (the `?file=` query) from the URL and read it read-only via `DesignSync
433
- get_file(<projectId>, <file>)`; `list_files(<projectId>)` to catch linked pages (shared nav/modals)
434
- this feature also changes. The link is self-contained — no stored project id. Build with the code UI
435
- kit (the `design_system_project`'s materialization: `@/components/ui/*` + tokens); read a primitive
436
- via `get_file` only if it's missing/stale in code. Mobile-first."_
437
- - `<SURFACE_TDD_STEP1>` — a **lead-in paragraph** above the TDD list (not a numbered item; it
438
- renders as nothing for a non-design surface, which is why the list must not start at it):
439
- _"**Pull the feature design first** (skip if your dispatch's design slot
440
- says `none`): `DesignSync get_file(<projectId>, <file>)` for each link in the slot and translate
441
- each into the code design system (`@/components/ui/*`, `cn()` + CVA), mobile-first — never ad-hoc
442
- CSS. Then:"_
443
- 3. **Add a §Conventions + §Testing stanza** for `S` in `PIPELINE.md` (mirror a sibling surface; keep it
444
- rule-shaped). If `S` is a shared-code surface, its convention is "single owner of shared X; slices
445
- consume, never redefine."
446
-
447
- Removing/merging a surface is the reverse: drop the `surfaces[]` entry, delete its agent file, fold its
448
- conventions. Never leave an agent file with no matching `surfaces[]` entry (orphan) or vice-versa.
449
-
450
- ## Release notes — the per-feature note the versioning tool consumes
451
-
452
- Many repos gate merges on a **per-feature release note**: Changesets' `changeset` CI job fails any PR
453
- that touches product code without a `.changeset/*.md`. That file is **not** something the release
454
- agent can invent — picking the bump level is project policy (semver over an API vs. over a product,
455
- a `0.x` rule forbidding `major`, what counts as user-visible), and the prose is outward-facing copy.
456
- It is the **lead's** to write, exactly like the contract.
457
-
458
- The failure mode this block exists to prevent is silent and reproducible: the requirement lives in the
459
- project's `CLAUDE.md`, which the ship flow never reads, so `/cohorte-ship` completes, opens the PR,
460
- moves the kanban card to **Shipped** — and CI goes red on a job nobody looked at. The feature reads as
461
- shipped while being unmergeable. Encoding it in the profile is what makes the step survive a stateless
462
- lead.
463
-
464
- - **Authored before the dispatch**, next to the `status: shipped` flip (`/cohorte-ship` §2b), so the note
465
- lands **inside** the release commit. Written after the fact it needs a second commit, and the PR is
466
- already open and red.
467
- - **One key in the front-matter** — `anchor_package`. Lockstep/`fixed` version groups propagate the bump
468
- from it to every other workspace; listing more is how a repo ends up with a package bumped twice.
469
- - **`forbid_levels`** encodes policy the tool itself may not enforce. The common one: while the product
470
- is `0.y.z`, a `major` changeset makes Changesets jump to `1.0.0` with no human deciding it — so `0.x`
471
- repos forbid `major` and declare a breaking change as `minor`.
472
- - **Ask, don't guess, on an ambiguous bump.** Between two defensible levels (a refactor that also changes
473
- what the user sees), state the reading and let the human pick — a wrong bump is a published version
474
- number, not a fixable draft.
475
- - **`empty_cmd`** covers the honest no-op: a PR that touches product code but must move no version.
476
- Prefer it to skipping the step; the CI job wants a file, not a version.
477
-
478
- `enabled: false` (or `tool: none`) ⇒ §2b is a no-op and nothing below applies.
479
-
480
- ## Reconcile — bringing generated files up to the current core
481
-
482
- `/cohorte-init-pipeline` is **one-time per project**. Afterwards, `/cohorte-update-pipeline` runs this procedure so a
483
- core upgrade never requires re-running init — new pipeline features flow into the repo's generated
484
- files automatically. It works because every generated artifact is a **deterministic function of
485
- (current template × the profile's data)**; nothing needs re-detecting or re-interviewing.
486
-
487
- 1. **Profile top-up.** Diff `PIPELINE.md`'s machine block against the current
488
- `pipeline/PIPELINE.template.md`: every block/field the template has and the profile lacks is added
489
- with its documented default (e.g. `surfaces[].model: sonnet`, `retrieval.provider: serena`).
490
- **Ask only when a new field is a genuine human decision** (batch into ONE question set); never
491
- change a value the profile already sets; never rewrite the prose sections. `release_notes` is one
492
- such decision and must not be defaulted blind: detect a versioning tool / note-enforcing CI job per
493
- `/cohorte-init-pipeline` Phase 1, and if there is one, ask Phase 2's release-notes question (anchor
494
- package, language, bump policy, forbidden levels). No tool found ⇒ top up with `enabled: false`.
495
- 2. **Re-render agent frontmatter + body.** For each `surfaces[]` entry, re-render
496
- `<agents>/<agent>.md` from the current `implementer.template.md` per §Rendering above. Safe by
497
- doctrine: rendered agents are regenerable artifacts — hand-written rules belong in `PIPELINE.md`
498
- §Conventions (which reconcile never touches), NOT in agent files, where they'd be clobbered here.
499
- 3. **Additive settings patch.** Bring `<state>/gate-config.json` — and, on a runtime with a settings
500
- file the pipeline generates, that too — up to the current init spec (missing `allow` entries,
501
- hooks per install mode): add what's missing, never remove or rewrite existing/custom keys.
502
- 4. **Capability wiring.** If a top-up added a capability needing external setup (e.g. a `retrieval`
503
- provider whose MCP server isn't registered yet), run its wiring step from `/cohorte-init-pipeline` Phase 4.
504
- Even when nothing new was added, re-run the provider's health check (§Code retrieval) — wiring
505
- rots (PATH changes, uninstalls, hand-edits) — and repair whatever fails.
506
- 5. **Global config seed.** If `<config>` is absent, seed it from the template
507
- (`profile/cohorte.config.template.yaml`) so the kanban + shared-vault config has a home. Never
508
- clobber an existing filled file; report what was seeded. Then **scrub the retired `telemetry:`
509
- block** if the file still carries one (every install seeded before 2.3.0 does): delete the block
510
- and its comment header, leaving the rest byte-identical. It is dead config — the sender is gone
511
- and nothing reads it — but a `telemetry.enabled: true` sitting in a file the human may open reads
512
- as "this is still sending", which is the one thing it must not imply. This is the single
513
- exception to "never rewrite the config": a targeted deletion of a block the core no longer
514
- defines, never a re-seed.
515
- 6. **Kanban sync.** Run the §Kanban reconcile: link/create the project's board if configured, verify
516
- its columns, and backfill/sync cards from `specs/*.md`. See §Kanban.
517
- 7. **Spec-template top-up.** `specs/_template.md` is seeded once at install and then **never**
518
- refreshed, so a repo keeps whatever front-matter the core shipped the day it was installed (a
519
- pre-1.6 copy's `status` comment still lists four states). Top it up the same way as the profile:
520
- add the **front-matter fields** the current `templates/spec.template.md` has and the repo's copy
521
- lacks, with their documented defaults, drop `loop_pass`/`loop_phase` (retired with
522
- `/cohorte-loop` in 2.2.0), and refresh the `status:` comment. Never rewrite its body — the section list is the human's to shape,
523
- and some repos have deliberately trimmed it. Nothing breaks without this (the fields are written on
524
- demand when a driver needs them); it just keeps a new spec's front-matter honest about the states
525
- the pipeline can put it in.
526
-
527
- 8. **Local-artifact hygiene.** The pipeline's own runtime files must stay out of git:
528
- `<state>/preflight.ok`, `<state>/pipeline-metrics.jsonl`, `specs/reports/`. Add any missing entry to
529
- `.gitignore`, and **untrack** what a pre-2.0.0 install let slip in —
530
- `git rm --cached --ignore-unmatch <state>/preflight.ok` (repeat per stray path). The stamp is the
531
- one that actively breaks: it records the tree it verified, the commit carrying it moves HEAD past
532
- that tree, and the committed copy lands in every clone and worktree — so the phase gate ends up
533
- blocking clean trees and greening unchecked ones. Report what was untracked; the human commits it.
534
-
535
- Re-running `/cohorte-init-pipeline` remains possible (it reconciles too) but is only *needed* when the stack
536
- itself changes in ways `/cohorte-build` §1.5 can't auto-grow (e.g. package manager or contract mechanism swap).
537
-
538
- ## Workflows — deterministic multi-agent runs (opt-in)
539
-
540
- Four scripts run under the Claude Code Workflow runtime instead of the lead reasoning out the
541
- fan-out turn by turn: `<core>/workflows/review.js`, `audit.js`, `refactor.js` — each the
542
- **workflow variant** of its same-named conversational command — plus `loop.js`
543
- (`/cohorte-loop`), which has **no conversational form at all** (below). For the variant pairs,
544
- the conversational commands (`/cohorte-review`, `/cohorte-audit`, `/cohorte-refactor`)
545
- **remain the default path and the fallback** — a workflow runs only when the human explicitly asks
546
- for it ("run the review workflow"), and requires Claude Code ≥ **2.1.154** with workflows
547
- enabled.
548
- `/cohorte-doctor` reports which path a session will take. The interactive commands (`/cohorte-init-pipeline`,
549
- `/cohorte-brainstorm`, `/cohorte-spec`) and the dispatch-only ones (`/cohorte-build`, `/cohorte-ship`) have **no** workflow variant on
550
- purpose: they're interviews or already a single parallel dispatch — a script adds nothing.
551
- `/cohorte-loop` does not change that: it **consumes** `/cohorte-build`'s outputs (the frozen
552
- spec, `readiness.json`, the lead-authored contract, `build.json`) — it is not a build variant,
553
- and adding one would put the lead-only steps (§1.5 reconcile, §2 contract authoring) inside a
554
- script that cannot ask.
555
-
556
- Shared design, all four scripts:
557
-
558
- - **Phase 0 is always `profile-reader`** — workflow scripts have no filesystem or shell access, so a
559
- dedicated agent (`core/agents/profile-reader.md`, haiku, read-only) reads `PIPELINE.md` and returns
560
- the `yaml pipeline-profile` block as JSON. Every later phase is parameterized from that object.
561
- - **Mechanical phases run on haiku** (profile read, preflight, diff staging, report merging/writing);
562
- judgment phases dispatch the same pinned agents the commands use (`review` at sonnet, the surface
563
- implementers at their `surfaces[].model` tier) — the per-surface `model:` routing carries over.
564
- - **Only the verdict comes back.** Bulk (diffs, reports, backlogs) is staged to the same disk
565
- buffers the commands use (`specs/reports/`, `specs/refactor-backlog.md`); the workflow's return is
566
- counts + verdict + paths.
567
- - **A dead agent is never a clean result.** `agent()` resolves to `null` when a subagent dies, and a
568
- dead *reviewer* returns zero findings — byte-identical to a surface that is genuinely clean. Any
569
- script that derives a verdict from "how many findings came back" must first subtract the agents
570
- that never answered: `review.js` names them in `unreviewedSurfaces` and refuses to score
571
- `SHIP`. `scripts/test-workflows.mjs`
572
- pins this — it is the one invariant the structural checks in `validate-core.mjs` cannot see.
573
- The conversational commands enforce the same rule by roll call (§Dead agents); it was the workflows
574
- that had it first, and for three releases they had it **alone** — the same crash on the
575
- conversational path went unreported.
576
- - **`review.js`** — preflight gate (aborts red, zero agents), one `git diff --stat` staged per
577
- touched surface, one reviewer per surface in parallel, then an **adversarial cross-check** phase
578
- that tries to refute each CRITICAL/security finding before it can trigger a fix loop.
579
- - **`audit.js`** — one auditor per domain (each surface + `shared`), concurrency capped by the
580
- runtime (~16), merged into the prioritized `specs/refactor-backlog.md`.
581
- - **`refactor.js`** — big domains only (it skips domains with a handful of open items — the
582
- conversational `/cohorte-refactor` is cheaper there): `shared` first and alone, then the other domains'
583
- implementers in parallel, each verified per-domain.
584
- - **`loop.js`** (`/cohorte-loop`) — build → review → [fix → review]* for ONE feature, unattended and
585
- resumable. Preconditions it verifies and refuses to work around: frozen/`in-review` spec, a fresh
586
- `readiness.json` at `READY`/`RESERVATIONS`, the contract on disk, every readiness surface owned by
587
- the profile. Round exits, in order: child abort relayed → `unreviewed` non-empty → `blocking == 0`
588
- ⇒ ship → same blocking-item identity two consecutive rounds ⇒ treading water → `maxRounds`
589
- (default 5). A blocking finding on the contract file aborts (`contract-change` — lead-only, per
590
- `/cohorte-fix` §1). It calls the review **workflow** per round and reads the same `verdict.json`
591
- contract the conversational `/cohorte-review` §3 writes; it stamps `in-progress` while running,
592
- `in-review` on ship, `blocked` on a give-up. **Workflow-only, no command file, ever**
593
- (`validate-core.mjs` pins it): if the runtime is unavailable it refuses explicitly rather than
594
- degrading to a lead re-reasoning the fan-out every round at session-model prices.
595
- - **No input mid-run.** A workflow runs to completion without questions; anything interactive
596
- (contract changes, human decisions) belongs to the conversational path. The gate hook still
597
- fires on workflow subagents (see
598
- §Preflight) — in unattended runs its asks become denies. Know what that means for edits:
599
- workflow subagents run in **`acceptEdits` whatever the session mode**, so for the length of a
600
- run — and `loop.js` runs long, unattended stretches — `hooks/gate.py` is the only brake on
601
- what agents write. That is stated here rather than discovered.
602
- - **Permissions:** `/cohorte-init-pipeline` and `/cohorte-update-pipeline` extend the generated `settings.json`
603
- `allow` list with what workflow agents need (the quiet commands, the shipped
604
- `pipeline/scripts/*.sh`, read-only git incl. `git rev-parse`, and the retrieval provider's MCP
605
- tools) so a run never stalls mid-workflow on a permission prompt nobody is watching.
606
-
607
- ## Kanban — mirroring the pipeline onto an Obsidian board
608
-
609
- An **optional, user-scoped** mirror of the dev flow: each pipeline stage moves a card across an
610
- [Obsidian Kanban](https://github.com/mgmeyers/obsidian-kanban) board, one board per project. Config
611
- lives in the consolidated global config `<config>` §`kanban` (NOT in
612
- `PIPELINE.md` — the board path points at the user's personal vault, so it is machine-specific and must
613
- not be committed). Everything below **no-ops silently** when the config is absent, `kanban.enabled` is
614
- false, no board is configured for the current project, or the board file is missing — the pipeline never
615
- blocks on the board.
616
-
617
- **Config & board resolution.** `kanban.boards` is keyed by the project's `PIPELINE.md` `name`. To resolve
618
- the current project's board: read `name` from `PIPELINE.md`, look up `kanban.boards[name]`. Found ⇒ the
619
- board file is `<obsidian.vault_path>/<boards[name].board>`, its columns are `boards[name].columns` if
620
- present else `kanban.columns`. Not found ⇒ kanban off for this project.
621
-
622
- **Card format.** A card is a Kanban list item under a `## <column>` heading:
623
- `- [ ] <human title> #<feature_id>`. The `#<feature_id>` tag is the join key between a card and its
624
- `specs/<feature_id>.md`; it is how every stage finds *its* card (Grep the board for `#<id>`). Free-text
625
- notes a human writes as sub-bullets under an Ideas card are seed context for `/cohorte-brainstorm`. Never touch
626
- the trailing `%% kanban:settings … %%` block or the `kanban-plugin: board` front-matter.
627
-
628
- Once shipped, `/cohorte-ship` appends the **PR number** to the card — `- [ ] <title> #<feature_id> — PR #<num>`.
629
- The bare `#<num>` is what a board reader renders as a clickable link to the GitHub PR, so `/cohorte-ship` always
630
- writes it when a PR was actually created.
631
-
632
- **Move a card (the core op).** One call — the script does resolution AND the move outside the
633
- agent's context (find, dedupe, sub-notes carried along, settings block preserved):
634
-
635
- ```
636
- <core>/pipeline/scripts/kanban-move.sh auto <id> <stage> [--pr <num>] [--title <title>]
637
- ```
638
-
639
- It creates
640
- the card in the target column when none exists, keeps the first and drops duplicates, and appends
641
- ` — PR #<num>` with `--pr`.
642
-
643
- **`auto` is not a convenience, it is the contract.** It reads `name` from `PIPELINE.md`, then
644
- `kanban.enabled` / `obsidian.vault_path` / `boards[name]` from `<config>`
645
- (override with `COHORTE_CONFIG`, or skip the profile with `--project <name>`), and it maps the
646
- **stage key** (`ideas` · `brainstorm` · `spec` · `ready` · `building` · `review` · `fix` · `ship` ·
647
- `shipped`) to that board's heading through `boards[name].columns` → `kanban.columns` → the built-in
648
- default. An explicit `<board.md>` path and a literal heading both still work, for one-off and
649
- non-pipeline moves.
650
-
651
- **Never conclude "no board is configured" without running it.** The command that resolves nothing
652
- prints `kanban: <reason>` — naming the missing link (no config file, `enabled: false`, no entry for
653
- this project, vault unset, board file gone) — and exits **0**. A configured board that cannot be
654
- moved is loud instead: exit 2 on usage, exit 3 on a missing board file or an unknown column. Both
655
- readings are on stdout, so a caller reports which one it got. This exists because inference was the
656
- actual failure mode: with only "no-op silently if no board" to go on, a fresh phase session (every
657
- phase runs after a `/clear`) decided there was no board without ever opening the config, and cards
658
- stopped moving mid-pipeline while every command still reported success.
659
-
660
- **Fallback when the script is absent** (older core): do it by hand, but never read the whole board
661
- into context — it grows with every feature ever tracked: `grep -n` for `#<id>` and the `## ` headings
662
- to locate lines, then use offset-limited Reads + targeted Edits around the matches. Either way: one
663
- card per `#<id>`, whole line moved tag-preserved, card created in the target column if missing.
664
-
665
- **Tag before you move.** The join key is the `#<id>` tag, and an **Ideas** card a human typed by hand
666
- does not have one. Moving it first finds nothing, creates a second card, and strands the original in
667
- Ideas — so `/cohorte-brainstorm` appends the tag to the picked line before its first move.
668
-
669
- **Stage → column**, used both by each pipeline command (to move its card live) and by backfill:
670
-
671
- | Pipeline moment | Column |
672
- | --------------------------------------- | --------------- |
673
- | human drops a raw idea (manual) | `ideas` |
674
- | `/cohorte-brainstorm` picks it up | `brainstorm` |
675
- | `/cohorte-spec` opens (draft) | `spec` |
676
- | `/cohorte-spec` freezes (`status: frozen`) | `ready` |
677
- | `/cohorte-patch` triages / freezes | `spec` → `ready` (card titled `[patch] <title>`, tag `#patch-<slug>`) |
678
- | `/cohorte-build` | `building` |
679
- | `/cohorte-review` | `review` |
680
- | `/cohorte-fix` | `fix` |
681
- | a round is under way (`in-progress`) | `building` |
682
- | a round gave up (`blocked`) | `fix` |
683
- | `/cohorte-ship` starts | `ship` |
684
- | PR opened (`status: shipped`) | `shipped` (+ `PR #<num>` on the card) |
685
-
686
- **Backfill / sync from specs (reconcile).** `specs/*.md` is the source of truth. For each spec, read its
687
- `feature_id` (front-matter or filename) and `status`, map `status`→column — `frozen`→`ready`,
688
- `in-progress`→`building`, `in-review`→`review`, `blocked`→`fix`, `shipped`→`shipped`, anything else / a spec with no
689
- status→`spec` — then **full
690
- sync**: card absent ⇒ add it in that column; card present ⇒ **move it** to that column so the board
691
- always reflects the specs (this repositions cards the human may have moved by hand). Report cards
692
- added vs. moved vs. already-correct.
693
-
694
- **Create a board.** When linking a project with no board file yet: write
695
- `<obsidian.vault_path>/<folder>/Tasks.md` with the `kanban-plugin: board` front-matter, one `## <heading>`
696
- per configured column in pipeline order, and the closing `%% kanban:settings %%` block
697
- (`{"kanban-plugin":"board","list-collapse":[false,…]}` with one `false` per column).
698
-