role-os 1.1.0 → 1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Added
6
+ - Pack auto-selection in `roleos route` — suggests best pack when confidence is high
7
+ - `roleos route --pack=<name>` — use a specific pack for routing
8
+ - Pack mismatch detection — warns when a pack doesn't fit the task, suggests the correct alternative
9
+ - Pack fallback — mismatched or unknown packs fall back to free routing automatically
10
+ - `checkPackMismatch()` API with 7 guard sets covering all pack×task-type combinations
11
+ - `getPackRoles()` API with conditional Orchestrator support
12
+
13
+ ### Changed
14
+ - Docs pack: Support Triage Lead now opens (was Feedback Synthesizer). Feedback Synthesizer is second. Release Engineer + Deployment Verifier moved to optional (overhead for docs-only tasks).
15
+ - Pack calibration applied from comparison evidence: conditional Orchestrator, Security Reviewer in Treatment, Product Strategist opens Research, mismatch guards on all 7 packs.
16
+
17
+ ### Evidence
18
+ - Pack comparison: calibrated packs now win or tie 6/7 (was 2/7 pre-calibration)
19
+ - Misfit honesty: 0 full bluffs, 0 undetected partial bluffs (was 1 + 3)
20
+ - 230 tests, zero failures
21
+
3
22
  ## 1.1.0
4
23
 
5
24
  ### Added
package/README.md CHANGED
@@ -180,7 +180,8 @@ Role OS operates **locally only**. It copies markdown templates and writes packe
180
180
  - v0.1–v0.4: Foundation — trials, adoption, treatment pack, starter pack
181
181
  - v1.0.0: 32 roles, full CLI, proven treatment, multi-repo portability
182
182
  - v1.0.2: Role OS lockdown (bootstrap truth fixes, init --force)
183
- - **v1.1.0**: 31 roles, full routing spine, conflict detection, escalation, evidence, dispatch, 7 proven team packs. 35 execution trials (30 gold + 5 negative). 212 tests.
183
+ - v1.1.0: 31 roles, full routing spine, conflict detection, escalation, evidence, dispatch, 7 proven team packs. 35 execution trials. 212 tests.
184
+ - **v1.2.0**: Calibrated packs promoted to default entry. Auto-selection, mismatch detection, alternative suggestion, free-routing fallback. 230 tests.
184
185
 
185
186
  ## License
186
187
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "role-os",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Role OS — a multi-Claude operating system where 31 specialized roles execute work through contracts, conflict detection, escalation, and structured evidence. 7 proven team packs for common task families.",
5
5
  "homepage": "https://mcp-tool-shop-org.github.io/role-os/",
