@ngockhoale/ukit 1.4.1 → 1.4.3

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.
@@ -49,6 +49,7 @@ async function main() {
49
49
  const rootDir = getRootDir(args);
50
50
  const sharedStatePath = path.join(rootDir, '.claude', 'ukit', 'skill-router-state.json');
51
51
  const routeCachePath = path.join(rootDir, '.claude', 'ukit', 'route-cache.json');
52
+ const routeAuditPath = path.join(rootDir, '.ukit', 'storage', 'cache', 'route-audit.json');
52
53
  const previousState = await readJson(sharedStatePath, {});
53
54
  const targetFile = readFlagValue(args, '--target');
54
55
  const taskType = readFlagValue(args, '--type');
@@ -138,13 +139,30 @@ async function main() {
138
139
  requestKey,
139
140
  });
140
141
  if (cachedRouteState) {
142
+ const rescuedRouteSummary = cachedRouteState.routeSummary
143
+ ? {
144
+ ...cachedRouteState.routeSummary,
145
+ continuationState: advanceContinuationState(
146
+ cachedRouteState.routeSummary.continuationState ?? null,
147
+ cachedRouteState.routeSummary.continuationState ?? null,
148
+ ),
149
+ }
150
+ : null;
151
+ if (rescuedRouteSummary) {
152
+ applyRescueBiasToRouteSummary(rescuedRouteSummary);
153
+ }
141
154
  const sharedState = {
142
155
  ...cachedRouteState,
143
156
  source: 'task-route',
144
157
  ts: Date.now(),
145
158
  requestKey,
159
+ routeSummary: rescuedRouteSummary,
146
160
  };
161
+ sharedState.fingerprint = buildRouteStateFingerprint(sharedState);
147
162
  await writeJson(sharedStatePath, sharedState);
163
+ await appendRouteAuditEntry(routeAuditPath, buildRouteAuditEntry({
164
+ state: sharedState,
165
+ }));
148
166
  printRouteState(sharedState);
149
167
  return;
150
168
  }
@@ -166,6 +184,7 @@ async function main() {
166
184
  useIndexedContext,
167
185
  previousContextSnapshot,
168
186
  recentOutputSnapshot,
187
+ previousRouteSummary: previousState?.routeSummary ?? null,
169
188
  });
170
189
  await seedHelperCaches({
171
190
  rootDir,
@@ -182,6 +201,10 @@ async function main() {
182
201
  indexGeneratedAtMs,
183
202
  });
184
203
  await writeJson(sharedStatePath, sharedState);
204
+ await appendRouteAuditEntry(routeAuditPath, buildRouteAuditEntry({
205
+ route,
206
+ state: sharedState,
207
+ }));
185
208
  await writeRecentCacheEntry(routeCachePath, compactRouteCacheState(sharedState), {
186
209
  maxEntries: DEFAULT_RECENT_CACHE_MAX_ENTRIES,
187
210
  });
@@ -255,6 +278,28 @@ async function prepareTaskRoute({
255
278
  selectedIds,
256
279
  targetFile: normalizedTarget,
257
280
  });
281
+ const executionScores = deriveExecutionScores({
282
+ promptText: normalizedPrompt,
283
+ commandText: normalizedCommand,
284
+ targetFile: normalizedTarget,
285
+ intentMode,
286
+ taskType: inferredTaskType,
287
+ });
288
+ const executionCandidates = buildExecutionModeCandidates({
289
+ promptText: normalizedPrompt,
290
+ commandText: normalizedCommand,
291
+ targetFile: normalizedTarget,
292
+ intentMode,
293
+ executionScores,
294
+ });
295
+ const executionMode = deriveExecutionMode({
296
+ promptText: normalizedPrompt,
297
+ commandText: normalizedCommand,
298
+ targetFile: normalizedTarget,
299
+ intentMode,
300
+ executionScores,
301
+ executionCandidates,
302
+ });
258
303
  const preservedPrompt = normalizedPrompt || String(lastExplicitUserPromptText || '').trim();
259
304
 
260
305
  return {
@@ -269,6 +314,9 @@ async function prepareTaskRoute({
269
314
  contextIntent,
270
315
  taskType: inferredTaskType,
271
316
  intentMode,
317
+ executionScores,
318
+ executionCandidates,
319
+ executionMode,
272
320
  },
273
321
  };
274
322
  }
@@ -283,6 +331,7 @@ async function finalizeTaskRoute({
283
331
  useIndexedContext = true,
284
332
  previousContextSnapshot = null,
285
333
  recentOutputSnapshot = null,
334
+ previousRouteSummary = null,
286
335
  } = {}) {
287
336
  const absoluteRoot = path.resolve(rootDir);
288
337
  const activeSkills = preparedRoute?.activeSkills ?? [];
@@ -347,10 +396,17 @@ async function finalizeTaskRoute({
347
396
  verificationRecommendation,
348
397
  nextAction,
349
398
  });
399
+ routeSummary.continuationState = advanceContinuationState(
400
+ routeSummary.continuationState,
401
+ previousRouteSummary?.continuationState ?? null,
402
+ );
403
+ applyRescueBiasToRouteSummary(routeSummary);
404
+ const approachSelector = routeSummary?.approachSelector ?? null;
350
405
 
351
406
  return {
352
407
  activeSkills,
353
408
  routingContext,
409
+ approachSelector,
354
410
  previousContext: previousContextSnapshot,
355
411
  recentOutput: recentOutputSnapshot,
356
412
  contextRecommendation,
@@ -445,7 +501,11 @@ function formatDisplayNextAction(routeSummary = null) {
445
501
  }
446
502
 
447
503
  const nextActionType = String(routeSummary.nextActionType ?? '').trim();
448
- if (!nextActionType || nextActionType === 'read-skill-instructions') {
504
+ if (
505
+ !nextActionType
506
+ || nextActionType === 'read-skill-instructions'
507
+ || nextActionType === 'pull-indexed-context'
508
+ ) {
449
509
  return '';
450
510
  }
451
511
 
@@ -492,6 +552,10 @@ function formatDisplayHelperHint(routeSummary = null, compactSummary = '') {
492
552
  return '';
493
553
  }
494
554
 
555
+ if (String(routeSummary.nextActionType ?? '').trim() === 'pull-indexed-context') {
556
+ return '';
557
+ }
558
+
495
559
  let helperHint = String(routeSummary.helperHint ?? '').trim().replace(/\s+/g, ' ');
496
560
  if (!helperHint) {
497
561
  return '';
@@ -671,6 +735,10 @@ function shouldKeepRouteEntryForIntent(entry, intentMode) {
671
735
  return false;
672
736
  }
673
737
 
738
+ if (entry.id === 'code-review' && intentMode === 'implement-specific') {
739
+ return false;
740
+ }
741
+
674
742
  return true;
675
743
  }
676
744
 
@@ -710,6 +778,10 @@ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
710
778
  routeSignals.commandRawText ?? '',
711
779
  routeSignals.commandNormalizedText ?? '',
712
780
  ].join('\n');
781
+ const rawPromptSignals = [
782
+ routeSignals.promptRawText ?? '',
783
+ routeSignals.commandRawText ?? '',
784
+ ].join('\n');
713
785
 
714
786
  if (
715
787
  skillId === 'testing-quality'
@@ -718,6 +790,14 @@ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
718
790
  return 1;
719
791
  }
720
792
 
793
+ if (
794
+ skillId === 'code-review'
795
+ && /\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(rawPromptSignals)
796
+ && !/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(rawPromptSignals)
797
+ ) {
798
+ return -4;
799
+ }
800
+
721
801
  return 0;
722
802
  }
723
803
 
@@ -768,13 +848,13 @@ function deriveIntentMode({ promptText = '', commandText = '', targetFile = null
768
848
  }
769
849
 
770
850
  if (concreteTask) {
771
- if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|login|timeout)\b/.test(lower)) {
851
+ if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|timeout)\b/.test(lower)) {
772
852
  return 'debug-specific';
773
853
  }
774
- if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(lower)) {
854
+ if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(raw)) {
775
855
  return 'review-specific';
776
856
  }
