cohorte 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +264 -0
- package/LICENSE +661 -0
- package/README.md +269 -0
- package/bin/cli.js +339 -0
- package/core/agents/implementer.template.md +74 -0
- package/core/agents/release.md +51 -0
- package/core/agents/review.md +85 -0
- package/core/commands/align-ds.md +32 -0
- package/core/commands/audit.md +31 -0
- package/core/commands/brainstorm.md +48 -0
- package/core/commands/build.md +91 -0
- package/core/commands/doctor.md +50 -0
- package/core/commands/fix.md +62 -0
- package/core/commands/init-pipeline.md +32 -0
- package/core/commands/refactor.md +38 -0
- package/core/commands/review.md +68 -0
- package/core/commands/ship.md +68 -0
- package/core/commands/smoke.md +55 -0
- package/core/commands/spec.md +67 -0
- package/core/commands/update-pipeline.md +96 -0
- package/core/hooks/__pycache__/gate.cpython-312.pyc +0 -0
- package/core/hooks/gate.py +129 -0
- package/core/templates/agent-handoff.md +34 -0
- package/core/templates/brainstorm-return.md +36 -0
- package/core/templates/design-brief.md +35 -0
- package/core/templates/pr-body.md +29 -0
- package/core/templates/review-feedback.md +36 -0
- package/core/templates/spec.template.md +84 -0
- package/core/templates/steps/init-pipeline/01-detect-stack.md +40 -0
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +41 -0
- package/core/templates/steps/init-pipeline/03-draft-profile.md +10 -0
- package/core/templates/steps/init-pipeline/04-write-render.md +88 -0
- package/core/templates/steps/init-pipeline/05-report.md +12 -0
- package/dashboard/README.md +54 -0
- package/dashboard/dist/apple-touch-icon-180.png +0 -0
- package/dashboard/dist/assets/index-CoBuEdy-.js +42 -0
- package/dashboard/dist/assets/index-DN5OGW9g.css +1 -0
- package/dashboard/dist/favicon-16.png +0 -0
- package/dashboard/dist/favicon-32.png +0 -0
- package/dashboard/dist/favicon-48.png +0 -0
- package/dashboard/dist/icon-192.png +0 -0
- package/dashboard/dist/icon-512.png +0 -0
- package/dashboard/dist/index.html +16 -0
- package/dashboard/server/doctor.js +266 -0
- package/dashboard/server/fleet.js +119 -0
- package/dashboard/server/index.js +306 -0
- package/dashboard/server/kanban.js +158 -0
- package/dashboard/server/versions.js +111 -0
- package/dashboard/server/yaml.js +126 -0
- package/install.ps1 +359 -0
- package/install.sh +301 -0
- package/package.json +40 -0
- package/profile/PIPELINE.template.md +208 -0
- package/profile/SCHEMA.md +303 -0
- package/profile/cohorte.config.template.yaml +43 -0
- package/scripts/new-feature.sh.template +89 -0
- package/scripts/remove-feature.sh.template +53 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# PIPELINE.md profile — field reference
|
|
2
|
+
|
|
3
|
+
`/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
|
+
| `repo.layout` | enum | build, audit | `monorepo` (many surfaces) or `single`. |
|
|
20
|
+
| `repo.workspace_tool` | enum | audit | `turborepo`/`nx`/`none`. |
|
|
21
|
+
| `retrieval.provider` | enum | init, update-pipeline, implementer | `serena` (default) / `graphify` / `none` — see §Code retrieval. |
|
|
22
|
+
| **`surfaces[]`** | list | **build, review, refactor, init** | One per independently-built area. Grows via reconcile (below). |
|
|
23
|
+
| `surfaces[].key` | string | build | Short id + review scope. |
|
|
24
|
+
| `surfaces[].path` | string | implementer | The ONLY tree that surface's agent may touch. |
|
|
25
|
+
| `surfaces[].label` | string | build, init (`<SURFACE_LABEL>`) | Human label + framework, e.g. `frontend (React)`. |
|
|
26
|
+
| `surfaces[].agent` | string | build (`subagent_type`) | Rendered agent file name. |
|
|
27
|
+
| `surfaces[].tools` | list | init | Frontmatter `tools:` for the rendered agent. |
|
|
28
|
+
| `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. |
|
|
29
|
+
| `surfaces[].*_cmd` | string | implementer | test/lint/format/typecheck/build commands. |
|
|
30
|
+
| `surfaces[].uses_design` | bool | build, frontend | Whether this surface consumes designs. |
|
|
31
|
+
| `contract.enabled` | bool | build | `false` ⇒ skip contract authoring (§2 of /build). |
|
|
32
|
+
| `contract.mechanism` | enum | build, lead | `shared-types-zod`/`openapi`/`protobuf`/`json-schema`/`none`. |
|
|
33
|
+
| `contract.path` `.ext` `.index` | string | build | Where `<feature_id>` contract is authored + barrel. |
|
|
34
|
+
| `contract.authored_by` | const `lead` | build | Implementers import it read-only, never edit. |
|
|
35
|
+
| `commands.*` | string | all | Repo-wide install/dev/lint/format/typecheck/test + migrate. |
|
|
36
|
+
| `rbac.enabled` | bool | brainstorm, review | Toggle RBAC personas + authz audit. |
|
|
37
|
+
| `rbac.hierarchy` | list | review | Highest→lowest role list. |
|
|
38
|
+
| `design.enabled` | bool | build, frontend, align-ds | `false` ⇒ design steps are no-ops. |
|
|
39
|
+
| `design.provider` | enum | frontend, align-ds | `claude-design`/`figma`/`none`. |
|
|
40
|
+
| `design.design_system_project` | id | align-ds, frontend | UI-kit source of truth. |
|
|
41
|
+
| `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). |
|
|
42
|
+
| `design.snapshot_dir` | path | align-ds | Committed DS snapshot for diffing. |
|
|
43
|
+
| `design.ui_kit_path` `.tokens_path` | path | align-ds, frontend | Where the kit + tokens live in code. |
|
|
44
|
+
| `isolation.enabled` | bool | new-feature script | `false` ⇒ build in main checkout. |
|
|
45
|
+
| `isolation.db_per_worktree` | bool | new-feature script | Create `<name>_<id>` DB per worktree. |
|
|
46
|
+
| `isolation.db_name_pattern` | string | new-feature script | `<name>_<id>`. |
|
|
47
|
+
| `isolation.port_base` | map | new-feature script | `api`/`web` base ports; +slot per worktree. |
|
|
48
|
+
| `isolation.compose_file` `.registry` | path | new-feature script | Docker stack + slot registry. |
|
|
49
|
+
| `gate.deny[]` | list | hooks/gate.py, settings | Command substrings hard-denied, on any branch. |
|
|
50
|
+
| `gate.ask[]` | list | hooks/gate.py, settings | Command substrings that require confirm, on any branch. |
|
|
51
|
+
| `gate.ask_on_default_branch[]` | list | hooks/gate.py | Confirm ONLY on `default_branch`; free on feature branches. |
|
|
52
|
+
| `gate.default_branch` | string | hooks/gate.py | Protected branch (default `main`); gate resolves via git. |
|
|
53
|
+
|
|
54
|
+
## Prose sections
|
|
55
|
+
|
|
56
|
+
- **Conventions** — per-surface rules the implementer follows and review audits.
|
|
57
|
+
- **Testing** — the TDD contract per surface (what a test must cover, DB isolation).
|
|
58
|
+
- **Design brief note** — feeds `/spec` §8 and the Claude Design step.
|
|
59
|
+
- **Personas** — the `/brainstorm` panel; include one per RBAC role when `rbac.enabled`.
|
|
60
|
+
|
|
61
|
+
## How the pieces reference this file
|
|
62
|
+
|
|
63
|
+
- **Agents** (`implementer`, `review`, `release`) are told at dispatch: _read `PIPELINE.md`
|
|
64
|
+
§Commands / §Conventions / §Surfaces first._ They have `Read`, so they load it live.
|
|
65
|
+
- **Commands** (`/build`, `/review`, …) parse the `yaml pipeline-profile` block to know how
|
|
66
|
+
many surfaces to dispatch, the contract mechanism, the commands, and the capability flags.
|
|
67
|
+
- **Hook** (`gate.py`) reads `gate.deny`/`gate.ask`/`gate.ask_on_default_branch`/`gate.default_branch`
|
|
68
|
+
from a generated `.claude/gate-config.json`. The last two make git + docker free on feature branches
|
|
69
|
+
but confirm-gated on the default branch (branch resolved at run time via `git rev-parse`).
|
|
70
|
+
- **Scripts** (`new-feature.sh`) read the `isolation` block (rendered in at init).
|
|
71
|
+
|
|
72
|
+
## Code retrieval — `retrieval.provider`
|
|
73
|
+
|
|
74
|
+
Agents spend most of their wall-clock reading the repo; a retrieval provider replaces grep-and-read
|
|
75
|
+
with symbol/graph queries. The flag is a **value, not a boolean**, so switching provider later is a
|
|
76
|
+
one-line profile change + re-running the wiring (no agent re-render needed — the guidance agents
|
|
77
|
+
follow is provider-agnostic: _"prefer the retrieval MCP tools over Grep/Glob + whole-file Reads"_).
|
|
78
|
+
|
|
79
|
+
| Provider | Mechanism | Freshness | Cost |
|
|
80
|
+
| --- | --- | --- | --- |
|
|
81
|
+
| `serena` (default) | live LSP symbol navigation (find symbol, references, semantic edits) | always current | none — no index |
|
|
82
|
+
| `graphify` | persistent tree-sitter knowledge graph over code + docs | as fresh as the last rescan | index step + re-index discipline |
|
|
83
|
+
| `none` | agents fall back to Grep/Glob/Read | — | — |
|
|
84
|
+
|
|
85
|
+
**Wiring (done by `/init-pipeline`, or `/update-pipeline` retroactively):**
|
|
86
|
+
|
|
87
|
+
- `serena` — requires the `serena` CLI (`uv tool install -p 3.13 serena-agent`). For day-to-day CLI
|
|
88
|
+
use it should also be on PATH (`uv tool update-shell`; uv installs to `~/.local/bin`). Register at
|
|
89
|
+
**project scope** so the registration is committed and portable (`--project-from-cwd` resolves the
|
|
90
|
+
project at server start, so the committed entry works on every machine) — and register the
|
|
91
|
+
**PATH-proof launcher**, not the bare command: Claude Code spawns MCP servers with whatever
|
|
92
|
+
environment it was launched from (a stale terminal, a GUI/IDE launch that never sourced a shell
|
|
93
|
+
profile), where `~/.local/bin` may be missing from PATH — a bare `serena` entry then dies with
|
|
94
|
+
ENOENT and agents silently fall back to Grep/Read:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
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'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
(Windows-native teams: no `sh` — register the bare `serena` form instead and ensure the uv tools
|
|
101
|
+
dir is on PATH; keep the `--open-web-dashboard False` flag.) `--open-web-dashboard False` keeps the
|
|
102
|
+
dashboard available (reachable at `http://localhost:24282/dashboard/`) but stops it popping a browser
|
|
103
|
+
tab on every server start — the flag overrides the machine's `serena_config.yml`, so the behaviour is
|
|
104
|
+
the same for everyone on the repo. Gitignore `.serena/` (per-machine cache/config). Optionally
|
|
105
|
+
pre-index large repos once: `serena project index`.
|
|
106
|
+
- `graphify` — requires `uv tool install graphify` + `graphify install`; build the initial graph
|
|
107
|
+
(`/graphify .`) and rescan incrementally after big changes (`--update`). See graphify.net.
|
|
108
|
+
- Rendered agents get the provider's MCP tools appended to their `tools:` list (e.g. `mcp__serena`
|
|
109
|
+
grants the whole server); `none` ⇒ nothing appended.
|
|
110
|
+
|
|
111
|
+
**Serena health check** — run after wiring in `/init-pipeline` AND on every `/update-pipeline`
|
|
112
|
+
reconcile (wiring that worked once can rot: PATH changes, tool uninstalled, entry hand-edited):
|
|
113
|
+
|
|
114
|
+
1. **CLI resolves:** `command -v serena`. Fails but `~/.local/bin/serena` exists ⇒ PATH repair
|
|
115
|
+
above; missing entirely ⇒ reinstall.
|
|
116
|
+
2. **Registered:** this repo's `.mcp.json` has the `serena` entry ⇒ else re-run the `claude mcp add`.
|
|
117
|
+
If the entry is the bare `serena` form on a POSIX machine, upgrade it to the PATH-proof launcher
|
|
118
|
+
above (immune to launch-environment PATH gaps). If a launcher entry predates the
|
|
119
|
+
`--open-web-dashboard False` flag, append it so the dashboard no longer auto-opens a browser tab.
|
|
120
|
+
3. **Gitignored:** `.serena/` is in `.gitignore` ⇒ else append it.
|
|
121
|
+
4. **Actually connected:** the `mcp__serena` tools are exposed in the session (or `claude mcp list`
|
|
122
|
+
shows serena connected). If 1–3 pass but this fails, a session restart is needed — say so
|
|
123
|
+
explicitly instead of reporting success.
|
|
124
|
+
|
|
125
|
+
Report each check's result; never report Serena "wired" on registration alone.
|
|
126
|
+
|
|
127
|
+
Teammates cloning the repo get the committed `.mcp.json` and only need the provider CLI installed
|
|
128
|
+
and on PATH — if either is missing, the MCP server fails to start and agents silently fall back to
|
|
129
|
+
Grep/Read; the health check above is the diagnostic.
|
|
130
|
+
|
|
131
|
+
## Specialization — when to split one surface into more agents
|
|
132
|
+
|
|
133
|
+
`/build` dispatches ONE agent per surface, in parallel, so build wall-clock ≈ the **slowest single
|
|
134
|
+
surface**. More agents only build faster when they let the *slowest* surface's work run concurrently —
|
|
135
|
+
and only if the split is safe. The invariant that keeps parallelism safe is **one owner per tree, and
|
|
136
|
+
the frozen contract as the only cross-surface channel**. So specialization means carving a surface into
|
|
137
|
+
**smaller non-overlapping surfaces**, never pointing two agents at the same tree.
|
|
138
|
+
|
|
139
|
+
**Split a surface into specialized sub-surfaces only when BOTH hold:**
|
|
140
|
+
|
|
141
|
+
1. **It's a bottleneck** — the surface is large (many modules / high LOC) and dominates build time.
|
|
142
|
+
2. **The boundary is clean** — its work partitions into trees that don't share files, e.g. feature
|
|
143
|
+
modules (`src/features/*`, `src/modules/*`), route groups, or independent services (`services/*`).
|
|
144
|
+
|
|
145
|
+
**Rules when splitting (non-negotiable — they preserve the invariant):**
|
|
146
|
+
|
|
147
|
+
- **Shared code gets its own surface with a single owner.** Anything two slices both touch — routing,
|
|
148
|
+
global state/store, the design-system kit + tokens, shared utils — becomes its OWN surface (e.g.
|
|
149
|
+
`web-shared`), owned by exactly one agent. Never let two feature-slice agents both edit shared trees.
|
|
150
|
+
- **Cross-slice references go through the contract**, not direct imports between slice trees. If
|
|
151
|
+
`web-checkout` needs a shape produced by `api-billing`, that shape lives in the frozen contract.
|
|
152
|
+
- **Don't over-split.** A slice too small to hold ≥1 real task, or one with tangled boundaries, is worse
|
|
153
|
+
than not splitting — the coordination + token cost (each stateless agent re-reads `PIPELINE.md` + spec)
|
|
154
|
+
outweighs the parallelism. When boundaries aren't clean, keep one surface.
|
|
155
|
+
|
|
156
|
+
Coarse first, specialize on evidence: start with one `frontend` / `backend` surface each; split only a
|
|
157
|
+
surface that's proven slow and cleanly separable. The evidence lives in
|
|
158
|
+
`.claude/pipeline-metrics.jsonl` (gitignored) — one JSONL line per dispatched agent
|
|
159
|
+
(`ts`/`feature`/`phase`/`surface`/`seconds`/`result`), appended by `/build`, `/review`, `/fix` and
|
|
160
|
+
`/smoke`. Read it before proposing a split: split the surface that actually dominates wall-clock,
|
|
161
|
+
not the one that feels big.
|
|
162
|
+
|
|
163
|
+
## Measuring cost — what's slow vs what's expensive
|
|
164
|
+
|
|
165
|
+
`pipeline-metrics.jsonl` records **wall-clock seconds** per dispatch (§Specialization) — it tells you what's
|
|
166
|
+
SLOW. It deliberately does NOT record tokens: the lead can't reliably read a subagent's token count to log
|
|
167
|
+
it. For what's EXPENSIVE, use Claude Code's own accounting:
|
|
168
|
+
|
|
169
|
+
- **`/cost`** (built-in, zero setup) — reports per-**subagent** and per-**slash-command** share of your usage
|
|
170
|
+
over the last 24 h / 7 d (e.g. _"Top subagents: frontend 7 %, backend 4 % · Top skills: /build 1 %,
|
|
171
|
+
/review 1 %"_). That IS the per-phase ledger — approximate (share-of-total, machine-local, not exact
|
|
172
|
+
tokens). Read it to see which surface/command actually dominates the bill before you tune a `model` tier.
|
|
173
|
+
- **OpenTelemetry** (exact numbers + dashboards) — add an `env` block to `~/.claude/settings.json`:
|
|
174
|
+
`{"env":{"CLAUDE_CODE_ENABLE_TELEMETRY":"1","OTEL_METRICS_EXPORTER":"otlp","OTEL_EXPORTER_OTLP_PROTOCOL":"http/protobuf","OTEL_EXPORTER_OTLP_ENDPOINT":"http://localhost:4318"}}`
|
|
175
|
+
and point it at a collector. Metrics `claude_code.token.usage` + `claude_code.cost.usage` carry
|
|
176
|
+
`session.id` + model + type (input/output/cacheRead). Subagent tokens roll into the session total;
|
|
177
|
+
per-subagent attribution needs traces (`CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1`, beta).
|
|
178
|
+
|
|
179
|
+
## Rendering / reconciling a surface agent (shared procedure)
|
|
180
|
+
|
|
181
|
+
Both `/init-pipeline` (initial render) and `/build` (auto-reconcile when a spec needs a new agent) use
|
|
182
|
+
this exact procedure so a surface is always defined the same way. To add surface `S`:
|
|
183
|
+
|
|
184
|
+
1. **Add the `surfaces[]` entry** to `PIPELINE.md`: `key`, `path` (the disjoint tree it exclusively
|
|
185
|
+
owns), `label`, `agent` (rendered file name), `tools` (add `DesignSync` only if `uses_design: true`;
|
|
186
|
+
append the retrieval provider's MCP tools when `retrieval.provider` ≠ `none` — e.g. `mcp__serena`),
|
|
187
|
+
`model` (tier for the rendered agent: `sonnet` (default) — the implementer mostly applies a frozen
|
|
188
|
+
contract, which Sonnet does well at a fraction of the Opus-lead cost; `haiku` for purely mechanical
|
|
189
|
+
scaffolding; `inherit` only when the surface makes real design decisions worth the lead's model),
|
|
190
|
+
the five `*_cmd`s (derive from the surface's `package.json` / workspace
|
|
191
|
+
filter, mirroring a sibling surface), and `uses_design`.
|
|
192
|
+
2. **Render the agent file** `.claude/agents/<agent>.md` from `pipeline/implementer.template.md`
|
|
193
|
+
(resolve bundled `.claude/` vs global `~/.claude/`), substituting `<SURFACE_AGENT>`, `<SURFACE_LABEL>`,
|
|
194
|
+
`<SURFACE_PATH>`, `<SURFACE_TOOLS>`, `<SURFACE_MODEL>`, `<PROJECT_NAME>`, and the surface-specific
|
|
195
|
+
blocks (`<SURFACE_EXTRA_NEVER>`, `<SURFACE_DESIGN_INPUT>`, `<SURFACE_TDD_STEP1>` — leave the design
|
|
196
|
+
ones empty unless `uses_design`). For a `uses_design` surface, fill them **link-based** (never with a
|
|
197
|
+
stored `design_project` id — that goes stale on a DS rebuild):
|
|
198
|
+
- `<SURFACE_DESIGN_INPUT>` — a 4th input bullet: _"The **feature design** — the pages this feature
|
|
199
|
+
touches, listed in the spec front-matter `design_files` as full links
|
|
200
|
+
(`https://claude.ai/design/p/<projectId>?file=<file>`). For each, extract the `<projectId>` (the
|
|
201
|
+
`/p/…` segment) and `<file>` (the `?file=` query) from the URL and read it read-only via `DesignSync
|
|
202
|
+
get_file(<projectId>, <file>)`; `list_files(<projectId>)` to catch linked pages (shared nav/modals)
|
|
203
|
+
this feature also changes. The link is self-contained — no stored project id. Build with the code UI
|
|
204
|
+
kit (the `design_system_project`'s materialization: `@/components/ui/*` + tokens); read a primitive
|
|
205
|
+
via `get_file` only if it's missing/stale in code. Mobile-first."_
|
|
206
|
+
- `<SURFACE_TDD_STEP1>` — _"**Pull the feature design first:** `DesignSync get_file(<projectId>,
|
|
207
|
+
<file>)` for each `design_files` link and translate each into the code design system
|
|
208
|
+
(`@/components/ui/*`, `cn()` + CVA), mobile-first — never ad-hoc CSS. Then:"_
|
|
209
|
+
3. **Add a §Conventions + §Testing stanza** for `S` in `PIPELINE.md` (mirror a sibling surface; keep it
|
|
210
|
+
rule-shaped). If `S` is a shared-code surface, its convention is "single owner of shared X; slices
|
|
211
|
+
consume, never redefine."
|
|
212
|
+
|
|
213
|
+
Removing/merging a surface is the reverse: drop the `surfaces[]` entry, delete its agent file, fold its
|
|
214
|
+
conventions. Never leave an agent file with no matching `surfaces[]` entry (orphan) or vice-versa.
|
|
215
|
+
|
|
216
|
+
## Reconcile — bringing generated files up to the current core
|
|
217
|
+
|
|
218
|
+
`/init-pipeline` is **one-time per project**. Afterwards, `/update-pipeline` runs this procedure so a
|
|
219
|
+
core upgrade never requires re-running init — new pipeline features flow into the repo's generated
|
|
220
|
+
files automatically. It works because every generated artifact is a **deterministic function of
|
|
221
|
+
(current template × the profile's data)**; nothing needs re-detecting or re-interviewing.
|
|
222
|
+
|
|
223
|
+
1. **Profile top-up.** Diff `PIPELINE.md`'s machine block against the current
|
|
224
|
+
`pipeline/PIPELINE.template.md`: every block/field the template has and the profile lacks is added
|
|
225
|
+
with its documented default (e.g. `surfaces[].model: sonnet`, `retrieval.provider: serena`).
|
|
226
|
+
**Ask only when a new field is a genuine human decision** (batch into ONE question set); never
|
|
227
|
+
change a value the profile already sets; never rewrite the prose sections.
|
|
228
|
+
2. **Re-render agent frontmatter + body.** For each `surfaces[]` entry, re-render
|
|
229
|
+
`.claude/agents/<agent>.md` from the current `implementer.template.md` per §Rendering above. Safe by
|
|
230
|
+
doctrine: rendered agents are regenerable artifacts — hand-written rules belong in `PIPELINE.md`
|
|
231
|
+
§Conventions (which reconcile never touches), NOT in agent files, where they'd be clobbered here.
|
|
232
|
+
3. **Additive settings patch.** Bring `.claude/settings.json` + `gate-config.json` up to the current
|
|
233
|
+
init spec (missing `allow` entries, hooks per install mode) — add what's missing, never remove or
|
|
234
|
+
rewrite existing/custom keys.
|
|
235
|
+
4. **Capability wiring.** If a top-up added a capability needing external setup (e.g. a `retrieval`
|
|
236
|
+
provider whose MCP server isn't registered yet), run its wiring step from `/init-pipeline` Phase 4.
|
|
237
|
+
Even when nothing new was added, re-run the provider's health check (§Code retrieval) — wiring
|
|
238
|
+
rots (PATH changes, uninstalls, hand-edits) — and repair whatever fails.
|
|
239
|
+
5. **Global config seed.** If `~/.claude/cohorte.config.yaml` is absent, seed it from the template
|
|
240
|
+
(`profile/cohorte.config.template.yaml`) so the kanban + shared-vault config has a home. Never
|
|
241
|
+
clobber an existing filled file; report what was seeded.
|
|
242
|
+
6. **Kanban sync.** Run the §Kanban reconcile: link/create the project's board if configured, verify
|
|
243
|
+
its columns, and backfill/sync cards from `specs/*.md`. See §Kanban.
|
|
244
|
+
|
|
245
|
+
Re-running `/init-pipeline` remains possible (it reconciles too) but is only *needed* when the stack
|
|
246
|
+
itself changes in ways `/build` §1.5 can't auto-grow (e.g. package manager or contract mechanism swap).
|
|
247
|
+
|
|
248
|
+
## Kanban — mirroring the pipeline onto an Obsidian board
|
|
249
|
+
|
|
250
|
+
An **optional, user-scoped** mirror of the dev flow: each pipeline stage moves a card across an
|
|
251
|
+
[Obsidian Kanban](https://github.com/mgmeyers/obsidian-kanban) board, one board per project. Config
|
|
252
|
+
lives in the consolidated global config `~/.claude/cohorte.config.yaml` §`kanban` (NOT in
|
|
253
|
+
`PIPELINE.md` — the board path points at the user's personal vault, so it is machine-specific and must
|
|
254
|
+
not be committed). Everything below **no-ops silently** when the config is absent, `kanban.enabled` is
|
|
255
|
+
false, no board is configured for the current project, or the board file is missing — the pipeline never
|
|
256
|
+
blocks on the board.
|
|
257
|
+
|
|
258
|
+
**Config & board resolution.** `kanban.boards` is keyed by the project's `PIPELINE.md` `name`. To resolve
|
|
259
|
+
the current project's board: read `name` from `PIPELINE.md`, look up `kanban.boards[name]`. Found ⇒ the
|
|
260
|
+
board file is `<obsidian.vault_path>/<boards[name].board>`, its columns are `boards[name].columns` if
|
|
261
|
+
present else `kanban.columns`. Not found ⇒ kanban off for this project.
|
|
262
|
+
|
|
263
|
+
**Card format.** A card is a Kanban list item under a `## <column>` heading:
|
|
264
|
+
`- [ ] <human title> #<feature_id>`. The `#<feature_id>` tag is the join key between a card and its
|
|
265
|
+
`specs/<feature_id>.md`; it is how every stage finds *its* card (Grep the board for `#<id>`). Free-text
|
|
266
|
+
notes a human writes as sub-bullets under an Ideas card are seed context for `/brainstorm`. Never touch
|
|
267
|
+
the trailing `%% kanban:settings … %%` block or the `kanban-plugin: board` front-matter.
|
|
268
|
+
|
|
269
|
+
Once shipped, `/ship` appends the **PR number** to the card — `- [ ] <title> #<feature_id> — PR #<num>`.
|
|
270
|
+
The bare `#<num>` is what the dashboard renders as a clickable link to the GitHub PR, so `/ship` always
|
|
271
|
+
writes it when a PR was actually created.
|
|
272
|
+
|
|
273
|
+
**Move a card (the core op).** To move card `#<id>` to a stage's column: find the list item carrying
|
|
274
|
+
`#<id>` under its current `## <column>` heading, delete it there, and append it (whole line, tag
|
|
275
|
+
preserved) under the target `## <column>` heading. If no card carries `#<id>` (feature started outside
|
|
276
|
+
the board), create the card in the target column instead of erroring. One card per `#<id>`; if
|
|
277
|
+
duplicates exist, keep the first and drop the rest.
|
|
278
|
+
|
|
279
|
+
**Stage → column**, used both by each pipeline command (to move its card live) and by backfill:
|
|
280
|
+
|
|
281
|
+
| Pipeline moment | Column |
|
|
282
|
+
| --------------------------------------- | --------------- |
|
|
283
|
+
| human drops a raw idea (manual) | `ideas` |
|
|
284
|
+
| `/brainstorm` picks it up | `brainstorm` |
|
|
285
|
+
| `/spec` opens (draft) | `spec` |
|
|
286
|
+
| `/spec` freezes (`status: frozen`) | `ready` |
|
|
287
|
+
| `/build` | `building` |
|
|
288
|
+
| `/smoke` · `/review` | `review` |
|
|
289
|
+
| `/fix` | `fix` |
|
|
290
|
+
| `/ship` starts | `ship` |
|
|
291
|
+
| PR opened (`status: shipped`) | `shipped` (+ `PR #<num>` on the card) |
|
|
292
|
+
|
|
293
|
+
**Backfill / sync from specs (reconcile).** `specs/*.md` is the source of truth. For each spec, read its
|
|
294
|
+
`feature_id` (front-matter or filename) and `status`, map `status`→column — `frozen`→`ready`,
|
|
295
|
+
`in-review`→`review`, `shipped`→`shipped`, anything else / a spec with no status→`spec` — then **full
|
|
296
|
+
sync**: card absent ⇒ add it in that column; card present ⇒ **move it** to that column so the board
|
|
297
|
+
always reflects the specs (this repositions cards the human may have moved by hand). Report cards
|
|
298
|
+
added vs. moved vs. already-correct.
|
|
299
|
+
|
|
300
|
+
**Create a board.** When linking a project with no board file yet: write
|
|
301
|
+
`<obsidian.vault_path>/<folder>/Tasks.md` with the `kanban-plugin: board` front-matter, one `## <heading>`
|
|
302
|
+
per configured column in pipeline order, and the closing `%% kanban:settings %%` block
|
|
303
|
+
(`{"kanban-plugin":"board","list-collapse":[false,…]}` with one `false` per column).
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ~/.claude/cohorte.config.yaml — GLOBAL, user-scoped config for the Cohorte
|
|
2
|
+
# pipeline capabilities. One machine, every project. NOT tied to any project's PIPELINE.md.
|
|
3
|
+
#
|
|
4
|
+
# Seeded once by the installer (npx cohorte install) and never clobbered on update. Wired
|
|
5
|
+
# interactively by /init-pipeline (new project) and /update-pipeline (existing project) —
|
|
6
|
+
# you should never need to hand-edit it.
|
|
7
|
+
#
|
|
8
|
+
# The `# cfg:<key>` markers below are anchors the installer's interactive prompts write to;
|
|
9
|
+
# leave them in place.
|
|
10
|
+
|
|
11
|
+
enabled: true # cfg:enabled — master switch; false disables every capability below
|
|
12
|
+
|
|
13
|
+
# ── obsidian ──────────────────────────────────────────────────────────────────
|
|
14
|
+
# Local vault used by the kanban mirror — defined here ONCE so no capability ever duplicates
|
|
15
|
+
# the path. Absolute path to the folder you open in Obsidian.
|
|
16
|
+
obsidian:
|
|
17
|
+
vault_path: "" # cfg:vault_path
|
|
18
|
+
|
|
19
|
+
# ── kanban (dev pipeline mirror) ─────────────────────────────────────────────
|
|
20
|
+
# Mirrors /brainstorm…/ship onto an Obsidian Kanban board — one board per project. The
|
|
21
|
+
# board path points at your PERSONAL vault, so it is machine-specific and lives here (never
|
|
22
|
+
# committed in a repo's PIPELINE.md). /init-pipeline + /update-pipeline write `boards` for you.
|
|
23
|
+
# See SCHEMA.md §Kanban.
|
|
24
|
+
kanban:
|
|
25
|
+
enabled: false # cfg:kanban_enabled — master switch for the kanban mirror
|
|
26
|
+
# Default column mapping: pipeline stage → the EXACT board column heading. Shared by every
|
|
27
|
+
# board; a board may override its own `columns` inside boards[] below.
|
|
28
|
+
columns:
|
|
29
|
+
ideas: "Ideas"
|
|
30
|
+
brainstorm: "Brainstorm"
|
|
31
|
+
spec: "Spec"
|
|
32
|
+
ready: "Ready to build"
|
|
33
|
+
building: "Building"
|
|
34
|
+
review: "Review"
|
|
35
|
+
fix: "Fix"
|
|
36
|
+
ship: "Ship"
|
|
37
|
+
shipped: "Shipped"
|
|
38
|
+
# One entry per project, keyed by the project's PIPELINE.md `name` (e.g. SAMO). `board` is a
|
|
39
|
+
# path RELATIVE to obsidian.vault_path. Written by /init-pipeline + /update-pipeline.
|
|
40
|
+
boards: {}
|
|
41
|
+
# SAMO:
|
|
42
|
+
# board: "Samo/Tasks.md"
|
|
43
|
+
# # columns: { ideas: "Idées", ... } # optional per-board override of the defaults above
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# new-feature.sh — spin up an ISOLATED git worktree for one feature so features
|
|
4
|
+
# can be built fully in parallel. Rendered from a template by /init-pipeline;
|
|
5
|
+
# the __TOKENS__ below are substituted from PIPELINE.md §isolation.
|
|
6
|
+
#
|
|
7
|
+
# Usage: scripts/new-feature.sh <feature_id>
|
|
8
|
+
#
|
|
9
|
+
# Creates a sibling worktree ../__PROJECT_SLUG__-<id> on branch
|
|
10
|
+
# __BRANCH_PREFIX__<id> (off __DEFAULT_BRANCH__) with its own isolation slot:
|
|
11
|
+
# • its own database __DB_NAME_PATTERN__ (if DB isolation is enabled)
|
|
12
|
+
# • its own API port __API_PORT_BASE__ + slot
|
|
13
|
+
# • its own web port __WEB_PORT_BASE__ + slot
|
|
14
|
+
# The infra servers stay a SINGLE shared stack — only the logical db + ports
|
|
15
|
+
# differ per worktree. Slot 0 = main checkout. New worktrees get slot >= 1.
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
id="${1:-}"
|
|
20
|
+
if [[ -z "$id" ]]; then
|
|
21
|
+
echo "usage: scripts/new-feature.sh <feature_id>" >&2
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
repo_root="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
|
|
26
|
+
branch="__BRANCH_PREFIX__${id}"
|
|
27
|
+
worktree_dir="$(dirname "$repo_root")/__PROJECT_SLUG__-${id}"
|
|
28
|
+
db_name="__PROJECT_SLUG___$(echo "$id" | tr -c 'a-zA-Z0-9' '_' | sed 's/_*$//')"
|
|
29
|
+
|
|
30
|
+
registry_dir="$repo_root/.worktrees"
|
|
31
|
+
registry="$registry_dir/slots.tsv"
|
|
32
|
+
mkdir -p "$registry_dir"
|
|
33
|
+
touch "$registry"
|
|
34
|
+
|
|
35
|
+
if [[ -e "$worktree_dir" ]]; then
|
|
36
|
+
echo "error: $worktree_dir already exists" >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
slot="$(awk -F'\t' -v id="$id" '$1==id{print $2}' "$registry" | head -n1)"
|
|
41
|
+
if [[ -z "$slot" ]]; then
|
|
42
|
+
slot=1
|
|
43
|
+
while awk -F'\t' -v s="$slot" '$2==s{found=1} END{exit !found}' "$registry"; do
|
|
44
|
+
slot=$((slot + 1))
|
|
45
|
+
done
|
|
46
|
+
printf '%s\t%s\n' "$id" "$slot" >>"$registry"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
api_port=$((__API_PORT_BASE__ + slot))
|
|
50
|
+
web_port=$((__WEB_PORT_BASE__ + slot))
|
|
51
|
+
|
|
52
|
+
echo "→ feature '$id' → slot $slot (db $db_name · api :$api_port · web :$web_port)"
|
|
53
|
+
echo "→ creating worktree $worktree_dir on $branch (off __DEFAULT_BRANCH__)"
|
|
54
|
+
git -C "$repo_root" fetch --quiet origin __DEFAULT_BRANCH__ || true
|
|
55
|
+
git -C "$repo_root" worktree add -b "$branch" "$worktree_dir" __DEFAULT_BRANCH__
|
|
56
|
+
|
|
57
|
+
# Replace (or append) KEY=VALUE in a dotenv file — one definitive line per key.
|
|
58
|
+
set_env() {
|
|
59
|
+
local file="$1" key="$2" value="$3" tmp
|
|
60
|
+
if [[ -s "$file" && -n "$(tail -c1 "$file")" ]]; then printf '\n' >>"$file"; fi
|
|
61
|
+
tmp="$(mktemp)"
|
|
62
|
+
grep -vE "^${key}=" "$file" >"$tmp" || true
|
|
63
|
+
mv "$tmp" "$file"
|
|
64
|
+
printf '%s=%s\n' "$key" "$value" >>"$file"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# --- per-worktree env (init customizes these stanzas per surface) -----------
|
|
68
|
+
# __ENV_STANZAS__
|
|
69
|
+
|
|
70
|
+
# --- create the isolated database (if DB isolation enabled) -----------------
|
|
71
|
+
# __DB_STANZA__
|
|
72
|
+
|
|
73
|
+
echo "→ ${repo_root##*/} install (shared store)"
|
|
74
|
+
( cd "$worktree_dir" && __INSTALL_CMD__ )
|
|
75
|
+
|
|
76
|
+
cat <<EOF
|
|
77
|
+
|
|
78
|
+
✓ worktree ready: $worktree_dir (branch $branch, slot $slot)
|
|
79
|
+
api http://localhost:$api_port
|
|
80
|
+
web http://localhost:$web_port
|
|
81
|
+
|
|
82
|
+
Fully isolated — run its dev + migrations alongside other worktrees:
|
|
83
|
+
cd $worktree_dir
|
|
84
|
+
__MIGRATE_CMD__
|
|
85
|
+
cd $worktree_dir && __DEV_CMD__
|
|
86
|
+
/brainstorm → /spec → (design) → /build $id → test → /review → /ship
|
|
87
|
+
|
|
88
|
+
Tear down when merged: scripts/remove-feature.sh $id
|
|
89
|
+
EOF
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# remove-feature.sh — tear down a feature's isolated worktree. Rendered from a
|
|
4
|
+
# template by /init-pipeline (__TOKENS__ from PIPELINE.md §isolation).
|
|
5
|
+
#
|
|
6
|
+
# Usage: scripts/remove-feature.sh <feature_id> [--drop-db]
|
|
7
|
+
#
|
|
8
|
+
# Removes the ../__PROJECT_SLUG__-<id> worktree, frees its slot, and (with
|
|
9
|
+
# --drop-db) drops its database. The db is kept by default. The shared stack and
|
|
10
|
+
# the main checkout are never touched.
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
id="${1:-}"
|
|
15
|
+
drop_db="no"
|
|
16
|
+
[[ "${2:-}" == "--drop-db" ]] && drop_db="yes"
|
|
17
|
+
|
|
18
|
+
if [[ -z "$id" ]]; then
|
|
19
|
+
echo "usage: scripts/remove-feature.sh <feature_id> [--drop-db]" >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
repo_root="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
|
|
24
|
+
worktree_dir="$(dirname "$repo_root")/__PROJECT_SLUG__-${id}"
|
|
25
|
+
db_name="__PROJECT_SLUG___$(echo "$id" | tr -c 'a-zA-Z0-9' '_' | sed 's/_*$//')"
|
|
26
|
+
registry="$repo_root/.worktrees/slots.tsv"
|
|
27
|
+
branch="__BRANCH_PREFIX__${id}"
|
|
28
|
+
|
|
29
|
+
echo "→ removing worktree $worktree_dir"
|
|
30
|
+
if git -C "$repo_root" worktree list | grep -q "$worktree_dir"; then
|
|
31
|
+
git -C "$repo_root" worktree remove "$worktree_dir" "${@:3}"
|
|
32
|
+
else
|
|
33
|
+
echo " (no such worktree registered — skipping)"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if git -C "$repo_root" show-ref --verify --quiet "refs/heads/$branch"; then
|
|
37
|
+
if git -C "$repo_root" branch -d "$branch" 2>/dev/null; then
|
|
38
|
+
echo "→ deleted merged branch $branch"
|
|
39
|
+
else
|
|
40
|
+
echo " ! branch $branch is not fully merged — kept it." >&2
|
|
41
|
+
echo " delete manually when ready: git branch -D $branch" >&2
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [[ -f "$registry" ]]; then
|
|
46
|
+
tmp="$(mktemp)"
|
|
47
|
+
awk -F'\t' -v id="$id" '$1!=id' "$registry" >"$tmp" && mv "$tmp" "$registry"
|
|
48
|
+
echo "→ freed slot for '$id'"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# __DB_DROP_STANZA__
|
|
52
|
+
|
|
53
|
+
echo "✓ feature '$id' torn down"
|