cohorte 2.10.1 → 3.0.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/assets/manifest.json +193 -0
- package/assets/migrations/config/README.md +12 -0
- package/assets/migrations/state/0001_init.sql +154 -0
- package/assets/prompts/README.md +11 -0
- package/assets/prompts/agents/fixer.md +7 -0
- package/assets/prompts/agents/implementer.md +12 -0
- package/assets/prompts/agents/reviewer.md +8 -0
- package/assets/prompts/agents/security-reviewer.md +7 -0
- package/assets/prompts/phases/build.md +5 -0
- package/assets/prompts/phases/review.md +5 -0
- package/assets/prompts/phases/test.md +5 -0
- package/assets/prompts/system.md +11 -0
- package/assets/schemas/agent-output.schema.json +225 -0
- package/assets/schemas/auth-status.schema.json +87 -0
- package/assets/schemas/command-result.schema.json +192 -0
- package/assets/schemas/commands.schema.json +952 -0
- package/assets/schemas/config.schema.json +2259 -0
- package/assets/schemas/doctor-report.schema.json +104 -0
- package/assets/schemas/events.schema.json +8514 -0
- package/assets/schemas/fake-script.schema.json +549 -0
- package/assets/schemas/inspect.schema.json +3133 -0
- package/assets/schemas/manifest.schema.json +79 -0
- package/assets/schemas/ownership.schema.json +52 -0
- package/assets/schemas/policy-verdict.schema.json +282 -0
- package/assets/schemas/project-model.schema.json +783 -0
- package/assets/schemas/project-status.schema.json +217 -0
- package/assets/schemas/reconcile-plan.schema.json +155 -0
- package/assets/schemas/run-diff.schema.json +177 -0
- package/assets/schemas/run-snapshot-manifest.schema.json +2104 -0
- package/assets/schemas/run-state.schema.json +1755 -0
- package/assets/schemas/runtime-capabilities.schema.json +1545 -0
- package/assets/schemas/sandbox-capabilities.schema.json +116 -0
- package/assets/schemas/skill.schema.json +87 -0
- package/assets/schemas/spec.schema.json +202 -0
- package/assets/schemas/tool-catalogue.schema.json +655 -0
- package/assets/schemas/trust-record.schema.json +38 -0
- package/assets/skills/README.md +9 -0
- package/assets/skills/inspect-surface.md +4 -0
- package/dist/agent-host.mjs +50 -0
- package/dist/agent-host.mjs.map +1 -0
- package/dist/bundle-manifest.json +102 -0
- package/dist/chunks/__host-DgGPyco9.mjs +18 -0
- package/dist/chunks/__host-DgGPyco9.mjs.map +1 -0
- package/dist/chunks/approve-Cv135UqI.mjs +19 -0
- package/dist/chunks/approve-Cv135UqI.mjs.map +1 -0
- package/dist/chunks/auth-BT5oVp76.mjs +132 -0
- package/dist/chunks/auth-BT5oVp76.mjs.map +1 -0
- package/dist/chunks/auth-D8rCVY33.mjs +84 -0
- package/dist/chunks/auth-D8rCVY33.mjs.map +1 -0
- package/dist/chunks/brainstorm-CU10T1tm.mjs +7 -0
- package/dist/chunks/brainstorm-CU10T1tm.mjs.map +1 -0
- package/dist/chunks/cancel-KV_cpsqS.mjs +15 -0
- package/dist/chunks/cancel-KV_cpsqS.mjs.map +1 -0
- package/dist/chunks/canonical-C8UxeEmB.mjs +57 -0
- package/dist/chunks/canonical-C8UxeEmB.mjs.map +1 -0
- package/dist/chunks/catalogue-BvwSrM_j.mjs +1549 -0
- package/dist/chunks/catalogue-BvwSrM_j.mjs.map +1 -0
- package/dist/chunks/compose-Cr0hP0qm.mjs +7303 -0
- package/dist/chunks/compose-Cr0hP0qm.mjs.map +1 -0
- package/dist/chunks/config-DefHA2Lw.mjs +96 -0
- package/dist/chunks/config-DefHA2Lw.mjs.map +1 -0
- package/dist/chunks/contract-B0ra2-bm.mjs +295 -0
- package/dist/chunks/contract-B0ra2-bm.mjs.map +1 -0
- package/dist/chunks/contract-vYbwYr0Y.mjs +123 -0
- package/dist/chunks/contract-vYbwYr0Y.mjs.map +1 -0
- package/dist/chunks/deny-ClTxSvHJ.mjs +15 -0
- package/dist/chunks/deny-ClTxSvHJ.mjs.map +1 -0
- package/dist/chunks/diff-Qw0II0sj.mjs +101 -0
- package/dist/chunks/diff-Qw0II0sj.mjs.map +1 -0
- package/dist/chunks/discover-30Ciya_0.mjs +20 -0
- package/dist/chunks/discover-30Ciya_0.mjs.map +1 -0
- package/dist/chunks/doctor-DfCrKpfT.mjs +222 -0
- package/dist/chunks/doctor-DfCrKpfT.mjs.map +1 -0
- package/dist/chunks/fix-D4Tj8RVA.mjs +18 -0
- package/dist/chunks/fix-D4Tj8RVA.mjs.map +1 -0
- package/dist/chunks/gc-BBXlBF5w.mjs +161 -0
- package/dist/chunks/gc-BBXlBF5w.mjs.map +1 -0
- package/dist/chunks/init-Okz4mlhW.mjs +30 -0
- package/dist/chunks/init-Okz4mlhW.mjs.map +1 -0
- package/dist/chunks/inspect-Buby4cQD.mjs +22 -0
- package/dist/chunks/inspect-Buby4cQD.mjs.map +1 -0
- package/dist/chunks/logs-Giq5sUau.mjs +25 -0
- package/dist/chunks/logs-Giq5sUau.mjs.map +1 -0
- package/dist/chunks/migrate-DRoFUg53.mjs +19 -0
- package/dist/chunks/migrate-DRoFUg53.mjs.map +1 -0
- package/dist/chunks/models-Btp6X2YZ.mjs +24 -0
- package/dist/chunks/models-Btp6X2YZ.mjs.map +1 -0
- package/dist/chunks/pause-BASBfKLO.mjs +16 -0
- package/dist/chunks/pause-BASBfKLO.mjs.map +1 -0
- package/dist/chunks/policy-DUIgzF3W.mjs +139 -0
- package/dist/chunks/policy-DUIgzF3W.mjs.map +1 -0
- package/dist/chunks/providers-Dx78imKj.mjs +19 -0
- package/dist/chunks/providers-Dx78imKj.mjs.map +1 -0
- package/dist/chunks/reconcile-BfD785gd.mjs +26 -0
- package/dist/chunks/reconcile-BfD785gd.mjs.map +1 -0
- package/dist/chunks/render-DyZr5Lgi.mjs +53 -0
- package/dist/chunks/render-DyZr5Lgi.mjs.map +1 -0
- package/dist/chunks/resume-GJQRb2Eb.mjs +15 -0
- package/dist/chunks/resume-GJQRb2Eb.mjs.map +1 -0
- package/dist/chunks/retry-CLrUem1H.mjs +15 -0
- package/dist/chunks/retry-CLrUem1H.mjs.map +1 -0
- package/dist/chunks/review-BDdjb6kB.mjs +17 -0
- package/dist/chunks/review-BDdjb6kB.mjs.map +1 -0
- package/dist/chunks/run-CgTWYlcd.mjs +82 -0
- package/dist/chunks/run-CgTWYlcd.mjs.map +1 -0
- package/dist/chunks/run-tool-C3rAv4XF.mjs +26 -0
- package/dist/chunks/run-tool-C3rAv4XF.mjs.map +1 -0
- package/dist/chunks/schema-C9QbUW4H.mjs +578 -0
- package/dist/chunks/schema-C9QbUW4H.mjs.map +1 -0
- package/dist/chunks/send-BdnJ85Nz.mjs +19 -0
- package/dist/chunks/send-BdnJ85Nz.mjs.map +1 -0
- package/dist/chunks/ship-DTuLGoaJ.mjs +19 -0
- package/dist/chunks/ship-DTuLGoaJ.mjs.map +1 -0
- package/dist/chunks/shutdown-BM92Iyv2.mjs +15 -0
- package/dist/chunks/shutdown-BM92Iyv2.mjs.map +1 -0
- package/dist/chunks/skip-DZG5kpcH.mjs +18 -0
- package/dist/chunks/skip-DZG5kpcH.mjs.map +1 -0
- package/dist/chunks/spec-Bq0P3jVh.mjs +20 -0
- package/dist/chunks/spec-Bq0P3jVh.mjs.map +1 -0
- package/dist/chunks/src-CId6kMsY.mjs +1689 -0
- package/dist/chunks/src-CId6kMsY.mjs.map +1 -0
- package/dist/chunks/src-CfAOde7C.mjs +5403 -0
- package/dist/chunks/src-CfAOde7C.mjs.map +1 -0
- package/dist/chunks/src-Cu3TxNob.mjs +783 -0
- package/dist/chunks/src-Cu3TxNob.mjs.map +1 -0
- package/dist/chunks/src-DcU4IXMe.mjs +117 -0
- package/dist/chunks/src-DcU4IXMe.mjs.map +1 -0
- package/dist/chunks/src-JxawBQBn.mjs +446 -0
- package/dist/chunks/src-JxawBQBn.mjs.map +1 -0
- package/dist/chunks/src-s7YRvk0t.mjs +2116 -0
- package/dist/chunks/src-s7YRvk0t.mjs.map +1 -0
- package/dist/chunks/status-SMjiOMYy.mjs +29 -0
- package/dist/chunks/status-SMjiOMYy.mjs.map +1 -0
- package/dist/chunks/tail-BuhT-2PH.mjs +25 -0
- package/dist/chunks/tail-BuhT-2PH.mjs.map +1 -0
- package/dist/chunks/update-BXIppsbi.mjs +16 -0
- package/dist/chunks/update-BXIppsbi.mjs.map +1 -0
- package/dist/cli.mjs +175 -0
- package/dist/cli.mjs.map +1 -0
- package/package.json +17 -32
- package/CHANGELOG.md +0 -1461
- package/bin/cli.js +0 -817
- package/bin/report.js +0 -141
- package/core/adapter/render.js +0 -415
- package/core/agents/implementer.template.md +0 -142
- package/core/agents/profile-reader.md +0 -50
- package/core/agents/release.md +0 -58
- package/core/agents/review.md +0 -179
- package/core/commands/cohorte-align-ds.md +0 -33
- package/core/commands/cohorte-audit.md +0 -58
- package/core/commands/cohorte-brainstorm.md +0 -70
- package/core/commands/cohorte-build.md +0 -219
- package/core/commands/cohorte-doctor.md +0 -194
- package/core/commands/cohorte-fix.md +0 -80
- package/core/commands/cohorte-fleet.md +0 -103
- package/core/commands/cohorte-init-pipeline.md +0 -31
- package/core/commands/cohorte-intake.md +0 -92
- package/core/commands/cohorte-patch.md +0 -118
- package/core/commands/cohorte-refactor.md +0 -59
- package/core/commands/cohorte-retro.md +0 -85
- package/core/commands/cohorte-review.md +0 -270
- package/core/commands/cohorte-ship.md +0 -122
- package/core/commands/cohorte-spec.md +0 -101
- package/core/commands/cohorte-update-pipeline.md +0 -165
- package/core/hooks/gate.py +0 -588
- package/core/runtimes/claude.json +0 -74
- package/core/runtimes/codex.json +0 -85
- package/core/runtimes/cursor.json +0 -76
- package/core/runtimes/gemini.json +0 -76
- package/core/runtimes/opencode.json +0 -73
- package/core/templates/agent-handoff.md +0 -34
- package/core/templates/brainstorm-return.md +0 -36
- package/core/templates/decisions.template.md +0 -42
- package/core/templates/design-brief.md +0 -44
- package/core/templates/patch.template.md +0 -86
- package/core/templates/pr-body.md +0 -25
- package/core/templates/review-feedback.md +0 -38
- package/core/templates/spec.template.md +0 -87
- package/core/templates/steps/init-pipeline/01-detect-stack.md +0 -44
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +0 -55
- package/core/templates/steps/init-pipeline/03-draft-profile.md +0 -10
- package/core/templates/steps/init-pipeline/04-write-render.md +0 -145
- package/core/templates/steps/init-pipeline/05-report.md +0 -12
- package/core/workflows/audit.js +0 -240
- package/core/workflows/loop.js +0 -641
- package/core/workflows/refactor.js +0 -276
- package/core/workflows/review.js +0 -456
- package/install.ps1 +0 -103
- package/install.sh +0 -114
- package/lib/doctor.js +0 -534
- package/lib/runtime.js +0 -139
- package/lib/versions.js +0 -149
- package/lib/yaml.js +0 -129
- package/profile/PIPELINE.template.md +0 -259
- package/profile/SCHEMA.md +0 -735
- package/profile/cohorte.config.template.yaml +0 -43
- package/scripts/assert-gate-hook.mjs +0 -75
- package/scripts/kanban-move.sh +0 -313
- package/scripts/metrics/collect.mjs +0 -531
- package/scripts/metrics/prices.json +0 -42
- package/scripts/new-feature.sh.template +0 -98
- package/scripts/preflight.sh +0 -124
- package/scripts/remove-feature.sh.template +0 -56
- package/scripts/test-adapter.mjs +0 -437
- package/scripts/test-gate.mjs +0 -443
- package/scripts/test-kanban.mjs +0 -304
- package/scripts/test-lib.mjs +0 -356
- package/scripts/test-metrics.mjs +0 -156
- package/scripts/test-workflows.mjs +0 -758
- package/scripts/validate-core.mjs +0 -325
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
// cohorte — /cohorte-refactor as a deterministic workflow (opt-in; the conversational
|
|
2
|
-
// /cohorte-refactor command remains the default path and the fallback).
|
|
3
|
-
//
|
|
4
|
-
// BIG domains only: a domain with just a handful of open backlog items is
|
|
5
|
-
// cheaper through the conversational /cohorte-refactor — this script skips it and says
|
|
6
|
-
// so. Invoke with args = {domains: ["backend", …]} or {domains: "all"}.
|
|
7
|
-
//
|
|
8
|
-
// Shape (SCHEMA.md §Workflows): profile via profile-reader (phase 0), the open
|
|
9
|
-
// backlog read once, the `shared` domain (contract package — every slice
|
|
10
|
-
// imports it) refactored FIRST and alone, then the other domains' surface
|
|
11
|
-
// implementers in parallel (their trees are disjoint by construction), each
|
|
12
|
-
// verified per-domain with one bounded retry round, and the backlog ticked.
|
|
13
|
-
|
|
14
|
-
export const meta = {
|
|
15
|
-
name: 'cohorte-refactor',
|
|
16
|
-
description: 'Apply the /cohorte-audit refactor backlog for big domains: shared first, then parallel surface implementers, per-domain verify + one retry',
|
|
17
|
-
whenToUse: 'Only when the human explicitly asks for the refactor workflow on big domains. args = {domains: ["<surface key>", …] | "all"}.',
|
|
18
|
-
phases: [
|
|
19
|
-
{ title: 'Profile', detail: 'PIPELINE.md → JSON via profile-reader', model: 'haiku' },
|
|
20
|
-
{ title: 'Backlog', detail: 'read the open items per requested domain', model: 'haiku' },
|
|
21
|
-
{ title: 'Shared', detail: 'refactor the contract package first, alone' },
|
|
22
|
-
{ title: 'Refactor', detail: 'one surface implementer per domain, parallel' },
|
|
23
|
-
{ title: 'Verify', detail: 'per-domain gates + item check, one retry round', model: 'haiku' },
|
|
24
|
-
{ title: 'Tick', detail: 'check cleared items off specs/refactor-backlog.md', model: 'haiku' },
|
|
25
|
-
],
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// A domain below this many open items is not "big" — the conversational
|
|
29
|
-
// /cohorte-refactor handles it with less overhead than a workflow run.
|
|
30
|
-
const MIN_ITEMS = 5
|
|
31
|
-
|
|
32
|
-
// The Workflow runtime hands `args` to a script verbatim, so a caller that passes a
|
|
33
|
-
// JSON-ENCODED STRING instead of a real object gets that string back here. Parse it
|
|
34
|
-
// back into the object it was meant to be. A bare slug is shorthand for the DOMAIN in
|
|
35
|
-
// this script — mapping it to {feature, target} (review.js's keys, once copy-pasted
|
|
36
|
-
// here) left ARGS.domains undefined, which fell through to 'all': the shorthand
|
|
37
|
-
// "backend" dispatched code-editing implementers on EVERY big domain, not the one
|
|
38
|
-
// the caller named.
|
|
39
|
-
const ARGS = (() => {
|
|
40
|
-
if (typeof args === 'string') {
|
|
41
|
-
const t = args.trim()
|
|
42
|
-
if (t.startsWith('{')) {
|
|
43
|
-
try { const o = JSON.parse(t); if (o && typeof o === 'object' && !Array.isArray(o)) return o } catch {}
|
|
44
|
-
}
|
|
45
|
-
return { domains: [t] }
|
|
46
|
-
}
|
|
47
|
-
return args && typeof args === 'object' ? args : {}
|
|
48
|
-
})()
|
|
49
|
-
|
|
50
|
-
const wanted = (() => {
|
|
51
|
-
const d = ARGS.domains
|
|
52
|
-
if (!d || d === 'all') return 'all'
|
|
53
|
-
return Array.isArray(d) ? d : [String(d)]
|
|
54
|
-
})()
|
|
55
|
-
|
|
56
|
-
// The profile-reader returns through a StructuredOutput tool call, and a haiku agent
|
|
57
|
-
// intermittently nests the whole profile as a JSON *string* under a single wrapper field
|
|
58
|
-
// ({"output": "{\"surfaces\": …}"}) instead of putting the profile's keys at the top level.
|
|
59
|
-
// The schema here used to be {type:'object', additionalProperties:true} — no declared
|
|
60
|
-
// properties, no required keys — so that wrapper validated cleanly and every field then read
|
|
61
|
-
// as undefined: `surfaces` fell back to [], parallel([]) dispatched zero agents, the
|
|
62
|
-
// dead-agent guard had no surfaces to find missing, and the run reported a verdict having
|
|
63
|
-
// done nothing. On the surface it is indistinguishable from a clean run with an empty diff.
|
|
64
|
-
// Declaring the shape gives the tool layer something to validate and the agent something to
|
|
65
|
-
// aim at; unwrapProfile() salvages a wrapped return that still gets through; and the
|
|
66
|
-
// zero-surface abort below makes the silent-success path impossible either way.
|
|
67
|
-
// See also the structured-output section of core/agents/profile-reader.md.
|
|
68
|
-
const PROFILE = {
|
|
69
|
-
type: 'object', additionalProperties: true,
|
|
70
|
-
properties: {
|
|
71
|
-
error: { type: 'string', description: 'set ONLY when PIPELINE.md is missing or unparseable' },
|
|
72
|
-
surfaces: {
|
|
73
|
-
type: 'array',
|
|
74
|
-
description: "one entry per surface, at the TOP LEVEL of this object — never a JSON string",
|
|
75
|
-
items: {
|
|
76
|
-
type: 'object', required: ['key'], additionalProperties: true,
|
|
77
|
-
properties: { key: { type: 'string' }, path: { type: 'string' }, agent: { type: 'string' } },
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Salvage a profile handed back as JSON text rather than as an object — either the whole
|
|
84
|
-
// return, or nested under a single wrapper field. Anything already shaped like a profile
|
|
85
|
-
// (has `surfaces`, or is the documented `{error}` failure shape) passes through untouched.
|
|
86
|
-
const unwrapProfile = p => {
|
|
87
|
-
if (typeof p === 'string') { try { return JSON.parse(p) } catch { return null } }
|
|
88
|
-
if (!p || typeof p !== 'object') return null
|
|
89
|
-
if (Array.isArray(p.surfaces) || p.error) return p
|
|
90
|
-
for (const v of Object.values(p)) {
|
|
91
|
-
if (typeof v !== 'string') continue
|
|
92
|
-
try {
|
|
93
|
-
const inner = JSON.parse(v)
|
|
94
|
-
if (inner && typeof inner === 'object' && !Array.isArray(inner)) return inner
|
|
95
|
-
} catch {}
|
|
96
|
-
}
|
|
97
|
-
return p
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const OPEN = {
|
|
101
|
-
type: 'object', required: ['domains'], additionalProperties: false,
|
|
102
|
-
properties: {
|
|
103
|
-
domains: {
|
|
104
|
-
type: 'array',
|
|
105
|
-
items: {
|
|
106
|
-
type: 'object', required: ['key', 'items'], additionalProperties: false,
|
|
107
|
-
properties: {
|
|
108
|
-
key: { type: 'string' },
|
|
109
|
-
items: { type: 'array', items: { type: 'string', description: 'the open `- [ ] …` line verbatim' } },
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const VERIFY = {
|
|
117
|
-
type: 'object', required: ['cleared', 'remaining', 'gatesGreen'], additionalProperties: false,
|
|
118
|
-
properties: {
|
|
119
|
-
cleared: { type: 'array', items: { type: 'string' } },
|
|
120
|
-
remaining: { type: 'array', items: { type: 'string' } },
|
|
121
|
-
gatesGreen: { type: 'boolean' },
|
|
122
|
-
failures: { type: 'string', description: 'one line per gate failure, no output dumps' },
|
|
123
|
-
},
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ── Phase 0 — profile ────────────────────────────────────────────────────────
|
|
127
|
-
phase('Profile')
|
|
128
|
-
const profile = unwrapProfile(await agent(
|
|
129
|
-
'Return this project\'s PIPELINE.md `yaml pipeline-profile` block as JSON, per your instructions.',
|
|
130
|
-
{ agentType: 'profile-reader', label: 'profile', schema: PROFILE, effort: 'low' },
|
|
131
|
-
))
|
|
132
|
-
if (!profile || profile.error) {
|
|
133
|
-
return { error: `profile unreadable: ${(profile && profile.error) || 'profile-reader returned nothing'}` }
|
|
134
|
-
}
|
|
135
|
-
const surfaces = Array.isArray(profile.surfaces) ? profile.surfaces : []
|
|
136
|
-
// A profile with no surfaces cannot do this workflow's work, and every later
|
|
137
|
-
// guard compares against `surfaces` — an empty list makes them all vacuously
|
|
138
|
-
// pass. Fail loudly here instead of finishing with nothing done.
|
|
139
|
-
if (!surfaces.length) {
|
|
140
|
-
return { error: 'profile has no surfaces — nothing would be refactored. the `yaml pipeline-profile` block in PIPELINE.md is empty or unparseable, or the profile-reader mis-returned; run /cohorte-doctor' }
|
|
141
|
-
}
|
|
142
|
-
const byKey = Object.fromEntries(surfaces.map(s => [s.key, s]))
|
|
143
|
-
const contractPath = (profile.contract && profile.contract.path) || ''
|
|
144
|
-
const base = (profile.vcs && profile.vcs.default_branch) || 'main'
|
|
145
|
-
const quiet = s => (s.test_quiet_cmd && !String(s.test_quiet_cmd).startsWith('<'))
|
|
146
|
-
? s.test_quiet_cmd : s.test_cmd ? `${s.test_cmd} 2>&1 | tail -40` : ''
|
|
147
|
-
|
|
148
|
-
// ── Phase 1 — read the open backlog ──────────────────────────────────────────
|
|
149
|
-
phase('Backlog')
|
|
150
|
-
const backlog = await agent(
|
|
151
|
-
'Read specs/refactor-backlog.md and return, per `## <domain>` heading, the OPEN `- [ ] …` item lines ' +
|
|
152
|
-
'verbatim (skip checked `- [x]` ones). File missing ⇒ return an empty domains array. ' +
|
|
153
|
-
`Requested domains: ${wanted === 'all' ? 'all' : wanted.join(', ')} — return only those (all ⇒ every domain with open items).`,
|
|
154
|
-
{ model: 'haiku', label: 'read-backlog', schema: OPEN, effort: 'low' },
|
|
155
|
-
)
|
|
156
|
-
// A dead reader is not "the backlog is empty" — reporting it as such sends the
|
|
157
|
-
// human to re-run /cohorte-audit on a backlog that is already there.
|
|
158
|
-
if (!backlog) return { error: 'the backlog-reading agent died — nothing was read; re-run the refactor workflow' }
|
|
159
|
-
const open = (backlog.domains || []).filter(d => d.items.length)
|
|
160
|
-
if (!open.length) return { error: 'no open backlog items for the requested domains — run /cohorte-audit (or the audit workflow) first' }
|
|
161
|
-
|
|
162
|
-
const big = open.filter(d => d.items.length >= MIN_ITEMS)
|
|
163
|
-
const small = open.filter(d => d.items.length < MIN_ITEMS)
|
|
164
|
-
for (const d of small) log(`Skipping ${d.key} (${d.items.length} open item(s) < ${MIN_ITEMS}) — use the conversational /cohorte-refactor ${d.key}, it's cheaper`)
|
|
165
|
-
if (!big.length) return { skipped: Object.fromEntries(small.map(d => [d.key, d.items.length])), reason: `every requested domain is below the ${MIN_ITEMS}-item workflow threshold — use /cohorte-refactor` }
|
|
166
|
-
|
|
167
|
-
const implementPrompt = d =>
|
|
168
|
-
'Refactor pass on your surface (no feature spec). Read PIPELINE.md first. Add the missing tests ' +
|
|
169
|
-
'FIRST (pin current behavior / cover the entry points), watch them pass, THEN refactor to clear each ' +
|
|
170
|
-
'item. Preserve current public behavior unless an item marks it a bug. Migrations stay additive. ' +
|
|
171
|
-
`Need the current state of your tree? Compute it yourself: git diff ${base} -- <your surface path>. ` +
|
|
172
|
-
'Lint + format before handoff; return the handoff in the format your agent instructions define. ' +
|
|
173
|
-
'Backlog items for YOUR surface (self-contained — clear exactly these, reading only the files they name):\n' +
|
|
174
|
-
d.items.join('\n')
|
|
175
|
-
|
|
176
|
-
const verifyDomain = async (d, implHandoff) => {
|
|
177
|
-
if (implHandoff == null) return { key: d.key, cleared: [], remaining: d.items, gatesGreen: false, failures: 'implementer died' }
|
|
178
|
-
const s = byKey[d.key]
|
|
179
|
-
const gateCmds = s ? [quiet(s), s.lint_quiet_cmd || (s.lint_cmd ? `${s.lint_cmd} 2>&1 | tail -40` : ''), s.typecheck_cmd]
|
|
180
|
-
.filter(c => c && !String(c).startsWith('<')) : []
|
|
181
|
-
let v = await agent(
|
|
182
|
-
`Verify a cohorte refactor round for domain ${d.key}. 1. Run these gates, each redirected to ` +
|
|
183
|
-
`specs/reports/refactor-verify.${d.key}.txt (append) — never print their output: ` +
|
|
184
|
-
`${gateCmds.map(c => JSON.stringify(c)).join(' · ') || '(none declared — skip gates, gatesGreen=true)'}. ` +
|
|
185
|
-
'2. For EACH backlog item below, open its file:line and check the prescribed fix actually landed. ' +
|
|
186
|
-
'Return the item lines split into cleared / remaining (verbatim), gatesGreen, and one line per gate failure.\n' +
|
|
187
|
-
'Items:\n' + d.items.join('\n'),
|
|
188
|
-
{ model: 'haiku', label: `verify:${d.key}`, phase: 'Verify', schema: VERIFY, effort: 'low' },
|
|
189
|
-
)
|
|
190
|
-
// One bounded retry: re-dispatch the implementer on what verification rejected.
|
|
191
|
-
// The re-verify covers ONLY the retried items, so round 1's cleared list is carried
|
|
192
|
-
// forward — overwriting it un-ticked every item the first pass verified, and the
|
|
193
|
-
// next /cohorte-refactor re-dispatched finished work.
|
|
194
|
-
if (v && (v.remaining.length || !v.gatesGreen) && byKey[d.key]) {
|
|
195
|
-
const cleared1 = v.cleared || []
|
|
196
|
-
// Never retry items round 1 already verified cleared: on a gates-red round with
|
|
197
|
-
// nothing remaining, retrying ALL items put the same lines in both `cleared` and
|
|
198
|
-
// `remaining` when the re-verifier died — ticked off the backlog AND reported open.
|
|
199
|
-
const retryItems = v.remaining.length ? v.remaining : d.items.filter(i => !cleared1.includes(i))
|
|
200
|
-
log(`${d.key}: ${v.remaining.length} item(s) remaining${v.gatesGreen ? '' : ' + red gates'} — one retry round`)
|
|
201
|
-
await agent(
|
|
202
|
-
implementPrompt({ key: d.key, items: retryItems }) + (v.failures ? `\nGate failures to clear too:\n${v.failures}` : ''),
|
|
203
|
-
{ agentType: byKey[d.key].agent, label: `retry:${d.key}`, phase: 'Refactor' },
|
|
204
|
-
)
|
|
205
|
-
const v2 = await agent(
|
|
206
|
-
`Re-verify domain ${d.key} after a retry round — same procedure as before (gates redirected to ` +
|
|
207
|
-
`specs/reports/refactor-verify.${d.key}.txt, per-item file:line check, verbatim cleared/remaining lines).\n` +
|
|
208
|
-
'Items:\n' + retryItems.join('\n'),
|
|
209
|
-
{ model: 'haiku', label: `reverify:${d.key}`, phase: 'Verify', schema: VERIFY, effort: 'low' },
|
|
210
|
-
)
|
|
211
|
-
// A dead re-verifier loses only the RETRY round's claim — round 1's verified
|
|
212
|
-
// clears stay cleared; the retried items stay open (unverified ≠ cleared).
|
|
213
|
-
v = v2
|
|
214
|
-
? { ...v2, cleared: [...new Set(cleared1.concat(v2.cleared || []))] }
|
|
215
|
-
: { cleared: cleared1, remaining: retryItems, gatesGreen: false, failures: 'verifier died on the retry round' }
|
|
216
|
-
}
|
|
217
|
-
return { key: d.key, ...(v || { cleared: [], remaining: d.items, gatesGreen: false, failures: 'verifier died' }) }
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// ── Phase 2 — `shared` first, alone (every slice imports the contract pkg) ───
|
|
221
|
-
const results = []
|
|
222
|
-
const shared = big.find(d => d.key === 'shared')
|
|
223
|
-
if (shared) {
|
|
224
|
-
phase('Shared')
|
|
225
|
-
const handoff = await agent(
|
|
226
|
-
`Refactor the shared contract package (${contractPath || 'the tree outside every surface'}) of this ` +
|
|
227
|
-
'cohorte project — normally lead-owned, so: additive changes only, never break a shape a surface ' +
|
|
228
|
-
'imports (grep consumers before changing any export), migrations stay additive, lint + format before ' +
|
|
229
|
-
'handoff. Clear exactly these open backlog items (self-contained; read only the files they name):\n' +
|
|
230
|
-
shared.items.join('\n'),
|
|
231
|
-
{ label: 'refactor:shared', phase: 'Shared' },
|
|
232
|
-
)
|
|
233
|
-
results.push(await verifyDomain(shared, handoff))
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// ── Phases 3+4 — the other domains in parallel, each verified as it lands ────
|
|
237
|
-
const rest = big.filter(d => d.key !== 'shared' && byKey[d.key])
|
|
238
|
-
for (const d of big) {
|
|
239
|
-
if (d.key !== 'shared' && !byKey[d.key]) log(`Skipping ${d.key} — no matching surface in PIPELINE.md`)
|
|
240
|
-
}
|
|
241
|
-
const restResults = await pipeline(
|
|
242
|
-
rest,
|
|
243
|
-
d => agent(implementPrompt(d), { agentType: byKey[d.key].agent, label: `refactor:${d.key}`, phase: 'Refactor' }),
|
|
244
|
-
(handoff, d) => verifyDomain(d, handoff),
|
|
245
|
-
)
|
|
246
|
-
results.push(...restResults.filter(Boolean))
|
|
247
|
-
|
|
248
|
-
// ── Phase 5 — tick the cleared items ─────────────────────────────────────────
|
|
249
|
-
phase('Tick')
|
|
250
|
-
const clearedAll = results.flatMap(r => r.cleared)
|
|
251
|
-
let tickedOk = true
|
|
252
|
-
if (clearedAll.length) {
|
|
253
|
-
const ticked = await agent(
|
|
254
|
-
'In specs/refactor-backlog.md flip EXACTLY these open `- [ ]` item lines to `- [x]` (match verbatim, ' +
|
|
255
|
-
'leave every other line untouched), then return the single word done:\n' + clearedAll.join('\n'),
|
|
256
|
-
{ model: 'haiku', label: 'tick-backlog', effort: 'low' },
|
|
257
|
-
)
|
|
258
|
-
// Reporting items as cleared while the backlog still shows them open means the
|
|
259
|
-
// next /cohorte-refactor re-dispatches work that is already done.
|
|
260
|
-
tickedOk = ticked != null && /done/i.test(String(ticked))
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
domains: Object.fromEntries(results.map(r => [r.key, {
|
|
265
|
-
cleared: r.cleared.length, remaining: r.remaining.length, gatesGreen: r.gatesGreen,
|
|
266
|
-
}])),
|
|
267
|
-
skippedSmall: Object.fromEntries(small.map(d => [d.key, d.items.length])),
|
|
268
|
-
stillOpen: results.flatMap(r => r.remaining.map(line => `[${r.key}] ${line}`)).slice(0, 15),
|
|
269
|
-
backlogTicked: tickedOk,
|
|
270
|
-
next: !tickedOk
|
|
271
|
-
? `${clearedAll.length} item(s) were cleared in code but NOT ticked off specs/refactor-backlog.md ` +
|
|
272
|
-
'(the ticking agent died) — tick them by hand, or the next /cohorte-refactor re-dispatches finished work'
|
|
273
|
-
: results.some(r => r.remaining.length || !r.gatesGreen)
|
|
274
|
-
? 'items remain — finish them with the conversational /cohorte-refactor <domain>'
|
|
275
|
-
: 'all dispatched domains clean — optionally close with one final /cohorte-audit',
|
|
276
|
-
}
|