role-os 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.0
4
+
5
+ ### Added
6
+
7
+ #### Unified Entry Path (Phase T)
8
+ - `roleos start <task>` — auto-decides mission vs pack vs free routing
9
+ - Three-level fallback ladder with confidence scores and alternatives
10
+ - Composite task detection warns when a task should be decomposed
11
+ - `--json` flag for machine-readable entry decisions
12
+ - 46 new tests: entry engine, comparison trials, CLI integration
13
+
14
+ #### Handbook Updates
15
+ - New Missions handbook page with full mission documentation
16
+ - Updated Getting Started to lead with `roleos start`
17
+ - Updated Reference with all CLI commands (start, mission, packs, artifacts, status, doctor)
18
+ - Updated handbook index with entry levels and 9 operating layers
19
+
20
+ #### README Overhaul
21
+ - "How it works" section leads with `roleos start` examples
22
+ - Quick Start updated with mission and start commands
23
+ - Added 6 Missions table
24
+ - Updated project structure with all 18 source modules
25
+ - Updated status history through v1.9.0
26
+
27
+ ### Evidence
28
+ - 527 tests, zero failures (46 new)
29
+ - Entry path trials validated against 20+ real task descriptions
30
+ - Fallback ladder tested: mission, pack, free-routing, composite, empty input
31
+
32
+ ## 1.8.0
33
+
34
+ ### Added
35
+
36
+ #### Mission Library (Phase S — Mission Hardening)
37
+ - 6 named, repeatable mission types: feature-ship, bugfix, treatment, docs-release, security-hardening, research-launch
38
+ - Each mission declares: pack, role chain, artifact flow, escalation branches, honest-partial definition, stop conditions, dispatch defaults, trial evidence
39
+ - Mission runner: create → step through → complete/fail → generate completion report
40
+ - Completion proof reporter with honest-partial and formatted text output
41
+ - `roleos mission list` — list all missions
42
+ - `roleos mission show <key>` — full mission detail
43
+ - `roleos mission suggest <text>` — signal-based mission suggestion
44
+ - `roleos mission validate [key]` — validate mission wiring against packs/roles
45
+
46
+ #### Mission Runner Engine
47
+ - `createRun()` — instantiate a mission with tracked steps
48
+ - `startNextStep()` / `completeStep()` / `failStep()` — step lifecycle
49
+ - `recordEscalation()` — re-opens completed steps on escalation loops
50
+ - `getRunPosition()` / `getArtifactChain()` — run introspection
51
+ - `generateCompletionReport()` / `formatCompletionReport()` — honest outcome reporting
52
+
53
+ ### Evidence
54
+ - 465 tests, zero failures (67 new)
55
+ - All 6 missions validate against live pack/role catalog
56
+ - Full lifecycle tests: end-to-end runs, escalation loops, partial completions, failure reporting
57
+
3
58
  ## 1.7.0
4
59
 
5
60
  ### Added
package/README.md CHANGED
@@ -28,12 +28,35 @@ Role OS is the professional way to use multi-Claude. It prevents the specific fa
28
28
 
29
29
  ## How it works
30
30
 
