cohorte 2.0.2 → 2.1.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 +28 -0
- package/core/agents/release.md +6 -0
- package/core/commands/cohorte-ship.md +40 -5
- package/core/templates/steps/init-pipeline/01-detect-stack.md +6 -2
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +6 -0
- package/package.json +1 -1
- package/profile/PIPELINE.template.md +20 -0
- package/profile/SCHEMA.md +43 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ short, user-facing, most recent first. One `## <version> — <YYYY-MM-DD>` secti
|
|
|
7
7
|
> They are history and are deliberately not rewritten — every command gained a `cohorte-` prefix
|
|
8
8
|
> in 2.0.0.
|
|
9
9
|
|
|
10
|
+
## 2.1.0 — 2026-08-09
|
|
11
|
+
|
|
12
|
+
- **A repo that gates merges on a per-feature release note shipped red PRs, and the flow reported
|
|
13
|
+
success.** Changesets' `changeset` job fails any PR that touches product code without a
|
|
14
|
+
`.changeset/*.md`, but that requirement lives in the project's `CLAUDE.md` — which the ship flow
|
|
15
|
+
never reads. So `/cohorte-ship` committed, pushed, opened the PR and moved the kanban card to
|
|
16
|
+
**Shipped** while the PR was unmergeable, red on a job nobody was watching.
|
|
17
|
+
|
|
18
|
+
The profile gains a `release_notes` block (`enabled`, `tool`, `dir`/`filename`,
|
|
19
|
+
`anchor_package`, `language`, `forbid_levels`, `empty_cmd`, `ci_job`, `guidance` — SCHEMA.md
|
|
20
|
+
§Release notes), and `/cohorte-ship` gains **§2b**: the lead writes the note itself, next to the
|
|
21
|
+
`status: shipped` flip, so it lands *inside* the release commit rather than in a second one after
|
|
22
|
+
the PR is already open. The bump level is project policy, not a git ritual — the release agent is
|
|
23
|
+
now explicitly forbidden from authoring or editing a note, and only stages the one it is handed.
|
|
24
|
+
§4 verifies the note is in the commit; §5 routes a red `ci_job` back to §2b instead of through
|
|
25
|
+
`/cohorte-fix`, which would treat a missing note as a code finding.
|
|
26
|
+
|
|
27
|
+
Two things the block encodes that the tool itself does not: `forbid_levels`, for the `0.x` repo
|
|
28
|
+
where a `major` changeset silently jumps to `1.0.0` with no human deciding it, and the rule to
|
|
29
|
+
**ask** rather than guess between two defensible levels — a wrong bump is a published version
|
|
30
|
+
number, not a fixable draft. `empty_cmd` covers the honest no-op, for a PR that must move no
|
|
31
|
+
version at all.
|
|
32
|
+
|
|
33
|
+
- **Existing projects are asked, not defaulted.** `/cohorte-init-pipeline` detects a versioning tool
|
|
34
|
+
or note-enforcing CI job in Phase 1 and asks the anchor package, language and bump policy in Phase
|
|
35
|
+
2; `/cohorte-update-pipeline` treats `release_notes` as a genuine human decision during reconcile
|
|
36
|
+
rather than topping it up blind. No tool found ⇒ `enabled: false`, and §2b is a silent no-op.
|
|
37
|
+
|
|
10
38
|
## 2.0.2 — 2026-08-08
|
|
11
39
|
|
|
12
40
|
- **Kanban cards stopped moving mid-pipeline, and every stage still reported success.** The
|
package/core/agents/release.md
CHANGED
|
@@ -23,11 +23,17 @@ committing, pushing, and opening the PR. You do **not** write features.
|
|
|
23
23
|
on pushed commits), or delete branches.
|
|
24
24
|
- Run anything in `PIPELINE.md` §`gate.deny` (destructive DB/history).
|
|
25
25
|
- Commit secrets — inspect `git status`/`git diff` and refuse if `.env` or credentials are staged.
|
|
26
|
+
- **Author or edit a release note** (`.changeset/*.md` or whatever `PIPELINE.md` §`release_notes`
|
|
27
|
+
declares). The lead writes it before dispatching you; picking a bump level is project policy, not a
|
|
28
|
+
git ritual. You only **stage** it. If `release_notes.enabled` and the file is absent, say so in your
|
|
29
|
+
report instead of inventing one — the lead fixes it.
|
|
26
30
|
|
|
27
31
|
## Your inputs
|
|
28
32
|
|
|
29
33
|
1. The spec path `specs/<id>.md` (title, goal, contract — for the PR body).
|
|
30
34
|
2. `feature_id` and the branch `<vcs.feature_branch_prefix><id>`.
|
|
35
|
+
3. If `PIPELINE.md` §`release_notes.enabled`, the already-written note at
|
|
36
|
+
`<release_notes.dir>/<release_notes.filename>` — stage it with everything else.
|
|
31
37
|
|
|
32
38
|
## Steps
|
|
33
39
|
|
|
@@ -6,7 +6,9 @@ argument-hint: <feature_id>
|
|
|
6
6
|
|
|
7
7
|
You are the **lead**. Ship feature **$ARGUMENTS**. This is the outward-facing gate.
|
|
8
8
|
|
|
9
|
-
> Read `PIPELINE.md` §`vcs` (host, remote, default_branch, feature_branch_prefix)
|
|
9
|
+
> Read `PIPELINE.md` §`vcs` (host, remote, default_branch, feature_branch_prefix) **and
|
|
10
|
+
> §`release_notes`** — §2b below is skipped or required based on it, and skipping it when it is
|
|
11
|
+
> required opens a PR that CI fails on arrival.
|
|
10
12
|
>
|
|
11
13
|
> **Kanban** (SCHEMA.md §Kanban) is mirrored in **explicit steps** below, not as an afterthought:
|
|
12
14
|
> §1 moves the card → **Ship**; §4 moves it → **Shipped** and writes the PR number. Both are one
|
|
@@ -41,19 +43,50 @@ Once the human confirms, edit `specs/$ARGUMENTS.md` front-matter `status: → sh
|
|
|
41
43
|
dispatching the release agent, so the status flip is part of the tree it commits (otherwise it lands
|
|
42
44
|
uncommitted after the PR opens). Only flip after the human's "yes"; if they decline, leave it.
|
|
43
45
|
|
|
46
|
+
## 2b. Write the release note (only if `release_notes.enabled` — same reason: it must ship in the commit)
|
|
47
|
+
|
|
48
|
+
`release_notes.enabled: false` (or `tool: none`) ⇒ skip this section entirely, silently.
|
|
49
|
+
|
|
50
|
+
Otherwise **you** write it — never the release agent, never the implementers. Picking the bump is
|
|
51
|
+
project policy and the prose is outward-facing copy; both are the lead's, exactly like the contract.
|
|
52
|
+
See SCHEMA.md §Release notes.
|
|
53
|
+
|
|
54
|
+
- Write `<release_notes.dir>/<release_notes.filename>` (`<feature_id>` substituted), front-matter
|
|
55
|
+
carrying the **single** key `release_notes.anchor_package` and the chosen level, then the prose body
|
|
56
|
+
in `release_notes.language`.
|
|
57
|
+
- **Choose the level** against `release_notes.guidance`, and refuse any level in
|
|
58
|
+
`release_notes.forbid_levels` (a `0.x` repo forbidding `major` declares the rupture `minor`).
|
|
59
|
+
- **Ambiguous between two defensible levels?** State your reading in one line and **ask the human to
|
|
60
|
+
pick** before writing. A wrong bump becomes a published version number.
|
|
61
|
+
- The body describes what changed **for the user**, from the spec §1/§2 — no client names, no internal
|
|
62
|
+
paths, no exploitable attack vector, no file lists.
|
|
63
|
+
- If the feature genuinely must move no version, use `release_notes.empty_cmd` instead. Prefer that to
|
|
64
|
+
skipping: the CI job wants a file, not a version.
|
|
65
|
+
- Then say in one line which level you chose and why — this is the human's last chance to correct it
|
|
66
|
+
before it is committed.
|
|
67
|
+
|
|
68
|
+
> **Why this is its own gate.** The requirement usually lives in the project's `CLAUDE.md`, which this
|
|
69
|
+
> flow never reads. Skip it and everything below still "succeeds": commit, push, PR opened, kanban card
|
|
70
|
+
> moved to **Shipped** — and CI red on a job nobody watched. The feature reads as shipped while being
|
|
71
|
+
> unmergeable.
|
|
72
|
+
|
|
44
73
|
## 3. Dispatch the `release` agent
|
|
45
74
|
|
|
46
75
|
Spawn one agent (`subagent_type: release`): "Release feature `$ARGUMENTS` on branch
|
|
47
76
|
`<feature_branch_prefix>$ARGUMENTS`. Read `PIPELINE.md` §vcs first. Spec: `specs/$ARGUMENTS.md` (already
|
|
48
77
|
`status: shipped` — stage it). Write conventional commit(s), push (no force), open the PR (use `gh` if
|
|
49
78
|
`host: github` + available; else emit the compare URL + drafted PR body from `.claude/templates/pr-body.md`).
|
|
50
|
-
Stage **all** the feature's changes including `specs/$ARGUMENTS.md
|
|
51
|
-
|
|
79
|
+
Stage **all** the feature's changes including `specs/$ARGUMENTS.md` and, if `release_notes.enabled`, the
|
|
80
|
+
release note at `<release_notes.dir>/<release_notes.filename>` — it is already written, stage it as-is and
|
|
81
|
+
never author or edit one yourself. Never edit source, never force-push, never run migrations."
|
|
52
82
|
|
|
53
83
|
## 4. Relay + move the card to Shipped (do not skip)
|
|
54
84
|
|
|
55
85
|
Print the release agent's report: commit SHA(s), pushed branch, PR URL (or compare URL + drafted body).
|
|
56
|
-
Confirm `specs/$ARGUMENTS.md` was committed as `status: shipped` (part of the release commit)
|
|
86
|
+
Confirm `specs/$ARGUMENTS.md` was committed as `status: shipped` (part of the release commit), and — if
|
|
87
|
+
`release_notes.enabled` — that the release note is in that same commit
|
|
88
|
+
(`git show <sha> --stat | grep <release_notes.dir>`). Missing ⇒ commit and push it now, before §5's CI
|
|
89
|
+
watch, rather than letting the job go red.
|
|
57
90
|
|
|
58
91
|
**Move the card to Shipped — required, and verify it actually moved.** Run
|
|
59
92
|
`<core>/pipeline/scripts/kanban-move.sh auto $ARGUMENTS shipped --pr <num>`, which **appends the PR
|
|
@@ -78,7 +111,9 @@ call. Silent no-op without consent; never ask about consent here.
|
|
|
78
111
|
## 5. After the PR — CI gate + teardown
|
|
79
112
|
|
|
80
113
|
- If `host: github` and `gh` is available, watch the PR's checks (`gh pr checks <url> --watch`) and
|
|
81
|
-
report the result — the human merges only on green. A red check ⇒ back to `/cohorte-fix $ARGUMENTS
|
|
114
|
+
report the result — the human merges only on green. A red check ⇒ back to `/cohorte-fix $ARGUMENTS`,
|
|
115
|
+
**except** a red `release_notes.ci_job`: that one is this command's own miss, not a code finding —
|
|
116
|
+
write the note per §2b, commit, push, and re-watch. Never send a missing release note through `/cohorte-fix`.
|
|
82
117
|
- Once the human confirms the PR is **merged**: if `isolation.enabled`, propose the teardown —
|
|
83
118
|
`scripts/remove-feature.sh $ARGUMENTS` (add `--drop-db` to also drop the feature db; kept by
|
|
84
119
|
default). It removes the worktree, deletes the merged branch, frees the slot. Never run it before
|
|
@@ -24,6 +24,10 @@ Gather evidence, then summarize what you found. Look for:
|
|
|
24
24
|
`package.json` scripts + the workspace filter syntax (e.g. `pnpm --filter <pkg> test`).
|
|
25
25
|
- **Contract mechanism:** a shared types/schema package (`packages/shared-types`, Zod/`z.`),
|
|
26
26
|
an `openapi.*`/`swagger.*` file, `.proto` files, or none.
|
|
27
|
+
- **Release notes / versioning:** a `.changeset/` directory (+ `config.json`, and whether it declares a
|
|
28
|
+
`fixed`/lockstep group — that names the anchor package), `changesets` in devDependencies, or a CI job
|
|
29
|
+
that fails a PR lacking a note (grep `.github/workflows/*` for `changeset`). Also note the root
|
|
30
|
+
`version`: a `0.y.z` product usually forbids `major`. None of these ⇒ `release_notes.enabled: false`.
|
|
27
31
|
- **DB / migrations:** migration tooling (`node ace make:migration`, `knex`, `prisma`, `alembic`,
|
|
28
32
|
`golang-migrate`), a `docker-compose.yml`, DB service.
|
|
29
33
|
- **Design system:** an existing `design-reference/` snapshot, `components/ui`, a DesignSync MCP
|
|
@@ -36,5 +40,5 @@ Gather evidence, then summarize what you found. Look for:
|
|
|
36
40
|
- **Existing `CLAUDE.md`** — read it; it may already state stack/conventions to fold in. **Existing
|
|
37
41
|
`PIPELINE.md`** — if present, this is a re-run: load it as the starting draft and only reconcile deltas.
|
|
38
42
|
|
|
39
|
-
Print a compact **Detection Report**: layout, surfaces (with framework + commands), contract,
|
|
40
|
-
design, vcs. Mark each field `detected` / `guessed` / `unknown`.
|
|
43
|
+
Print a compact **Detection Report**: layout, surfaces (with framework + commands), contract, release
|
|
44
|
+
notes, DB, design, vcs. Mark each field `detected` / `guessed` / `unknown`.
|
|
@@ -24,6 +24,12 @@ Ask ONLY what you couldn't confidently detect. Batch related questions. Cover:
|
|
|
24
24
|
the runner has no such flag, leave `""` — consumers then fall back to `<cmd> 2>&1 | tail -40`.
|
|
25
25
|
- **Contract** — mechanism (`shared-types-zod` / `openapi` / `protobuf` / `json-schema` / `none`) and
|
|
26
26
|
where feature contracts are authored. If `none`, surfaces sync by the spec prose alone.
|
|
27
|
+
- **Release notes (only if Phase 1 found a versioning tool or a note-enforcing CI job)** — confirm the
|
|
28
|
+
anchor package (a `fixed`/lockstep group means ONE key propagates to all), the note's language, and the
|
|
29
|
+
**bump policy** the lead will apply at `/cohorte-ship`: what counts as major/minor/patch here, and
|
|
30
|
+
whether any level is forbidden (a `0.y.z` product normally forbids `major` — Changesets would jump it
|
|
31
|
+
to `1.0.0` with no human deciding). Policy prose lands in `release_notes.guidance`. Nothing detected ⇒
|
|
32
|
+
don't ask; leave `release_notes.enabled: false`.
|
|
27
33
|
- **UI language** — language of all user-facing copy.
|
|
28
34
|
- **RBAC** — is there a role hierarchy? If yes, list it highest→lowest.
|
|
29
35
|
- **Design system** — enabled? provider (Claude Design / Figma / none) + project ids + kit/token paths.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cohorte",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Portable, stack-agnostic multi-agent development pipeline for Claude Code — install the core, run /cohorte-init-pipeline, and it adapts to your project's stack.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cohorte": "bin/cli.js"
|
|
@@ -97,6 +97,26 @@ contract:
|
|
|
97
97
|
index: packages/shared-types/src/index.ts # barrel to export from, or "" if none
|
|
98
98
|
authored_by: lead # NEVER the implementer agents
|
|
99
99
|
|
|
100
|
+
# ── release notes (optional) ────────────────────────────────────────────────
|
|
101
|
+
# A per-feature release note the project's versioning tool consumes. When
|
|
102
|
+
# enabled, /cohorte-ship authors it BEFORE dispatching the release agent, so it
|
|
103
|
+
# ships inside the release commit. Repos whose CI fails a PR that lacks one
|
|
104
|
+
# (Changesets' `changeset` job) MUST set this — otherwise every ship goes red.
|
|
105
|
+
release_notes:
|
|
106
|
+
enabled: <true | false> # false ⇒ /cohorte-ship §2b is a no-op
|
|
107
|
+
tool: <changesets | none> # what consumes the file
|
|
108
|
+
dir: .changeset # where the note is authored
|
|
109
|
+
filename: "<feature_id>.md" # one file per feature
|
|
110
|
+
anchor_package: shared-types # sole key in the front-matter; a lockstep/fixed group propagates the bump
|
|
111
|
+
language: <ui_language | English> # language of the note's prose
|
|
112
|
+
forbid_levels: [] # bumps project policy refuses, e.g. [major] while 0.x
|
|
113
|
+
empty_cmd: "pnpm changeset --empty" # when no version should move; "" if the tool has no such escape hatch
|
|
114
|
+
ci_job: changeset # CI job that fails when the note is missing (name it so /cohorte-ship §5 recognizes the red)
|
|
115
|
+
guidance: | # rules the lead follows when writing the note (project policy)
|
|
116
|
+
major = the deploy needs an operator action; minor = a user-visible capability;
|
|
117
|
+
patch = fix/perf/refactor with no change of use. No client names, no internal
|
|
118
|
+
paths, no exploitable attack vector.
|
|
119
|
+
|
|
100
120
|
# ── repo-wide commands (root unless noted) ──────────────────────────────────
|
|
101
121
|
commands:
|
|
102
122
|
install: pnpm install
|
package/profile/SCHEMA.md
CHANGED
|
@@ -33,6 +33,15 @@ generic pipeline uses it, so a stateless agent can read/regenerate the profile c
|
|
|
33
33
|
| `contract.mechanism` | enum | build, lead | `shared-types-zod`/`openapi`/`protobuf`/`json-schema`/`none`. |
|
|
34
34
|
| `contract.path` `.ext` `.index` | string | build | Where `<feature_id>` contract is authored + barrel. |
|
|
35
35
|
| `contract.authored_by` | const `lead` | build | Implementers import it read-only, never edit. |
|
|
36
|
+
| `release_notes.enabled` | bool | ship | `false` ⇒ skip note authoring (§2b of /cohorte-ship). See §Release notes. |
|
|
37
|
+
| `release_notes.tool` | enum | ship | `changesets`/`none` — what consumes the file. |
|
|
38
|
+
| `release_notes.dir` `.filename` | string | ship | Where the per-feature note is authored, e.g. `.changeset/<feature_id>.md`. |
|
|
39
|
+
| `release_notes.anchor_package` | string | ship | Sole key in the note's front-matter; a lockstep/`fixed` group propagates the bump. |
|
|
40
|
+
| `release_notes.language` | string | ship | Language of the note's prose — usually `ui_language`. |
|
|
41
|
+
| `release_notes.forbid_levels` | list | ship | Bump levels project policy refuses, e.g. `[major]` while `0.x`. |
|
|
42
|
+
| `release_notes.empty_cmd` | string | ship | Escape hatch when no version should move; `""` if none. |
|
|
43
|
+
| `release_notes.ci_job` | string | ship | CI job that fails on a missing note — lets §5 name the red check. |
|
|
44
|
+
| `release_notes.guidance` | string | ship | Project policy the lead follows when picking the bump + writing the prose. |
|
|
36
45
|
| `commands.*` | string | all | Repo-wide install/dev/lint/format/typecheck/test + migrate. |
|
|
37
46
|
| `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. |
|
|
38
47
|
| `rbac.enabled` | bool | brainstorm, review | Toggle RBAC personas + authz audit. |
|
|
@@ -412,6 +421,36 @@ this exact procedure so a surface is always defined the same way. To add surface
|
|
|
412
421
|
Removing/merging a surface is the reverse: drop the `surfaces[]` entry, delete its agent file, fold its
|
|
413
422
|
conventions. Never leave an agent file with no matching `surfaces[]` entry (orphan) or vice-versa.
|
|
414
423
|
|
|
424
|
+
## Release notes — the per-feature note the versioning tool consumes
|
|
425
|
+
|
|
426
|
+
Many repos gate merges on a **per-feature release note**: Changesets' `changeset` CI job fails any PR
|
|
427
|
+
that touches product code without a `.changeset/*.md`. That file is **not** something the release
|
|
428
|
+
agent can invent — picking the bump level is project policy (semver over an API vs. over a product,
|
|
429
|
+
a `0.x` rule forbidding `major`, what counts as user-visible), and the prose is outward-facing copy.
|
|
430
|
+
It is the **lead's** to write, exactly like the contract.
|
|
431
|
+
|
|
432
|
+
The failure mode this block exists to prevent is silent and reproducible: the requirement lives in the
|
|
433
|
+
project's `CLAUDE.md`, which the ship flow never reads, so `/cohorte-ship` completes, opens the PR,
|
|
434
|
+
moves the kanban card to **Shipped** — and CI goes red on a job nobody looked at. The feature reads as
|
|
435
|
+
shipped while being unmergeable. Encoding it in the profile is what makes the step survive a stateless
|
|
436
|
+
lead.
|
|
437
|
+
|
|
438
|
+
- **Authored before the dispatch**, next to the `status: shipped` flip (`/cohorte-ship` §2b), so the note
|
|
439
|
+
lands **inside** the release commit. Written after the fact it needs a second commit, and the PR is
|
|
440
|
+
already open and red.
|
|
441
|
+
- **One key in the front-matter** — `anchor_package`. Lockstep/`fixed` version groups propagate the bump
|
|
442
|
+
from it to every other workspace; listing more is how a repo ends up with a package bumped twice.
|
|
443
|
+
- **`forbid_levels`** encodes policy the tool itself may not enforce. The common one: while the product
|
|
444
|
+
is `0.y.z`, a `major` changeset makes Changesets jump to `1.0.0` with no human deciding it — so `0.x`
|
|
445
|
+
repos forbid `major` and declare a breaking change as `minor`.
|
|
446
|
+
- **Ask, don't guess, on an ambiguous bump.** Between two defensible levels (a refactor that also changes
|
|
447
|
+
what the user sees), state the reading and let the human pick — a wrong bump is a published version
|
|
448
|
+
number, not a fixable draft.
|
|
449
|
+
- **`empty_cmd`** covers the honest no-op: a PR that touches product code but must move no version.
|
|
450
|
+
Prefer it to skipping the step; the CI job wants a file, not a version.
|
|
451
|
+
|
|
452
|
+
`enabled: false` (or `tool: none`) ⇒ §2b is a no-op and nothing below applies.
|
|
453
|
+
|
|
415
454
|
## Reconcile — bringing generated files up to the current core
|
|
416
455
|
|
|
417
456
|
`/cohorte-init-pipeline` is **one-time per project**. Afterwards, `/cohorte-update-pipeline` runs this procedure so a
|
|
@@ -423,7 +462,10 @@ files automatically. It works because every generated artifact is a **determinis
|
|
|
423
462
|
`pipeline/PIPELINE.template.md`: every block/field the template has and the profile lacks is added
|
|
424
463
|
with its documented default (e.g. `surfaces[].model: sonnet`, `retrieval.provider: serena`).
|
|
425
464
|
**Ask only when a new field is a genuine human decision** (batch into ONE question set); never
|
|
426
|
-
change a value the profile already sets; never rewrite the prose sections.
|
|
465
|
+
change a value the profile already sets; never rewrite the prose sections. `release_notes` is one
|
|
466
|
+
such decision and must not be defaulted blind: detect a versioning tool / note-enforcing CI job per
|
|
467
|
+
`/cohorte-init-pipeline` Phase 1, and if there is one, ask Phase 2's release-notes question (anchor
|
|
468
|
+
package, language, bump policy, forbidden levels). No tool found ⇒ top up with `enabled: false`.
|
|
427
469
|
2. **Re-render agent frontmatter + body.** For each `surfaces[]` entry, re-render
|
|
428
470
|
`.claude/agents/<agent>.md` from the current `implementer.template.md` per §Rendering above. Safe by
|
|
429
471
|
doctrine: rendered agents are regenerable artifacts — hand-written rules belong in `PIPELINE.md`
|