@ngockhoale/ukit 1.4.1 → 1.4.2

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.
@@ -138,12 +138,23 @@ async function main() {
138
138
  requestKey,
139
139
  });
140
140
  if (cachedRouteState) {
141
+ const rescuedRouteSummary = cachedRouteState.routeSummary
142
+ ? {
143
+ ...cachedRouteState.routeSummary,
144
+ continuationState: advanceContinuationState(
145
+ cachedRouteState.routeSummary.continuationState ?? null,
146
+ cachedRouteState.routeSummary.continuationState ?? null,
147
+ ),
148
+ }
149
+ : null;
141
150
  const sharedState = {
142
151
  ...cachedRouteState,
143
152
  source: 'task-route',
144
153
  ts: Date.now(),
145
154
  requestKey,
155
+ routeSummary: rescuedRouteSummary,
146
156
  };
157
+ sharedState.fingerprint = buildRouteStateFingerprint(sharedState);
147
158
  await writeJson(sharedStatePath, sharedState);
148
159
  printRouteState(sharedState);
149
160
  return;
@@ -166,6 +177,7 @@ async function main() {
166
177
  useIndexedContext,
167
178
  previousContextSnapshot,
168
179
  recentOutputSnapshot,
180
+ previousRouteSummary: previousState?.routeSummary ?? null,
169
181
  });
170
182
  await seedHelperCaches({
171
183
  rootDir,
@@ -255,6 +267,21 @@ async function prepareTaskRoute({
255
267
  selectedIds,
256
268
  targetFile: normalizedTarget,
257
269
  });
270
+ const executionScores = deriveExecutionScores({
271
+ promptText: normalizedPrompt,
272
+ commandText: normalizedCommand,
273
+ targetFile: normalizedTarget,
274
+ intentMode,
275
+ taskType: inferredTaskType,
276
+ });
277
+ const executionMode = deriveExecutionMode({
278
+ promptText: normalizedPrompt,
279
+ commandText: normalizedCommand,
280
+ targetFile: normalizedTarget,
281
+ intentMode,
282
+ taskType: inferredTaskType,
283
+ executionScores,
284
+ });
258
285
  const preservedPrompt = normalizedPrompt || String(lastExplicitUserPromptText || '').trim();
259
286
 
260
287
  return {
@@ -269,6 +296,8 @@ async function prepareTaskRoute({
269
296
  contextIntent,
270
297
  taskType: inferredTaskType,
271
298
  intentMode,
299
+ executionScores,
300
+ executionMode,
272
301
  },
273
302
  };
274
303
  }
@@ -283,6 +312,7 @@ async function finalizeTaskRoute({
283
312
  useIndexedContext = true,
284
313
  previousContextSnapshot = null,
285
314
  recentOutputSnapshot = null,
315
+ previousRouteSummary = null,
286
316
  } = {}) {
287
317
  const absoluteRoot = path.resolve(rootDir);
288
318
  const activeSkills = preparedRoute?.activeSkills ?? [];
@@ -347,10 +377,16 @@ async function finalizeTaskRoute({
347
377
  verificationRecommendation,
348
378
  nextAction,
349
379
  });
380
+ routeSummary.continuationState = advanceContinuationState(
381
+ routeSummary.continuationState,
382
+ previousRouteSummary?.continuationState ?? null,
383
+ );
384
+ const approachSelector = routeSummary?.approachSelector ?? null;
350
385
 
351
386
  return {
352
387
  activeSkills,
353
388
  routingContext,
389
+ approachSelector,
354
390
  previousContext: previousContextSnapshot,
355
391
  recentOutput: recentOutputSnapshot,
356
392
  contextRecommendation,
@@ -445,7 +481,11 @@ function formatDisplayNextAction(routeSummary = null) {
445
481
  }
446
482
 
447
483
  const nextActionType = String(routeSummary.nextActionType ?? '').trim();
448
- if (!nextActionType || nextActionType === 'read-skill-instructions') {
484
+ if (
485
+ !nextActionType
486
+ || nextActionType === 'read-skill-instructions'
487
+ || nextActionType === 'pull-indexed-context'
488
+ ) {
449
489
  return '';
450
490
  }
451
491
 
@@ -492,6 +532,10 @@ function formatDisplayHelperHint(routeSummary = null, compactSummary = '') {
492
532
  return '';
493
533
  }
494
534
 
535
+ if (String(routeSummary.nextActionType ?? '').trim() === 'pull-indexed-context') {
536
+ return '';
537
+ }
538
+
495
539
  let helperHint = String(routeSummary.helperHint ?? '').trim().replace(/\s+/g, ' ');
496
540
  if (!helperHint) {
497
541
  return '';
@@ -671,6 +715,10 @@ function shouldKeepRouteEntryForIntent(entry, intentMode) {
671
715
  return false;
672
716
  }
673
717
 
718
+ if (entry.id === 'code-review' && intentMode === 'implement-specific') {
719
+ return false;
720
+ }
721
+
674
722
  return true;
675
723
  }
676
724
 
@@ -710,6 +758,10 @@ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
710
758
  routeSignals.commandRawText ?? '',
711
759
  routeSignals.commandNormalizedText ?? '',
712
760
  ].join('\n');
761
+ const rawPromptSignals = [
762
+ routeSignals.promptRawText ?? '',
763
+ routeSignals.commandRawText ?? '',
764
+ ].join('\n');
713
765
 
714
766
  if (
715
767
  skillId === 'testing-quality'
@@ -718,6 +770,14 @@ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
718
770
  return 1;
719
771
  }
720
772
 
773
+ if (
774
+ skillId === 'code-review'
775
+ && /\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(rawPromptSignals)
776
+ && !/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(rawPromptSignals)
777
+ ) {
778
+ return -4;
779
+ }
780
+
721
781
  return 0;
722
782
  }
723
783
 
@@ -771,10 +831,10 @@ function deriveIntentMode({ promptText = '', commandText = '', targetFile = null
771
831
  if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|login|timeout)\b/.test(lower)) {
772
832
  return 'debug-specific';
773
833
  }
774
- if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(lower)) {
834
+ if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(raw)) {
775
835
  return 'review-specific';
776
836
  }
777
- if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature)\b/.test(lower)) {
837
+ if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(raw)) {
778
838
  return 'implement-specific';
779
839
  }