31
- 1. **Create a packet** define what needs to exist when the work is done
32
- 2. **Route through a chain** — `roleos route` scores all 31 roles against the packet content, assembles a dynamic chain ordered by work phase, and explains why each role was chosen
33
- 3. **Validate the team** — 4-pass conflict detection catches hard conflicts, sequence errors, redundancy, and coverage gaps before execution starts
34
- 4. **Each role produces a handoff** structured output with evidence items that reduce ambiguity for the next role
35
- 5. **Critic reviews against contract** accepts, rejects, or blocks based on structured evidence, not impression
36
- 6. **Recovery routes automatically** blocked or rejected work gets routed to the right resolver with a reason, recovery type, and required artifact
31
+ Describe your task. Role OS decides the right level of orchestration automatically.
32
+
33
+ ```bash
34
+ roleos start "fix the crash in save handler"
35
+ # MISSION: Bugfix & Diagnosis (70% confidence)
36
+ # Chain: Repo Researcher Backend Engineer Test Engineer Critic Reviewer
37
+
38
+ roleos start "add a new export command"
39
+ # → PACK: Feature Build (50% confidence)
40
+ # Roles: Orchestrator, Product Strategist, Spec Writer, Backend Engineer, Test Engineer, Critic Reviewer
41
+
42
+ roleos start "something completely novel"
43
+ # → FREE-ROUTING (10% confidence)
44
+ # Hint: Create a packet and run `roleos route` for role-level routing
45
+ ```
46
+
47
+ **The fallback ladder:**
48
+
49
+ 1. **Mission** — when the task matches a proven recurring workflow (bugfix, treatment, feature-ship, docs, security, research). Known role chain, artifact flow, escalation branches, and honest-partial definitions.
50
+ 2. **Pack** — when the task is a known family but not a full mission shape. 7 calibrated team packs with auto-selection and mismatch guards.
51
+ 3. **Free routing** — when the task is novel, mixed, or uncertain. Scores all 31 roles against packet content and assembles a dynamic chain.
52
+
53
+ The system never forces work through the wrong abstraction. It explains why it chose each level and offers alternatives.
54
+
55
+ **Once routed:**
56
+
57
+ 1. **Each role produces a handoff** — structured output with evidence items that reduce ambiguity for the next role
58
+ 2. **Critic reviews against contract** — accepts, rejects, or blocks based on structured evidence, not impression
59
+ 3. **Recovery routes automatically** — blocked or rejected work gets routed to the right resolver with a reason, recovery type, and required artifact
37
60
 
38
61
  ## Org rollout state
39
62
 
@@ -73,11 +96,20 @@ Every role has a full contract: mission, use when, do not use when, expected inp
73
96
  ```bash
74
97
  npx role-os init
75
98
 
76
- # Fill context/ files for your project, then:
99
+ # Describe what you need Role OS picks the right level:
100
+ roleos start "fix the crash in save handler"
101
+
102
+ # Or go manual:
77
103
  roleos packet new feature
78
104
  roleos route .claude/packets/my-feature.md
79
105
  roleos review .claude/packets/my-feature.md accept
80
106
  roleos status
107
+
108
+ # Explore missions and packs:
109
+ roleos mission list
110
+ roleos mission show bugfix
111
+ roleos packs list
112
+ roleos packs show feature
81
113
  ```
82
114
 
83
115
  ## When not to use Role OS
@@ -132,30 +164,26 @@ These are non-negotiable. If a change weakens any of them, reject it.
132
164
  role-os/
133
165
  bin/roleos.mjs ← CLI entrypoint
134
166
  src/
167
+ entry.mjs ← Unified entry: mission → pack → free routing
168
+ entry-cmd.mjs ← `roleos start` CLI command
169
+ mission.mjs ← 6 named mission types (feature, bugfix, treatment, docs, security, research)
170
+ mission-run.mjs ← Mission runner: create → step → complete → report
171
+ mission-cmd.mjs ← `roleos mission` CLI commands
135
172
  route.mjs ← 31-role routing + dynamic chain builder
173
+ packs.mjs ← 7 calibrated team packs + auto-selection
136
174
  conflicts.mjs ← 4-pass conflict detection
137
175
  escalation.mjs ← Auto-routing for blocked/rejected/split
138
176
  evidence.mjs ← Structured evidence + role-aware requirements
139
177
  dispatch.mjs ← Runtime dispatch manifests for multi-claude
