cohorte 2.7.0 → 2.9.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 +118 -0
- package/README.md +15 -10
- package/bin/cli.js +15 -2
- package/core/agents/review.md +4 -2
- package/core/commands/cohorte-brainstorm.md +5 -1
- package/core/commands/cohorte-build.md +3 -1
- package/core/commands/cohorte-doctor.md +5 -3
- package/core/commands/cohorte-fix.md +6 -5
- package/core/commands/cohorte-fleet.md +104 -0
- package/core/commands/cohorte-intake.md +92 -0
- package/core/commands/cohorte-patch.md +6 -1
- package/core/commands/cohorte-retro.md +85 -0
- package/core/commands/cohorte-review.md +85 -23
- package/core/commands/cohorte-ship.md +2 -1
- package/core/commands/cohorte-spec.md +1 -1
- package/core/hooks/gate.py +10 -4
- package/core/workflows/audit.js +14 -2
- package/core/workflows/loop.js +641 -0
- package/core/workflows/refactor.js +21 -8
- package/core/workflows/review.js +92 -12
- package/dashboard/dist/assets/{index-D1rsbLat.js → index-vtFc6Gyc.js} +12 -12
- package/dashboard/dist/index.html +1 -1
- package/dashboard/server/doctor.js +8 -2
- package/dashboard/server/index.js +6 -1
- package/dashboard/server/kanban.js +15 -4
- package/dashboard/server/metrics.js +8 -1
- package/dashboard/server/runtime.js +20 -1
- package/install.ps1 +16 -333
- package/install.sh +27 -297
- package/package.json +1 -1
- package/profile/SCHEMA.md +45 -14
- package/profile/cohorte.config.template.yaml +1 -1
- package/scripts/kanban-move.sh +15 -5
- package/scripts/new-feature.sh.template +8 -1
- package/scripts/preflight.sh +10 -2
- package/scripts/remove-feature.sh.template +3 -1
- package/scripts/test-dashboard.mjs +53 -5
- package/scripts/test-gate.mjs +6 -0
- package/scripts/test-workflows.mjs +415 -6
- package/scripts/validate-core.mjs +68 -34
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,124 @@ 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.9.0 — 2026-08-22
|
|
11
|
+
|
|
12
|
+
- **`/cohorte-intake` — the door before the doors.** Work has always entered the pipeline
|
|
13
|
+
pre-distilled by a human: a ticket became a `/cohorte-patch` paste, an email became a
|
|
14
|
+
brainstorm idea, by hand. Intake does the distillation: paste anything that arrives and it
|
|
15
|
+
triages — bug ⇒ the exact structure patch interviews for (inferred repro steps labeled as
|
|
16
|
+
such); feature ⇒ a brainstorm seed whose open-questions list is the panel's agenda, with any
|
|
17
|
+
`_decisions.md` line it contradicts named; noise ⇒ says so and stops, because inventing a
|
|
18
|
+
spec from noise costs a whole pipeline run downstream. The distillate lands on disk and on
|
|
19
|
+
the kanban's Ideas column; freezing anything stays the human's move.
|
|
20
|
+
|
|
21
|
+
- **`/cohorte-retro` — the pipeline learns from its own findings.** Every review leaves
|
|
22
|
+
structured residue (verdict.json, the specs' Remediation history, the deferred backlog) that
|
|
23
|
+
nothing ever read back. Retro mines it for patterns — the same finding kind on the same
|
|
24
|
+
surface across features, a hotspot module, a recurring fix family — and turns each into ONE
|
|
25
|
+
rule-shaped §Conventions line the human ratifies. Adopted rules re-render the affected
|
|
26
|
+
surface agents (the conventions slice is baked at render time — a rule without a re-render
|
|
27
|
+
is one reviewers enforce and implementers never saw) and land in the decisions journal.
|
|
28
|
+
Findings → rules → the next build never produces the finding: cheaper than any number of
|
|
29
|
+
review rounds catching it.
|
|
30
|
+
|
|
31
|
+
- **`/cohorte-fleet` — parallel features get a flight controller.** The worktree isolation
|
|
32
|
+
existed; the coordination lived in your head. `plan` builds the feature×surface overlap
|
|
33
|
+
matrix from the specs themselves (contract dependencies ⇒ merge order; same-tree writes ⇒
|
|
34
|
+
serialize or drop one), provisions the worktrees, and prints one launch line per feature.
|
|
35
|
+
`status` is one row per feature ending in the single next action. `sync` does the post-merge
|
|
36
|
+
sweep everyone forgets — rebase every survivor, report conflicts verbatim to their owner's
|
|
37
|
+
session, and say out loud that a rebase invalidates the freshness stamp. What it will never
|
|
38
|
+
do is spawn the work headless: that is the retired 2.2.0 driver's grave, and each feature's
|
|
39
|
+
loop keeps running in its own supervised session.
|
|
40
|
+
|
|
41
|
+
- **`/cohorte-review --pr <num>` — the pipeline reviews incoming work.** Same reviewers, same
|
|
42
|
+
report, none of the pipeline's certifications: the PR is fetched into a throwaway worktree
|
|
43
|
+
(your checkout untouched), reviewed in audit mode (no spec to conform to), and a red
|
|
44
|
+
preflight makes the mechanical failures the review instead of wasting reviewers on code that
|
|
45
|
+
doesn't compile. Posting the report as a PR comment always asks first — it is outward-facing
|
|
46
|
+
— and declining leaves the report on disk as a complete outcome.
|
|
47
|
+
|
|
48
|
+
- **What a feature costs, finally on the dashboard.** The workflow paths now stamp an
|
|
49
|
+
approximate `tokens` field into their metrics lines from the runtime's own counter
|
|
50
|
+
(`budget.spent()` deltas — the figure a conversational lead cannot read), the loop's history
|
|
51
|
+
carries cost per round, and the dashboard shows `~Nk tok` per feature and per phase. Marks
|
|
52
|
+
are placed around the review child so build/fix deltas never double-count it; token-less
|
|
53
|
+
conversational lines aggregate as absent, never as "free".
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## 2.8.0 — 2026-08-22
|
|
58
|
+
|
|
59
|
+
- **`/cohorte-loop` is back — as a workflow, which is the whole point.** The 2.2.0 driver was
|
|
60
|
+
retired because it spawned headless child processes it could not supervise: it stalled on a
|
|
61
|
+
permission prompt it could not see, read a missing `build.json` as an empty one, and had a
|
|
62
|
+
session killed mid-write with nothing noticing. `core/workflows/loop.js` runs
|
|
63
|
+
build → review → [fix → review]* for one feature inside the Workflow runtime instead — the
|
|
64
|
+
runtime holds control flow, a dead agent resolves to `null` rather than to silence, and there
|
|
65
|
+
is no prompt to stall on.
|
|
66
|
+
|
|
67
|
+
What it will not do is as designed as what it does: it *verifies* `/cohorte-build`'s outputs
|
|
68
|
+
(frozen spec, fresh `readiness.json`, the lead-authored contract) as preconditions and aborts —
|
|
69
|
+
naming the gap — on anything that is a human's call: a `NOT-READY` spec, a surface the profile
|
|
70
|
+
doesn't own, a blocking finding on the contract file itself. A dead reviewer's zero findings
|
|
71
|
+
can never read as ship (`unreviewed` is checked before `blocking`, in that order on purpose);
|
|
72
|
+
identical blocking findings two rounds running abort as treading water instead of burning the
|
|
73
|
+
remaining rounds; `maxRounds` (default 5) is the last net, never the first. State is four files
|
|
74
|
+
in `specs/reports/` — re-invoking resumes, and every file older than the spec is treated as
|
|
75
|
+
absent. There is **no** `core/commands/cohorte-loop.md` and `validate-core.mjs` now fails if
|
|
76
|
+
one appears: without the Workflow runtime the loop refuses explicitly rather than degrading to
|
|
77
|
+
a lead re-reasoning the fan-out every round at session prices. It stamps `in-progress` /
|
|
78
|
+
`in-review` / `blocked` on the spec — the driver states SCHEMA.md kept alive since 2.2.0 have
|
|
79
|
+
a producer again.
|
|
80
|
+
|
|
81
|
+
To serve it, the review workflow now writes the same `specs/reports/<id>.verdict.json` the
|
|
82
|
+
conversational `/cohorte-review` §3 guarantees (blocking count, normalized `blocking_items`,
|
|
83
|
+
sha256 fingerprint, the degraded `aborted: "preflight"` form) — one machine contract, two
|
|
84
|
+
producers.
|
|
85
|
+
|
|
86
|
+
- **A deny behind an ask was reachable with one click.** `gate.py` scanned command segments in
|
|
87
|
+
order and returned on the first match, so `git commit -m x && node ace migration:fresh`
|
|
88
|
+
surfaced only the benign `git commit` confirm — and the human's single "yes" ran the
|
|
89
|
+
hard-denied migration behind it. Deny patterns are now matched across the whole chain before
|
|
90
|
+
any ask is offered. Global installs pick this up with `cohorte update`; bundled repos via
|
|
91
|
+
`/cohorte-update-pipeline`.
|
|
92
|
+
|
|
93
|
+
- **The SHIP stamp is now earned, not implied.** The conversational `/cohorte-review` ticked the
|
|
94
|
+
DoD and wrote the freshness stamp on every SHIP — including one carrying HIGH findings, which
|
|
95
|
+
`/cohorte-ship`'s gates then happily certified, while the workflow variant refused the same
|
|
96
|
+
state. Both paths now agree: tick + stamp only when nothing above LOW survived; surviving
|
|
97
|
+
HIGH/MEDIUM routes to `/cohorte-fix` (or an explicit park) first.
|
|
98
|
+
|
|
99
|
+
- **A dozen bugs the release audit surfaced**, the sharper ones being: `cohorte install` seeded
|
|
100
|
+
`cohorte.config.yaml` under `~/.claude` even when `CLAUDE_CONFIG_DIR` pointed elsewhere — a
|
|
101
|
+
config no reader ever probed (CI now asserts the seed lands where the readers look);
|
|
102
|
+
the refactor workflow's retry round discarded the items its first verify had already cleared,
|
|
103
|
+
so the backlog re-dispatched finished work — and invoked with a bare `"backend"` it silently
|
|
104
|
+
refactored *every* big domain; the dashboard trusted the absolute paths in a committed
|
|
105
|
+
`runtimes.json`, going all-red on any cloned or moved checkout; `preflight.sh` fed a UTC
|
|
106
|
+
timestamp to a local-time `touch -t`, future-dating the throwaway index west of UTC (and an
|
|
107
|
+
all-empty command list stamped a green preflight that had verified nothing);
|
|
108
|
+
`new-feature.sh` branched worktrees off the *local* default branch its own fetch never
|
|
109
|
+
updated; a CRLF kanban board failed with "column not found" on a column that exists; and a
|
|
110
|
+
dead mechanical-gates agent read as "0 failures" in the audit workflow. Plus a sweep of doc
|
|
111
|
+
drift (the first-feature walkthrough skipped build *and* review; "Node-less" installers that
|
|
112
|
+
require Node; stale counts of agents, scripts and board columns).
|
|
113
|
+
|
|
114
|
+
- **The shell installers are now the thin delegators they already were.** Since 2.2.0 both
|
|
115
|
+
scripts handed everything to `bin/cli.js` and then carried ~300 lines of unreachable legacy
|
|
116
|
+
copy code below the hand-off — dead text that `validate-core`'s "does the installer copy X"
|
|
117
|
+
checks were vacuously matching, which is worse than no check: it reads as coverage. The dead
|
|
118
|
+
code is gone; the checks now assert CI's install dry-run postconditions — tests against the
|
|
119
|
+
copy that actually runs. Along the way: `sh install.sh` from inside a checkout silently
|
|
120
|
+
*cloned the remote* instead of installing the local tree (`$0` arrives with no slash and the
|
|
121
|
+
self-locate case missed it); both installers now refuse a Node older than 18 up front instead
|
|
122
|
+
of half-installing before `fs.cpSync` crashes; and the review agent's `tools:` names both
|
|
123
|
+
retrieval providers (`mcp__serena`, `mcp__graphify`) — one fixed agent file, shared across
|
|
124
|
+
projects, only the wired provider is live in a session, and graphify projects' reviewers were
|
|
125
|
+
silently cut off from their own index.
|
|
126
|
+
|
|
127
|
+
|
|
10
128
|
## 2.7.0 — 2026-08-22
|
|
11
129
|
|
|
12
130
|
- **The metrics panel billed Sonnet 5 fifty percent over.** `prices.json` carried $3/$15 because
|
package/README.md
CHANGED
|
@@ -229,8 +229,8 @@ those actions.
|
|
|
229
229
|
✅/⚠️/❌ checklist (each failure with its fix), the **Surfaces ↔ agents** map from `PIPELINE.md`,
|
|
230
230
|
and one board: a **Kanban** if the project has a linked Obsidian board (columns + cards from the
|
|
231
231
|
vault, with clickable PR links + live open/merged/closed status and a ship-date-sorted Shipped
|
|
232
|
-
column, via `gh`), otherwise a **Specs board** from `specs/*.md` (by `draft · frozen · in-
|
|
233
|
-
shipped`). The Kanban supersedes the Specs board when both would apply.
|
|
232
|
+
column, via `gh`), otherwise a **Specs board** from `specs/*.md` (by `draft · frozen · in-progress ·
|
|
233
|
+
in-review · blocked · shipped`). The Kanban supersedes the Specs board when both would apply.
|
|
234
234
|
- **Actions** (stream their output live) — **Update / Install core** (the shared global core, or a
|
|
235
235
|
repo's bundled core); **Init-pipeline / Update-pipeline / Audit**, which run those Claude Code
|
|
236
236
|
commands **headless** (`claude -p`, autonomous — Init skips the interactive interview, so review
|
|
@@ -276,7 +276,7 @@ will do:
|
|
|
276
276
|
|
|
277
277
|
```sh
|
|
278
278
|
npm i -g cohorte
|
|
279
|
-
francois ext install TheBidouilleAgency/cohorte
|
|
279
|
+
francois ext install TheBidouilleAgency/francois-plugin-cohorte
|
|
280
280
|
```
|
|
281
281
|
|
|
282
282
|
## Releasing (maintainers)
|
|
@@ -301,15 +301,18 @@ so a CLI pinned at an old version cannot quietly re-lay an old core.
|
|
|
301
301
|
| Command | Role |
|
|
302
302
|
| -------------------- | ------------------------------------------------------------------------------------- |
|
|
303
303
|
| `/cohorte-init-pipeline` | Detect stack → interview → generate the profile + agents. Run once per project. |
|
|
304
|
+
| `/cohorte-intake [paste]` | Triage anything that arrives (ticket, email, trace, thread) into a `/cohorte-patch` handoff or a brainstorm seed — staged to disk, carded in Ideas. |
|
|
304
305
|
| `/cohorte-brainstorm` | Interactive persona panel that pressure-tests a feature idea. |
|
|
305
306
|
| `/cohorte-spec` | Freeze the feature spec + contract into `specs/<id>.md` (UI features also get a standalone design brief at `specs/design/<id>.md`). Also applies review returns. |
|
|
306
307
|
| `/cohorte-patch [bug]` | Bug-fix entry: triage a bug and freeze a ~60-line patch spec (`specs/patch-<slug>.md`) whose regression test replaces the contract. Then the normal `/cohorte-build → review → ship`. |
|
|
307
308
|
| `/cohorte-build <id>` | Readiness gate on the frozen spec, then the lead authors the contract and dispatches one implementer per surface in parallel. |
|
|
308
|
-
| `/cohorte-review <id>` | Read-only review agents (one per touched surface, parallel) audit the diff vs the spec; out-of-scope findings go to the refactor backlog. |
|
|
309
|
+
| `/cohorte-review <id>` | Read-only review agents (one per touched surface, parallel) audit the diff vs the spec; out-of-scope findings go to the refactor backlog. `--pr <num>` reviews an **incoming** GitHub PR in a throwaway worktree and offers to post the report as a comment. |
|
|
309
310
|
| `/cohorte-fix <id>` | Apply a review report: remediation into the spec, re-dispatch only the surfaces with findings. |
|
|
311
|
+
| `/cohorte-fleet plan\|status\|sync` | Fly several features in parallel: overlap matrix + merge order, one worktree per feature, live status, post-merge rebase sweep. Coordination only — never headless execution. |
|
|
310
312
|
| `/cohorte-ship <id>` | Release agent commits, pushes, opens the PR; watches CI; proposes worktree teardown. |
|
|
311
313
|
| `/cohorte-audit [path]` | Prioritized refactor backlog for existing code. |
|
|
312
314
|
| `/cohorte-refactor <domain>` | Apply the backlog for one surface, TDD-first. |
|
|
315
|
+
| `/cohorte-retro [last n]` | Mine the accumulated review findings for patterns; ratified ones become §Conventions rules and the surface agents are re-rendered — the next build never produces the finding. |
|
|
313
316
|
| `/cohorte-align-ds` | Align the code UI kit to the design system (no-op if none configured). |
|
|
314
317
|
| `/cohorte-update-pipeline` | Refresh the installed core (global or bundled) to the latest published version. |
|
|
315
318
|
| `/cohorte-doctor` | Diagnose the installation (core, agents↔surfaces, hooks, gate, retrieval, worktrees). |
|
|
@@ -365,15 +368,17 @@ Rules that make it safe:
|
|
|
365
368
|
|
|
366
369
|
### Workflows — deterministic multi-agent runs (opt-in)
|
|
367
370
|
|
|
368
|
-
Four
|
|
369
|
-
|
|
370
|
-
|
|
371
|
+
Four **workflow scripts** ship for the Claude Code Workflow runtime — the same fan-out the
|
|
372
|
+
commands orchestrate, but driven by a deterministic script instead of the lead reasoning it out
|
|
373
|
+
turn by turn. Three are opt-in variants of their same-named commands; the fourth, `loop.js`,
|
|
374
|
+
exists **only** as a workflow:
|
|
371
375
|
|
|
372
376
|
| Script | What it runs |
|
|
373
377
|
| ----------------------- | ----------------------------------------------------------------------------------------- |
|
|
374
378
|
| `workflows/review.js` | Preflight gate (aborts while red — zero agents), one reviewer per touched surface, adversarial cross-check of CRITICAL/security findings, merged verdict only. |
|
|
375
379
|
| `workflows/audit.js` | One auditor per domain (every surface + shared) concurrently, prioritized `specs/refactor-backlog.md`. |
|
|
376
380
|
| `workflows/refactor.js` | Big domains only: `shared` first and alone, then parallel surface implementers, per-domain verify + one retry. |
|
|
381
|
+
| `workflows/loop.js` | `/cohorte-loop` — build → review → [fix → review]* for one feature, unattended and resumable; exits on zero blocking findings, treading water, maxRounds, or anything that needs a human (contract change, unreviewed surface). No conversational fallback, on purpose. |
|
|
377
382
|
|
|
378
383
|
The essentials:
|
|
379
384
|
|
|
@@ -413,15 +418,15 @@ See `profile/SCHEMA.md` for every field in `PIPELINE.md` and how the pipeline us
|
|
|
413
418
|
|
|
414
419
|
```
|
|
415
420
|
package.json # npm package (cohorte) — semver source of truth
|
|
416
|
-
bin/cli.js # the npm CLI: install / update / dashboard / version (cross-platform, no deps)
|
|
417
|
-
install.sh # script installer (fresh + --update) for
|
|
421
|
+
bin/cli.js # the npm CLI: install / update / dashboard / specs / doctor / metrics / version (cross-platform, no deps)
|
|
422
|
+
install.sh # script installer (fresh + --update) for npm-less setups — still needs Node
|
|
418
423
|
install.ps1 # same installer for Windows PowerShell (fresh + -Update)
|
|
419
424
|
core/ # copied verbatim into ~/.claude (global) or <project>/.claude (bundled)
|
|
420
425
|
agents/ # implementer.template.md (rendered per surface) + review / release / profile-reader
|
|
421
426
|
commands/ # init-pipeline + the pipeline commands + /cohorte-update-pipeline
|
|
422
427
|
hooks/ # gate.py (destructive-command gate; branch-aware; preflight phase gate)
|
|
423
428
|
templates/ # handoff / brainstorm-return / design-brief / review-feedback / pr-body / spec
|
|
424
|
-
workflows/ #
|
|
429
|
+
workflows/ # Workflow-runtime scripts: review.js / audit.js / refactor.js (opt-in variants) + loop.js (workflow-only)
|
|
425
430
|
profile/
|
|
426
431
|
PIPELINE.template.md # the profile skeleton /cohorte-init-pipeline fills
|
|
427
432
|
SCHEMA.md # field reference
|
package/bin/cli.js
CHANGED
|
@@ -562,7 +562,20 @@ async function seedConfig() {
|
|
|
562
562
|
// being told: `~/.claude` for Claude Code (unchanged — existing files stay authoritative),
|
|
563
563
|
// `~/.cohorte` for every other runtime. The shipped scripts probe both, in that order, so
|
|
564
564
|
// a human who drives one repo from two agents still has a single board and a single consent.
|
|
565
|
-
|
|
565
|
+
//
|
|
566
|
+
// CLAUDE_CONFIG_DIR moves the whole `~/.claude` tree (globalDir already follows it), but
|
|
567
|
+
// adapter.configPath() speaks in literal `~/.claude/…` — expanding through HOME alone
|
|
568
|
+
// seeded a file at a path no reader probes (kanban-move.sh and the dashboard follow
|
|
569
|
+
// CLAUDE_CONFIG_DIR), missed a filled config there (re-seeding disabled defaults beside
|
|
570
|
+
// it — the exact two-file fork this function's comments forbid), and printed a banner
|
|
571
|
+
// path that did not exist. Re-root the claude-shaped path onto globalDir so the seed,
|
|
572
|
+
// the existing-file check, the banner and every reader agree on one file.
|
|
573
|
+
const homeClaude = path.join(os.homedir(), '.claude');
|
|
574
|
+
const rerootClaude = (p) =>
|
|
575
|
+
p === homeClaude || p.startsWith(homeClaude + path.sep)
|
|
576
|
+
? path.join(globalDir, p.slice(homeClaude.length + 1) || '.')
|
|
577
|
+
: p;
|
|
578
|
+
const cfg = rerootClaude(adapter.expandHome(adapter.configPath(runtime)));
|
|
566
579
|
fs.mkdirSync(path.dirname(cfg), { recursive: true });
|
|
567
580
|
// Pre-rename names, newest first — read as a fallback so upgrades don't lose the config.
|
|
568
581
|
const legacy = ['thebidouille.config.yaml']
|
|
@@ -571,7 +584,7 @@ async function seedConfig() {
|
|
|
571
584
|
// A second runtime must not fork the config: two files means two boards and two consent
|
|
572
585
|
// records, and the human edits whichever one they happen to open. The scripts read
|
|
573
586
|
// `~/.cohorte` first, so seeding it here would SHADOW a filled `~/.claude` copy.
|
|
574
|
-
const claudeCfg = adapter.expandHome(adapter.configPath({ id: 'claude' }));
|
|
587
|
+
const claudeCfg = rerootClaude(adapter.expandHome(adapter.configPath({ id: 'claude' })));
|
|
575
588
|
if (cfg !== claudeCfg && fs.existsSync(claudeCfg)) {
|
|
576
589
|
console.log(` · reusing your existing ${claudeCfg} (the scripts read it as a fallback)`);
|
|
577
590
|
return;
|
package/core/agents/review.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review
|
|
3
3
|
description: Read-only reviewer. Compares the implementation against the frozen spec, then audits code quality, security, and (if the profile declares it) mobile-first. Emits the REVIEW REPORT. Dispatched by /cohorte-review — one per touched surface on multi-surface diffs. Cannot modify anything.
|
|
4
|
-
tools: Read, Grep, Glob, mcp__serena
|
|
4
|
+
tools: Read, Grep, Glob, mcp__serena, mcp__graphify
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -40,7 +40,9 @@ be precise and self-contained.
|
|
|
40
40
|
a default.
|
|
41
41
|
- If `retrieval.provider` in `PIPELINE.md` is not `none`, its MCP tools are in your toolset —
|
|
42
42
|
prefer them over Grep/Glob + whole-file Reads: locate code by symbol, read only the definitions
|
|
43
|
-
you need. Fall back to Grep/Read when they are unavailable or come up empty.
|
|
43
|
+
you need. Fall back to Grep/Read when they are unavailable or come up empty. (Your `tools:`
|
|
44
|
+
list names every provider this agent file is shared across — only the one the project actually
|
|
45
|
+
wired is live in your session; the others simply are not there.)
|
|
44
46
|
|
|
45
47
|
## What you check, in order
|
|
46
48
|
|
|
@@ -31,7 +31,11 @@ Idea (may be empty): **$ARGUMENTS**
|
|
|
31
31
|
|
|
32
32
|
If the idea is empty: when a board is configured and its **Ideas** column has cards, list them (with any
|
|
33
33
|
sub-bullet notes as seed context) and let the human pick one — otherwise ask **"What are we building?"**.
|
|
34
|
-
Either way, wait. If the idea is non-empty, restate it in one line and confirm you've got it
|
|
34
|
+
Either way, wait. If the idea is non-empty, restate it in one line and confirm you've got it — and
|
|
35
|
+
**if `specs/reports/intake-<idea>.md` exists, read it FIRST**: it is `/cohorte-intake`'s staged seed
|
|
36
|
+
(who is asking with verbatim quotes, goals/non-goals, an open-questions list that is this panel's
|
|
37
|
+
agenda, prior art incl. any `_decisions.md` line the request contradicts). The panel argues against
|
|
38
|
+
the distillate, never against the bare slug.
|
|
35
39
|
|
|
36
40
|
## Run the panel
|
|
37
41
|
|
|
@@ -143,7 +143,9 @@ _Only if `contract.enabled`._ From §5 of the spec, write/update the feature's c
|
|
|
143
143
|
`<contract.path>/$ARGUMENTS.<contract.ext>` in the profile's `mechanism` (e.g. Zod v4 schemas + inferred
|
|
144
144
|
types for `shared-types-zod`). Export it from `contract.index` if set. This is the ONLY file the agents
|
|
145
145
|
share; they import it read-only and must not edit it. If `contract.enabled` is false, the spec prose is
|
|
146
|
-
the sync channel — say so and skip
|
|
146
|
+
the sync channel — say so and skip, but still run `date +%s` on its own (same as the patch skip
|
|
147
|
+
above: §4's wall-clock start comes from here, and a build with no start epoch writes a metrics
|
|
148
|
+
line with no duration). **Postcondition (if `contract.enabled`):**
|
|
147
149
|
`test -f <contract.path>/$ARGUMENTS.<contract.ext> && date +%s` — the contract file must exist before
|
|
148
150
|
you dispatch §3, or the stateless agents have nothing to build against (the epoch output is §4's
|
|
149
151
|
wall-clock start — no separate timing call).
|
|
@@ -85,7 +85,7 @@ fix only with the human's go-ahead (or hand them the command).
|
|
|
85
85
|
check: CLI resolvable from PATH, `.mcp.json` entry present in PATH-proof launcher form,
|
|
86
86
|
`.serena/` gitignored, server actually connects.
|
|
87
87
|
5. **Design** (if `design.enabled`). `snapshot_dir` exists and is committed; `ui_kit_path` +
|
|
88
|
-
`tokens_path` exist; if `provider: claude-design`, `DesignSync` responds (`
|
|
88
|
+
`tokens_path` exist; if `provider: claude-design`, `DesignSync` responds (`list_files` on the `design_system_project`) and
|
|
89
89
|
`design_system_project` is reachable. Recall: spec `design_files` are full
|
|
90
90
|
`…/design/p/<projectId>?file=<file>` links that carry their own project + page; `design_project` is
|
|
91
91
|
only a legacy fallback for old bare-filename specs (default `none`).
|
|
@@ -114,8 +114,10 @@ fix only with the human's go-ahead (or hand them the command).
|
|
|
114
114
|
check; do NOT raise it to match a newer feature's floor, or every install between the two
|
|
115
115
|
versions reads as broken while its workflows run fine. When `design.inline` is on, report the
|
|
116
116
|
design floor (≥ 2.1.234) as its own line under check 8b — separate prerequisite, separate verdict.
|
|
117
|
-
- **Scripts present:** `<core>/workflows/review.js` + `audit.js` + `refactor.js` —
|
|
118
|
-
missing on a current core ⇒ half-done install, re-run install/update.
|
|
117
|
+
- **Scripts present:** `<core>/workflows/review.js` + `audit.js` + `refactor.js` + `loop.js` —
|
|
118
|
+
missing on a current core ⇒ half-done install, re-run install/update. (`/cohorte-loop` is
|
|
119
|
+
**workflow-only** — no command file exists on purpose; without this runtime it refuses
|
|
120
|
+
rather than degrading to a conversational loop.)
|
|
119
121
|
- **Phase-0 agent present:** `<agents>/profile-reader.md` — the workflows abort without it.
|
|
120
122
|
- **Workflows enabled in this session** — the `Workflow` tool is in your own toolset right now;
|
|
121
123
|
absent ⇒ disabled for this session (a setting or an old client), conversational path.
|
|
@@ -31,8 +31,8 @@ that change the *contract*; `/cohorte-fix` is for everything else.
|
|
|
31
31
|
re-author the contract file yourself now (lead-only, per `/cohorte-build` §2) — agents never edit it. If
|
|
32
32
|
the contract change ripples into surfaces *without* findings, fall back to full `/cohorte-build` instead
|
|
33
33
|
and say so.
|
|
34
|
-
- **Note the epoch** (`date +%s`) in the first Bash call you make here — §3's metrics line
|
|
35
|
-
|
|
34
|
+
- **Note the epoch** (`date +%s`) in the first Bash call you make here — §3's metrics line carries
|
|
35
|
+
`seconds`, and there is no separate timing call.
|
|
36
36
|
|
|
37
37
|
## 2. Scope the re-dispatch — only surfaces with findings
|
|
38
38
|
|
|
@@ -42,10 +42,11 @@ that change the *contract*; `/cohorte-fix` is for everything else.
|
|
|
42
42
|
to the most relevant surface — say which.
|
|
43
43
|
- Re-dispatch **ONLY the surfaces owning ≥1 item**, in parallel, in a **single message** — the exact
|
|
44
44
|
dispatch template from `/cohorte-build` §3 (one byte-stable template for builds and fix loops; you do NOT
|
|
45
|
-
paste a diff — the agent computes its own, scoped to its tree). Fill the template's
|
|
46
|
-
slot with that surface's open `- [ ]` item lines **verbatim**, so the agent needs no spec re-read to
|
|
45
|
+
paste a diff — the agent computes its own, scoped to its tree). Fill the template's **Remediation
|
|
46
|
+
slot** with that surface's open `- [ ]` item lines **verbatim**, so the agent needs no spec re-read to
|
|
47
47
|
find its work; fill the design slot with `none` when a `uses_design` surface's open items are all
|
|
48
|
-
non-visual (no DesignSync re-fetch for a type fix)
|
|
48
|
+
non-visual (no DesignSync re-fetch for a type fix), and the readiness-gaps slot with `none` (a fix
|
|
49
|
+
loop has no fresh §1.6 verdict). Surfaces without findings are NOT re-dispatched —
|
|
49
50
|
that is the point.
|
|
50
51
|
|
|
51
52
|
## 3. Integrate & check off what's fixed
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
model: sonnet
|
|
3
|
+
description: Fly several features in parallel — overlap analysis and merge order, one isolated worktree per feature, a live status board, and the post-merge rebase sweep nobody remembers to do.
|
|
4
|
+
argument-hint: plan <id> <id> [...] | status | sync
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are the **fleet controller**. Parallel features already work in this pipeline — isolated
|
|
8
|
+
worktrees, per-feature DBs and ports, one session per worktree — but the coordination between
|
|
9
|
+
them is tribal knowledge: which specs collide, which merges first, and who rebases the
|
|
10
|
+
survivors after each ship. This command owns exactly that coordination, and deliberately
|
|
11
|
+
nothing more.
|
|
12
|
+
|
|
13
|
+
**What this command will never do: spawn the work headless.** The obvious "cool" version —
|
|
14
|
+
fire a loop per worktree from here — is the 2.2.0 driver's grave: child sessions nobody
|
|
15
|
+
supervises, stalling on prompts nobody sees. Each feature's build/review/loop runs in **its own
|
|
16
|
+
worktree's session**, supervised like any other; the fleet plans, watches and rebases. State
|
|
17
|
+
lives in `specs/reports/fleet.json` (the main checkout's gitignored buffer — not to be confused
|
|
18
|
+
with the dashboard's multi-*project* fleet registry; this file is this repo's multi-*feature*
|
|
19
|
+
flight plan).
|
|
20
|
+
|
|
21
|
+
> Read `PIPELINE.md` §`pipeline-profile` first: `surfaces` (paths — the overlap analysis keys
|
|
22
|
+
> on them), `contract`, `vcs.default_branch`, and the `isolation` block. _Skip the re-read if
|
|
23
|
+
> already in context and unmodified since._ **`isolation.enabled: false` ⇒ stop at `plan`**:
|
|
24
|
+
> N features in one checkout is a merge-conflict generator with extra steps — name the fix
|
|
25
|
+
> (`/cohorte-init-pipeline` wires isolation) and go no further.
|
|
26
|
+
|
|
27
|
+
## `plan <id> <id> [...]` — collide, order, provision
|
|
28
|
+
|
|
29
|
+
1. **Every spec must be `frozen`** (front-matter grep, ~15-line reads — never full specs yet).
|
|
30
|
+
Anything else (`draft`, `in-review`, missing) ⇒ name it and stop; a fleet of half-frozen
|
|
31
|
+
specs is N problems flying in formation.
|
|
32
|
+
2. **Overlap analysis** — read each spec's §5 contract entries and §6 surface tasks, then build
|
|
33
|
+
the matrix *feature × surface*, and flag the two collision classes:
|
|
34
|
+
- **Contract dependency** — spec B's §5 references shapes spec A introduces ⇒ B ships
|
|
35
|
+
**after** A, and B's worktree must rebase once A merges (the `sync` mode's job). This is
|
|
36
|
+
an ordering, not a blocker.
|
|
37
|
+
- **Same-tree writes** — two specs whose §6 tasks land in the same `surfaces[].path` (worse:
|
|
38
|
+
the same module). This is where parallel merges bleed; propose either an order (lighter
|
|
39
|
+
feature first) or — when the overlap is one file both must edit — say plainly that these
|
|
40
|
+
two should not fly together, and let the human drop one from the fleet.
|
|
41
|
+
3. **Propose the merge order** from those edges (dependencies first, then ascending overlap),
|
|
42
|
+
show the matrix + order in a compact table, and get the human's go-ahead — the order is a
|
|
43
|
+
plan they will live with for days.
|
|
44
|
+
4. **Provision worktrees** — for each feature without one: `scripts/new-feature.sh <id>` (the
|
|
45
|
+
rendered isolation script: worktree + branch + DB + port slot). Relay each script's output
|
|
46
|
+
line; a script failure stops the plan for that feature, never silently.
|
|
47
|
+
5. **Write `specs/reports/fleet.json`** (overwrite): `{"ts":"<ISO>","order":[...ids in merge
|
|
48
|
+
order...],"features":{"<id>":{"worktree":"<path>","branch":"<branch>","dependsOn":[...]}}}`.
|
|
49
|
+
6. **Print the launch plan** — one line per feature, in order: the worktree path to open a
|
|
50
|
+
session in, and the first command to run there (`/cohorte-build <id>`, or "ask for the loop
|
|
51
|
+
workflow: `{feature: \"<id>\"}`" on a runtime that has it). The human launches them; the
|
|
52
|
+
fleet does not.
|
|
53
|
+
|
|
54
|
+
## `status` — one table, no archaeology
|
|
55
|
+
|
|
56
|
+
Read `fleet.json` (absent ⇒ say `plan` comes first, stop). For each feature, **mechanical reads
|
|
57
|
+
only, redirected — always from THAT feature's worktree**, never the main checkout (each worktree
|
|
58
|
+
carries its own copy of `specs/<id>.md` and `specs/reports/`, and that copy is the one its run
|
|
59
|
+
has been writing): `<worktree>/specs/<id>.md` front-matter `status` · the worktree's
|
|
60
|
+
`specs/reports/<id>.loop.json` (`phase`/`round`/`outcome`) and `<id>.verdict.json`
|
|
61
|
+
(`verdict`/`blocking`) when present · then ONE
|
|
62
|
+
`git -C <worktree> fetch --quiet origin <default_branch> || true` and
|
|
63
|
+
`git -C <worktree> rev-list --count origin/<default_branch>..HEAD` and `..origin/<default_branch>`
|
|
64
|
+
(ahead / **behind** — behind is the number that matters, and against the *remote* ref: the local
|
|
65
|
+
one goes stale the moment a PR merges on the host, which is precisely when status gets asked).
|
|
66
|
+
One row per feature, in merge order:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
<id> · <status> · loop: <phase> r<round> | <outcome> · blocking: <n> · ↑<ahead> ↓<behind> · next: <the one action>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`next` is the whole point of the mode: the single action per feature (a command to run in its
|
|
73
|
+
worktree, "waiting on <dep> to merge", "ready to ship — its turn in the order", or "rebase
|
|
74
|
+
needed — run sync"). A worktree registered in `fleet.json` but gone from
|
|
75
|
+
`git worktree list` is reported as such, never silently dropped.
|
|
76
|
+
|
|
77
|
+
## `sync` — the post-merge sweep
|
|
78
|
+
|
|
79
|
+
Run after every merge (the human says which feature shipped, or you detect it: spec
|
|
80
|
+
`status: shipped` + branch merged into `<default_branch>`):
|
|
81
|
+
|
|
82
|
+
1. Drop the shipped feature from `fleet.json` (rewrite, keep order of the rest) — and if its
|
|
83
|
+
worktree still exists, remind the teardown `/cohorte-ship` proposes:
|
|
84
|
+
`scripts/remove-feature.sh <id>` (`--drop-db` at the human's call). Never run it unasked.
|
|
85
|
+
2. For each surviving worktree, in merge order — `git -C <worktree> fetch origin
|
|
86
|
+
<default_branch>` **always**, then decide whether the rebase is THIS session's to run:
|
|
87
|
+
- **Tree dirty** (`git -C <worktree> status --porcelain` non-empty — the NORMAL mid-flight
|
|
88
|
+
state: feature work stays uncommitted until `/cohorte-ship`, so `git rebase` would refuse
|
|
89
|
+
with "unstaged changes" before any conflict even exists) **or a run in flight** (the
|
|
90
|
+
worktree's `loop.json` has no `outcome`): **do not touch it.** Report the row as
|
|
91
|
+
`rebase needed — run \`git rebase origin/<default_branch>\` from that worktree's own
|
|
92
|
+
session` (mutating a branch from outside its session, mid-run, is how work disappears).
|
|
93
|
+
- **Clean and idle**: rebase it, output redirected to `specs/reports/fleet-sync.txt`. A
|
|
94
|
+
conflict is reported verbatim and left for its owner (`git rebase --abort` restores) —
|
|
95
|
+
never resolved, never `--force`-anything from here.
|
|
96
|
+
3. **Say the consequence out loud, per rebased (or rebase-needed) worktree:** the rebase moves
|
|
97
|
+
every commit, so any `reviewed_base`/`reviewed_digest` in that spec then describes a tree
|
|
98
|
+
that no longer exists — `/cohorte-ship` will (rightly) refuse until a fresh
|
|
99
|
+
`/cohorte-review` re-stamps it. A clean rebase is not a re-verdict; the re-review is.
|
|
100
|
+
4. Reprint the `status` table.
|
|
101
|
+
|
|
102
|
+
In chat, every mode prints its table/plan and nothing else — the matrix evidence and rebase
|
|
103
|
+
logs live in `specs/reports/`. **Recommend a `/clear`** after `plan` (the flight plan is on
|
|
104
|
+
disk; the sessions doing the flying are elsewhere anyway).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Triage anything that arrives — a ticket, a client email, a stack trace, a Slack thread — into the pipeline's entry point: a patch handoff or a brainstorm seed.
|
|
3
|
+
argument-hint: [paste the raw material — ticket, email, trace, thread — or empty to be asked]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the **intake triager**. Work is arriving from OUTSIDE the pipeline — a bug report, a
|
|
7
|
+
client email, a support thread, a stack trace, a half-idea — and today a human distills it by
|
|
8
|
+
hand before anything can start. Your job is that distillation: read the raw material, decide
|
|
9
|
+
which door it enters through, and stage a handoff the next command can consume verbatim.
|
|
10
|
+
|
|
11
|
+
> Read `PIPELINE.md` §`pipeline-profile` first — `name`, `one_liner`, `surfaces` (to guess which
|
|
12
|
+
> surface a symptom lives in) and `ui_language`. _Skip the re-read if it's already in your
|
|
13
|
+
> context this session and unmodified since._ Read `specs/_decisions.md` §Live if it exists —
|
|
14
|
+
> an incoming request that contradicts a settled decision must be flagged as such, not triaged
|
|
15
|
+
> as a fresh idea.
|
|
16
|
+
|
|
17
|
+
## 1. Ingest & triage
|
|
18
|
+
|
|
19
|
+
The material is pasted after the command; nothing pasted ⇒ ask for it and wait (paste, file
|
|
20
|
+
path, or a `gh issue view <n>` you run yourself when they name an issue number). Then decide —
|
|
21
|
+
and say which signals decided it:
|
|
22
|
+
|
|
23
|
+
- **Bug** — existing behavior misbehaving: a repro or trace, "used to work", an error message,
|
|
24
|
+
a version where it broke. → §2.
|
|
25
|
+
- **Feature** — behavior that does not exist yet: "could we", "it should also", a workflow the
|
|
26
|
+
product doesn't cover. → §3.
|
|
27
|
+
- **Both tangled together** (a bug report whose fix half describes a new feature): split it —
|
|
28
|
+
§2 for the defect, §3 for the rest — and say you split it.
|
|
29
|
+
- **Neither** (a question, a config issue on their side, praise, noise): say so in one line and
|
|
30
|
+
stop. Not everything that arrives is work; inventing a spec from noise costs a whole pipeline
|
|
31
|
+
run downstream.
|
|
32
|
+
|
|
33
|
+
Pick a short kebab-case **slug** from the content (`checkout-double-charge`, `csv-export`) —
|
|
34
|
+
it becomes the join key for the file, the kanban card and the follow-up command.
|
|
35
|
+
|
|
36
|
+
## 2. Bug → a `/cohorte-patch` handoff
|
|
37
|
+
|
|
38
|
+
Distill the raw material into exactly the structure `/cohorte-patch` §1 interviews for — so the
|
|
39
|
+
patch triage starts loaded instead of re-asking:
|
|
40
|
+
|
|
41
|
+
- **Symptom** — one sentence, observed behavior vs expected.
|
|
42
|
+
- **Repro** — numbered steps as far as the material supports them; mark every step you inferred
|
|
43
|
+
(`(inferred)`) rather than silently guessing. A trace with no steps ⇒ the trace IS the repro
|
|
44
|
+
material, say so.
|
|
45
|
+
- **Environment** — version/browser/OS/role if present; `unknown` where absent.
|
|
46
|
+
- **Suspected surface(s)** — map the symptom onto `surfaces[]` by what the paths/stack frames
|
|
47
|
+
name; a guess is fine, label it one.
|
|
48
|
+
- **Severity signal** — who is blocked and how hard, in the reporter's own words.
|
|
49
|
+
|
|
50
|
+
**Stage it** to `specs/reports/intake-<slug>.md` (overwrite; `mkdir -p specs/reports` first —
|
|
51
|
+
the gitignored buffer dir, so the handoff survives a `/clear`). Then:
|
|
52
|
+
|
|
53
|
+
> **Kanban** (SCHEMA.md §Kanban): run
|
|
54
|
+
> `<core>/pipeline/scripts/kanban-move.sh auto patch-<slug> ideas --title "[patch] <one-line title>"`.
|
|
55
|
+
> The id is **`patch-<slug>`, prefix included, and the title prefix is `[patch]`** — that is the
|
|
56
|
+
> exact join key and the exact title `/cohorte-patch` §1 looks for, so its later move finds THIS
|
|
57
|
+
> card instead of creating a duplicate and stranding this one in Ideas (the failure SCHEMA
|
|
58
|
+
> §Kanban's "tag before you move" exists to prevent). `auto` resolves the board from the config
|
|
59
|
+
> itself and exits 0 with a `kanban: <reason>` line when there is none — so **never decide "no
|
|
60
|
+
> board is configured" without running it**.
|
|
61
|
+
|
|
62
|
+
Close with: `→ /cohorte-patch <slug>` — `/cohorte-patch` reads the staged
|
|
63
|
+
`specs/reports/intake-<slug>.md` when it exists, so the triage starts loaded. Do **not** run it
|
|
64
|
+
yourself — freezing a spec is a decision the human confirms, and intake's job ends at the door.
|
|
65
|
+
|
|
66
|
+
## 3. Feature → a `/cohorte-brainstorm` seed
|
|
67
|
+
|
|
68
|
+
Distill into the seed the panel argues best about:
|
|
69
|
+
|
|
70
|
+
- **Title + one-liner** — in the product's language (`ui_language` for user-facing wording).
|
|
71
|
+
- **Who is asking & why now** — verbatim quotes where the material has them; the panel argues
|
|
72
|
+
better against a real voice than a paraphrase.
|
|
73
|
+
- **Goals / explicit non-goals** — only what the material actually states; never pad.
|
|
74
|
+
- **Open questions** — every ambiguity you'd otherwise have guessed at, as questions. This list
|
|
75
|
+
is the seed's real value: it is the brainstorm's agenda.
|
|
76
|
+
- **Prior art in this repo** — one grep pass: existing specs/decisions touching the same area
|
|
77
|
+
(`grep -l` over `specs/*.md`, redirected — never a file read per name). Contradicts a
|
|
78
|
+
`_decisions.md` line ⇒ name the line verbatim; the panel must argue against it knowingly.
|
|
79
|
+
|
|
80
|
+
**Stage it** to `specs/reports/intake-<slug>.md` (overwrite), then the kanban call with the
|
|
81
|
+
bare id: `<core>/pipeline/scripts/kanban-move.sh auto <slug> ideas --title "<title>"` (same
|
|
82
|
+
"never decide without running it" rule as §2). The card is the board's join key; **the seed
|
|
83
|
+
itself travels in the staged file** — `/cohorte-brainstorm <slug>` reads
|
|
84
|
+
`specs/reports/intake-<slug>.md` when it exists, so the panel argues against the distillate,
|
|
85
|
+
not against a bare slug.
|
|
86
|
+
|
|
87
|
+
Close with: `→ /cohorte-brainstorm <slug>` — the seed is on disk and on the board;
|
|
88
|
+
**recommend a `/clear` first**, the handoff is complete.
|
|
89
|
+
|
|
90
|
+
In chat print ONLY: the triage verdict + its signals (one line), the staged file path, the
|
|
91
|
+
kanban result line, and the follow-up command. Never echo the full distillate into chat — it
|
|
92
|
+
is on disk, and this session's history is re-sent at input price on every turn.
|
|
@@ -28,7 +28,12 @@ Bug (may be empty): **$ARGUMENTS**
|
|
|
28
28
|
|
|
29
29
|
## 1. Get the bug
|
|
30
30
|
|
|
31
|
-
If `$ARGUMENTS` is non-empty, restate it in one line and confirm you've got it
|
|
31
|
+
If `$ARGUMENTS` is non-empty, restate it in one line and confirm you've got it — and if it is
|
|
32
|
+
(or names) a slug with a staged `specs/reports/intake-<slug>.md`, **read that file first**: it is
|
|
33
|
+
`/cohorte-intake`'s distillate (symptom, repro with inferred steps labeled, environment, suspected
|
|
34
|
+
surfaces, severity signal), so this triage starts loaded instead of re-asking. Keep intake's slug —
|
|
35
|
+
its kanban card is already tagged `#patch-<slug>` and titled `[patch] …`, exactly what the move
|
|
36
|
+
below joins on.
|
|
32
37
|
|
|
33
38
|
If it is empty: when a board is configured and its **Ideas** column has cards, list them (with any
|
|
34
39
|
sub-bullet notes as context) — **cards titled `[patch]` first**, since those are the ones a human
|