6
6
  "bugs": {
package/src/packs.mjs CHANGED
@@ -1,14 +1,30 @@
1
1
  /**
2
- * Proven Team Packs.
2
+ * Proven Team Packs — Calibrated.
3
3
  *
4
4
  * Battle-tested role combinations for common task families.
5
- * Each pack was proven through execution trials (G1–G10).
6
- * Packs outperform raw routing on their task family by starting
7
- * from a validated lineup instead of building from scratch.
5
+ * Each pack was proven through execution trials (G1–G10) and
6
+ * calibrated by pack comparison trials (PACK-COMPARISON.md).
7
+ *
8
+ * Calibration findings applied:
9
+ * - Orchestrator is conditional (only when task is multi-role + ambiguous)
10
+ * - Every pack has mismatch detection + alternative suggestion
11
+ * - Treatment includes Security Reviewer by default
12
+ * - Research opens with Product Strategist (framing before research)
13
+ * - Docs has upstream-synthesis gate
8
14
  *
9
15
  * Usage: `roleos route --pack feature` or auto-detected from packet content.
10
16
  */
11
17
 
18
+ // ── Mismatch detection ────────────────────────────────────────────────────────
19
+ // Each pack declares what it is NOT for, and which pack IS right.
20
+
21
+ /**
22
+ * @typedef {Object} MismatchGuard
23
+ * @property {string[]} notForSignals - Content patterns that indicate this pack is wrong
24
+ * @property {string} suggestInstead - Which pack to suggest instead
25
+ * @property {string} reason - Why this pack is wrong for that signal
26
+ */
27
+
12
28
  // ── Pack definitions ──────────────────────────────────────────────────────────
13
29
 
14
30
  export const TEAM_PACKS = {
@@ -24,6 +40,7 @@ export const TEAM_PACKS = {
24
40
  "Test Engineer",
25
41
  "Critic Reviewer",
26
42
  ],
43
+ orchestratorRequired: true, // multi-role, cross-functional — Orchestrator adds value
27
44
  optionalRoles: ["UI Designer", "Frontend Developer", "Security Reviewer"],
28
45
  chainOrder: "Product Strategist → Spec Writer → Backend Engineer → Test Engineer",
29
46
  requiredArtifacts: ["scope doc", "spec", "implementation", "test results", "verdict"],
@@ -34,7 +51,11 @@ export const TEAM_PACKS = {
34
51
  ],
35
52
  escalationOwner: "Orchestrator",
36
53
  dispatchDefaults: { model: "sonnet", maxTurns: 30, maxBudgetUsd: 5.0 },
37
- trialEvidence: "G1 (Product), G2 (Engineering) — 6/6 gold-task passes",
54
+ trialEvidence: "G1 (Product), G2 (Engineering) — 6/6 gold-task passes. Pack comparison: wins vs free routing.",
55
+ mismatchGuards: [
56
+ { notForSignals: ["security review", "threat model", "vulnerability", "injection"], suggestInstead: "security", reason: "This is a security review, not a feature build" },
57
+ { notForSignals: ["launch", "announce", "release notes", "messaging"], suggestInstead: "launch", reason: "This is launch/messaging work, not feature implementation" },
58
+ ],
38
59
  },
39
60
 
40
61
  // ── Bugfix / Repair ───────────────────────────────────────────────────────
@@ -42,22 +63,26 @@ export const TEAM_PACKS = {
42
63
  name: "Bugfix / Repair",
43
64
  description: "Diagnose → fix → verify → review. Minimal chain, fast turnaround.",
44
65
  roles: [
45
- "Orchestrator",
46
66
  "Repo Researcher",
47
67
  "Backend Engineer",
48
68
  "Test Engineer",
49
69
  "Critic Reviewer",
50
70
  ],
71
+ orchestratorRequired: false, // clear scope, single-domain — Orchestrator is overhead
51
72
  optionalRoles: ["Frontend Developer", "Performance Engineer"],
52
73
  chainOrder: "Repo Researcher → Backend Engineer → Test Engineer",
53
74
  requiredArtifacts: ["repo map / diagnosis", "fix implementation", "regression tests", "verdict"],
54
75
  stopConditions: [
55
- "Repo Researcher cannot reproduce → escalate to Orchestrator",
76
+ "Repo Researcher cannot reproduce → escalate to user",
56
77
  "Fix introduces new failures → loop back to Backend Engineer",
57
78
  ],
58
- escalationOwner: "Orchestrator",
79
+ escalationOwner: "Critic Reviewer",
59
80
  dispatchDefaults: { model: "sonnet", maxTurns: 20, maxBudgetUsd: 3.0 },
60
- trialEvidence: "G2 (Engineering), G7 (Repo Researcher) roles proven",
81
+ trialEvidence: "G2 (Engineering), G7 (Repo Researcher), I-2 (shipped real fix). Pack comparison: free routing wins (Orchestrator overhead).",
82
+ mismatchGuards: [
83
+ { notForSignals: ["launch", "announce", "release notes"], suggestInstead: "launch", reason: "This is launch work, not a bugfix" },
84
+ { notForSignals: ["research", "should we", "tradeoff", "strategy"], suggestInstead: "research", reason: "This is a research/strategy question, not a bug to fix" },
85
+ ],
61
86
  },
62
87
 
63
88
  // ── Security Review ───────────────────────────────────────────────────────
@@ -65,57 +90,66 @@ export const TEAM_PACKS = {
65
90
  name: "Security Review",
66
91
  description: "Threat model → code review → dependency audit → verdict",
67
92
  roles: [
68
- "Orchestrator",
69
93
  "Security Reviewer",
70
94
  "Dependency Auditor",
71
95
  "Critic Reviewer",
72
96
  ],
97
+ orchestratorRequired: false, // single-domain, clear scope
73
98
  optionalRoles: ["Backend Engineer", "Test Engineer"],
74
99
  chainOrder: "Security Reviewer → Dependency Auditor",
75
100
  requiredArtifacts: ["threat model", "code review findings", "dependency audit", "verdict"],
76
101
  stopConditions: [
77
- "Critical vulnerability found → immediate escalation to Orchestrator",
102
+ "Critical vulnerability found → immediate escalation to user",
78
103
  "Dependency with known CVE → flag for Engineering",
79
104
  ],
80
- escalationOwner: "Orchestrator",
105
+ escalationOwner: "Security Reviewer",
81
106
  dispatchDefaults: { model: "sonnet", maxTurns: 25, maxBudgetUsd: 4.0 },
82
- trialEvidence: "G6 (Security Reviewer, Dependency Auditor) — 2/2 gold-task passes",
107
+ trialEvidence: "G6 (Security Reviewer, Dependency Auditor) — 2/2 gold-task passes. I-3 Critic found 3 gaps prior roles missed.",
108
+ mismatchGuards: [
109
+ { notForSignals: ["documentation", "handbook", "restructure", "navigation"], suggestInstead: "docs", reason: "This is docs/structure work, not a security review" },
110
+ { notForSignals: ["feature", "implement", "build", "add command"], suggestInstead: "feature", reason: "This is feature work, not a security review" },
111
+ ],
83
112
  },
84
113
 
85
114
  // ── Docs / Handbook / Release ─────────────────────────────────────────────
86
115
  docs: {
87
- name: "Docs / Handbook / Release",
88
- description: "Structurewritemetadatareleaseverify deployment",
116
+ name: "Docs / Handbook",
117
+ description: "Triagesynthesizestructurewritemetadata → review",
89
118
  roles: [
90
- "Orchestrator",
91
- "Docs Architect",
92
- "Metadata Curator",
93
- "Release Engineer",
94
- "Deployment Verifier",
119
+ "Support Triage Lead", // interpret raw input (triage reports, issue lists, feedback)
120
+ "Feedback Synthesizer", // cluster and theme the interpreted input
121
+ "Docs Architect", // structure and write the docs
122
+ "Metadata Curator", // verify metadata alignment
95
123
  "Critic Reviewer",
96
124
  ],
97
- optionalRoles: ["Repo Translator", "Brand Guardian"],
98
- chainOrder: "Docs Architect Metadata Curator → Release Engineer Deployment Verifier",
99
- requiredArtifacts: ["docs structure", "metadata audit", "release package", "deployment verification", "verdict"],
125
+ orchestratorRequired: false,
126
+ optionalRoles: ["Repo Translator", "Brand Guardian", "Release Engineer", "Deployment Verifier"],
127
+ chainOrder: "Support Triage Lead Feedback Synthesizer Docs Architect → Metadata Curator",
128
+ requiredArtifacts: ["classified input", "synthesized themes", "docs structure", "metadata audit", "verdict"],
100
129
  stopConditions: [
130
+ "Support Triage Lead finds input data ambiguous → request clarification",
131
+ "Feedback Synthesizer finds insufficient signal → escalate to user",
101
132
  "Docs Architect finds product direction unclear → escalate to Product Strategist",
102
- "Deployment Verifier finds broken artifacts → loop back to Release Engineer",
103
133
  ],
104
- escalationOwner: "Orchestrator",
134
+ escalationOwner: "Docs Architect",
105
135
  dispatchDefaults: { model: "sonnet", maxTurns: 25, maxBudgetUsd: 4.0 },
106
- trialEvidence: "G4 (Docs Architect), G7 (Treatment cluster) 5/5 gold-task passes",
136
+ trialEvidence: "G4 (Docs Architect), G7 (Treatment), I-4 (shipped page). Calibrated: Support Triage Lead + Feedback Synthesizer upstream. Release/Deploy moved to optional (overhead for docs-only tasks).",
137
+ mismatchGuards: [
138
+ { notForSignals: ["research", "should we", "competitive", "strategy"], suggestInstead: "research", reason: "This is a research/strategy question — decide before documenting" },
139
+ { notForSignals: ["security", "threat", "vulnerability"], suggestInstead: "security", reason: "This is a security review, not docs work" },
140
+ ],
107
141
  },
108
142
 
109
143
  // ── Launch / Messaging ────────────────────────────────────────────────────
110
144
  launch: {
111
145
  name: "Launch / Messaging",
112
- description: "Plan launch → write copy → content strategy. Hard pipeline: Strategist → Copywriter.",
146
+ description: "Plan launch → write copy. Hard pipeline: Strategist → Copywriter.",
113
147
  roles: [
114
- "Orchestrator",
115
148
  "Launch Strategist",
116
149
  "Launch Copywriter",
117
150
  "Critic Reviewer",
118
151
  ],
152
+ orchestratorRequired: false, // smallest pack, hard pipeline, no decomposition needed
119
153
  optionalRoles: ["Content Strategist", "Community Manager"],
120
154
  chainOrder: "Launch Strategist → Launch Copywriter",
121
155
  requiredArtifacts: ["launch plan", "release copy", "verdict"],
@@ -123,42 +157,51 @@ export const TEAM_PACKS = {
123
157
  "Launch Strategist finds no proof assets → delay launch",
124
158
  "Launch Copywriter finds product claims unverifiable → escalate to Product Strategist",
125
159
  ],
126
- escalationOwner: "Orchestrator",
160
+ escalationOwner: "Launch Strategist",
127
161
  dispatchDefaults: { model: "sonnet", maxTurns: 20, maxBudgetUsd: 3.0 },
128
- trialEvidence: "G3 (Launch Strategist, Launch Copywriter) 2/2 gold-task passes, pipeline proven",
162
+ trialEvidence: "G3 (pipeline proven), I-5 (v1.1.0 launch, Accept). Pack comparison: tie/marginal win. TRUE DEFAULT.",
163
+ mismatchGuards: [
164
+ { notForSignals: ["bug", "fix", "crash", "broken", "error"], suggestInstead: "bugfix", reason: "This is a bug to fix, not a launch to plan" },
165
+ { notForSignals: ["implement", "build", "add command", "new feature"], suggestInstead: "feature", reason: "This is feature work — build first, launch second" },
166
+ ],
129
167
  },
130
168
 
131
169
  // ── Research / Strategy ───────────────────────────────────────────────────
132
170
  research: {
133
171
  name: "Research / Strategy",
134
- description: "UX researchcompetitive analysistrend assessment user synthesis → product strategy",
172
+ description: "Frame decisiongather evidencesynthesizerecommend",
135
173
  roles: [
136
- "Orchestrator",
174
+ "Product Strategist", // REORDERED: framing first, then research
137
175
  "UX Researcher",
138
176
  "Competitive Analyst",
139
177
  "Feedback Synthesizer",
140
- "Product Strategist",
141
178
  "Critic Reviewer",
142
179
  ],
180
+ orchestratorRequired: false, // clear pipeline, Product Strategist frames
143
181
  optionalRoles: ["Trend Researcher", "User Interview Synthesizer"],
144
- chainOrder: "UX Researcher → Competitive Analyst → Feedback Synthesizer → Product Strategist",
145
- requiredArtifacts: ["friction inventory", "competitive landscape", "signal synthesis", "product recommendations", "verdict"],
182
+ chainOrder: "Product Strategist → UX Researcher → Competitive Analyst → Feedback Synthesizer",
183
+ requiredArtifacts: ["decision frame", "friction inventory", "competitive landscape", "signal synthesis", "verdict"],
146
184
  stopConditions: [
147
- "UX Researcher finds insufficient user dataescalate to Orchestrator",
185
+ "Product Strategist finds the question too vague request clarification",
186
+ "UX Researcher finds insufficient user data → escalate to Product Strategist",
148
187
  "Competitive Analyst finds no comparable products → narrow scope",
149
188
  ],
150
- escalationOwner: "Orchestrator",
189
+ escalationOwner: "Product Strategist",
151
190
  dispatchDefaults: { model: "sonnet", maxTurns: 25, maxBudgetUsd: 4.0 },
152
- trialEvidence: "G8 (Research cluster), G9 (Growth/Product) 8/8 gold-task passes",
191
+ trialEvidence: "G8 (Research cluster), G9 (Growth/Product), I-6 (game dev decision). Calibrated: Product Strategist now opens (framing before research).",
192
+ mismatchGuards: [
193
+ { notForSignals: ["implement", "build", "add command", "write code"], suggestInstead: "feature", reason: "This is implementation work, not research" },
194
+ { notForSignals: ["bug", "fix", "crash", "broken"], suggestInstead: "bugfix", reason: "This is a bugfix, not a research question" },
195
+ ],
153
196
  },
154
197
 
155
198
  // ── Treatment (repo polish) ───────────────────────────────────────────────
156
199
  treatment: {
157
200
  name: "Treatment (Repo Polish)",
158
- description: "Full repo treatment: research → audit → docs → metadata → release → deploy → verify",
201
+ description: "Full repo treatment: research → security → audit → docs → metadata → release → deploy → verify",
159
202
  roles: [
160
- "Orchestrator",
161
203
  "Repo Researcher",
204
+ "Security Reviewer", // ADDED: was optional, now default (pack comparison finding)
162
205
  "Coverage Auditor",
163
206
  "Docs Architect",
164
207
  "Metadata Curator",
@@ -166,16 +209,22 @@ export const TEAM_PACKS = {
166
209
  "Deployment Verifier",
167
210
  "Critic Reviewer",
168
211
  ],
169
- optionalRoles: ["Brand Guardian", "Repo Translator", "Security Reviewer"],
170
- chainOrder: "Repo Researcher Coverage Auditor → Docs Architect → Metadata Curator → Release Engineer → Deployment Verifier",
171
- requiredArtifacts: ["repo map", "coverage audit", "docs", "metadata audit", "release", "deployment verification", "verdict"],
212
+ orchestratorRequired: false, // long but sequential — each role has a clear handoff
213
+ optionalRoles: ["Brand Guardian", "Repo Translator", "Dependency Auditor"],
214
+ chainOrder: "Repo Researcher Security Reviewer Coverage Auditor Docs Architect → Metadata Curator → Release Engineer → Deployment Verifier",
215
+ requiredArtifacts: ["repo map", "security findings", "coverage audit", "docs", "metadata audit", "release", "deployment verification", "verdict"],
172
216
  stopConditions: [
217
+ "Security Reviewer finds critical vulnerability → block release until resolved",
173
218
  "Coverage Auditor finds false confidence → flag for Test Engineer",
174
219
  "Deployment Verifier finds broken live artifacts → loop back to Release Engineer",
175
220
  ],
176
- escalationOwner: "Orchestrator",
221
+ escalationOwner: "Repo Researcher",
177
222
  dispatchDefaults: { model: "sonnet", maxTurns: 30, maxBudgetUsd: 5.0 },
178
- trialEvidence: "G6 (Coverage Auditor), G7 (Treatment cluster) roles proven across multiple trials",
223
+ trialEvidence: "G6-G7 (roles proven), I-7 (full chain, Accept-with-notes). Calibrated: Security Reviewer now default (was optional — pack comparison loss).",
224
+ mismatchGuards: [
225
+ { notForSignals: ["launch", "announce", "release notes", "social", "messaging"], suggestInstead: "launch", reason: "This is launch/messaging work — Treatment audits repos, it doesn't write announcements" },
226
+ { notForSignals: ["research", "should we", "competitive", "strategy"], suggestInstead: "research", reason: "This is a research/strategy question, not a repo treatment" },
227
+ ],
179
228
  },
180
229
  };
181
230
 
@@ -193,9 +242,6 @@ const PACK_KEYWORDS = {
193
242
 
194
243
  /**
195
244
  * Suggest the best pack for a packet based on content analysis.
196
- *
197
- * @param {string} content - Packet markdown content
198
- * @returns {{ pack: string, confidence: string, scores: Record<string, number> } | null}
199
245
  */
200
246
  export function suggestPack(content) {
201
247
  const lower = content.toLowerCase();
@@ -219,10 +265,52 @@ export function suggestPack(content) {
219
265
  }
220
266
 
221
267
  /**
222
- * Get a pack by name.
268
+ * Check if a pack is a mismatch for the given content.
269
+ * Returns null if no mismatch, or the suggested alternative if mismatch detected.
270
+ *
271
+ * @param {string} packName
272
+ * @param {string} content - Packet content
273
+ * @returns {{ suggestInstead: string, reason: string } | null}
274
+ */
275
+ export function checkPackMismatch(packName, content) {
276
+ const pack = TEAM_PACKS[packName];
277
+ if (!pack || !pack.mismatchGuards) return null;
278
+
279
+ const lower = content.toLowerCase();
280
+ for (const guard of pack.mismatchGuards) {
281
+ const triggered = guard.notForSignals.some(signal => lower.includes(signal));
282
+ if (triggered) {
283
+ return { suggestInstead: guard.suggestInstead, reason: guard.reason };
284
+ }
285
+ }
286
+ return null;
287
+ }
288
+
289
+ /**
290
+ * Get a pack's effective roles (with conditional Orchestrator).
223
291
  *
224
- * @param {string} name
225
- * @returns {object | null}
292
+ * @param {string} packName
293
+ * @param {boolean} [forceOrchestrator=false]
294
+ * @returns {string[] | null}
295
+ */
296
+ export function getPackRoles(packName, forceOrchestrator = false) {
297
+ const pack = TEAM_PACKS[packName];
298
+ if (!pack) return null;
299
+
300
+ const roles = [...pack.roles];
301
+ // Add Orchestrator only if the pack requires it or forced
302
+ if ((pack.orchestratorRequired || forceOrchestrator) && !roles.includes("Orchestrator")) {
303
+ roles.unshift("Orchestrator");
304
+ }
305
+ // Remove Orchestrator if pack doesn't require it and not forced
306
+ if (!pack.orchestratorRequired && !forceOrchestrator && roles[0] === "Orchestrator") {
307
+ roles.shift();
308
+ }
309
+ return roles;
310
+ }
311
+
312
+ /**
313
+ * Get a pack by name.
226
314
  */
227
315
  export function getPack(name) {
228
316
  return TEAM_PACKS[name] || null;
@@ -230,8 +318,6 @@ export function getPack(name) {
230
318
 
231
319
  /**
232
320
  * List all available packs.
233
- *
234
- * @returns {Array<{name: string, description: string, roleCount: number}>}
235
321
  */
236
322
  export function listPacks() {
237
323
  return Object.entries(TEAM_PACKS).map(([key, pack]) => ({
@@ -240,5 +326,6 @@ export function listPacks() {
240
326
  description: pack.description,
241
327
  roleCount: pack.roles.length,
242
328
  optionalCount: pack.optionalRoles.length,
329
+ orchestratorRequired: pack.orchestratorRequired,
243
330
  }));
244
331
  }
package/src/route.mjs CHANGED
@@ -3,6 +3,7 @@ import { resolve, dirname } from "node:path";
3
3
  import { readFileSafe } from "./fs-utils.mjs";
4
4
  import { detectConflicts } from "./conflicts.mjs";
5
5
  import { resolveConflict, resolveSplit, formatEscalation } from "./escalation.mjs";
6
+ import { suggestPack, getPack, checkPackMismatch, getPackRoles } from "./packs.mjs";
6
7
 
7
8
  // ── Full 32-Role Catalog ─────────────────────────────────────────────────────
8
9
  // Every role in the OS is scoreable. Keywords from routing-rules.md + contracts.
@@ -407,6 +408,8 @@ const HANDOFF_HINTS = {
407
408
 
408
409
  export async function routeCommand(args) {
409
410
  const verbose = args.includes("--verbose");
411
+ const packFlag = args.find(a => a.startsWith("--pack="));
412
+ const requestedPack = packFlag ? packFlag.split("=")[1] : null;
410
413
  const packetFile = args.find(a => !a.startsWith("--"));
411
414
 
412
415
  if (!packetFile) {
@@ -454,7 +457,39 @@ export async function routeCommand(args) {
454
457
  console.log(`\nroleos route — ${packetFile}\n`);
455
458
  console.log(`Detected type: ${type}`);
456
459
  if (deliverableType) console.log(`Deliverable type: ${deliverableType}`);
457
- console.log(`Routing confidence: ${confidence}`);
460
+
461
+ // ── Pack suggestion / selection ──
462
+ const packSuggestion = suggestPack(content);
463
+ if (requestedPack) {
464
+ const pack = getPack(requestedPack);
465
+ if (!pack) {
466
+ console.log(`\n⚠ Unknown pack: "${requestedPack}". Falling back to free routing.`);
467
+ } else {
468
+ const mismatch = checkPackMismatch(requestedPack, content);
469
+ if (mismatch) {
470
+ console.log(`\n⚠ Pack mismatch detected: ${mismatch.reason}`);
471
+ console.log(` → Suggested alternative: roleos route --pack=${mismatch.suggestInstead} ${packetFile}`);
472
+ console.log(` Falling back to free routing for this task.`);
473
+ } else {
474
+ const packRoles = getPackRoles(requestedPack);
475
+ console.log(`\nUsing pack: ${pack.name} (${packRoles.length} roles)`);
476
+ console.log(`Chain: ${pack.chainOrder}`);
477
+ console.log(`Roles: ${packRoles.join(" → ")}`);
478
+ console.log(`\nPack artifacts: ${pack.requiredArtifacts.join(", ")}`);
479
+ console.log(`Stop conditions:`);
480
+ for (const sc of pack.stopConditions) {
481
+ console.log(` • ${sc}`);
482
+ }
483
+ console.log(`\nNext: assign roles and begin execution.`);
484
+ return; // Pack selected — skip free routing output
485
+ }
486
+ }
487
+ } else if (packSuggestion && packSuggestion.confidence !== "low") {
488
+ console.log(`\nSuggested pack: ${packSuggestion.pack} (${packSuggestion.confidence} confidence)`);
489
+ console.log(` → Use: roleos route --pack=${packSuggestion.pack} ${packetFile}`);
490
+ }
491
+
492
+ console.log(`\nRouting confidence: ${confidence}`);
458
493
 
459
494
  if (confidence === "low") {
460
495
  console.log(` ↳ Few strong role signals detected. Consider reviewing the packet for missing context.`);