cohorte 1.5.0 → 2.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 +169 -3
- package/README.md +65 -57
- package/bin/cli.js +31 -15
- package/core/agents/implementer.template.md +3 -3
- package/core/agents/release.md +1 -1
- package/core/agents/review.md +25 -2
- package/core/commands/{audit.md → cohorte-audit.md} +11 -3
- package/core/commands/{brainstorm.md → cohorte-brainstorm.md} +9 -3
- package/core/commands/{build.md → cohorte-build.md} +95 -10
- package/core/commands/{doctor.md → cohorte-doctor.md} +22 -9
- package/core/commands/{fix.md → cohorte-fix.md} +20 -13
- package/core/commands/{init-pipeline.md → cohorte-init-pipeline.md} +1 -1
- package/core/commands/cohorte-loop.md +110 -0
- package/core/commands/{refactor.md → cohorte-refactor.md} +3 -3
- package/core/commands/{review.md → cohorte-review.md} +70 -20
- package/core/commands/{ship.md → cohorte-ship.md} +5 -5
- package/core/commands/{spec.md → cohorte-spec.md} +32 -12
- package/core/commands/{update-pipeline.md → cohorte-update-pipeline.md} +16 -6
- package/core/hooks/gate.py +101 -6
- package/core/templates/brainstorm-return.md +4 -4
- package/core/templates/decisions.template.md +42 -0
- package/core/templates/design-brief.md +1 -1
- package/core/templates/spec.template.md +8 -6
- package/core/templates/steps/init-pipeline/01-detect-stack.md +1 -1
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +6 -6
- package/core/templates/steps/init-pipeline/03-draft-profile.md +1 -1
- package/core/templates/steps/init-pipeline/04-write-render.md +16 -12
- package/core/templates/steps/init-pipeline/05-report.md +5 -5
- package/core/workflows/audit.js +6 -6
- package/core/workflows/refactor.js +14 -14
- package/core/workflows/review.js +62 -20
- package/dashboard/README.md +2 -2
- package/dashboard/dist/assets/{index-dkO8UUVl.css → index-BZ_LQlEj.css} +1 -1
- package/dashboard/dist/assets/{index-8owBnqyv.js → index-P1I1JGtj.js} +11 -11
- package/dashboard/dist/index.html +2 -2
- package/dashboard/server/doctor.js +75 -18
- package/dashboard/server/index.js +5 -5
- package/dashboard/server/metrics.js +1 -1
- package/install.ps1 +31 -14
- package/install.sh +31 -14
- package/package.json +2 -2
- package/profile/PIPELINE.template.md +17 -16
- package/profile/SCHEMA.md +199 -48
- package/profile/cohorte.config.template.yaml +8 -8
- package/scripts/loop-detach.sh +153 -0
- package/scripts/loop.sh +202 -25
- package/scripts/metrics/collect.mjs +17 -8
- package/scripts/new-feature.sh.template +3 -3
- package/scripts/preflight.sh +40 -4
- package/scripts/remove-feature.sh.template +2 -2
- package/scripts/test-dashboard.mjs +34 -7
- package/scripts/test-gate.mjs +58 -0
- package/scripts/test-loop.mjs +269 -0
- package/scripts/test-metrics.mjs +23 -11
- package/scripts/test-workflows.mjs +33 -5
- package/scripts/validate-core.mjs +46 -9
- package/core/commands/loop.md +0 -61
- /package/core/commands/{align-ds.md → cohorte-align-ds.md} +0 -0
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<link rel="icon" type="image/png" sizes="16x16" href="./favicon-16.png" />
|
|
8
8
|
<link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon-180.png" />
|
|
9
9
|
<title>cohorte · dashboard</title>
|
|
10
|
-
<script type="module" crossorigin src="./assets/index-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
10
|
+
<script type="module" crossorigin src="./assets/index-P1I1JGtj.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="./assets/index-BZ_LQlEj.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="root"></div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
// Programmatic port of the /doctor checks (core/commands/doctor.md), for the dashboard.
|
|
2
|
+
// Programmatic port of the /cohorte-doctor checks (core/commands/doctor.md), for the dashboard.
|
|
3
3
|
// Read-only: inspects files only. Checks that need a live process (MCP connectivity,
|
|
4
4
|
// git worktree state, DesignSync) are reported as `skip` with a note — the node server
|
|
5
5
|
// can't run them, and honest "not checked here" beats a false green.
|
|
@@ -19,12 +19,15 @@ const FIXED_AGENTS = new Set([
|
|
|
19
19
|
'implementer.template',
|
|
20
20
|
]);
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// The spec lifecycle (SCHEMA.md §Spec status). `in-progress` and `blocked` are written by
|
|
23
|
+
// the /cohorte-loop driver — they are what makes an interrupted autonomous loop resumable, so a
|
|
24
|
+
// dashboard that flagged them as invalid would report the pipeline's own state as a defect.
|
|
25
|
+
const VALID_STATUS = ['draft', 'frozen', 'in-progress', 'in-review', 'shipped', 'blocked'];
|
|
23
26
|
|
|
24
27
|
// Artifacts the pipeline itself writes into specs/ that are NOT feature specs and have no
|
|
25
|
-
// front-matter status. `/audit` writes specs/refactor-backlog.md by design, so scanning it
|
|
26
|
-
// as a spec made /doctor warn about a file cohorte had just created — a false positive that
|
|
27
|
-
// fired in every project that had ever run /audit. `_`-prefixed files (e.g. _template.md)
|
|
28
|
+
// front-matter status. `/cohorte-audit` writes specs/refactor-backlog.md by design, so scanning it
|
|
29
|
+
// as a spec made /cohorte-doctor warn about a file cohorte had just created — a false positive that
|
|
30
|
+
// fired in every project that had ever run /cohorte-audit. `_`-prefixed files (e.g. _template.md)
|
|
28
31
|
// are already skipped by the reader below.
|
|
29
32
|
const NON_SPEC_FILES = new Set(['refactor-backlog.md']);
|
|
30
33
|
|
|
@@ -59,7 +62,7 @@ function checkCore(v) {
|
|
|
59
62
|
if (v.freshness === -1) {
|
|
60
63
|
return mk('core', 'Core & pointer', 'warn',
|
|
61
64
|
`core ${v.installedVersion} installed (${v.installMode}); npm latest is ${v.latest}`,
|
|
62
|
-
'/update-pipeline (or npx cohorte update)');
|
|
65
|
+
'/cohorte-update-pipeline (or npx cohorte update)');
|
|
63
66
|
}
|
|
64
67
|
const tail = v.latest ? `, npm latest ${v.latest}` : ', npm unreachable';
|
|
65
68
|
return mk('core', 'Core & pointer', 'ok', `core ${v.installedVersion} (${v.installMode})${tail}`);
|
|
@@ -68,12 +71,12 @@ function checkCore(v) {
|
|
|
68
71
|
function checkProfile(profile, hasPipelineMd) {
|
|
69
72
|
if (!hasPipelineMd) {
|
|
70
73
|
return mk('profile', 'Profile (PIPELINE.md)', 'bad', 'PIPELINE.md not found',
|
|
71
|
-
'/init-pipeline (generate the project profile)');
|
|
74
|
+
'/cohorte-init-pipeline (generate the project profile)');
|
|
72
75
|
}
|
|
73
76
|
if (!profile) {
|
|
74
77
|
return mk('profile', 'Profile (PIPELINE.md)', 'bad',
|
|
75
78
|
'PIPELINE.md present but its `yaml pipeline-profile` block is missing or unparseable',
|
|
76
|
-
'/init-pipeline (or fix the fenced yaml block)');
|
|
79
|
+
'/cohorte-init-pipeline (or fix the fenced yaml block)');
|
|
77
80
|
}
|
|
78
81
|
const n = (profile.surfaces || []).length;
|
|
79
82
|
return mk('profile', 'Profile (PIPELINE.md)', n ? 'ok' : 'warn',
|
|
@@ -98,7 +101,7 @@ function checkAgents(profile, projectRoot) {
|
|
|
98
101
|
if (missing.length) {
|
|
99
102
|
return mk('agents', 'Surfaces ↔ agents', 'bad',
|
|
100
103
|
`surface(s) with no rendered agent: ${missing.join(', ')}`,
|
|
101
|
-
'/init-pipeline (re-render surface agents)');
|
|
104
|
+
'/cohorte-init-pipeline (re-render surface agents)');
|
|
102
105
|
}
|
|
103
106
|
if (orphans.length) {
|
|
104
107
|
return mk('agents', 'Surfaces ↔ agents', 'warn',
|
|
@@ -117,7 +120,7 @@ function checkGate(profile, projectRoot) {
|
|
|
117
120
|
const cfg = readJson(path.join(projectRoot, '.claude', 'gate-config.json'));
|
|
118
121
|
if (!cfg) {
|
|
119
122
|
return mk('gate', 'Gate config', 'bad', '.claude/gate-config.json missing or unreadable',
|
|
120
|
-
'/init-pipeline (regenerate gate-config.json from the gate block)');
|
|
123
|
+
'/cohorte-init-pipeline (regenerate gate-config.json from the gate block)');
|
|
121
124
|
}
|
|
122
125
|
const drifted = [];
|
|
123
126
|
if (!sameSet(cfg.deny, gate.deny)) drifted.push('deny');
|
|
@@ -144,6 +147,55 @@ function checkGate(profile, projectRoot) {
|
|
|
144
147
|
(branchGated ? `, ${branchGated} gated on ${gate.default_branch || 'main'}` : '') + ')');
|
|
145
148
|
}
|
|
146
149
|
|
|
150
|
+
// Local-only pipeline artifacts. `.claude/preflight.ok` is the one that BREAKS things when
|
|
151
|
+
// versioned rather than merely being noise: the stamp names the tree it verified, committing
|
|
152
|
+
// it moves HEAD past that, and the committed copy then rides into every fresh clone and
|
|
153
|
+
// worktree — the phase gate either blocks a clean tree or greens one it never checked.
|
|
154
|
+
const LOCAL_ARTIFACTS = [
|
|
155
|
+
{ path: '.claude/preflight.ok', why: 'the phase-gate stamp — a versioned one breaks the gate in every clone and worktree' },
|
|
156
|
+
{ path: '.claude/pipeline-metrics.jsonl', why: 'the per-dispatch metrics sink (local, append-only)' },
|
|
157
|
+
{ path: 'specs/reports/', why: 'the /cohorte-review report buffer (derived, regenerated each run)' },
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
// Does any .gitignore in play cover `rel`? Deliberately literal — it recognizes the exact
|
|
161
|
+
// path, its basename, a trailing-slash dir prefix and a `*.<ext>` glob, which is every form
|
|
162
|
+
// /cohorte-init-pipeline writes. Anything more clever would need a real gitignore engine.
|
|
163
|
+
function ignoreCovers(projectRoot, rel) {
|
|
164
|
+
const base = rel.replace(/\/$/, '').split('/').pop();
|
|
165
|
+
const ext = base.includes('.') ? '*' + base.slice(base.lastIndexOf('.')) : null;
|
|
166
|
+
const files = [
|
|
167
|
+
path.join(projectRoot, '.gitignore'),
|
|
168
|
+
path.join(projectRoot, '.claude', '.gitignore'),
|
|
169
|
+
];
|
|
170
|
+
for (const f of files) {
|
|
171
|
+
const text = readText(f);
|
|
172
|
+
if (!text) continue;
|
|
173
|
+
for (let line of text.split('\n')) {
|
|
174
|
+
line = line.trim();
|
|
175
|
+
if (!line || line.startsWith('#')) continue;
|
|
176
|
+
const p = line.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
177
|
+
if (!p) continue;
|
|
178
|
+
if (p === rel.replace(/\/$/, '') || p === base || p === ext) return true;
|
|
179
|
+
// A directory rule covers everything under it (`.claude/` ignores the stamp too).
|
|
180
|
+
if (rel.startsWith(p + '/')) return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function checkLocalArtifacts(projectRoot) {
|
|
187
|
+
const unignored = LOCAL_ARTIFACTS.filter(a => !ignoreCovers(projectRoot, a.path));
|
|
188
|
+
if (!unignored.length) {
|
|
189
|
+
return mk('artifacts', 'Local artifacts', 'ok',
|
|
190
|
+
`${LOCAL_ARTIFACTS.length} local-only artifact path(s) all gitignored`);
|
|
191
|
+
}
|
|
192
|
+
const stamp = unignored.find(a => a.path === '.claude/preflight.ok');
|
|
193
|
+
return mk('artifacts', 'Local artifacts', stamp ? 'bad' : 'warn',
|
|
194
|
+
`not gitignored: ${unignored.map(a => a.path).join(', ')} — ${unignored[0].why}`,
|
|
195
|
+
`add ${unignored.map(a => a.path).join(' + ')} to .gitignore`
|
|
196
|
+
+ (stamp ? ', then `git rm --cached --ignore-unmatch .claude/preflight.ok`' : ''));
|
|
197
|
+
}
|
|
198
|
+
|
|
147
199
|
function gateRegs(settingsPath) {
|
|
148
200
|
const data = readJson(settingsPath);
|
|
149
201
|
const pre = data && data.hooks && Array.isArray(data.hooks.PreToolUse) ? data.hooks.PreToolUse : [];
|
|
@@ -156,7 +208,7 @@ function gateRegs(settingsPath) {
|
|
|
156
208
|
|
|
157
209
|
function checkHooks(projectRoot, globalDir, installMode) {
|
|
158
210
|
// A registration in EITHER scope serves the project: bundled repos get it from
|
|
159
|
-
// /init-pipeline in project settings, but on a machine with the global core the
|
|
211
|
+
// /cohorte-init-pipeline in project settings, but on a machine with the global core the
|
|
160
212
|
// hook usually lives (correctly, exactly once) in global settings — warning
|
|
161
213
|
// there would prescribe a re-registration that double-prompts.
|
|
162
214
|
const scopes = [
|
|
@@ -170,7 +222,7 @@ function checkHooks(projectRoot, globalDir, installMode) {
|
|
|
170
222
|
return mk('hooks', 'Gate hook', 'warn', 'gate.py not registered in project or global settings.json',
|
|
171
223
|
installMode === 'global'
|
|
172
224
|
? 'npx cohorte install --global (re-registers the hook)'
|
|
173
|
-
: '/init-pipeline (register the PreToolUse gate hook)');
|
|
225
|
+
: '/cohorte-init-pipeline (register the PreToolUse gate hook)');
|
|
174
226
|
}
|
|
175
227
|
const total = found.reduce((n, s) => n + s.regs.length, 0);
|
|
176
228
|
if (total > 1) {
|
|
@@ -192,7 +244,7 @@ function checkRetrieval(profile, projectRoot) {
|
|
|
192
244
|
if (!provider || provider === 'none' || String(provider).startsWith('<')) {
|
|
193
245
|
return mk('retrieval', 'Code retrieval', 'skip', 'provider: none');
|
|
194
246
|
}
|
|
195
|
-
// The profile alone isn't proof the provider was ever wired: /init-pipeline
|
|
247
|
+
// The profile alone isn't proof the provider was ever wired: /cohorte-init-pipeline
|
|
196
248
|
// registers it at project scope in .mcp.json. Verify the entry exists on disk;
|
|
197
249
|
// live connectivity still needs a session — note it, don't fake green.
|
|
198
250
|
const mcp = readJson(path.join(projectRoot, '.mcp.json'));
|
|
@@ -201,10 +253,10 @@ function checkRetrieval(profile, projectRoot) {
|
|
|
201
253
|
if (!wired) {
|
|
202
254
|
return mk('retrieval', 'Code retrieval', 'warn',
|
|
203
255
|
`profile says provider: ${provider} but .mcp.json has no matching server entry`,
|
|
204
|
-
'/init-pipeline or /update-pipeline (re-wire the retrieval provider)');
|
|
256
|
+
'/cohorte-init-pipeline or /cohorte-update-pipeline (re-wire the retrieval provider)');
|
|
205
257
|
}
|
|
206
258
|
return mk('retrieval', 'Code retrieval', 'ok',
|
|
207
|
-
`provider: ${provider} — registered in .mcp.json (connectivity needs /doctor in-session)`);
|
|
259
|
+
`provider: ${provider} — registered in .mcp.json (connectivity needs /cohorte-doctor in-session)`);
|
|
208
260
|
}
|
|
209
261
|
|
|
210
262
|
function checkDesign(profile, projectRoot) {
|
|
@@ -231,14 +283,14 @@ function checkIsolation(profile, projectRoot) {
|
|
|
231
283
|
}
|
|
232
284
|
if (problems.length) {
|
|
233
285
|
return mk('isolation', 'Isolation', 'warn', problems.join('; '),
|
|
234
|
-
'/init-pipeline (re-render the isolation scripts)');
|
|
286
|
+
'/cohorte-init-pipeline (re-render the isolation scripts)');
|
|
235
287
|
}
|
|
236
288
|
return mk('isolation', 'Isolation', 'ok', 'feature scripts rendered (worktree state not checked here)');
|
|
237
289
|
}
|
|
238
290
|
|
|
239
291
|
// Workflow variants (review/audit/refactor as deterministic multi-agent runs) are opt-in;
|
|
240
292
|
// the conversational commands stay the default path, so nothing here is ever 'bad'.
|
|
241
|
-
// Whether the session has workflows ENABLED needs a live Claude session — /doctor
|
|
293
|
+
// Whether the session has workflows ENABLED needs a live Claude session — /cohorte-doctor
|
|
242
294
|
// in-session checks that; here we only check what's on disk.
|
|
243
295
|
function checkWorkflows(projectRoot, globalDir, installMode) {
|
|
244
296
|
if (installMode === 'none') return mk('workflows', 'Workflows', 'skip', 'no core installed');
|
|
@@ -266,7 +318,7 @@ function checkWorkflows(projectRoot, globalDir, installMode) {
|
|
|
266
318
|
}
|
|
267
319
|
return mk('workflows', 'Workflows', 'ok',
|
|
268
320
|
'scripts + profile-reader installed — opt-in per run; needs Claude Code ≥ 2.1.154 with ' +
|
|
269
|
-
'workflows enabled (run /doctor in-session to check the live half)');
|
|
321
|
+
'workflows enabled (run /cohorte-doctor in-session to check the live half)');
|
|
270
322
|
}
|
|
271
323
|
|
|
272
324
|
function scanSpecs(projectRoot) {
|
|
@@ -281,12 +333,16 @@ function scanSpecs(projectRoot) {
|
|
|
281
333
|
const body = fm ? fm[1] : '';
|
|
282
334
|
const get = k => { const m = body.match(new RegExp(`^${k}:\\s*(.*)$`, 'm')); return m ? m[1].trim() : null; };
|
|
283
335
|
const status = get('status');
|
|
336
|
+
// The loop driver's resume state, when a /cohorte-loop is (or was) running on this spec.
|
|
337
|
+
const pass = parseInt(get('loop_pass'), 10);
|
|
338
|
+
const phase = get('loop_phase');
|
|
284
339
|
specs.push({
|
|
285
340
|
file: f,
|
|
286
341
|
id: get('feature_id') || f.replace(/\.md$/, ''),
|
|
287
342
|
title: get('title'),
|
|
288
343
|
status: status ? status.split('#')[0].trim() : null,
|
|
289
344
|
branch: get('branch'),
|
|
345
|
+
loop: pass > 0 ? { pass, phase: phase && phase !== 'done' ? phase : null } : null,
|
|
290
346
|
});
|
|
291
347
|
}
|
|
292
348
|
return specs;
|
|
@@ -320,6 +376,7 @@ async function state({ projectRoot, globalDir, cliVersion }) {
|
|
|
320
376
|
checkProfile(profile, pipelineMd != null),
|
|
321
377
|
checkAgents(profile, projectRoot),
|
|
322
378
|
checkGate(profile, projectRoot),
|
|
379
|
+
checkLocalArtifacts(projectRoot),
|
|
323
380
|
checkHooks(projectRoot, globalDir, v.installMode),
|
|
324
381
|
checkRetrieval(profile, projectRoot),
|
|
325
382
|
checkDesign(profile, projectRoot),
|
|
@@ -143,8 +143,8 @@ function runAction(req, res, body, { pkgRoot }) {
|
|
|
143
143
|
function runClaude(req, res, body) {
|
|
144
144
|
const project = body.project ? path.resolve(body.project) : null;
|
|
145
145
|
const command = String(body.command || '');
|
|
146
|
-
if (!/^\/(init-pipeline|update-pipeline|audit)$/.test(command)) {
|
|
147
|
-
return sendJson(res, 400, { error: 'unsupported command (only /init-pipeline, /update-pipeline or /audit)' });
|
|
146
|
+
if (!/^\/cohorte-(init-pipeline|update-pipeline|audit)$/.test(command)) {
|
|
147
|
+
return sendJson(res, 400, { error: 'unsupported command (only /cohorte-init-pipeline, /cohorte-update-pipeline or /cohorte-audit)' });
|
|
148
148
|
}
|
|
149
149
|
if (!project || !fs.existsSync(project)) {
|
|
150
150
|
return sendJson(res, 400, { error: 'project path not found' });
|
|
@@ -174,7 +174,7 @@ function runClaude(req, res, body) {
|
|
|
174
174
|
// Full project reset: back up the project's pipeline footprint (.claude/, PIPELINE.md, and
|
|
175
175
|
// optionally specs/) to .claude.bak-<ts>, remove it, then reinstall a fresh BUNDLED core (or,
|
|
176
176
|
// for global-mode projects, leave the shared ~/.claude core untouched). Never touches ~/.claude.
|
|
177
|
-
// Streams progress; ends with __EXIT__ <code>. The profile is regenerated by /init-pipeline after.
|
|
177
|
+
// Streams progress; ends with __EXIT__ <code>. The profile is regenerated by /cohorte-init-pipeline after.
|
|
178
178
|
function runReset(req, res, body, { pkgRoot, globalDir }) {
|
|
179
179
|
const project = body.project ? path.resolve(body.project) : null;
|
|
180
180
|
const purgeSpecs = !!body.purgeSpecs;
|
|
@@ -230,14 +230,14 @@ function runReset(req, res, body, { pkgRoot, globalDir }) {
|
|
|
230
230
|
child.stdout.on('data', d => res.write(d));
|
|
231
231
|
child.stderr.on('data', d => res.write(d));
|
|
232
232
|
child.on('close', code => {
|
|
233
|
-
log('\n✔ Reset complete. Now run /init-pipeline in Claude Code to regenerate PIPELINE.md + the surface agents.');
|
|
233
|
+
log('\n✔ Reset complete. Now run /cohorte-init-pipeline in Claude Code to regenerate PIPELINE.md + the surface agents.');
|
|
234
234
|
done(code);
|
|
235
235
|
});
|
|
236
236
|
child.on('error', err => { log(`\nspawn error: ${err.message}`); done(1); });
|
|
237
237
|
req.on('close', () => { try { child.kill(); } catch { /* already gone */ } });
|
|
238
238
|
} else {
|
|
239
239
|
log('\n✔ Reset complete. The shared global core stays installed in ~/.claude.');
|
|
240
|
-
log('Now run /init-pipeline in Claude Code to regenerate this project\'s PIPELINE.md + agents.');
|
|
240
|
+
log('Now run /cohorte-init-pipeline in Claude Code to regenerate this project\'s PIPELINE.md + agents.');
|
|
241
241
|
done(0);
|
|
242
242
|
}
|
|
243
243
|
} catch (e) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
// Read a project's `.claude/pipeline-metrics.jsonl` (one line per phase batch, appended by
|
|
3
|
-
// /build, /review and /fix) and aggregate it per feature: wall-clock per phase, fix
|
|
3
|
+
// /cohorte-build, /cohorte-review and /cohorte-fix) and aggregate it per feature: wall-clock per phase, fix
|
|
4
4
|
// rounds, and per-surface results. Dependency-free; a missing file is simply "no data yet".
|
|
5
5
|
//
|
|
6
6
|
// Two line formats coexist in the file:
|
package/install.ps1
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# .\install.ps1 -Update -Global
|
|
15
15
|
#
|
|
16
16
|
# Per-project install copies the core into <target>\.claude; global install copies it once
|
|
17
|
-
# into ~\.claude and registers the gate hook there. Either way you then run `/init-pipeline`
|
|
17
|
+
# into ~\.claude and registers the gate hook there. Either way you then run `/cohorte-init-pipeline`
|
|
18
18
|
# in each repo to generate PIPELINE.md + render the surface agents. Update refreshes ONLY the
|
|
19
19
|
# stack-agnostic files; generated profiles, rendered agents, gate-config.json and any project
|
|
20
20
|
# settings.json are left untouched.
|
|
@@ -152,6 +152,7 @@ try {
|
|
|
152
152
|
Copy-Item (Join-Path $src 'scripts\telemetry-send.sh') (Join-Path $dest 'pipeline\scripts') -Force
|
|
153
153
|
Copy-Item (Join-Path $src 'scripts\preflight.sh') (Join-Path $dest 'pipeline\scripts') -Force
|
|
154
154
|
Copy-Item (Join-Path $src 'scripts\loop.sh') (Join-Path $dest 'pipeline\scripts') -Force
|
|
155
|
+
Copy-Item (Join-Path $src 'scripts\loop-detach.sh') (Join-Path $dest 'pipeline\scripts') -Force
|
|
155
156
|
Copy-Item (Join-Path $src 'core\agents\implementer.template.md') (Join-Path $dest 'pipeline') -Force
|
|
156
157
|
if (Test-Path (Join-Path $src 'CHANGELOG.md')) { Copy-Item (Join-Path $src 'CHANGELOG.md') (Join-Path $dest 'pipeline') -Force }
|
|
157
158
|
[System.IO.File]::WriteAllText((Join-Path $dest 'pipeline\VERSION'), "$ver`n", [System.Text.UTF8Encoding]::new($false))
|
|
@@ -193,6 +194,22 @@ try {
|
|
|
193
194
|
# 1.5.0 removed the /smoke phase; copy-over never deletes, so scrub the orphan agent.
|
|
194
195
|
Remove-Item -LiteralPath (Join-Path $dest 'agents\smoke.md') -Force -ErrorAction SilentlyContinue
|
|
195
196
|
Remove-Item -LiteralPath (Join-Path $dest 'commands\smoke.md') -Force -ErrorAction SilentlyContinue
|
|
197
|
+
# 1.4.0 removed /cycle and its workflow — and no installer ever scrubbed them, so every
|
|
198
|
+
# install since has kept offering a command that dispatches a workflow whose phases were
|
|
199
|
+
# later deleted. A dead command is worse than a missing one: the model can still fire it.
|
|
200
|
+
Remove-Item -LiteralPath (Join-Path $dest 'commands\cycle.md') -Force -ErrorAction SilentlyContinue
|
|
201
|
+
Remove-Item -LiteralPath (Join-Path $dest 'workflows\cycle.js') -Force -ErrorAction SilentlyContinue
|
|
202
|
+
# 1.6.0 renamed /loop → /drive: Claude Code's own built-in /loop shadowed ours, so a leftover
|
|
203
|
+
# commands\loop.md is a command the user can never reach — scrub it rather than leave a decoy.
|
|
204
|
+
Remove-Item -LiteralPath (Join-Path $dest 'commands\loop.md') -Force -ErrorAction SilentlyContinue
|
|
205
|
+
# 2.0.0 prefixed every command with `cohorte-`, which ends the shadowing problem for good.
|
|
206
|
+
# Copy-Item never deletes, so all 13 bare names would survive an upgrade as decoys — and a
|
|
207
|
+
# stale /build is the worst kind: it still dispatches implementers, from a 1.x command file
|
|
208
|
+
# that knows nothing of this core's contract. /drive goes too (it became /cohorte-loop).
|
|
209
|
+
foreach ($c in @('align-ds','audit','brainstorm','build','doctor','drive','fix',
|
|
210
|
+
'init-pipeline','refactor','review','ship','spec','update-pipeline')) {
|
|
211
|
+
Remove-Item -LiteralPath (Join-Path $dest "commands\$c.md") -Force -ErrorAction SilentlyContinue
|
|
212
|
+
}
|
|
196
213
|
# 0.1.19 split the bi-mode questionnaire-researcher into research-agent + questionnaire-architect;
|
|
197
214
|
# copy-over never deletes, so scrub the retired agent lest a dead subagent_type linger.
|
|
198
215
|
Remove-Item -LiteralPath (Join-Path $dest 'agents\questionnaire-researcher.md') -Force -ErrorAction SilentlyContinue
|
|
@@ -216,8 +233,8 @@ try {
|
|
|
216
233
|
|
|
217
234
|
# pipeline capability config is USER-level (vault, Notion DB, kanban boards) — it lives in
|
|
218
235
|
# the user's .claude regardless of install scope. Seed only if neither the consolidated nor
|
|
219
|
-
# the legacy copy exists. Non-interactive here: seeds disabled defaults; /init-pipeline +
|
|
220
|
-
# /update-pipeline wire it (npx's installer offers a quick interview instead).
|
|
236
|
+
# the legacy copy exists. Non-interactive here: seeds disabled defaults; /cohorte-init-pipeline +
|
|
237
|
+
# /cohorte-update-pipeline wire it (npx's installer offers a quick interview instead).
|
|
221
238
|
function Initialize-Config {
|
|
222
239
|
$userClaude = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME '.claude' }
|
|
223
240
|
$cfg = Join-Path $userClaude 'cohorte.config.yaml'
|
|
@@ -229,11 +246,11 @@ try {
|
|
|
229
246
|
Write-Host " - kept your existing $cfg"
|
|
230
247
|
} elseif ($legacy) {
|
|
231
248
|
Write-Host " - found legacy $legacy — kept as-is (read as a fallback)."
|
|
232
|
-
Write-Host " Run /update-pipeline to migrate it into cohorte.config.yaml + wire the kanban."
|
|
249
|
+
Write-Host " Run /cohorte-update-pipeline to migrate it into cohorte.config.yaml + wire the kanban."
|
|
233
250
|
} else {
|
|
234
251
|
New-Item -ItemType Directory -Force -Path $userClaude | Out-Null
|
|
235
252
|
Copy-Item (Join-Path $src 'profile\cohorte.config.template.yaml') $cfg
|
|
236
|
-
Write-Host " - seeded $cfg (disabled defaults — enable via /init-pipeline or /update-pipeline)"
|
|
253
|
+
Write-Host " - seeded $cfg (disabled defaults — enable via /cohorte-init-pipeline or /cohorte-update-pipeline)"
|
|
237
254
|
}
|
|
238
255
|
}
|
|
239
256
|
|
|
@@ -310,24 +327,24 @@ try {
|
|
|
310
327
|
OK pipeline core installed globally into $dest (version $ver)
|
|
311
328
|
gate hook: $hookState (reads each repo's .claude/gate-config.json; silent where absent)
|
|
312
329
|
|
|
313
|
-
The commands (/init-pipeline, /brainstorm, /build ...) and the review/release agents are now
|
|
330
|
+
The commands (/cohorte-init-pipeline, /cohorte-brainstorm, /cohorte-build ...) and the review/release agents are now
|
|
314
331
|
available in EVERY project on this machine — nothing is copied per repo.
|
|
315
332
|
|
|
316
333
|
Per repo:
|
|
317
334
|
1. Open the project in Claude Code.
|
|
318
|
-
2. Run /init-pipeline — it generates PIPELINE.md, renders the surface agents, writes
|
|
335
|
+
2. Run /cohorte-init-pipeline — it generates PIPELINE.md, renders the surface agents, writes
|
|
319
336
|
.claude/gate-config.json, and drops a committed .claude/pipeline.json pointer so
|
|
320
337
|
teammates know to install the global core ($repoUrl).
|
|
321
|
-
3. Commit PIPELINE.md + .claude/, then /brainstorm to start a feature.
|
|
338
|
+
3. Commit PIPELINE.md + .claude/, then /cohorte-brainstorm to start a feature.
|
|
322
339
|
|
|
323
|
-
Code retrieval (Serena — the default provider /init-pipeline wires per repo):
|
|
340
|
+
Code retrieval (Serena — the default provider /cohorte-init-pipeline wires per repo):
|
|
324
341
|
uv tool install -p 3.13 serena-agent # once per machine
|
|
325
342
|
Make sure the uv tools dir is on PATH (uv tool update-shell) — otherwise the
|
|
326
343
|
registered MCP server silently fails to start.
|
|
327
344
|
|
|
328
345
|
Global kanban config, user-scoped — optional:
|
|
329
346
|
· One consolidated file: ~/.claude/cohorte.config.yaml (don't hand-edit it).
|
|
330
|
-
· /init-pipeline (new project) and /update-pipeline (existing) wire it for you: creating +
|
|
347
|
+
· /cohorte-init-pipeline (new project) and /cohorte-update-pipeline (existing) wire it for you: creating +
|
|
331
348
|
syncing an Obsidian kanban board of the pipeline in your shared vault.
|
|
332
349
|
"@
|
|
333
350
|
return
|
|
@@ -348,11 +365,11 @@ OK pipeline core installed into $dest (version $ver)
|
|
|
348
365
|
|
|
349
366
|
Next:
|
|
350
367
|
1. Open the project in Claude Code.
|
|
351
|
-
2. Run /init-pipeline — it detects your stack, asks the gaps, and generates
|
|
368
|
+
2. Run /cohorte-init-pipeline — it detects your stack, asks the gaps, and generates
|
|
352
369
|
PIPELINE.md + renders one implementer agent per surface.
|
|
353
|
-
3. Commit PIPELINE.md, then /brainstorm to start a feature.
|
|
370
|
+
3. Commit PIPELINE.md, then /cohorte-brainstorm to start a feature.
|
|
354
371
|
|
|
355
|
-
Code retrieval (Serena — the default provider /init-pipeline wires per repo):
|
|
372
|
+
Code retrieval (Serena — the default provider /cohorte-init-pipeline wires per repo):
|
|
356
373
|
uv tool install -p 3.13 serena-agent # once per machine
|
|
357
374
|
Make sure the uv tools dir is on PATH (uv tool update-shell) — otherwise the
|
|
358
375
|
registered MCP server silently fails to start.
|
|
@@ -370,7 +387,7 @@ Prefer one shared core across all your repos? Re-run with -Global.
|
|
|
370
387
|
Write-Host @"
|
|
371
388
|
|
|
372
389
|
OK core refreshed to $ver. Your PIPELINE.md, rendered surface agents, gate-config.json and
|
|
373
|
-
settings.json were left as-is. Re-run /init-pipeline if your stack changed.
|
|
390
|
+
settings.json were left as-is. Re-run /cohorte-init-pipeline if your stack changed.
|
|
374
391
|
"@
|
|
375
392
|
}
|
|
376
393
|
} finally {
|
package/install.sh
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
# sh install.sh --update --global
|
|
17
17
|
#
|
|
18
18
|
# Per-project install copies the core into <target>/.claude; global install copies it once
|
|
19
|
-
# into ~/.claude and registers the gate hook there. Either way you then run `/init-pipeline`
|
|
19
|
+
# into ~/.claude and registers the gate hook there. Either way you then run `/cohorte-init-pipeline`
|
|
20
20
|
# in each repo to generate PIPELINE.md + render the surface agents. Update refreshes ONLY the
|
|
21
21
|
# stack-agnostic files; generated profiles, rendered agents, gate-config.json and any project
|
|
22
22
|
# settings.json are left untouched.
|
|
@@ -108,8 +108,10 @@ copy_core() {
|
|
|
108
108
|
cp "$src/scripts/telemetry-send.sh" "$dest/pipeline/scripts/"
|
|
109
109
|
cp "$src/scripts/preflight.sh" "$dest/pipeline/scripts/"
|
|
110
110
|
cp "$src/scripts/loop.sh" "$dest/pipeline/scripts/"
|
|
111
|
+
cp "$src/scripts/loop-detach.sh" "$dest/pipeline/scripts/"
|
|
111
112
|
chmod +x "$dest/pipeline/scripts/kanban-move.sh" "$dest/pipeline/scripts/telemetry-send.sh" \
|
|
112
|
-
"$dest/pipeline/scripts/preflight.sh" "$dest/pipeline/scripts/loop.sh"
|
|
113
|
+
"$dest/pipeline/scripts/preflight.sh" "$dest/pipeline/scripts/loop.sh" \
|
|
114
|
+
"$dest/pipeline/scripts/loop-detach.sh" 2>/dev/null || true
|
|
113
115
|
cp "$src/core/agents/implementer.template.md" "$dest/pipeline/"
|
|
114
116
|
[ -f "$src/CHANGELOG.md" ] && cp "$src/CHANGELOG.md" "$dest/pipeline/"
|
|
115
117
|
printf '%s\n' "$ver" > "$dest/pipeline/VERSION"
|
|
@@ -154,6 +156,21 @@ copy_fixed_agents() {
|
|
|
154
156
|
"$dest/agents/"
|
|
155
157
|
# 1.5.0 removed the /smoke phase; copy-over never deletes, so scrub the orphan agent.
|
|
156
158
|
rm -f "$dest/agents/smoke.md" "$dest/commands/smoke.md"
|
|
159
|
+
# 1.4.0 removed /cycle and its workflow — and no installer ever scrubbed them, so every
|
|
160
|
+
# install since has kept offering a command that dispatches a workflow whose phases were
|
|
161
|
+
# later deleted. A dead command is worse than a missing one: the model can still fire it.
|
|
162
|
+
rm -f "$dest/commands/cycle.md" "$dest/workflows/cycle.js"
|
|
163
|
+
# 1.6.0 renamed /loop → /drive: Claude Code's own built-in /loop shadowed ours, so a leftover
|
|
164
|
+
# commands/loop.md is a command the user can never reach — scrub it rather than leave a decoy.
|
|
165
|
+
rm -f "$dest/commands/loop.md"
|
|
166
|
+
# 2.0.0 prefixed every command with `cohorte-`, which ends the shadowing problem for good.
|
|
167
|
+
# Copy-over never deletes, so all 13 bare names would survive an upgrade as decoys — and a
|
|
168
|
+
# stale /build is the worst kind: it still dispatches implementers, from a 1.x command file
|
|
169
|
+
# that knows nothing of this core's contract. /drive goes too (it became /cohorte-loop).
|
|
170
|
+
for c in align-ds audit brainstorm build doctor drive fix init-pipeline \
|
|
171
|
+
refactor review ship spec update-pipeline; do
|
|
172
|
+
rm -f "$dest/commands/$c.md"
|
|
173
|
+
done
|
|
157
174
|
# 0.1.19 split the bi-mode questionnaire-researcher into research-agent + questionnaire-architect;
|
|
158
175
|
# copy-over never deletes, so scrub the retired agent lest a dead subagent_type linger.
|
|
159
176
|
rm -f "$dest/agents/questionnaire-researcher.md"
|
|
@@ -179,7 +196,7 @@ scrub_research_questionnaire() {
|
|
|
179
196
|
# pipeline capability config is USER-level (vault, Notion DB, kanban boards) — it lives in
|
|
180
197
|
# ~/.claude regardless of install scope. Seed it only if neither the consolidated nor the
|
|
181
198
|
# legacy copy exists. This piped installer is non-interactive: it seeds disabled defaults;
|
|
182
|
-
# /init-pipeline + /update-pipeline wire it (npx's installer offers a quick interview instead).
|
|
199
|
+
# /cohorte-init-pipeline + /cohorte-update-pipeline wire it (npx's installer offers a quick interview instead).
|
|
183
200
|
seed_config() {
|
|
184
201
|
base="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
185
202
|
cfg="$base/cohorte.config.yaml"
|
|
@@ -191,11 +208,11 @@ seed_config() {
|
|
|
191
208
|
echo " · kept your existing $cfg"
|
|
192
209
|
elif [ -n "$legacy" ]; then
|
|
193
210
|
echo " · found legacy $legacy — kept as-is (read as a fallback)."
|
|
194
|
-
echo " Run /update-pipeline to migrate it into cohorte.config.yaml + wire the kanban."
|
|
211
|
+
echo " Run /cohorte-update-pipeline to migrate it into cohorte.config.yaml + wire the kanban."
|
|
195
212
|
else
|
|
196
213
|
mkdir -p "$base"
|
|
197
214
|
cp "$src/profile/cohorte.config.template.yaml" "$cfg"
|
|
198
|
-
echo " · seeded $cfg (disabled defaults — enable via /init-pipeline or /update-pipeline)"
|
|
215
|
+
echo " · seeded $cfg (disabled defaults — enable via /cohorte-init-pipeline or /cohorte-update-pipeline)"
|
|
199
216
|
fi
|
|
200
217
|
}
|
|
201
218
|
|
|
@@ -283,24 +300,24 @@ if [ "$scope" = "global" ]; then
|
|
|
283
300
|
✓ pipeline core installed globally into $dest (version $ver)
|
|
284
301
|
gate hook: $hook_state (reads each repo's .claude/gate-config.json; silent where absent)
|
|
285
302
|
|
|
286
|
-
The commands (/init-pipeline, /brainstorm, /build …) and the review/release agents are now
|
|
303
|
+
The commands (/cohorte-init-pipeline, /cohorte-brainstorm, /cohorte-build …) and the review/release agents are now
|
|
287
304
|
available in EVERY project on this machine — nothing is copied per repo.
|
|
288
305
|
|
|
289
306
|
Per repo:
|
|
290
307
|
1. Open the project in Claude Code.
|
|
291
|
-
2. Run /init-pipeline — it generates PIPELINE.md, renders the surface agents, writes
|
|
308
|
+
2. Run /cohorte-init-pipeline — it generates PIPELINE.md, renders the surface agents, writes
|
|
292
309
|
.claude/gate-config.json, and drops a committed .claude/pipeline.json pointer so
|
|
293
310
|
teammates know to install the global core ($REPO_URL).
|
|
294
|
-
3. Commit PIPELINE.md + .claude/, then /brainstorm to start a feature.
|
|
311
|
+
3. Commit PIPELINE.md + .claude/, then /cohorte-brainstorm to start a feature.
|
|
295
312
|
|
|
296
|
-
Code retrieval (Serena — the default provider /init-pipeline wires per repo):
|
|
313
|
+
Code retrieval (Serena — the default provider /cohorte-init-pipeline wires per repo):
|
|
297
314
|
uv tool install -p 3.13 serena-agent # once per machine
|
|
298
315
|
Make sure ~/.local/bin is on PATH (uv tool update-shell) — otherwise the
|
|
299
316
|
registered MCP server silently fails to start.
|
|
300
317
|
|
|
301
318
|
Global kanban config, user-scoped — optional:
|
|
302
319
|
· One consolidated file: ~/.claude/cohorte.config.yaml (don't hand-edit it).
|
|
303
|
-
· /init-pipeline (new project) and /update-pipeline (existing) wire it for you: creating +
|
|
320
|
+
· /cohorte-init-pipeline (new project) and /cohorte-update-pipeline (existing) wire it for you: creating +
|
|
304
321
|
syncing an Obsidian kanban board of the pipeline in your shared vault.
|
|
305
322
|
EOF
|
|
306
323
|
exit 0
|
|
@@ -319,11 +336,11 @@ if [ "$mode" = "install" ]; then
|
|
|
319
336
|
|
|
320
337
|
Next:
|
|
321
338
|
1. Open the project in Claude Code.
|
|
322
|
-
2. Run /init-pipeline — it detects your stack, asks the gaps, and generates
|
|
339
|
+
2. Run /cohorte-init-pipeline — it detects your stack, asks the gaps, and generates
|
|
323
340
|
PIPELINE.md + renders one implementer agent per surface.
|
|
324
|
-
3. Commit PIPELINE.md, then /brainstorm to start a feature.
|
|
341
|
+
3. Commit PIPELINE.md, then /cohorte-brainstorm to start a feature.
|
|
325
342
|
|
|
326
|
-
Code retrieval (Serena — the default provider /init-pipeline wires per repo):
|
|
343
|
+
Code retrieval (Serena — the default provider /cohorte-init-pipeline wires per repo):
|
|
327
344
|
uv tool install -p 3.13 serena-agent # once per machine
|
|
328
345
|
Make sure ~/.local/bin is on PATH (uv tool update-shell) — otherwise the
|
|
329
346
|
registered MCP server silently fails to start.
|
|
@@ -339,6 +356,6 @@ else
|
|
|
339
356
|
cat <<EOF
|
|
340
357
|
|
|
341
358
|
✓ core refreshed to $ver. Your PIPELINE.md, rendered surface agents, gate-config.json and
|
|
342
|
-
settings.json were left as-is. Re-run /init-pipeline if your stack changed.
|
|
359
|
+
settings.json were left as-is. Re-run /cohorte-init-pipeline if your stack changed.
|
|
343
360
|
EOF
|
|
344
361
|
fi
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cohorte",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Portable, stack-agnostic multi-agent development pipeline for Claude Code — install the core, run /init-pipeline, and it adapts to your project's stack.",
|
|
3
|
+
"version": "2.0.0",
|
|
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"
|
|
7
7
|
},
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# PIPELINE.md — <PROJECT_NAME> profile
|
|
2
2
|
|
|
3
|
-
> **Generated by `/init-pipeline`.** This is the project profile: the single place the
|
|
3
|
+
> **Generated by `/cohorte-init-pipeline`.** This is the project profile: the single place the
|
|
4
4
|
> portable multi-agent pipeline reads for everything stack-specific. The pipeline core
|
|
5
5
|
> under `.claude/` is generic and references THIS file by section — keep project facts
|
|
6
|
-
> here, never hardcoded into agent prompts. Kept current by `/update-pipeline` (reconcile:
|
|
6
|
+
> here, never hardcoded into agent prompts. Kept current by `/cohorte-update-pipeline` (reconcile:
|
|
7
7
|
> new fields are topped up, your values never overwritten). Rendered agent files are
|
|
8
8
|
> regenerated from this profile on every reconcile — customize agents through §Conventions
|
|
9
9
|
> here, never by editing the agent files. Free-form notes belong in `CLAUDE.md`.
|
|
@@ -36,15 +36,15 @@ repo:
|
|
|
36
36
|
# wired. serena = live LSP symbol navigation (default, no index to maintain);
|
|
37
37
|
# graphify = persistent tree-sitter knowledge graph over code + docs (needs an
|
|
38
38
|
# index step + rescans); none = agents fall back to Grep/Glob/Read.
|
|
39
|
-
# Registered by /init-pipeline as a project-scope MCP server (committed .mcp.json).
|
|
39
|
+
# Registered by /cohorte-init-pipeline as a project-scope MCP server (committed .mcp.json).
|
|
40
40
|
retrieval:
|
|
41
41
|
provider: serena # serena | graphify | none
|
|
42
42
|
|
|
43
43
|
# ── surfaces ────────────────────────────────────────────────────────────────
|
|
44
|
-
# One entry per independently-implemented code area. /build dispatches ONE
|
|
44
|
+
# One entry per independently-implemented code area. /cohorte-build dispatches ONE
|
|
45
45
|
# implementer agent per surface, in parallel. Each surface is rendered by
|
|
46
|
-
# /init-pipeline into its own agent file (agent field) with the tools listed.
|
|
47
|
-
# This list GROWS automatically: /build §1.5 adds a surface (and renders its
|
|
46
|
+
# /cohorte-init-pipeline into its own agent file (agent field) with the tools listed.
|
|
47
|
+
# This list GROWS automatically: /cohorte-build §1.5 adds a surface (and renders its
|
|
48
48
|
# agent) when a spec touches a tree no surface owns, or splits a bottleneck
|
|
49
49
|
# surface into specialized sub-surfaces. Keep one owner per tree; shared code
|
|
50
50
|
# (routing/state/DS) is its own single-owner surface. See SCHEMA.md §Specialization.
|
|
@@ -61,7 +61,7 @@ surfaces:
|
|
|
61
61
|
test_cmd: pnpm --filter api test
|
|
62
62
|
# Bridled variants — what agents actually RUN (dot reporter / failures-only /
|
|
63
63
|
# --quiet), so a green run costs lines, not pages. "" ⇒ callers fall back to
|
|
64
|
-
# `<cmd> 2>&1 | tail -40`. /init-pipeline asks for these; never store a bare
|
|
64
|
+
# `<cmd> 2>&1 | tail -40`. /cohorte-init-pipeline asks for these; never store a bare
|
|
65
65
|
# `pnpm test` as the thing agents execute.
|
|
66
66
|
test_quiet_cmd: pnpm --filter api test --reporter=dot
|
|
67
67
|
lint_cmd: pnpm --filter api lint
|
|
@@ -90,7 +90,7 @@ surfaces:
|
|
|
90
90
|
|
|
91
91
|
# ── contract (the only cross-surface sync channel) ──────────────────────────
|
|
92
92
|
contract:
|
|
93
|
-
enabled: <true | false> # false ⇒ no shared contract; skip §2 of /build
|
|
93
|
+
enabled: <true | false> # false ⇒ no shared contract; skip §2 of /cohorte-build
|
|
94
94
|
mechanism: <shared-types-zod | openapi | protobuf | json-schema | none>
|
|
95
95
|
path: packages/shared-types/src # where <feature_id>.<ext> is authored
|
|
96
96
|
ext: ts
|
|
@@ -106,7 +106,7 @@ commands:
|
|
|
106
106
|
format: pnpm format
|
|
107
107
|
typecheck: pnpm check-types
|
|
108
108
|
test: pnpm test
|
|
109
|
-
test_quiet: pnpm test --reporter=dot # bridled variant — what the /review preflight runs
|
|
109
|
+
test_quiet: pnpm test --reporter=dot # bridled variant — what the /cohorte-review preflight runs
|
|
110
110
|
# migration commands — omit / leave "" if the project has no DB migrations
|
|
111
111
|
migrate: "cd apps/api && node ace migration:run"
|
|
112
112
|
make_migration: "cd apps/api && node ace make:migration"
|
|
@@ -119,11 +119,11 @@ rbac:
|
|
|
119
119
|
|
|
120
120
|
# ── design system (optional) ────────────────────────────────────────────────
|
|
121
121
|
design:
|
|
122
|
-
enabled: <true | false> # false ⇒ /align-ds + design gates are no-ops
|
|
122
|
+
enabled: <true | false> # false ⇒ /cohorte-align-ds + design gates are no-ops
|
|
123
123
|
provider: <claude-design | figma | none>
|
|
124
124
|
design_system_project: <uuid-or-id> # the UI-kit source of truth
|
|
125
125
|
design_project: none # legacy fallback for bare-filename design_files only; keep `none` — new specs use full links that carry their own project, so nothing here goes stale on a DS rebuild
|
|
126
|
-
snapshot_dir: apps/web/design-reference # committed DS snapshot for /align-ds diff
|
|
126
|
+
snapshot_dir: apps/web/design-reference # committed DS snapshot for /cohorte-align-ds diff
|
|
127
127
|
direction: design-to-code # NEVER push code→design for a curated DS
|
|
128
128
|
ui_kit_path: apps/web/src/components/ui
|
|
129
129
|
tokens_path: apps/web/src/index.css
|
|
@@ -166,7 +166,8 @@ gate:
|
|
|
166
166
|
- "docker compose"
|
|
167
167
|
# Phase gate: review dispatches require a fresh `.claude/preflight.ok` stamp,
|
|
168
168
|
# written by pipeline/scripts/preflight.sh when typecheck+lint+tests are green —
|
|
169
|
-
# gate.py "ask"s the dispatch when the stamp is missing, stale, or
|
|
169
|
+
# gate.py "ask"s the dispatch when the stamp is missing, stale, or the code changed since.
|
|
170
|
+
# The stamp is local: keep `.claude/preflight.ok` gitignored, never committed.
|
|
170
171
|
preflight:
|
|
171
172
|
enabled: true
|
|
172
173
|
agents: [review] # subagent_types the stamp gates
|
|
@@ -176,7 +177,7 @@ gate:
|
|
|
176
177
|
|
|
177
178
|
> **Note.** The **kanban** board link is **user-scoped** — NOT configured here. Its facts live in the
|
|
178
179
|
> consolidated `~/.claude/cohorte.config.yaml`, read at runtime (the kanban board is keyed by this
|
|
179
|
-
> profile's `name`). This project profile only governs the dev pipeline (`/brainstorm…/ship`).
|
|
180
|
+
> profile's `name`). This project profile only governs the dev pipeline (`/cohorte-brainstorm…/cohorte-ship`).
|
|
180
181
|
|
|
181
182
|
---
|
|
182
183
|
|
|
@@ -205,18 +206,18 @@ gate:
|
|
|
205
206
|
|
|
206
207
|
---
|
|
207
208
|
|
|
208
|
-
## Design brief note (feeds `/spec` §8 and Claude Design)
|
|
209
|
+
## Design brief note (feeds `/cohorte-spec` §8 and Claude Design)
|
|
209
210
|
|
|
210
211
|
- <design system name + primitives to use · mobile-first · copy language · brand/theming constraints>
|
|
211
212
|
- A feature lists its design pages in the spec front-matter `design_files` as **full links** of the form
|
|
212
213
|
`https://claude.ai/design/p/<projectId>?file=<file>` — each carries its own project (`/p/<projectId>`)
|
|
213
214
|
and page (`?file=`), so an agent extracts both and reads it via `DesignSync get_file(<projectId>,
|
|
214
|
-
<file>)`. Paste the link at `/build`'s design gate. No stored project id ⇒ a design-system rebuild
|
|
215
|
+
<file>)`. Paste the link at `/cohorte-build`'s design gate. No stored project id ⇒ a design-system rebuild
|
|
215
216
|
(new project) just means pasting the new links. (Bare file names are legacy, resolved in `design_project`.)
|
|
216
217
|
|
|
217
218
|
---
|
|
218
219
|
|
|
219
|
-
## Personas for `/brainstorm` (optional — the panel)
|
|
220
|
+
## Personas for `/cohorte-brainstorm` (optional — the panel)
|
|
220
221
|
|
|
221
222
|
<Panel members with a job AND a personality who challenge the idea from their angle and
|
|
222
223
|
disagree with each other. Include a role-coverage guard if `rbac.enabled`. Example:>
|