777
- if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature)\b/.test(lower)) {
857
+ if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(raw)) {
778
858
  return 'implement-specific';
779
859
  }
780
860
  }
@@ -806,7 +886,7 @@ function hasConcreteTaskSignal(lower, raw, targetFile, { taskQueueNext = false }
806
886
  return true;
807
887
  }
808
888
 
809
- return /\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|review|audit|diff|pr feedback|code review|auth|login|api|endpoint|test|spec)\b/.test(lower)
889
+ return /\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply|review|audit|diff|pr feedback|code review|auth|login|api|endpoint|test|spec)\b/.test(lower)
810
890
  || /\b(sửa|fix|lỗi|bug|debug|implement|cài|thêm|review|kiểm tra|soát|đăng nhập)\b/.test(raw);
811
891
  }
812
892
 
@@ -1225,6 +1305,23 @@ function buildRouteSummary({
1225
1305
  const compactHelperLane = nextAction?.type === 'pull-indexed-context'
1226
1306
  && typeof contextRecommendation?.command === 'string'
1227
1307
  && contextRecommendation.command.trim();
1308
+ const executionMode = routingContext.executionMode ?? null;
1309
+ const executionScores = routingContext.executionScores ?? null;
1310
+ const executionCandidates = routingContext.executionCandidates ?? null;
1311
+ const approachSelector = buildApproachSelectorResult({
1312
+ executionMode,
1313
+ executionScores,
1314
+ executionCandidates,
1315
+ });
1316
+ const executionContract = buildExecutionContract(executionMode);
1317
+ const completionState = buildCompletionState({
1318
+ executionMode,
1319
+ verificationRecommendation,
1320
+ });
1321
+ const continuationState = buildContinuationState({
1322
+ nextActionType: nextAction?.type ?? null,
1323
+ completionState,
1324
+ });
1228
1325
  const helperHint = compactHelperHint(
1229
1326
  compactHelperLane
1230
1327
  ? contextRecommendation?.command
@@ -1243,7 +1340,6 @@ function buildRouteSummary({
1243
1340
  editGuardHint ? `editGuard=${editGuardHint}` : null,
1244
1341
  delegationRecommendation?.hint ? `delegate=${delegationRecommendation.hint}` : null,
1245
1342
  policyMode ? `policy=${policyMode}` : null,
1246
- contextMode ? `mode=${contextMode}` : null,
1247
1343
  ].filter(Boolean).join(' | ');
1248
1344
 
1249
1345
  return {
@@ -1252,6 +1348,13 @@ function buildRouteSummary({
1252
1348
  preferredOrder,
1253
1349
  policyMode,
1254
1350
  editGuardHint,
1351
+ executionMode,
1352
+ executionScores,
1353
+ executionCandidates,
1354
+ approachSelector,
1355
+ executionContract,
1356
+ completionState,
1357
+ continuationState,
1255
1358
  intentMode: routingContext.intentMode ?? null,
1256
1359
  delegateHint: delegationRecommendation?.hint ?? null,
1257
1360
  nextActionType: nextAction?.type ?? null,
@@ -1268,6 +1371,559 @@ function deriveContextMode(taskType) {
1268
1371
  return null;
1269
1372
  }
1270
1373
 
1374
+ function deriveExecutionScores({
1375
+ promptText = '',
1376
+ commandText = '',
1377
+ targetFile = null,
1378
+ intentMode = null,
1379
+ taskType = null,
1380
+ } = {}) {
1381
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
1382
+ const sharedRisk = isSharedImpactFile(targetFile);
1383
+ const directTransformSignal = /\b(change|replace|set|rename|convert|swap)\b/.test(raw) || /\bchange\s+.+\s+to\s+.+\b/.test(raw);
1384
+ const smallFixSignal = /\b(adjust|tweak|patch|fix|modify|update|apply)\b/.test(raw);
1385
+ const buildSignal = /\b(build|create|add|implement|feature)\b/.test(raw);
1386
+ const debugSignal = intentMode === 'debug-specific' || /\b(root cause|debug|triage|flaky|investigate|why)\b/.test(raw);
1387
+ const reviewSignal = intentMode === 'review-specific' || /\b(review|audit|verify|release readiness)\b/.test(raw);
1388
+ const failureSignal = /\b(failing|failed|broken|error|crash|timeout|undefined|exception|eacces|trace)\b/.test(raw);
1389
+ const impactSignal = /\b(map impact|impact|blast radius|all affected|across (?:runtime|templates|helpers|adapters|mirrors)|affected adapters|affected mirrors|inspect impact)\b/.test(raw);
1390
+ const boundedEditSignal = /\b(one[- ]line|small|tiny|single|local|guard|log line|loading message|label)\b/.test(raw);
1391
+ const implementSignal = /\b(implement|apply|update|modify|add|create|ship|deliver|fix)\b/.test(raw)
1392
+ || /\bchange\s+.+\s+to\s+.+\b/.test(raw);
1393
+ const explicitTarget = Boolean(targetFile);
1394
+
1395
+ let editCertainty = 0;
1396
+ if (directTransformSignal) {
1397
+ editCertainty = 3;
1398
+ } else if (smallFixSignal) {
1399
+ editCertainty = 2;
1400
+ } else if (buildSignal) {
1401
+ editCertainty = 1;
1402
+ }
1403
+
1404
+ let investigationNeed = 0;
1405
+ if (debugSignal) {
1406
+ investigationNeed = 3;
1407
+ } else if (failureSignal) {
1408
+ investigationNeed = 2;
1409
+ }
1410
+
1411
+ let blastRadius = 0;
1412
+ if (sharedRisk) {
1413
+ blastRadius = 3;
1414
+ } else if (taskType === 'non-trivial' || taskType === 'shared-simple') {
1415
+ blastRadius = 2;
1416
+ } else if (!targetFile) {
1417
+ blastRadius = 1;
1418
+ }
1419
+
1420
+ let verificationBurden = 1;
1421
+ if (reviewSignal || sharedRisk) {
1422
+ verificationBurden = 3;
1423
+ } else if (debugSignal || taskType === 'non-trivial') {
1424
+ verificationBurden = 2;
1425
+ }
1426
+
1427
+ let ambiguity = 0;
1428
+ if (!targetFile) {
1429
+ ambiguity = 2;
1430
+ } else if (buildSignal && !directTransformSignal && !smallFixSignal) {
1431
+ ambiguity = 1;
1432
+ }
1433
+
1434
+ return {
1435
+ editCertainty,
1436
+ investigationNeed,
1437
+ blastRadius,
1438
+ verificationBurden,
1439
+ ambiguity,
1440
+ sharedRisk,
1441
+ directTransformSignal,
1442
+ smallFixSignal,
1443
+ buildSignal,
1444
+ debugSignal,
1445
+ reviewSignal,
1446
+ failureSignal,
1447
+ impactSignal,
1448
+ boundedEditSignal,
1449
+ implementSignal,
1450
+ explicitTarget,
1451
+ };
1452
+ }
1453
+
1454
+ function deriveExecutionMode({
1455
+ promptText = '',
1456
+ commandText = '',
1457
+ targetFile = null,
1458
+ intentMode = null,
1459
+ executionScores = {},
1460
+ executionCandidates = null,
1461
+ } = {}) {
1462
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
1463
+ const scores = executionScores;
1464
+ const candidates = executionCandidates ?? buildExecutionModeCandidates({
1465
+ promptText,
1466
+ commandText,
1467
+ targetFile,
1468
+ intentMode,
1469
+ executionScores,
1470
+ });
1471
+ const explicitReviewLead = /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw);
1472
+ const strongImpactLead = scores.sharedRisk
1473
+ && (scores.impactSignal || /\b(check all affected|map all affected|across all affected)\b/.test(raw));
1474
+ const boundedLocalBuildCandidate = scores.buildSignal && scores.boundedEditSignal && scores.explicitTarget && !scores.sharedRisk;
1475
+
1476
+ if ((intentMode === 'review-specific' || explicitReviewLead) && !scores.implementSignal) {
1477
+ return 'review-release';
1478
+ }
1479
+
1480
+ if (strongImpactLead || (scores.blastRadius >= 3 && (scores.ambiguity >= 2 || scores.investigationNeed >= 2))) {
1481
+ return 'map-impact';
1482
+ }
1483
+
1484
+ if (scores.sharedRisk && scores.failureSignal && scores.investigationNeed >= 2 && !scores.impactSignal) {
1485
+ return 'shared-edit';
1486
+ }
1487
+
1488
+ if (scores.blastRadius >= 3 || scores.sharedRisk) {
1489
+ return 'shared-edit';
1490
+ }
1491
+
1492
+ if (scores.investigationNeed >= 3 || (scores.failureSignal && scores.investigationNeed >= 2)) {
1493
+ return 'find-cause';
1494
+ }
1495
+
1496
+ if (scores.directTransformSignal && !scores.explicitTarget && scores.investigationNeed === 0 && !scores.sharedRisk) {
1497
+ return 'local-build';
1498
+ }
1499
+
1500
+ if (
1501
+ scores.editCertainty >= 3
1502
+ && scores.ambiguity <= 1
1503
+ && scores.blastRadius === 0
1504
+ && scores.verificationBurden <= 1
1505
+ ) {
1506
+ return 'tiny-fix';
1507
+ }
1508
+
1509
+ if (boundedLocalBuildCandidate && scores.investigationNeed === 0) {
1510
+ return 'local-fix';
1511
+ }
1512
+
1513
+ if (
1514
+ /\b(build|create|add|implement|feature|summary card)\b/.test(raw)
1515
+ && scores.investigationNeed === 0
1516
+ && scores.blastRadius < 3
1517
+ && !scores.boundedEditSignal
1518
+ ) {
1519
+ return 'local-build';
1520
+ }
1521
+
1522
+ if (scores.editCertainty >= 2 && scores.investigationNeed === 0 && scores.blastRadius === 0) {
1523
+ return 'local-fix';
1524
+ }
1525
+
1526
+ return applySafeUpwardBias(candidates, 'local-build');
1527
+ }
1528
+
1529
+ function buildExecutionModeCandidates({
1530
+ promptText = '',
1531
+ commandText = '',
1532
+ targetFile = null,
1533
+ intentMode = null,
1534
+ executionScores = {},
1535
+ } = {}) {
1536
+ const scores = executionScores;
1537
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
1538
+ const explicitReviewLead = (intentMode === 'review-specific' || /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw))
1539
+ && !scores.implementSignal;
1540
+
1541
+ const modeScores = {
1542
+ 'tiny-fix': (
1543
+ (scores.editCertainty * 4)
1544
+ + (scores.directTransformSignal ? 4 : 0)
1545
+ + (scores.explicitTarget ? 1 : 0)
1546
+ - (scores.failureSignal ? 4 : 0)
1547
+ - (scores.blastRadius * 4)
1548
+ - (scores.ambiguity * 2)
1549
+ ),
1550
+ 'local-fix': (
1551
+ (scores.editCertainty * 3)
1552
+ + (scores.boundedEditSignal ? 3 : 0)
1553
+ + (scores.explicitTarget ? 1 : 0)
1554
+ - (scores.failureSignal ? 2 : 0)
1555
+ - (scores.blastRadius * 4)
1556
+ ),
1557
+ 'local-build': (
1558
+ (scores.buildSignal ? 6 : 0)
1559
+ + (scores.editCertainty * 1)
1560
+ + (scores.explicitTarget ? 1 : 0)
1561
+ - (scores.boundedEditSignal ? 3 : 0)
1562
+ - (scores.failureSignal ? 3 : 0)
1563
+ - (scores.blastRadius * 4)
1564
+ ),
1565
+ 'find-cause': (
1566
+ (scores.investigationNeed * 4)
1567
+ + (scores.failureSignal ? 3 : 0)
1568
+ + (scores.debugSignal ? 2 : 0)
1569
+ - (scores.blastRadius >= 3 ? 1 : 0)
1570
+ ),
1571
+ 'shared-edit': (
1572
+ (scores.sharedRisk ? 8 : 0)
1573
+ + (scores.editCertainty * 1)
1574
+ + (scores.buildSignal ? 1 : 0)
1575
+ - (scores.impactSignal ? 2 : 0)
1576
+ ),
1577
+ 'map-impact': (
1578
+ (scores.sharedRisk ? 7 : 0)
1579
+ + (scores.impactSignal ? 5 : 0)
1580
+ + (scores.ambiguity * 2)
1581
+ + scores.investigationNeed
1582
+ ),
1583
+ 'review-release': (
1584
+ (explicitReviewLead ? 10 : 0)
1585
+ + (scores.reviewSignal ? 3 : 0)
1586
+ - (scores.implementSignal ? 5 : 0)
1587
+ ),
1588
+ };
1589
+
1590
+ return Object.entries(modeScores)
1591
+ .map(([mode, score]) => ({ mode, score }))
1592
+ .sort((a, b) => b.score - a.score || executionModeRank(a.mode) - executionModeRank(b.mode));
1593
+ }
1594
+
1595
+ function applySafeUpwardBias(candidates = [], fallbackMode = 'local-build') {
1596
+ const [topCandidate, competingCandidate] = candidates;
1597
+ if (!topCandidate) {
1598
+ return fallbackMode;
1599
+ }
1600
+
1601
+ if (!competingCandidate) {
1602
+ return topCandidate.mode;
1603
+ }
1604
+
1605
+ const scoreGap = Number(topCandidate.score ?? 0) - Number(competingCandidate.score ?? 0);
1606
+ const rankGap = executionModeRank(competingCandidate.mode) - executionModeRank(topCandidate.mode);
1607
+ if (scoreGap <= 1 && rankGap === 1) {
1608
+ return competingCandidate.mode;
1609
+ }
1610
+
1611
+ return topCandidate.mode;
1612
+ }
1613
+
1614
+ function executionModeRank(mode = '') {
1615
+ const orderedModes = [
1616
+ 'tiny-fix',
1617
+ 'local-fix',
1618
+ 'local-build',
1619
+ 'find-cause',
1620
+ 'shared-edit',
1621
+ 'map-impact',
1622
+ 'review-release',
1623
+ ];
1624
+
1625
+ const index = orderedModes.indexOf(mode);
1626
+ return index >= 0 ? index : orderedModes.length;
1627
+ }
1628
+
1629
+ function buildExecutionContract(executionMode = null) {
1630
+ if (!executionMode) {
1631
+ return null;
1632
+ }
1633
+
1634
+ const contracts = {
1635
+ 'tiny-fix': {
1636
+ maxReadPasses: 0,
1637
+ maxContextPulls: 0,
1638
+ verificationPolicy: 'minimal-or-targeted',
1639
+ completionRule: 'never-claim-done-without-write',
1640
+ delegationPolicy: 'disallow',
1641
+ completionEvidence: ['write-evidence'],
1642
+ },
1643
+ 'local-fix': {
1644
+ maxReadPasses: 1,
1645
+ maxContextPulls: 1,
1646
+ verificationPolicy: 'targeted-if-covered',
1647
+ completionRule: 'require-write',
1648
+ delegationPolicy: 'disallow',
1649
+ completionEvidence: ['write-evidence'],
1650
+ },
1651
+ 'local-build': {
1652
+ maxReadPasses: 2,
1653
+ maxContextPulls: 1,
1654
+ verificationPolicy: 'targeted-if-covered',
1655
+ completionRule: 'require-write',
1656
+ delegationPolicy: 'disallow-by-default',
1657
+ completionEvidence: ['write-evidence'],
1658
+ },
1659
+ 'find-cause': {
1660
+ maxReadPassesBeforeReassess: 3,
1661
+ verificationPolicy: 'root-cause-then-targeted',
1662
+ completionRule: 'never-claim-fixed-without-write-and-verification',
1663
+ delegationPolicy: 'allow-specialized-debug-lane',
1664
+ completionEvidence: ['write-evidence', 'verification-evidence'],
1665
+ },
1666
+ 'shared-edit': {
1667
+ maxReadPasses: 2,
1668
+ maxContextPulls: 2,
1669
+ verificationPolicy: 'targeted-then-widen-on-risk',
1670
+ completionRule: 'require-write-and-verification',
1671
+ delegationPolicy: 'allow-qualified-sidecar',
1672
+ completionEvidence: ['write-evidence', 'verification-evidence'],
1673
+ mirrorConsistencyRequired: true,
1674
+ },
1675
+ 'map-impact': {
1676
+ maxReadPasses: 3,
1677
+ maxContextPulls: 3,
1678
+ verificationPolicy: 'impact-first-then-targeted-then-widen-on-risk',
1679
+ completionRule: 'require-impact-evidence-before-edit-claim',
1680
+ delegationPolicy: 'allow-impact-sidecar',
1681
+ completionEvidence: ['impact-evidence', 'write-evidence', 'verification-evidence'],
1682
+ mirrorConsistencyRequired: true,
1683
+ },
1684
+ 'review-release': {
1685
+ verificationPolicy: 'evidence-first',
1686
+ completionRule: 'report-findings-not-implementation',
1687
+ delegationPolicy: 'allow-review-sidecar',
1688
+ completionEvidence: ['verification-evidence'],
1689
+ },
1690
+ };
1691
+
1692
+ return contracts[executionMode] ? { ...contracts[executionMode] } : null;
1693
+ }
1694
+
1695
+ function buildApproachSelectorResult({
1696
+ executionMode = null,
1697
+ executionScores = null,
1698
+ executionCandidates = null,
1699
+ } = {}) {
1700
+ if (!executionMode) {
1701
+ return null;
1702
+ }
1703
+
1704
+ const executionContract = buildExecutionContract(executionMode);
1705
+ const policyByMode = {
1706
+ 'tiny-fix': {
1707
+ riskLevel: 'minimal',
1708
+ contextPolicy: 'confirm-target-only',
1709
+ },
1710
+ 'local-fix': {
1711
+ riskLevel: 'local',
1712
+ contextPolicy: 'bounded-local',
1713
+ },
1714
+ 'local-build': {
1715
+ riskLevel: 'local',
1716
+ contextPolicy: 'bounded-with-related-files',
1717
+ },
1718
+ 'find-cause': {
1719
+ riskLevel: 'investigation',
1720
+ contextPolicy: 'trace-then-bounded-context',
1721
+ },
1722
+ 'shared-edit': {
1723
+ riskLevel: 'shared',
1724
+ contextPolicy: 'bounded-with-related-tests',
1725
+ },
1726
+ 'map-impact': {
1727
+ riskLevel: 'wide',
1728
+ contextPolicy: 'impact-map-first',
1729
+ },
1730
+ 'review-release': {
1731
+ riskLevel: 'release',
1732
+ contextPolicy: 'evidence-review-only',
1733
+ },
1734
+ };
1735
+ const selectedPolicy = policyByMode[executionMode] ?? {
1736
+ riskLevel: 'local',
1737
+ contextPolicy: 'bounded-local',
1738
+ };
1739
+
1740
+ return {
1741
+ executionMode,
1742
+ executionScores: executionScores ? { ...executionScores } : null,
1743
+ riskLevel: selectedPolicy.riskLevel,
1744
+ contextPolicy: selectedPolicy.contextPolicy,
1745
+ verificationPolicy: executionContract?.verificationPolicy ?? null,
1746
+ completionRule: executionContract?.completionRule ?? null,
1747
+ competingMode: executionCandidates?.[1]?.mode ?? null,
1748
+ competingScoreGap: executionCandidates?.length >= 2
1749
+ ? Number(executionCandidates[0]?.score ?? 0) - Number(executionCandidates[1]?.score ?? 0)
1750
+ : null,
1751
+ candidateModes: (executionCandidates ?? []).slice(0, 3).map((entry) => ({
1752
+ mode: entry.mode,
1753
+ score: entry.score,
1754
+ })),
1755
+ };
1756
+ }
1757
+
1758
+ function buildCompletionState({ executionMode = null, verificationRecommendation = null } = {}) {
1759
+ if (!executionMode) {
1760
+ return null;
1761
+ }
1762
+
1763
+ const contract = buildExecutionContract(executionMode);
1764
+ const missingEvidence = [...(contract?.completionEvidence ?? [])];
1765
+ const requiresVerification = missingEvidence.includes('verification-evidence');
1766
+ if (
1767
+ requiresVerification
1768
+ && verificationRecommendation
1769
+ && !(verificationRecommendation.commands?.length || verificationRecommendation.fallbackCommands?.length)
1770
+ ) {
1771
+ missingEvidence.push('verification-plan');
1772
+ }
1773
+
1774
+ let reason = 'completion evidence is still required';
1775
+ if (['tiny-fix', 'local-fix', 'local-build', 'shared-edit'].includes(executionMode)) {
1776
+ reason = 'implement request has not produced an edit yet';
1777
+ } else if (executionMode === 'review-release') {
1778
+ reason = 'review/release evidence is still required before final claim';
1779
+ } else if (executionMode === 'map-impact') {
1780
+ reason = 'impact evidence is still required before safe completion claim';
1781
+ }
1782
+
1783
+ return {
1784
+ claimAllowed: false,
1785
+ missingEvidence,
1786
+ reason,
1787
+ };
1788
+ }
1789
+
1790
+ function buildContinuationState({
1791
+ nextActionType = null,
1792
+ completionState = null,
1793
+ } = {}) {
1794
+ const reasons = [];
1795
+ const missingEvidence = unique(completionState?.missingEvidence ?? []);
1796
+
1797
+ if (nextActionType === 'pull-indexed-context') {
1798
+ reasons.push('pending-bounded-context');
1799
+ }
1800
+
1801
+ if (nextActionType === 'read-skill-instructions') {
1802
+ reasons.push('pending-skill-read');
1803
+ }
1804
+
1805
+ if (missingEvidence.includes('write-evidence')) {
1806
+ reasons.push('missing-write-evidence');
1807
+ }
1808
+
1809
+ if (missingEvidence.includes('verification-evidence') || missingEvidence.includes('verification-plan')) {
1810
+ reasons.push('missing-verification-evidence');
1811
+ }
1812
+
1813
+ if (missingEvidence.includes('impact-evidence')) {
1814
+ reasons.push('missing-impact-evidence');
1815
+ }
1816
+
1817
+ const nextMilestone = reasons.includes('pending-bounded-context')
1818
+ ? 'complete-bounded-context-then-continue'
1819
+ : (
1820
+ reasons.includes('missing-write-evidence')
1821
+ ? 'produce-write-evidence'
1822
+ : (
1823
+ reasons.includes('missing-verification-evidence')
1824
+ ? 'produce-verification-evidence'
1825
+ : (
1826
+ reasons.includes('missing-impact-evidence')
1827
+ ? 'produce-impact-evidence'
1828
+ : null
1829
+ )
1830
+ )
1831
+ );
1832
+
1833
+ return {
1834
+ required: reasons.length > 0,
1835
+ reasons,
1836
+ doneLanguageBlocked: reasons.length > 0,
1837
+ stopAllowedOnlyFor: reasons.length > 0 ? ['real-blocker'] : [],
1838
+ nextMilestone,
1839
+ repeatCount: reasons.length > 0 ? 1 : 0,
1840
+ stuckRisk: null,
1841
+ rescueMode: null,
1842
+ wideningBlocked: false,
1843
+ milestonePriority: reasons.length > 0 ? 'normal' : null,
1844
+ };
1845
+ }
1846
+
1847
+ function advanceContinuationState(continuationState = null, previousContinuationState = null) {
1848
+ if (!continuationState || typeof continuationState !== 'object') {
1849
+ return continuationState;
1850
+ }
1851
+
1852
+ if (!continuationState.required) {
1853
+ return {
1854
+ ...continuationState,
1855
+ repeatCount: 0,
1856
+ stuckRisk: null,
1857
+ rescueMode: null,
1858
+ wideningBlocked: false,
1859
+ milestonePriority: null,
1860
+ };
1861
+ }
1862
+
1863
+ const currentReasons = unique(continuationState.reasons ?? []);
1864
+ const previousReasons = unique(previousContinuationState?.reasons ?? []);
1865
+ const sameMilestone = Boolean(
1866
+ previousContinuationState?.required
1867
+ && continuationState.nextMilestone
1868
+ && previousContinuationState.nextMilestone === continuationState.nextMilestone
1869
+ && JSON.stringify(previousReasons) === JSON.stringify(currentReasons)
1870
+ );
1871
+ const repeatCount = sameMilestone
1872
+ ? Math.max(1, Number(previousContinuationState?.repeatCount ?? 1) + 1)
1873
+ : 1;
1874
+ const rescueMode = repeatCount >= 2
1875
+ ? (
1876
+ currentReasons.includes('pending-bounded-context')
1877
+ ? 'finish-current-milestone-before-more-reading'
1878
+ : (
1879
+ currentReasons.includes('missing-write-evidence')
1880
+ ? 'finish-write-before-more-analysis'
1881
+ : 'finish-required-milestone-before-widening'
1882
+ )
1883
+ )
1884
+ : null;
1885
+ const stuckRisk = repeatCount >= 3
1886
+ ? 'high'
1887
+ : (repeatCount === 2 ? 'elevated' : null);
1888
+ const wideningBlocked = repeatCount >= 2 && Boolean(rescueMode);
1889
+ const nextMilestone = repeatCount >= 2 && currentReasons.includes('missing-write-evidence')
1890
+ ? 'finish-write-before-more-analysis'
1891
+ : (
1892
+ repeatCount >= 2 && currentReasons.includes('pending-bounded-context')
1893
+ ? 'finish-current-milestone-before-more-reading'
1894
+ : continuationState.nextMilestone
1895
+ );
1896
+
1897
+ return {
1898
+ ...continuationState,
1899
+ nextMilestone,
1900
+ repeatCount,
1901
+ stuckRisk,
1902
+ rescueMode,
1903
+ wideningBlocked,
1904
+ milestonePriority: wideningBlocked ? 'execute-current-milestone' : 'normal',
1905
+ };
1906
+ }
1907
+
1908
+ function applyRescueBiasToRouteSummary(routeSummary = null) {
1909
+ if (!routeSummary || typeof routeSummary !== 'object') {
1910
+ return routeSummary;
1911
+ }
1912
+
1913
+ const continuationState = routeSummary.continuationState;
1914
+ if (
1915
+ continuationState?.repeatCount >= 2
1916
+ && continuationState?.wideningBlocked
1917
+ && (continuationState.reasons ?? []).includes('missing-write-evidence')
1918
+ ) {
1919
+ routeSummary.nextActionType = 'execute-current-milestone';
1920
+ routeSummary.nextActionCommand = null;
1921
+ routeSummary.helperHint = null;
1922
+ }
1923
+
1924
+ return routeSummary;
1925
+ }
1926
+
1271
1927
  function deriveExecutionPolicy({
1272
1928
  taskType = 'simple',
1273
1929
  mode = 'minimal',
@@ -1687,6 +2343,7 @@ function buildRouteRequestKey({
1687
2343
  targetFile: routingContext.targetFile ?? '',
1688
2344
  contextIntent: routingContext.contextIntent ?? '',
1689
2345
  taskType: routingContext.taskType ?? '',
2346
+ executionMode: routingContext.executionMode ?? '',
1690
2347
  });
1691
2348
  }
1692
2349
 
@@ -1704,10 +2361,23 @@ function buildStructuredRouteFingerprintPayload(route = {}) {
1704
2361
  previousContextIds: unique(route.previousContext?.selectedIds ?? []),
1705
2362
  recentOutputLine: route.recentOutput?.line ?? null,
1706
2363
  policyMode: routeSummary?.policyMode ?? null,
2364
+ executionMode: routeSummary?.executionMode ?? null,
2365
+ approachRiskLevel: routeSummary?.approachSelector?.riskLevel ?? null,
2366
+ approachContextPolicy: routeSummary?.approachSelector?.contextPolicy ?? null,
2367
+ approachVerificationPolicy: routeSummary?.approachSelector?.verificationPolicy ?? null,
2368
+ approachCompletionRule: routeSummary?.approachSelector?.completionRule ?? null,
2369
+ continuationRequired: routeSummary?.continuationState?.required ?? null,
2370
+ continuationReasons: unique(routeSummary?.continuationState?.reasons ?? []),
2371
+ continuationMilestone: routeSummary?.continuationState?.nextMilestone ?? null,
2372
+ continuationRepeatCount: routeSummary?.continuationState?.repeatCount ?? null,
2373
+ continuationStuckRisk: routeSummary?.continuationState?.stuckRisk ?? null,
2374
+ continuationRescueMode: routeSummary?.continuationState?.rescueMode ?? null,
1707
2375
  delegateHint: routeSummary?.delegateHint ?? null,
1708
2376
  nextActionType: routeSummary?.nextActionType ?? null,
1709
2377
  nextActionCommand: routeSummary?.nextActionCommand ?? null,
1710
2378
  helperHint: routeSummary?.helperHint ?? null,
2379
+ completionRule: routeSummary?.executionContract?.completionRule ?? null,
2380
+ completionMissingEvidence: unique(routeSummary?.completionState?.missingEvidence ?? []),
1711
2381
  primaryCommands: unique(routeSummary?.primaryCommands ?? []),
1712
2382
  fallbackCommands: unique(routeSummary?.fallbackCommands ?? []),
1713
2383
  preferredOrder: unique(routeSummary?.preferredOrder ?? []),
@@ -1784,6 +2454,7 @@ function compactRoutingContext(routingContext = {}) {
1784
2454
  lastExplicitUserPromptText: routingContext.lastExplicitUserPromptText ?? '',
1785
2455
  taskType: routingContext.taskType ?? null,
1786
2456
  intentMode: routingContext.intentMode ?? null,
2457
+ executionMode: routingContext.executionMode ?? null,
1787
2458
  };
1788
2459
  }
1789
2460
 
@@ -1846,6 +2517,12 @@ function compactRouteSummary(routeSummary = null) {
1846
2517
  fallbackCommands: unique(routeSummary.fallbackCommands ?? []).slice(0, 3),
1847
2518
  preferredOrder: unique(routeSummary.preferredOrder ?? []).slice(0, 5),
1848
2519
  policyMode: routeSummary.policyMode ?? null,
2520
+ executionMode: routeSummary.executionMode ?? null,
2521
+ executionScores: routeSummary.executionScores ?? null,
2522
+ approachSelector: routeSummary.approachSelector ?? null,
2523
+ executionContract: routeSummary.executionContract ?? null,
2524
+ completionState: routeSummary.completionState ?? null,
2525
+ continuationState: routeSummary.continuationState ?? null,
1849
2526
  delegateHint: routeSummary.delegateHint ?? null,
1850
2527
  nextActionType: routeSummary.nextActionType ?? null,
1851
2528
  nextActionCommand: routeSummary.nextActionCommand ?? null,
@@ -1898,6 +2575,67 @@ function shouldIncludeRecentOutput({
1898
2575
  return true;
1899
2576
  }
1900
2577
 
2578
+ function buildRouteAuditEntry({ route = null, state = null } = {}) {
2579
+ const routingContext = route?.routingContext ?? state?.routingContext ?? {};
2580
+ const routeSummary = route?.routeSummary ?? state?.routeSummary ?? {};
2581
+ const candidateModes = Array.isArray(routeSummary?.approachSelector?.candidateModes)
2582
+ ? routeSummary.approachSelector.candidateModes
2583
+ : [];
2584
+
2585
+ return {
2586
+ ts: Date.now(),
2587
+ source: state?.source ?? 'task-route',
2588
+ requestKey: state?.requestKey ?? null,
2589
+ adapter: routingContext.adapter ?? 'claude',
2590
+ promptFingerprint: stableMachineDigest({
2591
+ prompt: routingContext.lastExplicitUserPromptText ?? '',
2592
+ adapter: routingContext.adapter ?? 'claude',
2593
+ }),
2594
+ targetFile: route?.routingContext?.targetFile ?? null,
2595
+ taskType: routingContext.taskType ?? null,
2596
+ executionMode: routeSummary.executionMode ?? null,
2597
+ competingMode: routeSummary?.approachSelector?.competingMode ?? null,
2598
+ competingScoreGap: routeSummary?.approachSelector?.competingScoreGap ?? null,
2599
+ candidateModes: candidateModes.slice(0, 3),
2600
+ nextActionType: routeSummary.nextActionType ?? null,
2601
+ nextMilestone: routeSummary?.continuationState?.nextMilestone ?? null,
2602
+ repeatCount: routeSummary?.continuationState?.repeatCount ?? null,
2603
+ rescueMode: routeSummary?.continuationState?.rescueMode ?? null,
2604
+ wideningBlocked: routeSummary?.continuationState?.wideningBlocked ?? null,
2605
+ };
2606
+ }
2607
+
2608
+ async function appendRouteAuditEntry(filePath, entry) {
2609
+ if (!entry || typeof entry !== 'object') {
2610
+ return;
2611
+ }
2612
+
2613
+ const existing = await readJson(filePath, { entries: [] });
2614
+ const entries = Array.isArray(existing?.entries) ? existing.entries : [];
2615
+ const dedupeKey = stableMachineDigest({
2616
+ requestKey: entry.requestKey,
2617
+ executionMode: entry.executionMode,
2618
+ nextActionType: entry.nextActionType,
2619
+ nextMilestone: entry.nextMilestone,
2620
+ repeatCount: entry.repeatCount,
2621
+ rescueMode: entry.rescueMode,
2622
+ });
2623
+ const filtered = entries.filter((item) => {
2624
+ const itemKey = stableMachineDigest({
2625
+ requestKey: item?.requestKey ?? null,
2626
+ executionMode: item?.executionMode ?? null,
2627
+ nextActionType: item?.nextActionType ?? null,
2628
+ nextMilestone: item?.nextMilestone ?? null,
2629
+ repeatCount: item?.repeatCount ?? null,
2630
+ rescueMode: item?.rescueMode ?? null,
2631
+ });
2632
+ return itemKey !== dedupeKey;
2633
+ });
2634
+ await writeJson(filePath, {
2635
+ entries: [entry, ...filtered].slice(0, 40),
2636
+ });
2637
+ }
2638
+
1901
2639
  async function readJson(filePath, fallback = null) {
1902
2640
  try {
1903
2641
  return JSON.parse(await fs.readFile(filePath, 'utf8'));