@lsctech/polaris 0.5.10 → 0.5.12

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.
@@ -139,6 +139,18 @@ function resolveProviderAndMode(options, role, config, routerContext) {
139
139
  },
140
140
  compatibilityMode: false,
141
141
  });
142
+ const workerPolicy = exec?.providerPolicy?.[role];
143
+ const noFallback = workerPolicy?.noFallback === true;
144
+ const fallbackEligible = !noFallback && decision.providersTried.length > 1;
145
+ const routingSummary = {
146
+ selected_provider: decision.selectedProvider ?? null,
147
+ selected_adapter: adapter,
148
+ selection_reason: decision.selectionReason,
149
+ effective_policy_order: decision.providersTried,
150
+ compatibility_mode: false,
151
+ registry_present: true,
152
+ fallback_eligible: fallbackEligible,
153
+ };
142
154
  return {
143
155
  provider: decision.selectedProvider,
144
156
  mode: decision.mode,
@@ -157,91 +169,108 @@ function resolveProviderAndMode(options, role, config, routerContext) {
157
169
  activeSlotsByProvider: routerContext?.activeSlotsByProvider,
158
170
  quotaAvailableByProvider: routerContext?.quotaAvailableByProvider,
159
171
  },
172
+ routingSummary,
160
173
  };
161
174
  }