140
- trial.mjs Role execution trial framework
141
- packet.mjs Packet creation
142
- review.mjs Verdict recording + escalation integration
143
- status.mjs ← Active packet + verdict status
144
- test/
145
- route.test.mjs 49 tests (routing + disambiguation)
146
- conflicts.test.mjs 13 tests (4 conflict types)
147
- escalation.test.mjs 22 tests (blocked/rejected/conflict/split)
148
- evidence.test.mjs 23 tests (schema + sufficiency)
149
- dispatch.test.mjs ← 21 tests (manifests + state + escalation packets)
150
- trial.test.mjs ← 12 tests (trial framework)
151
- cli.test.mjs ← 22 tests (CLI integration)
152
- .claude/
153
- agents/ ← 31 role contracts across 8 packs
154
- schemas/ ← Packet, handoff, verdict formats
155
- policy/ ← Routing rules, permissions, escalation, done
156
- workflows/ ← Ship feature, fix bug, launch update, full treatment
157
- context/ ← Fill these for your repo
158
- trials/ ← Execution trial packets + results
178
+ artifacts.mjs 20 per-role artifact contracts + 7 pack handoffs
179
+ decompose.mjs Composite task detection + splitting
180
+ composite.mjs Dependency-ordered execution + recovery
181
+ replan.mjs ← Mid-run adaptive replanning
182
+ calibration.mjs ← Outcome recording + weight tuning
183
+ hooks.mjs 5 lifecycle hooks for runtime enforcement
184
+ session.mjs Session scaffolding + doctor
185
+ test/ 527 tests across 20 test files
186
+ starter-pack/ Drop-in role contracts, policies, schemas, workflows
159
187
  ```
160
188
 
161
189
  ## Security
@@ -181,6 +209,22 @@ Role OS operates **locally only**. It copies markdown templates and writes packe
181
209
  | **Session spine** | `roleos init claude` scaffolds CLAUDE.md, /roleos-route, /roleos-review, /roleos-status. `roleos doctor` verifies wiring. Route cards prove engagement. | ✓ Shipped |
182
210
  | **Hook spine** | 5 lifecycle hooks (SessionStart, PromptSubmit, PreToolUse, SubagentStart, Stop). Advisory enforcement: route card reminders, write-tool gating, subagent role injection, completion audit. | ✓ Shipped |
183
211
  | **Artifact spine** | 20 per-role artifact contracts. 7 pack handoff contracts. Structural validation. Chain completeness checks. Downstream roles never guess what they received. | ✓ Shipped |
212
+ | **Mission library** | 6 named missions (feature-ship, bugfix, treatment, docs-release, security-hardening, research-launch). Each declares pack, role chain, artifact flow, escalation branches, honest-partial definition. All 6 trial-run and hardened. | ✓ Shipped |
213
+ | **Mission runner** | Create runs, step through with tracked state, complete/fail with honest reporting. Blocked-step propagation, out-of-chain escalation warnings, last-step re-opening. | ✓ Shipped |
214
+ | **Unified entry** | `roleos start` decides mission vs pack vs free routing automatically. Fallback ladder with confidence scores, alternatives, and composite detection. | ✓ Shipped |
215
+
216
+ ## 6 missions
217
+
218
+ | Mission | Pack | Roles | When to use |
219
+ |---------|------|-------|-------------|
220
+ | `feature-ship` | feature | 5 | Full feature delivery: scope → spec → implement → test → review |
221
+ | `bugfix` | bugfix | 4 | Diagnose root cause, fix, test, verify |
222
+ | `treatment` | treatment | 4 | Shipcheck + polish + docs + CI verify + review |
223
+ | `docs-release` | docs | 2 | Write/update documentation, release notes |
224
+ | `security-hardening` | security | 4 | Threat model, audit, fix vulnerabilities, re-audit, verify |
225
+ | `research-launch` | research | 4 | Frame question, research, document findings, decide |
226
+
227
+ Each mission includes honest-partial definitions — when work stalls, the system documents what was completed and what remains instead of bluffing completion.
184
228
 
185
229
  ## Status
186
230
 
@@ -193,7 +237,9 @@ Role OS operates **locally only**. It copies markdown templates and writes packe
193
237
  - v1.4.0: Session spine — `roleos init claude`, `roleos doctor`, route cards, /roleos-route + /roleos-review + /roleos-status commands. 335 tests.
194
238
  - v1.5.0: Hook spine — 5 lifecycle hooks for runtime enforcement. 358 tests.
195
239
  - v1.6.0: Artifact spine — 20 per-role artifact contracts, 7 pack handoff contracts, structural validation. 385 tests.
196
- - **v1.7.0**: Completion proof — real tasks run through the full stack. `roleos artifacts` CLI. Honest escalation on structural fixes. 398 tests.
240
+ - v1.7.0: Completion proof — real tasks run through the full stack. `roleos artifacts` CLI. Honest escalation on structural fixes. 398 tests.
241
+ - v1.8.0: Mission library (Phase S) — 6 named missions, runner engine, completion reports. Hardened from 6 real trial runs. 481 tests.
242
+ - **v1.9.0**: Unified entry path (Phase T) — `roleos start` auto-decides mission vs pack vs free routing. Fallback ladder, composite detection, entry-path comparison trials. 527 tests.
197
243
 
198
244
  ## License
199
245
 
package/bin/roleos.mjs CHANGED
@@ -11,6 +11,8 @@ import { statusCommand } from "../src/status.mjs";
11
11
  import { packsCommand } from "../src/packs-cmd.mjs";
12
12
  import { scaffoldClaude, doctor, formatDoctor } from "../src/session.mjs";
13
13
  import { artifactsCommand } from "../src/artifacts-cmd.mjs";
14
+ import { missionCommand } from "../src/mission-cmd.mjs";
15
+ import { startCommand } from "../src/entry-cmd.mjs";
14
16
 
15
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
16
18
  const VERSION = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8")).version;
@@ -20,6 +22,7 @@ function printHelp() {
20
22
  roleos v${VERSION} — Role OS bootstrap CLI
21
23
 
22
24
  Usage:
25
+ roleos start <task> Decide entry path: mission, pack, or free routing
23
26
  roleos init Scaffold Role OS into .claude/
24
27
  roleos init --force Update canonical files (protects context/)
25
28
  roleos packet new <type> Create a new packet (feature|integration|identity)
@@ -35,6 +38,10 @@ Usage:
35
38
  roleos artifacts show <role> Show artifact contract for a role
36
39
  roleos artifacts validate <role> <file> Validate a file against a contract
37
40
  roleos artifacts chain <pack> Show pack handoff flow
41
+ roleos mission list List all missions
42
+ roleos mission show <key> Show full mission detail
43
+ roleos mission suggest <text> Suggest a mission for a task
44
+ roleos mission validate [key] Validate mission wiring
38
45
  roleos doctor Verify repo is wired for Role OS sessions
39
46
  roleos help Show this help
40
47
 
@@ -70,6 +77,9 @@ const args = process.argv.slice(3);
70
77
 
71
78
  try {
72
79
  switch (command) {
80
+ case "start":
81
+ await startCommand(args);
82
+ break;
73
83
  case "init":
74
84
  if (args[0] === "claude") {
75
85
  const force = args.includes("--force");
@@ -111,6 +121,9 @@ try {
111
121
  case "artifacts":
112
122
  await artifactsCommand(args);
113
123
  break;
124
+ case "mission":
125
+ await missionCommand(args);
126
+ break;
114
127
  case "help":
115
128
  case "--help":
116
129
  case "-h":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "role-os",
3
- "version": "1.7.0",
3
+ "version": "1.9.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": {
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Entry CLI — Phase T (v1.9.0)
3
+ *
4
+ * roleos start <task description> Decide best entry path automatically
5
+ * roleos start --json <text> Output as JSON
6
+ */
7
+
8
+ import { decideEntry, formatEntryDecision } from "./entry.mjs";
9
+
10
+ /**
11
+ * @param {string[]} args
12
+ */
13
+ export async function startCommand(args) {
14
+ const isJson = args.includes("--json");
15
+ const textArgs = args.filter(a => a !== "--json");
16
+ const text = textArgs.join(" ").trim();
17
+
18
+ if (!text) {
19
+ const err = new Error(
20
+ "Usage: roleos start <task description>\n" +
21
+ " Decides whether to route through a mission, pack, or free routing.\n\n" +
22
+ "Examples:\n" +
23
+ ' roleos start "fix the crash in save handler"\n' +
24
+ ' roleos start "add a new export command"\n' +
25
+ ' roleos start "run the full treatment before publishing"'
26
+ );
27
+ err.exitCode = 1;
28
+ err.hint = "Describe the task you want to accomplish.";
29
+ throw err;
30
+ }
31
+
32
+ const decision = decideEntry(text);
33
+
34
+ if (isJson) {
35
+ console.log(JSON.stringify(decision, null, 2));
36
+ } else {
37
+ console.log("");
38
+ console.log(formatEntryDecision(decision));
39
+ console.log("");
40
+
41
+ // Action guidance
42
+ if (decision.level === "mission") {
43
+ console.log("Next steps:");
44
+ console.log(` 1. roleos mission show ${decision.mission.key} # review the mission`);
45
+ console.log(` 2. Create a packet and start working through the mission steps`);
46
+ console.log(` 3. Use the mission runner to track progress and artifacts`);
47
+ } else if (decision.level === "pack") {
48
+ console.log("Next steps:");
49
+ console.log(` 1. roleos packs show ${decision.pack.key} # review the pack`);
50
+ console.log(` 2. roleos packet new feature # create a packet`);
51
+ console.log(` 3. roleos route <packet> --pack=${decision.pack.key} # route with pack`);
52
+ } else {
53
+ console.log("Next steps:");
54
+ console.log(" 1. roleos packet new feature # create a packet");
55
+ console.log(" 2. roleos route <packet> # free routing");
56
+ }
57
+ console.log("");
58
+ }
59
+ }
package/src/entry.mjs ADDED
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Unified Entry Path — Phase T (v1.9.0)
3
+ *
4
+ * When a user brings Role-OS a task, the system decides the best
5
+ * abstraction level automatically:
6
+ *
7
+ * 1. MISSION — when the task matches a proven recurring workflow
8
+ * 2. PACK — when the task is a known family but not a full mission shape
9
+ * 3. FREE ROUTING — when the task is novel, mixed, or uncertain
10
+ *
11
+ * The entry path is honest: it explains WHY it chose each level,
12
+ * and never forces work through the wrong abstraction.
13
+ */
14
+
15
+ import { suggestMission, getMission, MISSIONS } from "./mission.mjs";
16
+ import { suggestPack, getPack, checkPackMismatch, TEAM_PACKS } from "./packs.mjs";
17
+ import { detectComposite } from "./decompose.mjs";
18
+ import { ROLE_CATALOG } from "./route.mjs";
19
+
20
+ // ── Entry levels ────────────────────────────────────────────────────────────
21
+
22
+ /**
23
+ * @typedef {"mission"|"pack"|"free-routing"} EntryLevel
24
+ */
25
+
26
+ /**
27
+ * @typedef {Object} EntryDecision
28
+ * @property {EntryLevel} level - Which abstraction level was chosen
29
+ * @property {string} reason - Human-readable explanation
30
+ * @property {number} confidence - 0-1 confidence in this choice
31
+ * @property {object|null} mission - Mission details (when level=mission)
32
+ * @property {object|null} pack - Pack details (when level=pack)
33
+ * @property {object|null} freeRouting - Routing hints (when level=free-routing)
34
+ * @property {object|null} alternative - Next-best option if operator disagrees
35
+ * @property {boolean} isComposite - Whether the task looks like multiple jobs
36
+ * @property {string[]} warnings - Any concerns about the choice
37
+ */
38
+
39
+ // ── Confidence thresholds ───────────────────────────────────────────────────
40
+
41
+ const MISSION_HIGH_THRESHOLD = 0.7; // strong mission match → use mission
42
+ const MISSION_MEDIUM_THRESHOLD = 0.4; // decent match → mission if pack also agrees
43
+ const PACK_HIGH_THRESHOLD = 0.6; // strong pack match → use pack
44
+ const PACK_MEDIUM_THRESHOLD = 0.3; // decent match → use pack as fallback
45
+
46
+ // ── Core entry function ─────────────────────────────────────────────────────
47
+
48
+ /**
49
+ * Decide the best entry path for a task description.
50
+ *
51
+ * The fallback ladder:
52
+ * mission (when fit is strong)
53
+ * → pack (when mission fit is weak but task family is clear)
54
+ * → free routing (when the task is novel or mixed)
55
+ *
56
+ * @param {string} taskDescription
57
+ * @returns {EntryDecision}
58
+ */
59
+ export function decideEntry(taskDescription) {
60
+ if (!taskDescription || taskDescription.trim().length === 0) {
61
+ return {
62
+ level: "free-routing",
63
+ reason: "No task description provided — defaulting to free routing.",
64
+ confidence: 0,
65
+ mission: null,
66
+ pack: null,
67
+ freeRouting: { hint: "Provide a task description for better routing." },
68
+ alternative: null,
69
+ isComposite: false,
70
+ warnings: ["Empty task description"],
71
+ };
72
+ }
73
+
74
+ const text = taskDescription.trim();
75
+ const warnings = [];
76
+
77
+ // Step 1: Check for composite tasks (multi-job detection)
78
+ const composite = detectComposite(text);
79
+ const isComposite = composite.isComposite;
80
+ if (isComposite) {
81
+ warnings.push(
82
+ `Task looks composite (${composite.detectedCategories.map(c => c.category).join(" + ")}). ` +
83
+ `Consider decomposing before routing.`
84
+ );
85
+ }
86
+
87
+ // Step 2: Try mission suggestion
88
+ const missionSuggestion = suggestMission(text);
89
+ const missionScore = scoreMissionFit(missionSuggestion);
90
+
91
+ // Step 3: Try pack suggestion
92
+ const packSuggestion = suggestPack(text);
93
+ const packScore = scorePackFit(packSuggestion);
94
+
95
+ // Step 4: Check agreement (mission and pack point to the same family)
96
+ const agreement = checkAgreement(missionSuggestion, packSuggestion);
97
+
98
+ // Step 5: Apply the fallback ladder
99
+ return applyLadder(text, missionSuggestion, missionScore, packSuggestion, packScore, agreement, isComposite, composite, warnings);
100
+ }
101
+
102
+ // ── Scoring helpers ─────────────────────────────────────────────────────────
103
+
104
+ /**
105
+ * Normalize mission suggestion into a 0-1 score.
106
+ */
107
+ function scoreMissionFit(suggestion) {
108
+ if (!suggestion) return 0;
109
+ switch (suggestion.confidence) {
110
+ case "high": return 0.9;
111
+ case "medium": return 0.55;
112
+ case "low": return 0.25;
113
+ default: return 0;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Normalize pack suggestion into a 0-1 score.
119
+ */
120
+ function scorePackFit(suggestion) {
121
+ if (!suggestion) return 0;
122
+ switch (suggestion.confidence) {
123
+ case "high": return 0.85;
124
+ case "medium": return 0.5;
125
+ case "low": return 0.2;
126
+ default: return 0;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Check if mission and pack suggestions agree (point to same family).
132
+ */
133
+ function checkAgreement(missionSuggestion, packSuggestion) {
134
+ if (!missionSuggestion || !packSuggestion) return false;
135
+ const mission = getMission(missionSuggestion.mission);
136
+ if (!mission) return false;
137
+ return mission.pack === packSuggestion.pack;
138
+ }
139
+
140
+ // ── Fallback ladder ─────────────────────────────────────────────────────────
141
+
142
+ function applyLadder(text, missionSug, missionScore, packSug, packScore, agreement, isComposite, composite, warnings) {
143
+ // ── MISSION: strong match, or medium match with pack agreement ──────────
144
+ if (missionScore >= MISSION_HIGH_THRESHOLD) {
145
+ const mission = getMission(missionSug.mission);
146
+ return {
147
+ level: "mission",
148
+ reason: `Strong mission match: "${mission.name}" (${missionSug.confidence} confidence, ${missionSug.reason}). ` +
149
+ `This is a proven recurring workflow with known role chain, artifact flow, and escalation branches.`,
150
+ confidence: missionScore,
151
+ mission: {
152
+ key: missionSug.mission,
153
+ name: mission.name,
154
+ pack: mission.pack,
155
+ roleChain: mission.roleChain,
156
+ entryPath: mission.entryPath,
157
+ stepCount: mission.artifactFlow.length,
158
+ },
159
+ pack: null,
160
+ freeRouting: null,
161
+ alternative: packSug ? {
162
+ level: "pack",
163
+ key: packSug.pack,
164
+ name: TEAM_PACKS[packSug.pack]?.name,
165
+ confidence: packScore,
166
+ } : null,
167
+ isComposite,
168
+ warnings,
169
+ };
170
+ }
171
+
172
+ if (missionScore >= MISSION_MEDIUM_THRESHOLD && agreement) {
173
+ const mission = getMission(missionSug.mission);
174
+ return {
175
+ level: "mission",
176
+ reason: `Mission match: "${mission.name}" (${missionSug.confidence} confidence). ` +
177
+ `Pack suggestion agrees (${packSug.pack}), reinforcing the mission choice.`,
178
+ confidence: Math.min(missionScore + 0.15, 0.85), // boost from agreement
179
+ mission: {
180
+ key: missionSug.mission,
181
+ name: mission.name,
182
+ pack: mission.pack,
183
+ roleChain: mission.roleChain,
184
+ entryPath: mission.entryPath,
185
+ stepCount: mission.artifactFlow.length,
186
+ },
187
+ pack: null,
188
+ freeRouting: null,
189
+ alternative: {
190
+ level: "pack",
191
+ key: packSug.pack,
192
+ name: TEAM_PACKS[packSug.pack]?.name,
193
+ confidence: packScore,
194
+ },
195
+ isComposite,
196
+ warnings,
197
+ };
198
+ }
199
+
200
+ // ── PACK: strong pack match but mission doesn't fit well ─────────────────
201
+ if (packScore >= PACK_HIGH_THRESHOLD) {
202
+ const pack = TEAM_PACKS[packSug.pack];
203
+ return {
204
+ level: "pack",
205
+ reason: `Task family match: "${pack.name}" pack (${packSug.confidence} confidence). ` +
206
+ (missionScore > 0
207
+ ? `Mission "${getMission(missionSug.mission)?.name}" was considered but fit was weak (${missionSug.confidence}).`
208
+ : `No mission matched — task family is clear but not a full recurring workflow.`),
209
+ confidence: packScore,
210
+ mission: null,
211
+ pack: {
212
+ key: packSug.pack,
213
+ name: pack.name,
214
+ roles: pack.roles,
215
+ chainOrder: pack.chainOrder,
216
+ description: pack.description,
217
+ },
218
+ freeRouting: null,
219
+ alternative: missionSug ? {
220
+ level: "mission",
221
+ key: missionSug.mission,
222
+ name: getMission(missionSug.mission)?.name,
223
+ confidence: missionScore,
224
+ } : null,
225
+ isComposite,
226
+ warnings,
227
+ };
228
+ }
229
+
230
+ if (packScore >= PACK_MEDIUM_THRESHOLD) {
231
+ const pack = TEAM_PACKS[packSug.pack];
232
+ return {
233
+ level: "pack",
234
+ reason: `Weak task family match: "${pack.name}" pack (${packSug.confidence} confidence). ` +
235
+ `Using pack as starting point — operator may want to adjust.`,
236
+ confidence: packScore,
237
+ mission: null,
238
+ pack: {
239
+ key: packSug.pack,
240
+ name: pack.name,
241
+ roles: pack.roles,
242
+ chainOrder: pack.chainOrder,
243
+ description: pack.description,
244
+ },
245
+ freeRouting: null,
246
+ alternative: {
247
+ level: "free-routing",
248
+ confidence: 0,
249
+ },
250
+ isComposite,
251
+ warnings: [...warnings, "Low pack confidence — consider free routing if this doesn't fit"],
252
+ };
253
+ }
254
+
255
+ // ── FREE ROUTING: novel, mixed, or uncertain ─────────────────────────────
256
+ const freeHints = buildFreeRoutingHints(text, missionSug, packSug, isComposite, composite);
257
+
258
+ return {
259
+ level: "free-routing",
260
+ reason: freeHints.reason,
261
+ confidence: 0.1,
262
+ mission: null,
263
+ pack: null,
264
+ freeRouting: freeHints,
265
+ alternative: missionSug ? {
266
+ level: "mission",
267
+ key: missionSug.mission,
268
+ name: getMission(missionSug.mission)?.name,
269
+ confidence: missionScore,
270
+ } : packSug ? {
271
+ level: "pack",
272
+ key: packSug.pack,
273
+ name: TEAM_PACKS[packSug.pack]?.name,
274
+ confidence: packScore,
275
+ } : null,
276
+ isComposite,
277
+ warnings: [...warnings, "Free routing selected — task will be scored against all 31 roles"],
278
+ };
279
+ }
280
+
281
+ function buildFreeRoutingHints(text, missionSug, packSug, isComposite, composite) {
282
+ let reason;
283
+ if (isComposite) {
284
+ reason = `Task looks composite (${composite.detectedCategories.map(c => c.category).join(" + ")}). ` +
285
+ `No single mission or pack covers all parts — use free routing with decomposition.`;
286
+ } else if (!missionSug && !packSug) {
287
+ reason = "No mission or pack matched. Task is novel — free routing will score all 31 roles.";
288
+ } else {
289
+ reason = "Mission and pack matches were too weak to commit. Free routing will let role scoring decide.";
290
+ }
291
+
292
+ return {
293
+ reason,
294
+ hint: isComposite
295
+ ? "Consider running `roleos route` on a packet for each sub-task."
296
+ : "Create a packet with `roleos packet new` and run `roleos route` for role-level routing.",
297
+ suggestedRoleCount: isComposite ? composite.detectedCategories.length * 3 : null,
298
+ };
299
+ }
300
+
301
+ // ── Format for display ──────────────────────────────────────────────────────
302
+
303
+ /**
304
+ * Format an entry decision as human-readable text.
305
+ * @param {EntryDecision} decision
306
+ * @returns {string}
307
+ */
308
+ export function formatEntryDecision(decision) {
309
+ const lines = [];
310
+ const conf = Math.round(decision.confidence * 100);
311
+
312
+ lines.push(`Entry Decision: ${decision.level.toUpperCase()} (${conf}% confidence)`);
313
+ lines.push("");
314
+ lines.push(`Reason: ${decision.reason}`);
315
+
316
+ if (decision.level === "mission" && decision.mission) {
317
+ lines.push("");
318
+ lines.push(`Mission: ${decision.mission.name} (${decision.mission.key})`);
319
+ lines.push(`Pack: ${decision.mission.pack}`);
320
+ lines.push(`Entry: ${decision.mission.entryPath}`);
321
+ lines.push(`Chain: ${decision.mission.roleChain.join(" → ")}`);
322
+ lines.push(`Steps: ${decision.mission.stepCount}`);
323
+ }
324
+
325
+ if (decision.level === "pack" && decision.pack) {
326
+ lines.push("");
327
+ lines.push(`Pack: ${decision.pack.name} (${decision.pack.key})`);
328
+ lines.push(`Roles: ${decision.pack.roles.join(", ")}`);
329
+ lines.push(`Chain: ${decision.pack.chainOrder}`);
330
+ }
331
+
332
+ if (decision.level === "free-routing" && decision.freeRouting) {
333
+ lines.push("");
334
+ lines.push(`Hint: ${decision.freeRouting.hint}`);
335
+ }
336
+
337
+ if (decision.alternative) {
338
+ lines.push("");
339
+ const altConf = Math.round((decision.alternative.confidence || 0) * 100);
340
+ lines.push(`Alternative: ${decision.alternative.level}${decision.alternative.name ? ` (${decision.alternative.name})` : ""} at ${altConf}% confidence`);
341
+ }
342
+
343
+ if (decision.isComposite) {
344
+ lines.push("");
345
+ lines.push(`Note: This task looks composite — consider decomposing before proceeding.`);
346
+ }
347
+
348
+ if (decision.warnings.length > 0) {
349
+ lines.push("");
350
+ lines.push("Warnings:");
351
+ for (const w of decision.warnings) {
352
+ lines.push(` - ${w}`);
353
+ }
354
+ }
355
+
356
+ return lines.join("\n");
357
+ }