780
840
  }
@@ -806,7 +866,7 @@ function hasConcreteTaskSignal(lower, raw, targetFile, { taskQueueNext = false }
806
866
  return true;
807
867
  }
808
868
 
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)
869
+ 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
870
  || /\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
871
  }
812
872
 
@@ -1225,6 +1285,21 @@ function buildRouteSummary({
1225
1285
  const compactHelperLane = nextAction?.type === 'pull-indexed-context'
1226
1286
  && typeof contextRecommendation?.command === 'string'
1227
1287
  && contextRecommendation.command.trim();
1288
+ const executionMode = routingContext.executionMode ?? null;
1289
+ const executionScores = routingContext.executionScores ?? null;
1290
+ const approachSelector = buildApproachSelectorResult({
1291
+ executionMode,
1292
+ executionScores,
1293
+ });
1294
+ const executionContract = buildExecutionContract(executionMode);
1295
+ const completionState = buildCompletionState({
1296
+ executionMode,
1297
+ verificationRecommendation,
1298
+ });
1299
+ const continuationState = buildContinuationState({
1300
+ nextActionType: nextAction?.type ?? null,
1301
+ completionState,
1302
+ });
1228
1303
  const helperHint = compactHelperHint(
1229
1304
  compactHelperLane
1230
1305
  ? contextRecommendation?.command
@@ -1243,7 +1318,6 @@ function buildRouteSummary({
1243
1318
  editGuardHint ? `editGuard=${editGuardHint}` : null,
1244
1319
  delegationRecommendation?.hint ? `delegate=${delegationRecommendation.hint}` : null,
1245
1320
  policyMode ? `policy=${policyMode}` : null,
1246
- contextMode ? `mode=${contextMode}` : null,
1247
1321
  ].filter(Boolean).join(' | ');
1248
1322
 
1249
1323
  return {
@@ -1252,6 +1326,12 @@ function buildRouteSummary({
1252
1326
  preferredOrder,
1253
1327
  policyMode,
1254
1328
  editGuardHint,
1329
+ executionMode,
1330
+ executionScores,
1331
+ approachSelector,
1332
+ executionContract,
1333
+ completionState,
1334
+ continuationState,
1255
1335
  intentMode: routingContext.intentMode ?? null,
1256
1336
  delegateHint: delegationRecommendation?.hint ?? null,
1257
1337
  nextActionType: nextAction?.type ?? null,
@@ -1268,6 +1348,375 @@ function deriveContextMode(taskType) {
1268
1348
  return null;
1269
1349
  }
1270
1350
 
1351
+ function deriveExecutionScores({
1352
+ promptText = '',
1353
+ commandText = '',
1354
+ targetFile = null,
1355
+ intentMode = null,
1356
+ taskType = null,
1357
+ } = {}) {
1358
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
1359
+ const sharedRisk = isSharedImpactFile(targetFile);
1360
+ const directTransformSignal = /\b(change|replace|set|rename|convert|swap)\b/.test(raw) || /\bchange\s+.+\s+to\s+.+\b/.test(raw);
1361
+ const smallFixSignal = /\b(adjust|tweak|patch|fix|modify|update|apply)\b/.test(raw);
1362
+ const buildSignal = /\b(build|create|add|implement|feature)\b/.test(raw);
1363
+ const debugSignal = intentMode === 'debug-specific' || /\b(root cause|debug|triage|flaky|investigate|why)\b/.test(raw);
1364
+ const reviewSignal = intentMode === 'review-specific' || /\b(review|audit|verify|release readiness)\b/.test(raw);
1365
+
1366
+ let editCertainty = 0;
1367
+ if (directTransformSignal) {
1368
+ editCertainty = 3;
1369
+ } else if (smallFixSignal) {
1370
+ editCertainty = 2;
1371
+ } else if (buildSignal) {
1372
+ editCertainty = 1;
1373
+ }
1374
+
1375
+ let investigationNeed = 0;
1376
+ if (debugSignal) {
1377
+ investigationNeed = 3;
1378
+ } else if (/\b(failing|broken|error|crash|timeout)\b/.test(raw)) {
1379
+ investigationNeed = 2;
1380
+ }
1381
+
1382
+ let blastRadius = 0;
1383
+ if (sharedRisk) {
1384
+ blastRadius = 3;
1385
+ } else if (taskType === 'non-trivial' || taskType === 'shared-simple') {
1386
+ blastRadius = 2;
1387
+ } else if (!targetFile) {
1388
+ blastRadius = 1;
1389
+ }
1390
+
1391
+ let verificationBurden = 1;
1392
+ if (reviewSignal || sharedRisk) {
1393
+ verificationBurden = 3;
1394
+ } else if (debugSignal || taskType === 'non-trivial') {
1395
+ verificationBurden = 2;
1396
+ }
1397
+
1398
+ let ambiguity = 0;
1399
+ if (!targetFile) {
1400
+ ambiguity = 2;
1401
+ } else if (buildSignal && !directTransformSignal && !smallFixSignal) {
1402
+ ambiguity = 1;
1403
+ }
1404
+
1405
+ return {
1406
+ editCertainty,
1407
+ investigationNeed,
1408
+ blastRadius,
1409
+ verificationBurden,
1410
+ ambiguity,
1411
+ };
1412
+ }
1413
+
1414
+ function deriveExecutionMode({
1415
+ promptText = '',
1416
+ commandText = '',
1417
+ targetFile = null,
1418
+ intentMode = null,
1419
+ executionScores = {},
1420
+ } = {}) {
1421
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
1422
+ const scores = executionScores;
1423
+ const explicitReviewLead = /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw);
1424
+
1425
+ if (intentMode === 'review-specific' || explicitReviewLead) {
1426
+ return 'review-release';
1427
+ }
1428
+
1429
+ if (scores.blastRadius >= 3 && (scores.ambiguity >= 2 || scores.investigationNeed >= 2)) {
1430
+ return 'map-impact';
1431
+ }
1432
+
1433
+ if (scores.blastRadius >= 3 || isSharedImpactFile(targetFile)) {
1434
+ return 'shared-edit';
1435
+ }
1436
+
1437
+ if (scores.investigationNeed >= 3) {
1438
+ return 'find-cause';
1439
+ }
1440
+
1441
+ if (
1442
+ scores.editCertainty >= 3
1443
+ && scores.ambiguity === 0
1444
+ && scores.blastRadius === 0
1445
+ && scores.verificationBurden <= 1
1446
+ ) {
1447
+ return 'tiny-fix';
1448
+ }
1449
+
1450
+ if (
1451
+ /\b(build|create|add|implement|feature|summary card)\b/.test(raw)
1452
+ && scores.investigationNeed === 0
1453
+ && scores.blastRadius < 3
1454
+ ) {
1455
+ return 'local-build';
1456
+ }
1457
+
1458
+ if (scores.editCertainty >= 2 && scores.investigationNeed === 0 && scores.blastRadius === 0) {
1459
+ return 'local-fix';
1460
+ }
1461
+
1462
+ return 'local-build';
1463
+ }
1464
+
1465
+ function buildExecutionContract(executionMode = null) {
1466
+ if (!executionMode) {
1467
+ return null;
1468
+ }
1469
+
1470
+ const contracts = {
1471
+ 'tiny-fix': {
1472
+ maxReadPasses: 0,
1473
+ maxContextPulls: 0,
1474
+ verificationPolicy: 'minimal-or-targeted',
1475
+ completionRule: 'never-claim-done-without-write',
1476
+ delegationPolicy: 'disallow',
1477
+ completionEvidence: ['write-evidence'],
1478
+ },
1479
+ 'local-fix': {
1480
+ maxReadPasses: 1,
1481
+ maxContextPulls: 1,
1482
+ verificationPolicy: 'targeted-if-covered',
1483
+ completionRule: 'require-write',
1484
+ delegationPolicy: 'disallow',
1485
+ completionEvidence: ['write-evidence'],
1486
+ },
1487
+ 'local-build': {
1488
+ maxReadPasses: 2,
1489
+ maxContextPulls: 1,
1490
+ verificationPolicy: 'targeted-if-covered',
1491
+ completionRule: 'require-write',
1492
+ delegationPolicy: 'disallow-by-default',
1493
+ completionEvidence: ['write-evidence'],
1494
+ },
1495
+ 'find-cause': {
1496
+ maxReadPassesBeforeReassess: 3,
1497
+ verificationPolicy: 'root-cause-then-targeted',
1498
+ completionRule: 'never-claim-fixed-without-write-and-verification',
1499
+ delegationPolicy: 'allow-specialized-debug-lane',
1500
+ completionEvidence: ['write-evidence', 'verification-evidence'],
1501
+ },
1502
+ 'shared-edit': {
1503
+ maxReadPasses: 2,
1504
+ maxContextPulls: 2,
1505
+ verificationPolicy: 'targeted-then-widen-on-risk',
1506
+ completionRule: 'require-write-and-verification',
1507
+ delegationPolicy: 'allow-qualified-sidecar',
1508
+ completionEvidence: ['write-evidence', 'verification-evidence'],
1509
+ mirrorConsistencyRequired: true,
1510
+ },
1511
+ 'map-impact': {
1512
+ maxReadPasses: 3,
1513
+ maxContextPulls: 3,
1514
+ verificationPolicy: 'impact-first-then-targeted-then-widen-on-risk',
1515
+ completionRule: 'require-impact-evidence-before-edit-claim',
1516
+ delegationPolicy: 'allow-impact-sidecar',
1517
+ completionEvidence: ['impact-evidence', 'write-evidence', 'verification-evidence'],
1518
+ mirrorConsistencyRequired: true,
1519
+ },
1520
+ 'review-release': {
1521
+ verificationPolicy: 'evidence-first',
1522
+ completionRule: 'report-findings-not-implementation',
1523
+ delegationPolicy: 'allow-review-sidecar',
1524
+ completionEvidence: ['verification-evidence'],
1525
+ },
1526
+ };
1527
+
1528
+ return contracts[executionMode] ? { ...contracts[executionMode] } : null;
1529
+ }
1530
+
1531
+ function buildApproachSelectorResult({
1532
+ executionMode = null,
1533
+ executionScores = null,
1534
+ } = {}) {
1535
+ if (!executionMode) {
1536
+ return null;
1537
+ }
1538
+
1539
+ const executionContract = buildExecutionContract(executionMode);
1540
+ const policyByMode = {
1541
+ 'tiny-fix': {
1542
+ riskLevel: 'minimal',
1543
+ contextPolicy: 'confirm-target-only',
1544
+ },
1545
+ 'local-fix': {
1546
+ riskLevel: 'local',
1547
+ contextPolicy: 'bounded-local',
1548
+ },
1549
+ 'local-build': {
1550
+ riskLevel: 'local',
1551
+ contextPolicy: 'bounded-with-related-files',
1552
+ },
1553
+ 'find-cause': {
1554
+ riskLevel: 'investigation',
1555
+ contextPolicy: 'trace-then-bounded-context',
1556
+ },
1557
+ 'shared-edit': {
1558
+ riskLevel: 'shared',
1559
+ contextPolicy: 'bounded-with-related-tests',
1560
+ },
1561
+ 'map-impact': {
1562
+ riskLevel: 'wide',
1563
+ contextPolicy: 'impact-map-first',
1564
+ },
1565
+ 'review-release': {
1566
+ riskLevel: 'release',
1567
+ contextPolicy: 'evidence-review-only',
1568
+ },
1569
+ };
1570
+ const selectedPolicy = policyByMode[executionMode] ?? {
1571
+ riskLevel: 'local',
1572
+ contextPolicy: 'bounded-local',
1573
+ };
1574
+
1575
+ return {
1576
+ executionMode,
1577
+ executionScores: executionScores ? { ...executionScores } : null,
1578
+ riskLevel: selectedPolicy.riskLevel,
1579
+ contextPolicy: selectedPolicy.contextPolicy,
1580
+ verificationPolicy: executionContract?.verificationPolicy ?? null,
1581
+ completionRule: executionContract?.completionRule ?? null,
1582
+ };
1583
+ }
1584
+
1585
+ function buildCompletionState({ executionMode = null, verificationRecommendation = null } = {}) {
1586
+ if (!executionMode) {
1587
+ return null;
1588
+ }
1589
+
1590
+ const contract = buildExecutionContract(executionMode);
1591
+ const missingEvidence = [...(contract?.completionEvidence ?? [])];
1592
+ const requiresVerification = missingEvidence.includes('verification-evidence');
1593
+ if (
1594
+ requiresVerification
1595
+ && verificationRecommendation
1596
+ && !(verificationRecommendation.commands?.length || verificationRecommendation.fallbackCommands?.length)
1597
+ ) {
1598
+ missingEvidence.push('verification-plan');
1599
+ }
1600
+
1601
+ let reason = 'completion evidence is still required';
1602
+ if (['tiny-fix', 'local-fix', 'local-build', 'shared-edit'].includes(executionMode)) {
1603
+ reason = 'implement request has not produced an edit yet';
1604
+ } else if (executionMode === 'review-release') {
1605
+ reason = 'review/release evidence is still required before final claim';
1606
+ } else if (executionMode === 'map-impact') {
1607
+ reason = 'impact evidence is still required before safe completion claim';
1608
+ }
1609
+
1610
+ return {
1611
+ claimAllowed: false,
1612
+ missingEvidence,
1613
+ reason,
1614
+ };
1615
+ }
1616
+
1617
+ function buildContinuationState({
1618
+ nextActionType = null,
1619
+ completionState = null,
1620
+ } = {}) {
1621
+ const reasons = [];
1622
+ const missingEvidence = unique(completionState?.missingEvidence ?? []);
1623
+
1624
+ if (nextActionType === 'pull-indexed-context') {
1625
+ reasons.push('pending-bounded-context');
1626
+ }
1627
+
1628
+ if (nextActionType === 'read-skill-instructions') {
1629
+ reasons.push('pending-skill-read');
1630
+ }
1631
+
1632
+ if (missingEvidence.includes('write-evidence')) {
1633
+ reasons.push('missing-write-evidence');
1634
+ }
1635
+
1636
+ if (missingEvidence.includes('verification-evidence') || missingEvidence.includes('verification-plan')) {
1637
+ reasons.push('missing-verification-evidence');
1638
+ }
1639
+
1640
+ if (missingEvidence.includes('impact-evidence')) {
1641
+ reasons.push('missing-impact-evidence');
1642
+ }
1643
+
1644
+ const nextMilestone = reasons.includes('pending-bounded-context')
1645
+ ? 'complete-bounded-context-then-continue'
1646
+ : (
1647
+ reasons.includes('missing-write-evidence')
1648
+ ? 'produce-write-evidence'
1649
+ : (
1650
+ reasons.includes('missing-verification-evidence')
1651
+ ? 'produce-verification-evidence'
1652
+ : (
1653
+ reasons.includes('missing-impact-evidence')
1654
+ ? 'produce-impact-evidence'
1655
+ : null
1656
+ )
1657
+ )
1658
+ );
1659
+
1660
+ return {
1661
+ required: reasons.length > 0,
1662
+ reasons,
1663
+ doneLanguageBlocked: reasons.length > 0,
1664
+ stopAllowedOnlyFor: reasons.length > 0 ? ['real-blocker'] : [],
1665
+ nextMilestone,
1666
+ repeatCount: reasons.length > 0 ? 1 : 0,
1667
+ stuckRisk: null,
1668
+ rescueMode: null,
1669
+ };
1670
+ }
1671
+
1672
+ function advanceContinuationState(continuationState = null, previousContinuationState = null) {
1673
+ if (!continuationState || typeof continuationState !== 'object') {
1674
+ return continuationState;
1675
+ }
1676
+
1677
+ if (!continuationState.required) {
1678
+ return {
1679
+ ...continuationState,
1680
+ repeatCount: 0,
1681
+ stuckRisk: null,
1682
+ rescueMode: null,
1683
+ };
1684
+ }
1685
+
1686
+ const currentReasons = unique(continuationState.reasons ?? []);
1687
+ const previousReasons = unique(previousContinuationState?.reasons ?? []);
1688
+ const sameMilestone = Boolean(
1689
+ previousContinuationState?.required
1690
+ && continuationState.nextMilestone
1691
+ && previousContinuationState.nextMilestone === continuationState.nextMilestone
1692
+ && JSON.stringify(previousReasons) === JSON.stringify(currentReasons)
1693
+ );
1694
+ const repeatCount = sameMilestone
1695
+ ? Math.max(1, Number(previousContinuationState?.repeatCount ?? 1) + 1)
1696
+ : 1;
1697
+ const rescueMode = repeatCount >= 2
1698
+ ? (
1699
+ currentReasons.includes('pending-bounded-context')
1700
+ ? 'finish-current-milestone-before-more-reading'
1701
+ : (
1702
+ currentReasons.includes('missing-write-evidence')
1703
+ ? 'finish-write-before-more-analysis'
1704
+ : 'finish-required-milestone-before-widening'
1705
+ )
1706
+ )
1707
+ : null;
1708
+ const stuckRisk = repeatCount >= 3
1709
+ ? 'high'
1710
+ : (repeatCount === 2 ? 'elevated' : null);
1711
+
1712
+ return {
1713
+ ...continuationState,
1714
+ repeatCount,
1715
+ stuckRisk,
1716
+ rescueMode,
1717
+ };
1718
+ }
1719
+
1271
1720
  function deriveExecutionPolicy({
1272
1721
  taskType = 'simple',
1273
1722
  mode = 'minimal',
@@ -1687,6 +2136,7 @@ function buildRouteRequestKey({
1687
2136
  targetFile: routingContext.targetFile ?? '',
1688
2137
  contextIntent: routingContext.contextIntent ?? '',
1689
2138
  taskType: routingContext.taskType ?? '',
2139
+ executionMode: routingContext.executionMode ?? '',
1690
2140
  });
1691
2141
  }
1692
2142
 
@@ -1704,10 +2154,23 @@ function buildStructuredRouteFingerprintPayload(route = {}) {
1704
2154
  previousContextIds: unique(route.previousContext?.selectedIds ?? []),
1705
2155
  recentOutputLine: route.recentOutput?.line ?? null,
1706
2156
  policyMode: routeSummary?.policyMode ?? null,
2157
+ executionMode: routeSummary?.executionMode ?? null,
2158
+ approachRiskLevel: routeSummary?.approachSelector?.riskLevel ?? null,
2159
+ approachContextPolicy: routeSummary?.approachSelector?.contextPolicy ?? null,
2160
+ approachVerificationPolicy: routeSummary?.approachSelector?.verificationPolicy ?? null,
2161
+ approachCompletionRule: routeSummary?.approachSelector?.completionRule ?? null,
2162
+ continuationRequired: routeSummary?.continuationState?.required ?? null,
2163
+ continuationReasons: unique(routeSummary?.continuationState?.reasons ?? []),
2164
+ continuationMilestone: routeSummary?.continuationState?.nextMilestone ?? null,
2165
+ continuationRepeatCount: routeSummary?.continuationState?.repeatCount ?? null,
2166
+ continuationStuckRisk: routeSummary?.continuationState?.stuckRisk ?? null,
2167
+ continuationRescueMode: routeSummary?.continuationState?.rescueMode ?? null,
1707
2168
  delegateHint: routeSummary?.delegateHint ?? null,
1708
2169
  nextActionType: routeSummary?.nextActionType ?? null,
1709
2170
  nextActionCommand: routeSummary?.nextActionCommand ?? null,
1710
2171
  helperHint: routeSummary?.helperHint ?? null,
2172
+ completionRule: routeSummary?.executionContract?.completionRule ?? null,
2173
+ completionMissingEvidence: unique(routeSummary?.completionState?.missingEvidence ?? []),
1711
2174
  primaryCommands: unique(routeSummary?.primaryCommands ?? []),
1712
2175
  fallbackCommands: unique(routeSummary?.fallbackCommands ?? []),
1713
2176
  preferredOrder: unique(routeSummary?.preferredOrder ?? []),
@@ -1784,6 +2247,7 @@ function compactRoutingContext(routingContext = {}) {
1784
2247
  lastExplicitUserPromptText: routingContext.lastExplicitUserPromptText ?? '',
1785
2248
  taskType: routingContext.taskType ?? null,
1786
2249
  intentMode: routingContext.intentMode ?? null,
2250
+ executionMode: routingContext.executionMode ?? null,
1787
2251
  };
1788
2252
  }
1789
2253
 
@@ -1846,6 +2310,12 @@ function compactRouteSummary(routeSummary = null) {
1846
2310
  fallbackCommands: unique(routeSummary.fallbackCommands ?? []).slice(0, 3),
1847
2311
  preferredOrder: unique(routeSummary.preferredOrder ?? []).slice(0, 5),
1848
2312
  policyMode: routeSummary.policyMode ?? null,
2313
+ executionMode: routeSummary.executionMode ?? null,
2314
+ executionScores: routeSummary.executionScores ?? null,
2315
+ approachSelector: routeSummary.approachSelector ?? null,
2316
+ executionContract: routeSummary.executionContract ?? null,
2317
+ completionState: routeSummary.completionState ?? null,
2318
+ continuationState: routeSummary.continuationState ?? null,
1849
2319
  delegateHint: routeSummary.delegateHint ?? null,
1850
2320
  nextActionType: routeSummary.nextActionType ?? null,
1851
2321
  nextActionCommand: routeSummary.nextActionCommand ?? null,