role-os 2.0.0 → 2.2.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/src/mission.mjs CHANGED
@@ -1,388 +1,508 @@
1
- /**
2
- * Mission Library — Phase S (v1.8.0)
3
- *
4
- * Named, repeatable job types that make recurring work boringly reliable.
5
- * Each mission declares:
6
- * - Default pack and role chain
7
- * - Expected artifact flow
8
- * - Common escalation branches
9
- * - What "honest partial" looks like
10
- * - Entry conditions and stop conditions
11
- *
12
- * Missions are NOT new abstractions — they are proven patterns
13
- * extracted from real execution (trials G1–G10, pack comparisons,
14
- * completion proof arc).
15
- */
16
-
17
- import { TEAM_PACKS } from "./packs.mjs";
18
- import { ROLE_ARTIFACT_CONTRACTS } from "./artifacts.mjs";
19
- import { ROLE_CATALOG } from "./route.mjs";
20
-
21
- // ── Mission definitions ─────────────────────────────────────────────────────
22
-
23
- export const MISSIONS = {
24
- // ── Feature Shipment ────────────────────────────────────────────────────
25
- "feature-ship": {
26
- name: "Feature Shipment",
27
- description: "Scope, spec, implement, test, and review a new feature end-to-end.",
28
- pack: "feature",
29
- entryPath: "Product Strategist frames scope → Spec Writer writes spec → implementation → test → review",
30
- roleChain: [
31
- "Product Strategist",
32
- "Spec Writer",
33
- "Backend Engineer",
34
- "Test Engineer",
35
- "Critic Reviewer",
36
- ],
37
- artifactFlow: [
38
- { role: "Product Strategist", produces: "strategy-brief", consumedBy: "Spec Writer" },
39
- { role: "Spec Writer", produces: "implementation-spec", consumedBy: "Backend Engineer" },
40
- { role: "Backend Engineer", produces: "change-plan", consumedBy: "Test Engineer" },
41
- { role: "Test Engineer", produces: "test-report", consumedBy: "Critic Reviewer" },
42
- { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
43
- ],
44
- escalationBranches: [
45
- { trigger: "scope ambiguity", from: "Spec Writer", to: "Product Strategist", action: "clarify scope before proceeding" },
46
- { trigger: "untestable spec", from: "Test Engineer", to: "Spec Writer", action: "revise interface spec for testability" },
47
- { trigger: "review rejection", from: "Critic Reviewer", to: "Backend Engineer", action: "address findings, re-test, re-review" },
48
- ],
49
- honestPartial: "Scope + spec complete but implementation blocked. Artifact chain intact up to the blocking point. Handoff includes what was tried and why it stalled.",
50
- stopConditions: [
51
- "Critic Reviewer accepts",
52
- "Escalation exhausts retry budget (3 loops max)",
53
- "Scope change invalidates the mission — replan required",
54
- ],
55
- dispatchDefaults: { model: "sonnet", maxTurns: 30, maxBudgetUsd: 5.0 },
56
- trialEvidence: "G1, G2 — 6/6 gold passes. Pack comparison: wins vs free routing.",
57
- },
58
-
59
- // ── Bugfix / Diagnosis ──────────────────────────────────────────────────
60
- "bugfix": {
61
- name: "Bugfix & Diagnosis",
62
- description: "Diagnose root cause, fix, test, and verify a bug or regression.",
63
- pack: "bugfix",
64
- entryPath: "Repo Researcher diagnoses root cause → Backend Engineer fixes → Test Engineer verifies → Critic reviews",
65
- roleChain: [
66
- "Repo Researcher",
67
- "Backend Engineer",
68
- "Test Engineer",
69
- "Critic Reviewer",
70
- ],
71
- artifactFlow: [
72
- { role: "Repo Researcher", produces: "diagnosis-report", consumedBy: "Backend Engineer" },
73
- { role: "Backend Engineer", produces: "change-plan", consumedBy: "Test Engineer" },
74
- { role: "Test Engineer", produces: "test-report", consumedBy: "Critic Reviewer" },
75
- { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
76
- ],
77
- escalationBranches: [
78
- { trigger: "root cause unclear", from: "Repo Researcher", to: "Repo Researcher", action: "gather more evidence, add reproduction steps" },
79
- { trigger: "fix introduces regression", from: "Test Engineer", to: "Backend Engineer", action: "revise fix approach" },
80
- { trigger: "diagnosis was wrong", from: "Backend Engineer", to: "Repo Researcher", action: "re-diagnose with new evidence from attempted fix" },
81
- ],
82
- honestPartial: "Root cause identified and documented but fix is non-trivial. Diagnosis report artifact is complete. Handoff includes attempted approaches and why they failed.",
83
- stopConditions: [
84
- "Critic Reviewer accepts fix",
85
- "Root cause confirmed but fix requires scope expansion — replan as feature-ship",
86
- "Bug is not reproducible — document findings and close",
87
- ],
88
- dispatchDefaults: { model: "sonnet", maxTurns: 20, maxBudgetUsd: 3.0 },
89
- trialEvidence: "G3, G4 — 4/4 gold passes. Honest partial validated in R1-2 (README anomaly).",
90
- },
91
-
92
- // ── Treatment (Repo Polish) ─────────────────────────────────────────────
93
- "treatment": {
94
- name: "Treatment (Repo Polish)",
95
- description: "Full treatment: shipcheck gates, polish, docs, translations, version bump, publish.",
96
- pack: "treatment",
97
- entryPath: "Shipcheck gates first → Security Reviewer audits → Docs Architect updates → Deployment Verifier verifies CI → Critic reviews",
98
- roleChain: [
99
- "Security Reviewer",
100
- "Docs Architect",
101
- "Deployment Verifier",
102
- "Critic Reviewer",
103
- ],
104
- artifactFlow: [
105
- { role: "Security Reviewer", produces: "security-audit", consumedBy: "Docs Architect" },
106
- { role: "Docs Architect", produces: "docs-update", consumedBy: "Deployment Verifier" },
107
- { role: "Deployment Verifier", produces: "ci-verification", consumedBy: "Critic Reviewer" },
108
- { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
109
- ],
110
- escalationBranches: [
111
- { trigger: "security gate fails", from: "Security Reviewer", to: "Backend Engineer", action: "fix security issue before treatment continues" },
112
- { trigger: "CI fails after changes", from: "Deployment Verifier", to: "Backend Engineer", action: "fix CI, re-verify" },
113
- { trigger: "shipcheck hard gate blocks", from: "Critic Reviewer", to: "Security Reviewer", action: "address blocking gate, restart treatment" },
114
- ],
115
- honestPartial: "Shipcheck passed but polish incomplete. Security and docs artifacts exist. Remaining items documented in handoff.",
116
- stopConditions: [
117
- "All shipcheck hard gates pass + Critic accepts",
118
- "Security gate A blocks — must fix before continuing",
119
- "Scope expands to feature work — decompose into treatment + feature-ship",
120
- ],
121
- dispatchDefaults: { model: "sonnet", maxTurns: 25, maxBudgetUsd: 4.0 },
122
- trialEvidence: "G5, G6 — treatment pack proven. Completion proof arc validated honest partial.",
123
- },
124
-
125
- // ── Docs / Release ──────────────────────────────────────────────────────
126
- "docs-release": {
127
- name: "Docs & Release",
128
- description: "Write or update documentation, prepare release notes, publish.",
129
- pack: "docs",
130
- entryPath: "Docs Architect synthesizes upstream → writes docs → Critic reviews completeness",
131
- roleChain: [
132
- "Docs Architect",
133
- "Critic Reviewer",
134
- ],
135
- artifactFlow: [
136
- { role: "Docs Architect", produces: "docs-update", consumedBy: "Critic Reviewer" },
137
- { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
138
- ],
139
- escalationBranches: [
140
- { trigger: "upstream source missing", from: "Docs Architect", to: "Product Strategist", action: "clarify what changed and why" },
141
- { trigger: "docs conflict with code", from: "Critic Reviewer", to: "Docs Architect", action: "reconcile docs with actual implementation" },
142
- ],
143
- honestPartial: "Core docs updated but supplementary pages (handbook, translations) pending. Main artifact complete.",
144
- stopConditions: [
145
- "Critic accepts docs as accurate and complete",
146
- "Upstream changes still in flight — park docs until code stabilizes",
147
- ],
148
- dispatchDefaults: { model: "sonnet", maxTurns: 15, maxBudgetUsd: 2.0 },
149
- trialEvidence: "G7, G8 — docs pack proven. Upstream-synthesis gate prevents stale docs.",
150
- },
151
-
152
- // ── Security Hardening ──────────────────────────────────────────────────
153
- "security-hardening": {
154
- name: "Security Hardening",
155
- description: "Threat model, audit, fix vulnerabilities, verify gates.",
156
- pack: "security",
157
- entryPath: "Security Reviewer threat-models → identifies issues → Backend Engineer fixes → re-audit",
158
- roleChain: [
159
- "Security Reviewer",
160
- "Backend Engineer",
161
- "Test Engineer",
162
- "Critic Reviewer",
163
- ],
164
- artifactFlow: [
165
- { role: "Security Reviewer", produces: "security-audit", consumedBy: "Backend Engineer" },
166
- { role: "Backend Engineer", produces: "change-plan", consumedBy: "Test Engineer" },
167
- { role: "Test Engineer", produces: "test-report", consumedBy: "Security Reviewer" },
168
- { role: "Security Reviewer", produces: "security-audit", consumedBy: "Critic Reviewer" },
169
- { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
170
- ],
171
- escalationBranches: [
172
- { trigger: "critical vulnerability found", from: "Security Reviewer", to: "Backend Engineer", action: "immediate fix, skip non-critical work" },
173
- { trigger: "fix breaks functionality", from: "Test Engineer", to: "Backend Engineer", action: "find fix that preserves both security and function" },
174
- { trigger: "threat model scope expands", from: "Security Reviewer", to: "Product Strategist", action: "reframe scope — is this a feature-ship or security mission?" },
175
- ],
176
- honestPartial: "Threat model complete, critical issues fixed, non-critical items documented as backlog. Audit artifact reflects actual state.",
177
- stopConditions: [
178
- "All critical findings fixed + Critic accepts",
179
- "No critical findings — document clean audit",
180
- "Scope expands beyond security — decompose into security + feature-ship",
181
- ],
182
- dispatchDefaults: { model: "sonnet", maxTurns: 25, maxBudgetUsd: 4.0 },
183
- trialEvidence: "G9, G10 — security pack proven. Treatment includes Security Reviewer by default.",
184
- },
185
-
186
- // ── Research → Framing → Launch ─────────────────────────────────────────
187
- "research-launch": {
188
- name: "Research → Framing → Launch",
189
- description: "Investigate a question, frame findings into a decision, prepare launch artifacts.",
190
- pack: "research",
191
- entryPath: "Product Strategist frames question → Competitive Analyst investigates → findings → launch decision",
192
- roleChain: [
193
- "Product Strategist",
194
- "Competitive Analyst",
195
- "Docs Architect",
196
- "Critic Reviewer",
197
- ],
198
- artifactFlow: [
199
- { role: "Product Strategist", produces: "strategy-brief", consumedBy: "Competitive Analyst" },
200
- { role: "Competitive Analyst", produces: "research-findings", consumedBy: "Product Strategist" },
201
- { role: "Docs Architect", produces: "docs-update", consumedBy: "Critic Reviewer" },
202
- { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
203
- ],
204
- escalationBranches: [
205
- { trigger: "research inconclusive", from: "Competitive Analyst", to: "Product Strategist", action: "narrow the question or accept uncertainty" },
206
- { trigger: "findings contradict assumption", from: "Competitive Analyst", to: "Product Strategist", action: "reframe strategy based on evidence" },
207
- { trigger: "launch blocked by findings", from: "Critic Reviewer", to: "Product Strategist", action: "decide: proceed with caveats or pivot" },
208
- ],
209
- honestPartial: "Research complete with findings documented. Launch decision pending stakeholder input. Research artifact is self-contained.",
210
- stopConditions: [
211
- "Critic accepts research findings and launch plan",
212
- "Research reveals the question is wrong — reframe mission",
213
- "Findings are clear but launch is premature — park with documented rationale",
214
- ],
215
- dispatchDefaults: { model: "sonnet", maxTurns: 20, maxBudgetUsd: 3.0 },
216
- trialEvidence: "Research pack opens with Product Strategist (framing before research). Validated in pack comparison.",
217
- },
218
- };
219
-
220
- // ── Mission catalog ─────────────────────────────────────────────────────────
221
-
222
- /**
223
- * List all missions with summary info.
224
- * @returns {Array<{key: string, name: string, description: string, pack: string, roleCount: number}>}
225
- */
226
- export function listMissions() {
227
- return Object.entries(MISSIONS).map(([key, m]) => ({
228
- key,
229
- name: m.name,
230
- description: m.description,
231
- pack: m.pack,
232
- roleCount: m.roleChain.length,
233
- }));
234
- }
235
-
236
- /**
237
- * Get a mission by key.
238
- * @param {string} key
239
- * @returns {object|null}
240
- */
241
- export function getMission(key) {
242
- return MISSIONS[key] || null;
243
- }
244
-
245
- /**
246
- * Suggest a mission for a task description (signal matching).
247
- * @param {string} taskDescription
248
- * @returns {{mission: string, confidence: "high"|"medium"|"low", reason: string}|null}
249
- */
250
- export function suggestMission(taskDescription) {
251
- const text = taskDescription.toLowerCase();
252
-
253
- const MISSION_SIGNALS = {
254
- "feature-ship": {
255
- signals: ["add feature", "implement", "build", "create", "new command", "ship feature", "develop", "add command", "new feature", "add support"],
256
- weight: 1,
257
- },
258
- "bugfix": {
259
- signals: ["fix", "bug", "crash", "broken", "regression", "repair", "diagnose", "not working"],
260
- weight: 1,
261
- },
262
- "treatment": {
263
- signals: ["treatment", "polish", "shipcheck", "pre-release", "ship-ready", "cleanup", "housekeeping"],
264
- weight: 1.2, // slightly prefer treatment when signals match (common task)
265
- },
266
- "docs-release": {
267
- signals: ["documentation", "docs", "readme", "release notes", "changelog", "handbook", "write docs"],
268
- weight: 1,
269
- },
270
- "security-hardening": {
271
- signals: ["security", "vulnerability", "threat model", "audit", "CVE", "injection", "hardening"],
272
- weight: 1.1,
273
- },
274
- "research-launch": {
275
- signals: ["research", "investigate", "should we", "evaluate", "competitive", "launch plan", "strategy"],
276
- weight: 1,
277
- },
278
- };
279
-
280
- let bestKey = null;
281
- let bestScore = 0;
282
-
283
- for (const [key, def] of Object.entries(MISSION_SIGNALS)) {
284
- let hits = 0;
285
- for (const signal of def.signals) {
286
- if (text.includes(signal)) hits++;
287
- }
288
- const score = hits * def.weight;
289
- if (score > bestScore) {
290
- bestScore = score;
291
- bestKey = key;
292
- }
293
- }
294
-
295
- if (!bestKey || bestScore === 0) return null;
296
-
297
- const confidence = bestScore >= 3 ? "high" : bestScore >= 1.5 ? "medium" : "low";
298
- const mission = MISSIONS[bestKey];
299
-
300
- return {
301
- mission: bestKey,
302
- confidence,
303
- reason: `Matched ${Math.floor(bestScore)} signal(s) for "${mission.name}"`,
304
- };
305
- }
306
-
307
- // ── Mission validation ──────────────────────────────────────────────────────
308
-
309
- /**
310
- * Validate that a mission's pack and roles are properly wired.
311
- * @param {string} key
312
- * @returns {{valid: boolean, issues: string[]}}
313
- */
314
- export function validateMission(key) {
315
- const mission = MISSIONS[key];
316
- if (!mission) return { valid: false, issues: [`Mission "${key}" not found`] };
317
-
318
- const issues = [];
319
-
320
- // Check pack exists
321
- if (!TEAM_PACKS[mission.pack]) {
322
- issues.push(`Pack "${mission.pack}" not found in TEAM_PACKS`);
323
- }
324
-
325
- // S6-F1: Check all role names exist in ROLE_CATALOG
326
- const catalogNames = new Set(ROLE_CATALOG.map((r) => r.name));
327
- for (const roleName of mission.roleChain) {
328
- if (!catalogNames.has(roleName)) {
329
- issues.push(`Role "${roleName}" in roleChain not found in ROLE_CATALOG`);
330
- }
331
- }
332
- for (const step of mission.artifactFlow) {
333
- if (!catalogNames.has(step.role)) {
334
- issues.push(`Role "${step.role}" in artifactFlow not found in ROLE_CATALOG`);
335
- }
336
- }
337
-
338
- // Check artifact contracts exist for producing roles
339
- for (const step of mission.artifactFlow) {
340
- if (ROLE_ARTIFACT_CONTRACTS[step.role]) {
341
- // Contract exists — good
342
- }
343
- // Not all roles need contracts (only chain-critical ones), so missing is a warning not error
344
- }
345
-
346
- // Check artifact flow continuity (each consumed artifact should be produced upstream)
347
- const produced = new Set();
348
- for (const step of mission.artifactFlow) {
349
- produced.add(step.produces);
350
- }
351
-
352
- // Check escalation branches reference roles in the chain or known roles
353
- for (const branch of mission.escalationBranches) {
354
- if (!mission.roleChain.includes(branch.from) && branch.from !== branch.to) {
355
- // from role should be in chain or self-referencing
356
- issues.push(`Escalation "from" role "${branch.from}" not in role chain`);
357
- }
358
- }
359
-
360
- // Check stop conditions exist
361
- if (!mission.stopConditions || mission.stopConditions.length === 0) {
362
- issues.push("Mission has no stop conditions");
363
- }
364
-
365
- // Check honest partial exists
366
- if (!mission.honestPartial) {
367
- issues.push("Mission has no honest-partial definition");
368
- }
369
-
370
- return { valid: issues.length === 0, issues };
371
- }
372
-
373
- /**
374
- * Validate ALL missions.
375
- * @returns {{allValid: boolean, results: Record<string, {valid: boolean, issues: string[]}>}}
376
- */
377
- export function validateAllMissions() {
378
- const results = {};
379
- let allValid = true;
380
-
381
- for (const key of Object.keys(MISSIONS)) {
382
- const result = validateMission(key);
383
- results[key] = result;
384
- if (!result.valid) allValid = false;
385
- }
386
-
387
- return { allValid, results };
388
- }
1
+ /**
2
+ * Mission Library — Phase S (v1.8.0)
3
+ *
4
+ * Named, repeatable job types that make recurring work boringly reliable.
5
+ * Each mission declares:
6
+ * - Default pack and role chain
7
+ * - Expected artifact flow
8
+ * - Common escalation branches
9
+ * - What "honest partial" looks like
10
+ * - Entry conditions and stop conditions
11
+ *
12
+ * Missions are NOT new abstractions — they are proven patterns
13
+ * extracted from real execution (trials G1–G10, pack comparisons,
14
+ * completion proof arc).
15
+ */
16
+
17
+ import { TEAM_PACKS } from "./packs.mjs";
18
+ import { ROLE_ARTIFACT_CONTRACTS } from "./artifacts.mjs";
19
+ import { ROLE_CATALOG } from "./route.mjs";
20
+
21
+ // ── Mission definitions ─────────────────────────────────────────────────────
22
+
23
+ export const MISSIONS = {
24
+ // ── Feature Shipment ────────────────────────────────────────────────────
25
+ "feature-ship": {
26
+ name: "Feature Shipment",
27
+ description: "Scope, spec, implement, test, and review a new feature end-to-end.",
28
+ pack: "feature",
29
+ entryPath: "Product Strategist frames scope → Spec Writer writes spec → implementation → test → review",
30
+ roleChain: [
31
+ "Product Strategist",
32
+ "Spec Writer",
33
+ "Backend Engineer",
34
+ "Test Engineer",
35
+ "Critic Reviewer",
36
+ ],
37
+ artifactFlow: [
38
+ { role: "Product Strategist", produces: "strategy-brief", consumedBy: "Spec Writer" },
39
+ { role: "Spec Writer", produces: "implementation-spec", consumedBy: "Backend Engineer" },
40
+ { role: "Backend Engineer", produces: "change-plan", consumedBy: "Test Engineer" },
41
+ { role: "Test Engineer", produces: "test-report", consumedBy: "Critic Reviewer" },
42
+ { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
43
+ ],
44
+ escalationBranches: [
45
+ { trigger: "scope ambiguity", from: "Spec Writer", to: "Product Strategist", action: "clarify scope before proceeding" },
46
+ { trigger: "untestable spec", from: "Test Engineer", to: "Spec Writer", action: "revise interface spec for testability" },
47
+ { trigger: "review rejection", from: "Critic Reviewer", to: "Backend Engineer", action: "address findings, re-test, re-review" },
48
+ ],
49
+ honestPartial: "Scope + spec complete but implementation blocked. Artifact chain intact up to the blocking point. Handoff includes what was tried and why it stalled.",
50
+ stopConditions: [
51
+ "Critic Reviewer accepts",
52
+ "Escalation exhausts retry budget (3 loops max)",
53
+ "Scope change invalidates the mission — replan required",
54
+ ],
55
+ dispatchDefaults: { model: "sonnet", maxTurns: 30, maxBudgetUsd: 5.0 },
56
+ trialEvidence: "G1, G2 — 6/6 gold passes. Pack comparison: wins vs free routing.",
57
+ },
58
+
59
+ // ── Bugfix / Diagnosis ──────────────────────────────────────────────────
60
+ "bugfix": {
61
+ name: "Bugfix & Diagnosis",
62
+ description: "Diagnose root cause, fix, test, and verify a bug or regression.",
63
+ pack: "bugfix",
64
+ entryPath: "Repo Researcher diagnoses root cause → Backend Engineer fixes → Test Engineer verifies → Critic reviews",
65
+ roleChain: [
66
+ "Repo Researcher",
67
+ "Backend Engineer",
68
+ "Test Engineer",
69
+ "Critic Reviewer",
70
+ ],
71
+ artifactFlow: [
72
+ { role: "Repo Researcher", produces: "diagnosis-report", consumedBy: "Backend Engineer" },
73
+ { role: "Backend Engineer", produces: "change-plan", consumedBy: "Test Engineer" },
74
+ { role: "Test Engineer", produces: "test-report", consumedBy: "Critic Reviewer" },
75
+ { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
76
+ ],
77
+ escalationBranches: [
78
+ { trigger: "root cause unclear", from: "Repo Researcher", to: "Repo Researcher", action: "gather more evidence, add reproduction steps" },
79
+ { trigger: "fix introduces regression", from: "Test Engineer", to: "Backend Engineer", action: "revise fix approach" },
80
+ { trigger: "diagnosis was wrong", from: "Backend Engineer", to: "Repo Researcher", action: "re-diagnose with new evidence from attempted fix" },
81
+ ],
82
+ honestPartial: "Root cause identified and documented but fix is non-trivial. Diagnosis report artifact is complete. Handoff includes attempted approaches and why they failed.",
83
+ stopConditions: [
84
+ "Critic Reviewer accepts fix",
85
+ "Root cause confirmed but fix requires scope expansion — replan as feature-ship",
86
+ "Bug is not reproducible — document findings and close",
87
+ ],
88
+ dispatchDefaults: { model: "sonnet", maxTurns: 20, maxBudgetUsd: 3.0 },
89
+ trialEvidence: "G3, G4 — 4/4 gold passes. Honest partial validated in R1-2 (README anomaly).",
90
+ },
91
+
92
+ // ── Treatment (Repo Polish) ─────────────────────────────────────────────
93
+ "treatment": {
94
+ name: "Treatment (Repo Polish)",
95
+ description: "Full treatment: shipcheck gates, polish, docs, translations, version bump, publish.",
96
+ pack: "treatment",
97
+ entryPath: "Shipcheck gates first → Security Reviewer audits → Docs Architect updates → Deployment Verifier verifies CI → Critic reviews",
98
+ roleChain: [
99
+ "Security Reviewer",
100
+ "Docs Architect",
101
+ "Deployment Verifier",
102
+ "Critic Reviewer",
103
+ ],
104
+ artifactFlow: [
105
+ { role: "Security Reviewer", produces: "security-audit", consumedBy: "Docs Architect" },
106
+ { role: "Docs Architect", produces: "docs-update", consumedBy: "Deployment Verifier" },
107
+ { role: "Deployment Verifier", produces: "ci-verification", consumedBy: "Critic Reviewer" },
108
+ { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
109
+ ],
110
+ escalationBranches: [
111
+ { trigger: "security gate fails", from: "Security Reviewer", to: "Backend Engineer", action: "fix security issue before treatment continues" },
112
+ { trigger: "CI fails after changes", from: "Deployment Verifier", to: "Backend Engineer", action: "fix CI, re-verify" },
113
+ { trigger: "shipcheck hard gate blocks", from: "Critic Reviewer", to: "Security Reviewer", action: "address blocking gate, restart treatment" },
114
+ ],
115
+ honestPartial: "Shipcheck passed but polish incomplete. Security and docs artifacts exist. Remaining items documented in handoff.",
116
+ stopConditions: [
117
+ "All shipcheck hard gates pass + Critic accepts",
118
+ "Security gate A blocks — must fix before continuing",
119
+ "Scope expands to feature work — decompose into treatment + feature-ship",
120
+ ],
121
+ dispatchDefaults: { model: "sonnet", maxTurns: 25, maxBudgetUsd: 4.0 },
122
+ trialEvidence: "G5, G6 — treatment pack proven. Completion proof arc validated honest partial.",
123
+ },
124
+
125
+ // ── Docs / Release ──────────────────────────────────────────────────────
126
+ "docs-release": {
127
+ name: "Docs & Release",
128
+ description: "Write or update documentation, prepare release notes, publish.",
129
+ pack: "docs",
130
+ entryPath: "Docs Architect synthesizes upstream → writes docs → Critic reviews completeness",
131
+ roleChain: [
132
+ "Docs Architect",
133
+ "Critic Reviewer",
134
+ ],
135
+ artifactFlow: [
136
+ { role: "Docs Architect", produces: "docs-update", consumedBy: "Critic Reviewer" },
137
+ { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
138
+ ],
139
+ escalationBranches: [
140
+ { trigger: "upstream source missing", from: "Docs Architect", to: "Product Strategist", action: "clarify what changed and why" },
141
+ { trigger: "docs conflict with code", from: "Critic Reviewer", to: "Docs Architect", action: "reconcile docs with actual implementation" },
142
+ ],
143
+ honestPartial: "Core docs updated but supplementary pages (handbook, translations) pending. Main artifact complete.",
144
+ stopConditions: [
145
+ "Critic accepts docs as accurate and complete",
146
+ "Upstream changes still in flight — park docs until code stabilizes",
147
+ ],
148
+ dispatchDefaults: { model: "sonnet", maxTurns: 15, maxBudgetUsd: 2.0 },
149
+ trialEvidence: "G7, G8 — docs pack proven. Upstream-synthesis gate prevents stale docs.",
150
+ },
151
+
152
+ // ── Security Hardening ──────────────────────────────────────────────────
153
+ "security-hardening": {
154
+ name: "Security Hardening",
155
+ description: "Threat model, audit, fix vulnerabilities, verify gates.",
156
+ pack: "security",
157
+ entryPath: "Security Reviewer threat-models → identifies issues → Backend Engineer fixes → re-audit",
158
+ roleChain: [
159
+ "Security Reviewer",
160
+ "Backend Engineer",
161
+ "Test Engineer",
162
+ "Critic Reviewer",
163
+ ],
164
+ artifactFlow: [
165
+ { role: "Security Reviewer", produces: "security-audit", consumedBy: "Backend Engineer" },
166
+ { role: "Backend Engineer", produces: "change-plan", consumedBy: "Test Engineer" },
167
+ { role: "Test Engineer", produces: "test-report", consumedBy: "Security Reviewer" },
168
+ { role: "Security Reviewer", produces: "security-audit", consumedBy: "Critic Reviewer" },
169
+ { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
170
+ ],
171
+ escalationBranches: [
172
+ { trigger: "critical vulnerability found", from: "Security Reviewer", to: "Backend Engineer", action: "immediate fix, skip non-critical work" },
173
+ { trigger: "fix breaks functionality", from: "Test Engineer", to: "Backend Engineer", action: "find fix that preserves both security and function" },
174
+ { trigger: "threat model scope expands", from: "Security Reviewer", to: "Product Strategist", action: "reframe scope — is this a feature-ship or security mission?" },
175
+ ],
176
+ honestPartial: "Threat model complete, critical issues fixed, non-critical items documented as backlog. Audit artifact reflects actual state.",
177
+ stopConditions: [
178
+ "All critical findings fixed + Critic accepts",
179
+ "No critical findings — document clean audit",
180
+ "Scope expands beyond security — decompose into security + feature-ship",
181
+ ],
182
+ dispatchDefaults: { model: "sonnet", maxTurns: 25, maxBudgetUsd: 4.0 },
183
+ trialEvidence: "G9, G10 — security pack proven. Treatment includes Security Reviewer by default.",
184
+ },
185
+
186
+ // ── Research → Framing → Launch ─────────────────────────────────────────
187
+ "research-launch": {
188
+ name: "Research → Framing → Launch",
189
+ description: "Investigate a question, frame findings into a decision, prepare launch artifacts.",
190
+ pack: "research",
191
+ entryPath: "Product Strategist frames question → Competitive Analyst investigates → findings → launch decision",
192
+ roleChain: [
193
+ "Product Strategist",
194
+ "Competitive Analyst",
195
+ "Docs Architect",
196
+ "Critic Reviewer",
197
+ ],
198
+ artifactFlow: [
199
+ { role: "Product Strategist", produces: "strategy-brief", consumedBy: "Competitive Analyst" },
200
+ { role: "Competitive Analyst", produces: "research-findings", consumedBy: "Product Strategist" },
201
+ { role: "Docs Architect", produces: "docs-update", consumedBy: "Critic Reviewer" },
202
+ { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
203
+ ],
204
+ escalationBranches: [
205
+ { trigger: "research inconclusive", from: "Competitive Analyst", to: "Product Strategist", action: "narrow the question or accept uncertainty" },
206
+ { trigger: "findings contradict assumption", from: "Competitive Analyst", to: "Product Strategist", action: "reframe strategy based on evidence" },
207
+ { trigger: "launch blocked by findings", from: "Critic Reviewer", to: "Product Strategist", action: "decide: proceed with caveats or pivot" },
208
+ ],
209
+ honestPartial: "Research complete with findings documented. Launch decision pending stakeholder input. Research artifact is self-contained.",
210
+ stopConditions: [
211
+ "Critic accepts research findings and launch plan",
212
+ "Research reveals the question is wrong — reframe mission",
213
+ "Findings are clear but launch is premature — park with documented rationale",
214
+ ],
215
+ dispatchDefaults: { model: "sonnet", maxTurns: 20, maxBudgetUsd: 3.0 },
216
+ trialEvidence: "Research pack opens with Product Strategist (framing before research). Validated in pack comparison.",
217
+ },
218
+
219
+ // ── Brainstorm (Structured Inquiry) ────────────────────────────────────────
220
+ "brainstorm": {
221
+ name: "Brainstorm (Structured Inquiry)",
222
+ description: "Specialized roles under law, with traceable disagreement and verdict-bearing output. Each analyst emits a role-native schema; cross-exam produces a dispute graph; synthesis consumes truth, never prose. Both truth and rendered layers are always available.",
223
+ pack: "brainstorm",
224
+ entryPath: "Frame 4 Analysts (parallel, role-native) Normalize (provenance atoms) Cross-Examine (directed challenges) → Rebut (defend/narrow/retract) → Synthesize (dispute-informed) → Expand → Judge → Render (opt-in) → Return",
225
+ roleChain: [
226
+ "Context Analyst",
227
+ "User Value Analyst",
228
+ "Mechanics Analyst",
229
+ "Positioning Analyst",
230
+ "Normalizer",
231
+ "Contrarian Analyst",
232
+ "Synthesizer",
233
+ "Product Expander",
234
+ "Judge",
235
+ ],
236
+ artifactFlow: [
237
+ // Layer 1: Truth role-native schemas, provenance atoms, dispute graph
238
+ { role: "Context Analyst", produces: "context-map", consumedBy: "Normalizer" },
239
+ { role: "User Value Analyst", produces: "user-value-map", consumedBy: "Normalizer" },
240
+ { role: "Mechanics Analyst", produces: "mechanics-map", consumedBy: "Normalizer" },
241
+ { role: "Positioning Analyst", produces: "positioning-map", consumedBy: "Normalizer" },
242
+ { role: "Normalizer", produces: "provenance-atoms", consumedBy: "Contrarian Analyst" },
243
+ { role: "Contrarian Analyst", produces: "challenge-set", consumedBy: "Normalizer" },
244
+ // Rebut: original analysts respond to challenges (defend/narrow/retract)
245
+ { role: "Normalizer", produces: "rebuttal-set", consumedBy: "Synthesizer" },
246
+ // Synthesis consumes truth layer: atoms + challenge edges + rebuttal edges
247
+ { role: "Synthesizer", produces: "synthesis-report", consumedBy: "Product Expander" },
248
+ { role: "Product Expander", produces: "expanded-concept", consumedBy: "Judge" },
249
+ { role: "Judge", produces: "judge-report", consumedBy: null },
250
+ // Layer 2: Render — human-legible presentation of truth artifacts (opt-in)
251
+ // Rendered artifacts: Boundary Memo, Field Notes, System Sketch, Claim Brief, Cross-Exam Transcript
252
+ // Debate transcript: readable exchange generated from challenge/rebuttal graph
253
+ // Trace links: every rendered sentence maps to a truth-layer atom
254
+ ],
255
+ escalationBranches: [
256
+ { trigger: "judge_revise_expand", from: "Judge", to: "Product Expander", action: "re-expand targeted directions with revision guidance" },
257
+ { trigger: "judge_revise_synthesize", from: "Judge", to: "Synthesizer", action: "reselect directions with judge feedback, then re-expand and re-judge" },
258
+ { trigger: "boundary_violation", from: "Normalizer", to: "Context Analyst", action: "re-analyze with out-of-lens claims rejected" },
259
+ { trigger: "challenge_rejected", from: "Normalizer", to: "Contrarian Analyst", action: "re-challenge with filtered targets (cross-exam matrix enforced)" },
260
+ ],
261
+ honestPartial: "Brainstorm explored multiple dimensions. Truth artifacts up to the blocking phase are intact, inspectable, and traceable. Dispute graph partial if stalled during cross-examine/rebut. Rendered artifacts only available for completed truth artifacts.",
262
+ stopConditions: [
263
+ "Judge accepts with disposition 'accept' — both layers assembled, trace links verified",
264
+ "Judge rejects mission returns honest partial with truth artifacts intact",
265
+ "Loop budget exhausted (3 revision loops) — Judge forced to accept or reject",
266
+ "Frame fails to resolve objective — mission aborts with guidance",
267
+ ],
268
+ dispatchDefaults: { model: "sonnet", maxTurns: 40, maxBudgetUsd: 6.0 },
269
+ trialEvidence: "v0.4 golden run — 894 tests green. Full chain of custody proven: truth artifacts, provenance atoms, dispute graph (4 challenges, 3 narrowed, 1 unresolved), rendered artifacts in 5 formats, debate transcript, 16+ trace links from rendered → truth. Architecture frozen 2026-03-27.",
270
+ },
271
+ // ── Deep Audit (Componentized Repo Understanding) ──────────────────────────
272
+ "deep-audit": {
273
+ name: "Deep Audit",
274
+ description: "Decompose a repo into bounded components, dispatch one auditor per component, inspect seams from the dependency graph, assess test truth, then synthesize into a ranked verdict and action plan. Worker count scales with the repo graph — not fixed.",
275
+ pack: "deep-audit",
276
+ entryPath: "Decompose repo → validate parcels → Component Auditor ×N (parallel) + Test Truth Auditor ×M → Seam Auditor ×K (from graph edges) → Audit Synthesizer → Critic reviews verdict",
277
+ // NOTE: This mission has a DYNAMIC role chain. The static chain below
278
+ // shows the role archetypes. At dispatch time, Component Auditor and
279
+ // Seam Auditor are instantiated once per component/boundary cluster.
280
+ // A 10-component repo with 4 risky boundaries = 10 + 4 + 2 + 1 + 1 = 18 tasks.
281
+ roleChain: [
282
+ "Component Auditor", // ×N — one per component from audit-manifest
283
+ "Test Truth Auditor", // ×M one per component or one overlay pass
284
+ "Seam Auditor", // ×K — one per risky boundary cluster from graph
285
+ "Audit Synthesizer", // ×1 consumes all outputs, produces verdict
286
+ "Critic Reviewer", // ×1 — final acceptance
287
+ ],
288
+ // Dynamic dispatch contract:
289
+ // Step 1 produces audit-manifest.json with components[] and boundaries[].
290
+ // Steps 2-4 are instantiated from the manifest:
291
+ // - One Component Auditor task per components[] entry
292
+ // - One Test Truth Auditor task per component (or grouped by layer)
293
+ // - One Seam Auditor task per boundary cluster
294
+ // Step 5 (Audit Synthesizer) runs after ALL step 2-4 tasks complete.
295
+ // Step 6 (Critic Reviewer) reviews the synthesis.
296
+ dynamicDispatch: {
297
+ scalingRoles: ["Component Auditor", "Test Truth Auditor", "Seam Auditor"],
298
+ manifestSource: "audit-manifest.json",
299
+ componentAuditorPer: "components",
300
+ testTruthAuditorPer: "components",
301
+ seamAuditorPer: "boundary_clusters",
302
+ synthesisAfter: ["Component Auditor", "Test Truth Auditor", "Seam Auditor"],
303
+ },
304
+ artifactFlow: [
305
+ // Step 1: Decomposition (done before mission dispatch — input artifact)
306
+ { role: "Component Auditor", produces: "component-audit-report", consumedBy: "Audit Synthesizer" },
307
+ { role: "Test Truth Auditor", produces: "test-truth-report", consumedBy: "Audit Synthesizer" },
308
+ { role: "Seam Auditor", produces: "seam-audit-report", consumedBy: "Audit Synthesizer" },
309
+ { role: "Audit Synthesizer", produces: "audit-summary", consumedBy: "Critic Reviewer" },
310
+ { role: "Audit Synthesizer", produces: "audit-action-plan", consumedBy: "Critic Reviewer" },
311
+ { role: "Critic Reviewer", produces: "review-verdict", consumedBy: null },
312
+ ],
313
+ escalationBranches: [
314
+ { trigger: "component exceeds 8K lines", from: "Component Auditor", to: "Component Auditor", action: "re-slice into sub-components, re-dispatch" },
315
+ { trigger: "circular dependency found", from: "Seam Auditor", to: "Audit Synthesizer", action: "elevate as architectural finding, do not attempt to resolve" },
316
+ { trigger: "parcel outputs inconsistent", from: "Audit Synthesizer", to: "Component Auditor", action: "re-audit the inconsistent component with narrower scope" },
317
+ { trigger: "critical finding spans 3+ components", from: "Audit Synthesizer", to: "Seam Auditor", action: "targeted cross-cut audit on the systemic issue" },
318
+ { trigger: "test suite is ceremonial", from: "Test Truth Auditor", to: "Audit Synthesizer", action: "flag as structural risk — false confidence in coverage" },
319
+ ],
320
+ honestPartial: "Component audits complete but seam inspection blocked or synthesis incomplete. Per-component findings are individually valid and actionable. Manifest and component reports exist even if synthesis does not.",
321
+ stopConditions: [
322
+ "Audit Synthesizer produces verdict + action plan, Critic accepts",
323
+ "Decomposition reveals repo is too tangled to slice — document why and abort",
324
+ "All component audits complete but seam audits blocked — synthesize with component-only truth",
325
+ "Budget exhausted synthesize from whatever component audits completed",
326
+ ],
327
+ dispatchDefaults: { model: "sonnet", maxTurns: 25, maxBudgetUsd: 3.0 },
328
+ trialEvidence: "New mission — no trial evidence yet. Architecture designed 2026-03-27.",
329
+ },
330
+ };
331
+
332
+ // ── Mission catalog ─────────────────────────────────────────────────────────
333
+
334
+ /**
335
+ * List all missions with summary info.
336
+ * @returns {Array<{key: string, name: string, description: string, pack: string, roleCount: number}>}
337
+ */
338
+ export function listMissions() {
339
+ return Object.entries(MISSIONS).map(([key, m]) => ({
340
+ key,
341
+ name: m.name,
342
+ description: m.description,
343
+ pack: m.pack,
344
+ roleCount: m.roleChain.length,
345
+ }));
346
+ }
347
+
348
+ /**
349
+ * Get a mission by key.
350
+ * @param {string} key
351
+ * @returns {object|null}
352
+ */
353
+ export function getMission(key) {
354
+ return MISSIONS[key] || null;
355
+ }
356
+
357
+ /**
358
+ * Suggest a mission for a task description (signal matching).
359
+ * @param {string} taskDescription
360
+ * @returns {{mission: string, confidence: "high"|"medium"|"low", reason: string}|null}
361
+ */
362
+ export function suggestMission(taskDescription) {
363
+ const text = taskDescription.toLowerCase();
364
+
365
+ const MISSION_SIGNALS = {
366
+ "feature-ship": {
367
+ signals: ["add feature", "implement", "build", "create", "new command", "ship feature", "develop", "add command", "new feature", "add support"],
368
+ weight: 1,
369
+ },
370
+ "bugfix": {
371
+ signals: ["fix", "bug", "crash", "broken", "regression", "repair", "diagnose", "not working"],
372
+ weight: 1,
373
+ },
374
+ "treatment": {
375
+ signals: ["treatment", "polish", "shipcheck", "pre-release", "ship-ready", "cleanup", "housekeeping"],
376
+ weight: 1.2, // slightly prefer treatment when signals match (common task)
377
+ },
378
+ "docs-release": {
379
+ signals: ["documentation", "docs", "readme", "release notes", "changelog", "handbook", "write docs"],
380
+ weight: 1,
381
+ },
382
+ "security-hardening": {
383
+ signals: ["security", "vulnerability", "threat model", "audit", "CVE", "injection", "hardening"],
384
+ weight: 1.1,
385
+ },
386
+ "research-launch": {
387
+ signals: ["research", "investigate", "should we", "evaluate", "competitive", "launch plan", "strategy"],
388
+ weight: 1,
389
+ },
390
+ "brainstorm": {
391
+ signals: ["brainstorm", "explore ideas", "explore directions", "opportunity map", "creative directions", "concept exploration", "what could we build", "divergent thinking", "ideate"],
392
+ weight: 1.1,
393
+ },
394
+ "deep-audit": {
395
+ signals: ["deep audit", "component audit", "decompose and audit", "audit components", "structural audit", "deep review", "code audit", "repo deep dive"],
396
+ weight: 1.2,
397
+ },
398
+ };
399
+
400
+ let bestKey = null;
401
+ let bestScore = 0;
402
+
403
+ for (const [key, def] of Object.entries(MISSION_SIGNALS)) {
404
+ let hits = 0;
405
+ for (const signal of def.signals) {
406
+ if (text.includes(signal)) hits++;
407
+ }
408
+ const score = hits * def.weight;
409
+ if (score > bestScore) {
410
+ bestScore = score;
411
+ bestKey = key;
412
+ }
413
+ }
414
+
415
+ if (!bestKey || bestScore === 0) return null;
416
+
417
+ const confidence = bestScore >= 3 ? "high" : bestScore >= 1.5 ? "medium" : "low";
418
+ const mission = MISSIONS[bestKey];
419
+
420
+ return {
421
+ mission: bestKey,
422
+ confidence,
423
+ reason: `Matched ${Math.floor(bestScore)} signal(s) for "${mission.name}"`,
424
+ };
425
+ }
426
+
427
+ // ── Mission validation ──────────────────────────────────────────────────────
428
+
429
+ /**
430
+ * Validate that a mission's pack and roles are properly wired.
431
+ * @param {string} key
432
+ * @returns {{valid: boolean, issues: string[]}}
433
+ */
434
+ export function validateMission(key) {
435
+ const mission = MISSIONS[key];
436
+ if (!mission) return { valid: false, issues: [`Mission "${key}" not found`] };
437
+
438
+ const issues = [];
439
+
440
+ // Check pack exists
441
+ if (!TEAM_PACKS[mission.pack]) {
442
+ issues.push(`Pack "${mission.pack}" not found in TEAM_PACKS`);
443
+ }
444
+
445
+ // S6-F1: Check all role names exist in ROLE_CATALOG
446
+ const catalogNames = new Set(ROLE_CATALOG.map((r) => r.name));
447
+ for (const roleName of mission.roleChain) {
448
+ if (!catalogNames.has(roleName)) {
449
+ issues.push(`Role "${roleName}" in roleChain not found in ROLE_CATALOG`);
450
+ }
451
+ }
452
+ for (const step of mission.artifactFlow) {
453
+ if (!catalogNames.has(step.role)) {
454
+ issues.push(`Role "${step.role}" in artifactFlow not found in ROLE_CATALOG`);
455
+ }
456
+ }
457
+
458
+ // Check artifact contracts exist for producing roles
459
+ for (const step of mission.artifactFlow) {
460
+ if (ROLE_ARTIFACT_CONTRACTS[step.role]) {
461
+ // Contract exists — good
462
+ }
463
+ // Not all roles need contracts (only chain-critical ones), so missing is a warning not error
464
+ }
465
+
466
+ // Check artifact flow continuity (each consumed artifact should be produced upstream)
467
+ const produced = new Set();
468
+ for (const step of mission.artifactFlow) {
469
+ produced.add(step.produces);
470
+ }
471
+
472
+ // Check escalation branches reference roles in the chain or known roles
473
+ for (const branch of mission.escalationBranches) {
474
+ if (!mission.roleChain.includes(branch.from) && branch.from !== branch.to) {
475
+ // from role should be in chain or self-referencing
476
+ issues.push(`Escalation "from" role "${branch.from}" not in role chain`);
477
+ }
478
+ }
479
+
480
+ // Check stop conditions exist
481
+ if (!mission.stopConditions || mission.stopConditions.length === 0) {
482
+ issues.push("Mission has no stop conditions");
483
+ }
484
+
485
+ // Check honest partial exists
486
+ if (!mission.honestPartial) {
487
+ issues.push("Mission has no honest-partial definition");
488
+ }
489
+
490
+ return { valid: issues.length === 0, issues };
491
+ }
492
+
493
+ /**
494
+ * Validate ALL missions.
495
+ * @returns {{allValid: boolean, results: Record<string, {valid: boolean, issues: string[]}>}}
496
+ */
497
+ export function validateAllMissions() {
498
+ const results = {};
499
+ let allValid = true;
500
+
501
+ for (const key of Object.keys(MISSIONS)) {
502
+ const result = validateMission(key);
503
+ results[key] = result;
504
+ if (!result.valid) allValid = false;
505
+ }
506
+
507
+ return { allValid, results };
508
+ }