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