162
175
  function resolveProviderAndModeLegacy(options, role, adapter, exec) {
163
- if (options.provider) {
164
- return {
165
- provider: options.provider,
166
- mode: "direct-worker",
167
- adapter,
168
- selectionReason: "cli-provider-override",
169
- overrideSource: "dispatch-flag",
170
- providersTried: [options.provider],
171
- };
172
- }
173
176
  const rolePolicy = exec?.providerPolicy?.[role];
174
177
  const roleProviders = rolePolicy?.providers ?? [];
178
+ const noFallback = rolePolicy?.noFallback === true;
175
179
  const rotation = exec?.rotation ?? [];
176
- if (roleProviders.length > 0) {
180
+ const configuredProviders = new Set(Object.keys(exec?.providers ?? {}));
181
+ let provider;
182
+ let selectionReason;
183
+ let overrideSource;
184
+ let fallbackFrom;
185
+ let fallbackReason;
186
+ let exhaustedReason;
187
+ let effectivePolicyOrder;
188
+ if (options.provider) {
189
+ provider = options.provider;
190
+ selectionReason = "cli-provider-override";
191
+ overrideSource = "dispatch-flag";
192
+ effectivePolicyOrder = [options.provider];
193
+ }
194
+ else if (roleProviders.length > 0) {
177
195
  // When a role policy is configured, filter the rotation to only include
178
196
  // providers that are allowed by the policy. This ensures that a provider
179
197
  // present in rotation but absent from the policy is never selected.
180
198
  const allowedByPolicy = new Set(roleProviders);
181
- const configuredProviders = new Set(Object.keys(exec?.providers ?? {}));
182
199
  const filteredRotation = rotation.filter((p) => allowedByPolicy.has(p));
200
+ const availablePolicyProviders = roleProviders.filter((p) => configuredProviders.has(p));
183
201
  if (filteredRotation.length > 0 && filteredRotation[0]) {
184
- return {
185
- provider: filteredRotation[0],
186
- mode: "direct-worker",
187
- adapter,
188
- selectionReason: "policy-filtered-rotation",
189
- providersTried: [filteredRotation[0]],
190
- };
202
+ provider = filteredRotation[0];
203
+ selectionReason = "policy-filtered-rotation";
204
+ effectivePolicyOrder = filteredRotation.slice();
205
+ // Surface remaining configured policy providers as fallback alternatives
206
+ for (const p of availablePolicyProviders) {
207
+ if (!effectivePolicyOrder.includes(p)) {
208
+ effectivePolicyOrder.push(p);
209
+ }
210
+ }
191
211
  }
192
- // No rotation entry is in the policy — fall back to first policy provider
193
- // that is actually configured. If none is configured, fail before mutation.
194
- const availablePolicyProviders = roleProviders.filter((p) => configuredProviders.has(p));
195
- if (availablePolicyProviders.length > 0 && availablePolicyProviders[0]) {
196
- return {
197
- provider: availablePolicyProviders[0],
198
- mode: "direct-worker",
199
- adapter,
200
- selectionReason: "role-policy",
201
- providersTried: [availablePolicyProviders[0]],
202
- };
212
+ else if (availablePolicyProviders.length > 0 && availablePolicyProviders[0]) {
213
+ provider = availablePolicyProviders[0];
214
+ selectionReason = "role-policy";
215
+ effectivePolicyOrder = availablePolicyProviders.slice();
216
+ }
217
+ else {
218
+ // No configured provider satisfies the policy — fail clearly before dispatch.
219
+ throw new Error(`provider dispatch forbidden: no configured provider satisfies the "${role}" role policy (policy=${roleProviders.join(",")})`);
203
220
  }
204
- // No configured provider satisfies the policy — fail clearly before dispatch.
205
- throw new Error(`provider dispatch forbidden: no configured provider satisfies the "${role}" role policy (policy=${roleProviders.join(",")})`);
206
221
  }
207
- const roleProvider = exec?.roles?.[role]?.provider;
208
- if (roleProvider) {
209
- return {
210
- provider: roleProvider,
211
- mode: "direct-worker",
212
- adapter,
213
- selectionReason: "role-config",
214
- providersTried: [roleProvider],
215
- };
222
+ else if (exec?.roles?.[role]?.provider) {
223
+ provider = exec.roles[role].provider;
224
+ selectionReason = "role-config";
225
+ effectivePolicyOrder = [provider];
226
+ // If a role policy exists for this role, include its other providers as alternatives
227
+ for (const p of roleProviders) {
228
+ if (p !== provider && !effectivePolicyOrder.includes(p)) {
229
+ effectivePolicyOrder.push(p);
230
+ }
231
+ }
216
232
  }
217
- if (rotation.length > 0 && rotation[0]) {
218
- return {
219
- provider: rotation[0],
220
- mode: "direct-worker",
221
- adapter,
222
- selectionReason: "config-rotation",
223
- providersTried: [rotation[0]],
224
- };
233
+ else if (rotation.length > 0 && rotation[0]) {
234
+ provider = rotation[0];
235
+ selectionReason = "config-rotation";
236
+ effectivePolicyOrder = rotation.slice();
225
237
  }
226
- const providers = Object.keys(exec?.providers ?? {});
227
- if (providers.length > 0 && providers[0]) {
228
- return {
229
- provider: providers[0],
230
- mode: "direct-worker",
231
- adapter,
232
- selectionReason: "config-first-provider",
233
- fallbackFrom: "rotation",
234
- fallbackReason: "rotation-empty",
235
- providersTried: [providers[0]],
236
- };
238
+ else {
239
+ const providers = Object.keys(exec?.providers ?? {});
240
+ if (providers.length > 0 && providers[0]) {
241
+ provider = providers[0];
242
+ selectionReason = "config-first-provider";
243
+ fallbackFrom = "rotation";
244
+ fallbackReason = "rotation-empty";
245
+ effectivePolicyOrder = providers.slice();
246
+ }
247
+ else {
248
+ selectionReason = "delegated-no-provider";
249
+ exhaustedReason = "no-configured-provider";
250
+ effectivePolicyOrder = [];
251
+ }
237
252
  }
253
+ const fallbackEligible = !noFallback && effectivePolicyOrder.length > 1;
254
+ const routingSummary = {
255
+ selected_provider: provider ?? null,
256
+ selected_adapter: adapter,
257
+ selection_reason: selectionReason,
258
+ effective_policy_order: effectivePolicyOrder,
259
+ compatibility_mode: true,
260
+ registry_present: false,
261
+ fallback_eligible: fallbackEligible,
262
+ };
238
263
  return {
239
- provider: undefined,
240
- mode: "delegated",
264
+ provider,
265
+ mode: provider ? "direct-worker" : "delegated",
241
266
  adapter,
242
- selectionReason: "delegated-no-provider",
243
- providersTried: [],
244
- exhaustedReason: "no-configured-provider",
267
+ selectionReason,
268
+ overrideSource,
269
+ fallbackFrom,
270
+ fallbackReason,
271
+ providersTried: provider ? [provider] : [],
272
+ exhaustedReason,
273
+ routingSummary,
245
274
  };
246
275
  }
247
276
  function fail(message) {
@@ -464,6 +493,7 @@ function emitProviderSelected(telemetryFile, dispatchId, runId, childId, decisio
464
493
  ...(decision.fallbackReason ? { fallback_reason: decision.fallbackReason } : {}),
465
494
  ...(decision.providersTried.length > 0 ? { providers_tried: decision.providersTried } : {}),
466
495
  ...(decision.exhaustedReason ? { router_exhausted_reason: decision.exhaustedReason } : {}),
496
+ ...(decision.routingSummary ? { routing_summary: decision.routingSummary } : {}),
467
497
  ...(decision.routerEvidence?.candidates?.length
468
498
  ? {
469
499
  router_candidates: decision.routerEvidence.candidates.map((candidate) => ({
@@ -484,6 +514,7 @@ function emitProviderSelected(telemetryFile, dispatchId, runId, childId, decisio
484
514
  active_slots: candidate.evidence.activeSlots,
485
515
  slot_limit: candidate.evidence.slotLimit,
486
516
  policy_matched: candidate.evidence.policyMatched,
517
+ fallback_eligible: candidate.evidence.fallbackEligible,
487
518
  },
488
519
  })),
489
520
  }
@@ -574,7 +605,7 @@ function assertProviderAllowedForRole(role, provider, policy, telemetryFile, dis
574
605
  /**
575
606
  * Create a dispatch record with all required evidence.
576
607
  */
577
- function createDispatchRecord(runId, clusterId, childId, packetPath, resultPath, roleContext, provider, providerSelectionReason, providerOverrideSource, providersTried) {
608
+ function createDispatchRecord(runId, clusterId, childId, packetPath, resultPath, roleContext, provider, providerSelectionReason, providerOverrideSource, providersTried, routingSummary) {
578
609
  const dispatchMode = determineDispatchMode(provider);
579
610
  const runtimeState = determineRuntimeState(dispatchMode);
580
611
  return {
@@ -588,6 +619,7 @@ function createDispatchRecord(runId, clusterId, childId, packetPath, resultPath,
588
619
  provider_selection_reason: providerSelectionReason,
589
620
  provider_override_source: providerOverrideSource,
590
621
  providers_tried: providersTried && providersTried.length > 0 ? providersTried : undefined,
622
+ routing_summary: routingSummary,
591
623
  dispatched_at: new Date().toISOString(),
592
624
  status: "dispatched",
593
625
  dispatch_mode: dispatchMode,
@@ -755,6 +787,7 @@ function ledgerLastCommit(state) {
755
787
  return state.last_commit && state.last_commit.length > 0 ? state.last_commit : null;
756
788
  }
757
789
  function appendDispatchLedgerEvent(repoRoot, state, childId) {
790
+ const routingSummary = state.open_children_meta?.[childId]?.dispatch_record?.routing_summary;
758
791
  new ledger_js_1.LedgerWriter((0, node_path_1.join)(repoRoot, ledger_js_1.DEFAULT_LEDGER_PATH)).append({
759
792
  schema_version: 1,
760
793
  event_id: (0, node_crypto_1.randomUUID)(),
@@ -772,6 +805,7 @@ function appendDispatchLedgerEvent(repoRoot, state, childId) {
772
805
  pr_url: null,
773
806
  timestamp: new Date().toISOString(),
774
807
  dispatch_epoch: state.dispatch_boundary?.dispatch_epoch ?? 0,
808
+ ...(routingSummary ? { routing_summary: routingSummary } : {}),
775
809
  });
776
810
  }
777
811
  function selectChild(state, requestedChild) {
@@ -1189,7 +1223,7 @@ function runLoopDispatch(options) {
1189
1223
  fail(`Failed to write packet artifact to ${packetPath}: ${errorMsg}`);
1190
1224
  }
1191
1225
  // Create dispatch record with all evidence
1192
- const dispatchRecord = createDispatchRecord(state.run_id, state.cluster_id, childId, relativePacketPath, relativeEffectiveResultPath, packet.role_context, resolvedProvider, providerDecision.selectionReason, providerDecision.overrideSource, providerDecision.providersTried);
1226
+ const dispatchRecord = createDispatchRecord(state.run_id, state.cluster_id, childId, relativePacketPath, relativeEffectiveResultPath, packet.role_context, resolvedProvider, providerDecision.selectionReason, providerDecision.overrideSource, providerDecision.providersTried, providerDecision.routingSummary);
1193
1227
  emitProviderFallbackAttempted(telemetryFile, dispatchRecord.dispatch_id, state.run_id, childId, providerDecision);
1194
1228
  emitProviderExhausted(telemetryFile, dispatchRecord.dispatch_id, state.run_id, childId, providerDecision);
1195
1229
  emitProviderSelected(telemetryFile, dispatchRecord.dispatch_id, state.run_id, childId, providerDecision);
@@ -1245,6 +1279,7 @@ function runLoopDispatch(options) {
1245
1279
  dispatch_mode: dispatchRecord.dispatch_mode,
1246
1280
  runtime_state: dispatchRecord.runtime_state,
1247
1281
  provider: dispatchRecord.provider ?? null,
1282
+ routing_summary: dispatchRecord.routing_summary ?? null,
1248
1283
  timestamp: new Date().toISOString(),
1249
1284
  });
1250
1285
  // ── Apply lifecycle transition for child-dispatch event ────────────────────
@@ -254,6 +254,7 @@ function appendChildCompletedLedgerEvent(repoRoot, state, completedChild, lastCo
254
254
  ...(completion?.providersTried?.length
255
255
  ? { providers_tried: completion.providersTried }
256
256
  : {}),
257
+ ...(completion?.routingSummary ? { routing_summary: completion.routingSummary } : {}),
257
258
  });
258
259
  }
259
260
  function appendClusterCompletedLedgerEvent(repoRoot, state) {
@@ -435,6 +436,7 @@ function buildParentDispatchRecord(args) {
435
436
  provider: args.provider,
436
437
  provider_selection_reason: args.providerSelectionReason,
437
438
  providers_tried: args.providersTried,
439
+ routing_summary: args.routingSummary,
438
440
  dispatched_at: args.dispatchedAt,
439
441
  status: "dispatched",
440
442
  dispatch_mode: "direct-worker",
@@ -557,15 +559,16 @@ async function syncClusterCompletion(args) {
557
559
  output: String(args.validationSummary),
558
560
  },
559
561
  };
562
+ const relativeResultFile = args.resultFile ? (0, node_path_1.relative)(args.repoRoot, args.resultFile) : args.resultFile;
560
563
  await (0, store_js_1.writeClusterState)(args.clusterId, {
561
564
  ...clusterState,
562
565
  state_generation: clusterState.state_generation + 1,
563
566
  child_states: childStates,
564
567
  claim_metadata: Object.fromEntries(Object.entries(clusterState.claim_metadata).filter(([childId]) => childId !== args.childId)),
565
- result_pointers: args.resultFile
568
+ result_pointers: relativeResultFile
566
569
  ? {
567
570
  ...clusterState.result_pointers,
568
- [args.childId]: args.resultFile,
571
+ [args.childId]: relativeResultFile,
569
572
  }
570
573
  : clusterState.result_pointers,
571
574
  validation_results: nextValidationResults,
@@ -594,6 +597,7 @@ async function syncClusterDispatch(args) {
594
597
  }
595
598
  : child)
596
599
  : [...clusterState.child_states, { id: args.childId, status: "dispatched" }];
600
+ const relativePacketPath = (0, node_path_1.relative)(args.repoRoot, args.packetPath);
597
601
  await (0, store_js_1.writeClusterState)(args.clusterId, {
598
602
  ...clusterState,
599
603
  state_generation: clusterState.state_generation + 1,
@@ -608,7 +612,7 @@ async function syncClusterDispatch(args) {
608
612
  },
609
613
  packet_pointers: {
610
614
  ...clusterState.packet_pointers,
611
- [args.childId]: args.packetPath,
615
+ [args.childId]: relativePacketPath,
612
616
  },
613
617
  }, args.repoRoot);
614
618
  return true;
@@ -692,10 +696,20 @@ async function runParentLoop(options) {
692
696
  let providerName;
693
697
  let providerSelectionReason;
694
698
  let providersTried;
699
+ let routingSummary;
695
700
  if (adapterName === "agent-subtask") {
696
701
  providerName = "agent-subtask";
697
702
  providerSelectionReason = "agent-subtask-adapter";
698
703
  providersTried = ["agent-subtask"];
704
+ routingSummary = {
705
+ selected_provider: "agent-subtask",
706
+ selected_adapter: "agent-subtask",
707
+ selection_reason: "agent-subtask-adapter",
708
+ effective_policy_order: ["agent-subtask"],
709
+ compatibility_mode: false,
710
+ registry_present: false,
711
+ fallback_eligible: false,
712
+ };
699
713
  }
700
714
  else {
701
715
  let evidence;
@@ -713,6 +727,7 @@ async function runParentLoop(options) {
713
727
  providerName = evidence.provider ?? "default";
714
728
  providerSelectionReason = evidence.selectionReason;
715
729
  providersTried = evidence.providersTried;
730
+ routingSummary = evidence.routingSummary;
716
731
  }
717
732
  const executionConfig = adapterName === "agent-subtask"
718
733
  ? { ...(config.execution ?? { providers: {} }), adapter: "agent-subtask" }
@@ -1489,6 +1504,7 @@ async function runParentLoop(options) {
1489
1504
  provider: providerName,
1490
1505
  providerSelectionReason,
1491
1506
  providersTried,
1507
+ routingSummary,
1492
1508
  workerId,
1493
1509
  dispatchedAt,
1494
1510
  });
@@ -1563,6 +1579,7 @@ async function runParentLoop(options) {
1563
1579
  dry_run: dryRun,
1564
1580
  selected_slot_claim: selectedSlotClaim ?? null,
1565
1581
  slot_claims: nextSlotClaims,
1582
+ routing_summary: routingSummary ?? null,
1566
1583
  timestamp: new Date().toISOString(),
1567
1584
  });
1568
1585
  emitBootstrapContextSize(telemetryFile, state.run_id, nextChild, stateFile, packet);
@@ -2213,6 +2230,7 @@ async function runParentLoop(options) {
2213
2230
  model: modelUsed,
2214
2231
  router_selection_reason: dispatchRecord?.provider_selection_reason,
2215
2232
  providers_tried: dispatchRecord?.providers_tried,
2233
+ routing_summary: dispatchRecord?.routing_summary ?? null,
2216
2234
  timestamp: new Date().toISOString(),
2217
2235
  };
2218
2236
  if (elapsedSeconds !== undefined) {
@@ -2228,6 +2246,7 @@ async function runParentLoop(options) {
2228
2246
  completionStatus: workerStatus,
2229
2247
  routerSelectionReason: dispatchRecord?.provider_selection_reason,
2230
2248
  providersTried: dispatchRecord?.providers_tried,
2249
+ routingSummary: dispatchRecord?.routing_summary,
2231
2250
  });
2232
2251
  }
2233
2252
  if (orchestrationMode === 'supervised') {
@@ -188,6 +188,7 @@ function decideWorkerRoute(input) {
188
188
  activeSlots,
189
189
  slotLimit: providerSlotLimit ?? globalSlotLimit,
190
190
  policyMatched,
191
+ fallbackEligible: policy?.fallbackEligible,
191
192
  },
192
193
  });
193
194
  }
@@ -220,7 +220,10 @@ async function ensureClusterRunState(options) {
220
220
  }
221
221
  }
222
222
  }
223
- const { openChildren, openChildrenMeta } = buildBootstrapPlan(clusterId, await loadOrSyncGraph(clusterId, repoRoot), repoRoot);
223
+ const graph = await loadOrSyncGraph(clusterId, repoRoot);
224
+ const { openChildren, openChildrenMeta } = buildBootstrapPlan(clusterId, graph, repoRoot);
225
+ // Persist canonical child ordering and any body-derived ordering dependencies.
226
+ await graph.save(clusterId, repoRoot);
224
227
  await bootstrapHandler({
225
228
  clusterId,
226
229
  openChildren,
@@ -10,6 +10,39 @@
10
10
  * WorkerPacket extends BootstrapPacket so all existing adapters that
11
11
  * accept BootstrapPacket transparently accept WorkerPacket.
12
12
  */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
13
46
  Object.defineProperty(exports, "__esModule", { value: true });
14
47
  exports.REPAIR_RETURN_CONTRACT = exports.STARTUP_RETURN_CONTRACT = exports.PREFLIGHT_RETURN_CONTRACT = exports.WORKER_SYMPTOM_CATEGORIES = exports.WORKER_PROHIBITED_WRITE_PATHS = exports.FINALIZE_RETURN_CONTRACT = exports.IMPL_RETURN_CONTRACT = void 0;
15
48
  exports.roleContextForWorkerRole = roleContextForWorkerRole;
@@ -20,6 +53,7 @@ exports.compileImplPacket = compileImplPacket;
20
53
  exports.compileFinalizePacket = compileFinalizePacket;
21
54
  exports.compilePreflightPacket = compilePreflightPacket;
22
55
  exports.compileRepairWorkerPacket = compileRepairWorkerPacket;
56
+ const path = __importStar(require("node:path"));
23
57
  const worker_prompt_js_1 = require("./worker-prompt.js");
24
58
  const body_parser_js_1 = require("./body-parser.js");
25
59
  const WORKER_ROLE_CONTEXT = {
@@ -172,6 +206,49 @@ function defaultLifecycle(maxConcurrent, cleanupOnExit) {
172
206
  cleanup_on_exit: cleanupOnExit,
173
207
  };
174
208
  }
209
+ /** Derives the adjacent test file path(s) for a single source TypeScript file. */
210
+ function deriveTestPaths(sourcePath) {
211
+ if (!sourcePath.endsWith('.ts') || sourcePath.endsWith('.test.ts') || sourcePath.endsWith('.d.ts')) {
212
+ return [];
213
+ }
214
+ if (sourcePath.includes('*') || sourcePath.includes('?')) {
215
+ return [];
216
+ }
217
+ const candidates = [sourcePath.replace(/\.ts$/, '.test.ts')];
218
+ if (sourcePath.endsWith('/index.ts')) {
219
+ const dir = sourcePath.slice(0, -'/index.ts'.length);
220
+ const dirName = path.basename(dir);
221
+ if (dirName) {
222
+ candidates.push(`${dir}/${dirName}.test.ts`);
223
+ }
224
+ }
225
+ return candidates;
226
+ }
227
+ /**
228
+ * Expands an allowed scope list with adjacent test paths when the validation
229
+ * commands include a vitest run. This lets workers touch test files for source
230
+ * files they are modifying, matching the `*.test.ts` validation commands.
231
+ */
232
+ function expandScopeWithTestPaths(scope, validationCommands) {
233
+ const hasVitest = validationCommands.some((cmd) => cmd.includes('vitest'));
234
+ if (!hasVitest || scope.length === 0)
235
+ return scope;
236
+ const testPaths = new Set();
237
+ for (const item of scope) {
238
+ for (const testPath of deriveTestPaths(item)) {
239
+ testPaths.add(testPath);
240
+ }
241
+ }
242
+ if (testPaths.size === 0)
243
+ return scope;
244
+ const expanded = [...scope];
245
+ for (const testPath of testPaths) {
246
+ if (!expanded.includes(testPath)) {
247
+ expanded.push(testPath);
248
+ }
249
+ }
250
+ return expanded;
251
+ }
175
252
  /**
176
253
  * Build a compiled startup worker packet.
177
254
  * Startup workers prepare tracker/graph/config/run-state evidence; the parent
@@ -257,6 +334,8 @@ function compileImplPacket(input) {
257
334
  const resolvedRequirements = input.issueContext?.key_requirements.length
258
335
  ? input.issueContext.key_requirements
259
336
  : bodyParsed?.requirements ?? [];
337
+ // Include adjacent test files when the validation commands indicate a vitest run.
338
+ const expandedScope = expandScopeWithTestPaths(resolvedScope, resolvedValidation);
260
339
  const requirementLines = resolvedRequirements.map((r, i) => ` ${i + 1}. ${r}`);
261
340
  const bodyLines = input.issueContext?.body
262
341
  ? [`Issue description:\n${input.issueContext.body}`]
@@ -285,7 +364,7 @@ function compileImplPacket(input) {
285
364
  stateFile: input.stateFile,
286
365
  telemetryFile: input.telemetryFile,
287
366
  issueContext: input.issueContext,
288
- allowedScope: resolvedScope,
367
+ allowedScope: expandedScope,
289
368
  validationCommands: resolvedValidation,
290
369
  mode: promptMode,
291
370
  simplicityMode: input.simplicityMode,
@@ -301,7 +380,7 @@ function compileImplPacket(input) {
301
380
  instructions: {
302
381
  primary_goal: promptResult.prompt,
303
382
  steps,
304
- allowed_scope: resolvedScope,
383
+ allowed_scope: expandedScope,
305
384
  validation_commands: resolvedValidation,
306
385
  issue_context: input.issueContext,
307
386
  },
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Polaris routing anomaly → Medic/state-repair signal classification.
4
+ *
5
+ * These signals are emitted by the loop/dispatch/runtime boundary and are
6
+ * reviewed by Medic, not used to mutate routing.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.classifyRoutingTelemetryEvent = classifyRoutingTelemetryEvent;
10
+ /**
11
+ * Map a raw telemetry event to a state-repair review signal.
12
+ *
13
+ * Only loop/dispatch boundary events that indicate runtime state-repair work
14
+ * are classified. The returned signal has occurrences=1 and a single child_id;
15
+ * consumers (such as SOL scoring) should aggregate by signal name.
16
+ */
17
+ function classifyRoutingTelemetryEvent(event) {
18
+ const eventName = event["event"];
19
+ if (typeof eventName !== "string")
20
+ return undefined;
21
+ const childId = typeof event["child_id"] === "string" ? event["child_id"] : undefined;
22
+ const childIds = childId ? [childId] : [];
23
+ switch (eventName) {
24
+ case "sealed-result-read-error":
25
+ return {
26
+ signal: "missing-sealed-result",
27
+ reason: "missing-sealed-result",
28
+ occurrences: 1,
29
+ child_ids: childIds,
30
+ };
31
+ case "stale-dispatch-aborted":
32
+ return {
33
+ signal: "stale-dispatch-abort",
34
+ reason: "stale-dispatch-abort",
35
+ occurrences: 1,
36
+ child_ids: childIds,
37
+ };
38
+ case "invalid-inline-attempt":
39
+ return {
40
+ signal: "invalid-inline-attempt",
41
+ reason: "invalid-inline-attempt",
42
+ occurrences: 1,
43
+ child_ids: childIds,
44
+ };
45
+ case "child-recovery-initiated": {
46
+ const recoveryReason = event["recovery_reason"];
47
+ if (recoveryReason === "stale-dispatch") {
48
+ return {
49
+ signal: "stale-dispatch-abort",
50
+ reason: "stale-dispatch",
51
+ occurrences: 1,
52
+ child_ids: childIds,
53
+ };
54
+ }
55
+ return undefined;
56
+ }
57
+ default:
58
+ return undefined;
59
+ }
60
+ }
package/dist/qc/policy.js CHANGED
@@ -59,6 +59,8 @@ function decideProviderFailureAction(reason, policy) {
59
59
  return policy?.timeout ?? "fail";
60
60
  case "parse-failed":
61
61
  return policy?.parseFailure ?? "fail";
62
+ case "rate-limited":
63
+ return policy?.rateLimited ?? "fail";
62
64
  default:
63
65
  return "fail";
64
66
  }