@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.
@@ -52,6 +52,28 @@ export async function deriveTaskRoute({
52
52
  selectedIds,
53
53
  targetFile: normalizedTarget,
54
54
  });
55
+ const executionScores = deriveExecutionScores({
56
+ promptText: normalizedPrompt,
57
+ commandText: normalizedCommand,
58
+ targetFile: normalizedTarget,
59
+ intentMode,
60
+ taskType: inferredTaskType,
61
+ });
62
+ const executionCandidates = buildExecutionModeCandidates({
63
+ promptText: normalizedPrompt,
64
+ commandText: normalizedCommand,
65
+ targetFile: normalizedTarget,
66
+ intentMode,
67
+ executionScores,
68
+ });
69
+ const executionMode = deriveExecutionMode({
70
+ promptText: normalizedPrompt,
71
+ commandText: normalizedCommand,
72
+ targetFile: normalizedTarget,
73
+ intentMode,
74
+ executionScores,
75
+ executionCandidates,
76
+ });
55
77
  const preservedPrompt = normalizedPrompt || String(lastExplicitUserPromptText || '').trim();
56
78
  const degradedWarnings = [];
57
79
  let contextResult = null;
@@ -128,11 +150,15 @@ export async function deriveTaskRoute({
128
150
  taskType: inferredTaskType,
129
151
  intentMode,
130
152
  autonomyLevel,
153
+ executionScores,
154
+ executionCandidates,
155
+ executionMode,
131
156
  },
132
157
  contextRecommendation,
133
158
  verificationRecommendation,
134
159
  nextAction,
135
160
  });
161
+ const approachSelector = routeSummary?.approachSelector ?? null;
136
162
 
137
163
  return {
138
164
  activeSkills,
@@ -145,7 +171,11 @@ export async function deriveTaskRoute({
145
171
  taskType: inferredTaskType,
146
172
  intentMode,
147
173
  autonomyLevel,
174
+ executionScores,
175
+ executionCandidates,
176
+ executionMode,
148
177
  },
178
+ approachSelector,
149
179
  contextRecommendation,
150
180
  verificationRecommendation,
151
181
  nextAction,
@@ -186,6 +216,23 @@ export function buildRouteSummary({
186
216
  const compactHelperLane = nextAction?.type === 'pull-indexed-context'
187
217
  && typeof contextRecommendation?.command === 'string'
188
218
  && contextRecommendation.command.trim();
219
+ const executionMode = routingContext.executionMode ?? null;
220
+ const executionScores = routingContext.executionScores ?? null;
221
+ const executionCandidates = routingContext.executionCandidates ?? null;
222
+ const approachSelector = buildApproachSelectorResult({
223
+ executionMode,
224
+ executionScores,
225
+ executionCandidates,
226
+ });
227
+ const executionContract = buildExecutionContract(executionMode);
228
+ const completionState = buildCompletionState({
229
+ executionMode,
230
+ verificationRecommendation,
231
+ });
232
+ const continuationState = buildContinuationState({
233
+ nextActionType: nextAction?.type ?? null,
234
+ completionState,
235
+ });
189
236
  const helperHint = compactHelperHint(
190
237
  compactHelperLane
191
238
  ? contextRecommendation?.command
@@ -204,7 +251,6 @@ export function buildRouteSummary({
204
251
  editGuardHint ? `editGuard=${editGuardHint}` : null,
205
252
  delegationRecommendation?.hint ? `delegate=${delegationRecommendation.hint}` : null,
206
253
  policyMode ? `policy=${policyMode}` : null,
207
- contextMode ? `mode=${contextMode}` : null,
208
254
  ].filter(Boolean).join(' | ');
209
255
 
210
256
  return {
@@ -213,6 +259,13 @@ export function buildRouteSummary({
213
259
  preferredOrder,
214
260
  policyMode,
215
261
  editGuardHint,
262
+ executionMode,
263
+ executionScores,
264
+ executionCandidates,
265
+ approachSelector,
266
+ executionContract,
267
+ completionState,
268
+ continuationState,
216
269
  intentMode: routingContext.intentMode ?? null,
217
270
  delegateHint: delegationRecommendation?.hint ?? null,
218
271
  nextActionType: nextAction?.type ?? null,
@@ -229,6 +282,479 @@ function deriveContextMode(taskType) {
229
282
  return null;
230
283
  }
231
284
 
285
+ function deriveExecutionScores({
286
+ promptText = '',
287
+ commandText = '',
288
+ targetFile = null,
289
+ intentMode = null,
290
+ taskType = null,
291
+ } = {}) {
292
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
293
+ const sharedRisk = isSharedImpactFile(targetFile);
294
+ const directTransformSignal = /\b(change|replace|set|rename|convert|swap)\b/.test(raw) || /\bchange\s+.+\s+to\s+.+\b/.test(raw);
295
+ const smallFixSignal = /\b(adjust|tweak|patch|fix|modify|update|apply)\b/.test(raw);
296
+ const buildSignal = /\b(build|create|add|implement|feature)\b/.test(raw);
297
+ const debugSignal = intentMode === 'debug-specific' || /\b(root cause|debug|triage|flaky|investigate|why)\b/.test(raw);
298
+ const reviewSignal = intentMode === 'review-specific' || /\b(review|audit|verify|release readiness)\b/.test(raw);
299
+ const failureSignal = /\b(failing|failed|broken|error|crash|timeout|undefined|exception|eacces|trace)\b/.test(raw);
300
+ 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);
301
+ const boundedEditSignal = /\b(one[- ]line|small|tiny|single|local|guard|log line|loading message|label)\b/.test(raw);
302
+ const implementSignal = /\b(implement|apply|update|modify|add|create|ship|deliver|fix)\b/.test(raw)
303
+ || /\bchange\s+.+\s+to\s+.+\b/.test(raw);
304
+ const explicitTarget = Boolean(targetFile);
305
+
306
+ let editCertainty = 0;
307
+ if (directTransformSignal) {
308
+ editCertainty = 3;
309
+ } else if (smallFixSignal) {
310
+ editCertainty = 2;
311
+ } else if (buildSignal) {
312
+ editCertainty = 1;
313
+ }
314
+
315
+ let investigationNeed = 0;
316
+ if (debugSignal) {
317
+ investigationNeed = 3;
318
+ } else if (failureSignal) {
319
+ investigationNeed = 2;
320
+ }
321
+
322
+ let blastRadius = 0;
323
+ if (sharedRisk) {
324
+ blastRadius = 3;
325
+ } else if (taskType === 'non-trivial' || taskType === 'shared-simple') {
326
+ blastRadius = 2;
327
+ } else if (!targetFile) {
328
+ blastRadius = 1;
329
+ }
330
+
331
+ let verificationBurden = 1;
332
+ if (reviewSignal || sharedRisk) {
333
+ verificationBurden = 3;
334
+ } else if (debugSignal || taskType === 'non-trivial') {
335
+ verificationBurden = 2;
336
+ }
337
+
338
+ let ambiguity = 0;
339
+ if (!targetFile) {
340
+ ambiguity = 2;
341
+ } else if (buildSignal && !directTransformSignal && !smallFixSignal) {
342
+ ambiguity = 1;
343
+ }
344
+
345
+ return {
346
+ editCertainty,
347
+ investigationNeed,
348
+ blastRadius,
349
+ verificationBurden,
350
+ ambiguity,
351
+ sharedRisk,
352
+ directTransformSignal,
353
+ smallFixSignal,
354
+ buildSignal,
355
+ debugSignal,
356
+ reviewSignal,
357
+ failureSignal,
358
+ impactSignal,
359
+ boundedEditSignal,
360
+ implementSignal,
361
+ explicitTarget,
362
+ };
363
+ }
364
+
365
+ function deriveExecutionMode({
366
+ promptText = '',
367
+ commandText = '',
368
+ targetFile = null,
369
+ intentMode = null,
370
+ executionScores = {},
371
+ executionCandidates = null,
372
+ } = {}) {
373
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
374
+ const scores = executionScores;
375
+ const candidates = executionCandidates ?? buildExecutionModeCandidates({
376
+ promptText,
377
+ commandText,
378
+ targetFile,
379
+ intentMode,
380
+ executionScores,
381
+ });
382
+ const explicitReviewLead = /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw);
383
+ const strongImpactLead = scores.sharedRisk
384
+ && (scores.impactSignal || /\b(check all affected|map all affected|across all affected)\b/.test(raw));
385
+ const boundedLocalBuildCandidate = scores.buildSignal && scores.boundedEditSignal && scores.explicitTarget && !scores.sharedRisk;
386
+
387
+ if ((intentMode === 'review-specific' || explicitReviewLead) && !scores.implementSignal) {
388
+ return 'review-release';
389
+ }
390
+
391
+ if (strongImpactLead || (scores.blastRadius >= 3 && (scores.ambiguity >= 2 || scores.investigationNeed >= 2))) {
392
+ return 'map-impact';
393
+ }
394
+
395
+ if (scores.sharedRisk && scores.failureSignal && scores.investigationNeed >= 2 && !scores.impactSignal) {
396
+ return 'shared-edit';
397
+ }
398
+
399
+ if (scores.blastRadius >= 3 || scores.sharedRisk) {
400
+ return 'shared-edit';
401
+ }
402
+
403
+ if (scores.investigationNeed >= 3 || (scores.failureSignal && scores.investigationNeed >= 2)) {
404
+ return 'find-cause';
405
+ }
406
+
407
+ if (scores.directTransformSignal && !scores.explicitTarget && scores.investigationNeed === 0 && !scores.sharedRisk) {
408
+ return 'local-build';
409
+ }
410
+
411
+ if (
412
+ scores.editCertainty >= 3
413
+ && scores.ambiguity <= 1
414
+ && scores.blastRadius === 0
415
+ && scores.verificationBurden <= 1
416
+ ) {
417
+ return 'tiny-fix';
418
+ }
419
+
420
+ if (boundedLocalBuildCandidate && scores.investigationNeed === 0) {
421
+ return 'local-fix';
422
+ }
423
+
424
+ if (
425
+ /\b(build|create|add|implement|feature|summary card)\b/.test(raw)
426
+ && scores.investigationNeed === 0
427
+ && scores.blastRadius < 3
428
+ && !scores.boundedEditSignal
429
+ ) {
430
+ return 'local-build';
431
+ }
432
+
433
+ if (scores.editCertainty >= 2 && scores.investigationNeed === 0 && scores.blastRadius === 0) {
434
+ return 'local-fix';
435
+ }
436
+
437
+ return applySafeUpwardBias(candidates, 'local-build');
438
+ }
439
+
440
+ function buildExecutionModeCandidates({
441
+ promptText = '',
442
+ commandText = '',
443
+ targetFile = null,
444
+ intentMode = null,
445
+ executionScores = {},
446
+ } = {}) {
447
+ const scores = executionScores;
448
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
449
+ const explicitReviewLead = (intentMode === 'review-specific' || /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw))
450
+ && !scores.implementSignal;
451
+
452
+ const modeScores = {
453
+ 'tiny-fix': (
454
+ (scores.editCertainty * 4)
455
+ + (scores.directTransformSignal ? 4 : 0)
456
+ + (scores.explicitTarget ? 1 : 0)
457
+ - (scores.failureSignal ? 4 : 0)
458
+ - (scores.blastRadius * 4)
459
+ - (scores.ambiguity * 2)
460
+ ),
461
+ 'local-fix': (
462
+ (scores.editCertainty * 3)
463
+ + (scores.boundedEditSignal ? 3 : 0)
464
+ + (scores.explicitTarget ? 1 : 0)
465
+ - (scores.failureSignal ? 2 : 0)
466
+ - (scores.blastRadius * 4)
467
+ ),
468
+ 'local-build': (
469
+ (scores.buildSignal ? 6 : 0)
470
+ + (scores.editCertainty * 1)
471
+ + (scores.explicitTarget ? 1 : 0)
472
+ - (scores.boundedEditSignal ? 3 : 0)
473
+ - (scores.failureSignal ? 3 : 0)
474
+ - (scores.blastRadius * 4)
475
+ ),
476
+ 'find-cause': (
477
+ (scores.investigationNeed * 4)
478
+ + (scores.failureSignal ? 3 : 0)
479
+ + (scores.debugSignal ? 2 : 0)
480
+ - (scores.blastRadius >= 3 ? 1 : 0)
481
+ ),
482
+ 'shared-edit': (
483
+ (scores.sharedRisk ? 8 : 0)
484
+ + (scores.editCertainty * 1)
485
+ + (scores.buildSignal ? 1 : 0)
486
+ - (scores.impactSignal ? 2 : 0)
487
+ ),
488
+ 'map-impact': (
489
+ (scores.sharedRisk ? 7 : 0)
490
+ + (scores.impactSignal ? 5 : 0)
491
+ + (scores.ambiguity * 2)
492
+ + scores.investigationNeed
493
+ ),
494
+ 'review-release': (
495
+ (explicitReviewLead ? 10 : 0)
496
+ + (scores.reviewSignal ? 3 : 0)
497
+ - (scores.implementSignal ? 5 : 0)
498
+ ),
499
+ };
500
+
501
+ return Object.entries(modeScores)
502
+ .map(([mode, score]) => ({ mode, score }))
503
+ .sort((a, b) => b.score - a.score || executionModeRank(a.mode) - executionModeRank(b.mode));
504
+ }
505
+
506
+ function applySafeUpwardBias(candidates = [], fallbackMode = 'local-build') {
507
+ const [topCandidate, competingCandidate] = candidates;
508
+ if (!topCandidate) {
509
+ return fallbackMode;
510
+ }
511
+
512
+ if (!competingCandidate) {
513
+ return topCandidate.mode;
514
+ }
515
+
516
+ const scoreGap = Number(topCandidate.score ?? 0) - Number(competingCandidate.score ?? 0);
517
+ const rankGap = executionModeRank(competingCandidate.mode) - executionModeRank(topCandidate.mode);
518
+ if (scoreGap <= 1 && rankGap === 1) {
519
+ return competingCandidate.mode;
520
+ }
521
+
522
+ return topCandidate.mode;
523
+ }
524
+
525
+ function executionModeRank(mode = '') {
526
+ const orderedModes = [
527
+ 'tiny-fix',
528
+ 'local-fix',
529
+ 'local-build',
530
+ 'find-cause',
531
+ 'shared-edit',
532
+ 'map-impact',
533
+ 'review-release',
534
+ ];
535
+
536
+ const index = orderedModes.indexOf(mode);
537
+ return index >= 0 ? index : orderedModes.length;
538
+ }
539
+
540
+ function buildExecutionContract(executionMode = null) {
541
+ if (!executionMode) {
542
+ return null;
543
+ }
544
+
545
+ const contracts = {
546
+ 'tiny-fix': {
547
+ maxReadPasses: 0,
548
+ maxContextPulls: 0,
549
+ verificationPolicy: 'minimal-or-targeted',
550
+ completionRule: 'never-claim-done-without-write',
551
+ delegationPolicy: 'disallow',
552
+ completionEvidence: ['write-evidence'],
553
+ },
554
+ 'local-fix': {
555
+ maxReadPasses: 1,
556
+ maxContextPulls: 1,
557
+ verificationPolicy: 'targeted-if-covered',
558
+ completionRule: 'require-write',
559
+ delegationPolicy: 'disallow',
560
+ completionEvidence: ['write-evidence'],
561
+ },
562
+ 'local-build': {
563
+ maxReadPasses: 2,
564
+ maxContextPulls: 1,
565
+ verificationPolicy: 'targeted-if-covered',
566
+ completionRule: 'require-write',
567
+ delegationPolicy: 'disallow-by-default',
568
+ completionEvidence: ['write-evidence'],
569
+ },
570
+ 'find-cause': {
571
+ maxReadPassesBeforeReassess: 3,
572
+ verificationPolicy: 'root-cause-then-targeted',
573
+ completionRule: 'never-claim-fixed-without-write-and-verification',
574
+ delegationPolicy: 'allow-specialized-debug-lane',
575
+ completionEvidence: ['write-evidence', 'verification-evidence'],
576
+ },
577
+ 'shared-edit': {
578
+ maxReadPasses: 2,
579
+ maxContextPulls: 2,
580
+ verificationPolicy: 'targeted-then-widen-on-risk',
581
+ completionRule: 'require-write-and-verification',
582
+ delegationPolicy: 'allow-qualified-sidecar',
583
+ completionEvidence: ['write-evidence', 'verification-evidence'],
584
+ mirrorConsistencyRequired: true,
585
+ },
586
+ 'map-impact': {
587
+ maxReadPasses: 3,
588
+ maxContextPulls: 3,
589
+ verificationPolicy: 'impact-first-then-targeted-then-widen-on-risk',
590
+ completionRule: 'require-impact-evidence-before-edit-claim',
591
+ delegationPolicy: 'allow-impact-sidecar',
592
+ completionEvidence: ['impact-evidence', 'write-evidence', 'verification-evidence'],
593
+ mirrorConsistencyRequired: true,
594
+ },
595
+ 'review-release': {
596
+ verificationPolicy: 'evidence-first',
597
+ completionRule: 'report-findings-not-implementation',
598
+ delegationPolicy: 'allow-review-sidecar',
599
+ completionEvidence: ['verification-evidence'],
600
+ },
601
+ };
602
+
603
+ return contracts[executionMode] ? { ...contracts[executionMode] } : null;
604
+ }
605
+
606
+ function buildApproachSelectorResult({
607
+ executionMode = null,
608
+ executionScores = null,
609
+ executionCandidates = null,
610
+ } = {}) {
611
+ if (!executionMode) {
612
+ return null;
613
+ }
614
+
615
+ const executionContract = buildExecutionContract(executionMode);
616
+ const policyByMode = {
617
+ 'tiny-fix': {
618
+ riskLevel: 'minimal',
619
+ contextPolicy: 'confirm-target-only',
620
+ },
621
+ 'local-fix': {
622
+ riskLevel: 'local',
623
+ contextPolicy: 'bounded-local',
624
+ },
625
+ 'local-build': {
626
+ riskLevel: 'local',
627
+ contextPolicy: 'bounded-with-related-files',
628
+ },
629
+ 'find-cause': {
630
+ riskLevel: 'investigation',
631
+ contextPolicy: 'trace-then-bounded-context',
632
+ },
633
+ 'shared-edit': {
634
+ riskLevel: 'shared',
635
+ contextPolicy: 'bounded-with-related-tests',
636
+ },
637
+ 'map-impact': {
638
+ riskLevel: 'wide',
639
+ contextPolicy: 'impact-map-first',
640
+ },
641
+ 'review-release': {
642
+ riskLevel: 'release',
643
+ contextPolicy: 'evidence-review-only',
644
+ },
645
+ };
646
+ const selectedPolicy = policyByMode[executionMode] ?? {
647
+ riskLevel: 'local',
648
+ contextPolicy: 'bounded-local',
649
+ };
650
+
651
+ return {
652
+ executionMode,
653
+ executionScores: executionScores ? { ...executionScores } : null,
654
+ riskLevel: selectedPolicy.riskLevel,
655
+ contextPolicy: selectedPolicy.contextPolicy,
656
+ verificationPolicy: executionContract?.verificationPolicy ?? null,
657
+ completionRule: executionContract?.completionRule ?? null,
658
+ competingMode: executionCandidates?.[1]?.mode ?? null,
659
+ competingScoreGap: executionCandidates?.length >= 2
660
+ ? Number(executionCandidates[0]?.score ?? 0) - Number(executionCandidates[1]?.score ?? 0)
661
+ : null,
662
+ candidateModes: (executionCandidates ?? []).slice(0, 3).map((entry) => ({
663
+ mode: entry.mode,
664
+ score: entry.score,
665
+ })),
666
+ };
667
+ }
668
+
669
+ function buildCompletionState({ executionMode = null, verificationRecommendation = null } = {}) {
670
+ if (!executionMode) {
671
+ return null;
672
+ }
673
+
674
+ const contract = buildExecutionContract(executionMode);
675
+ const missingEvidence = [...(contract?.completionEvidence ?? [])];
676
+ const requiresVerification = missingEvidence.includes('verification-evidence');
677
+ if (
678
+ requiresVerification
679
+ && verificationRecommendation
680
+ && !(verificationRecommendation.commands?.length || verificationRecommendation.fallbackCommands?.length)
681
+ ) {
682
+ missingEvidence.push('verification-plan');
683
+ }
684
+
685
+ let reason = 'completion evidence is still required';
686
+ if (['tiny-fix', 'local-fix', 'local-build', 'shared-edit'].includes(executionMode)) {
687
+ reason = 'implement request has not produced an edit yet';
688
+ } else if (executionMode === 'review-release') {
689
+ reason = 'review/release evidence is still required before final claim';
690
+ } else if (executionMode === 'map-impact') {
691
+ reason = 'impact evidence is still required before safe completion claim';
692
+ }
693
+
694
+ return {
695
+ claimAllowed: false,
696
+ missingEvidence,
697
+ reason,
698
+ };
699
+ }
700
+
701
+ function buildContinuationState({
702
+ nextActionType = null,
703
+ completionState = null,
704
+ } = {}) {
705
+ const reasons = [];
706
+ const missingEvidence = unique(completionState?.missingEvidence ?? []);
707
+
708
+ if (nextActionType === 'pull-indexed-context') {
709
+ reasons.push('pending-bounded-context');
710
+ }
711
+
712
+ if (nextActionType === 'read-skill-instructions') {
713
+ reasons.push('pending-skill-read');
714
+ }
715
+
716
+ if (missingEvidence.includes('write-evidence')) {
717
+ reasons.push('missing-write-evidence');
718
+ }
719
+
720
+ if (missingEvidence.includes('verification-evidence') || missingEvidence.includes('verification-plan')) {
721
+ reasons.push('missing-verification-evidence');
722
+ }
723
+
724
+ if (missingEvidence.includes('impact-evidence')) {
725
+ reasons.push('missing-impact-evidence');
726
+ }
727
+
728
+ const nextMilestone = reasons.includes('pending-bounded-context')
729
+ ? 'complete-bounded-context-then-continue'
730
+ : (
731
+ reasons.includes('missing-write-evidence')
732
+ ? 'produce-write-evidence'
733
+ : (
734
+ reasons.includes('missing-verification-evidence')
735
+ ? 'produce-verification-evidence'
736
+ : (
737
+ reasons.includes('missing-impact-evidence')
738
+ ? 'produce-impact-evidence'
739
+ : null
740
+ )
741
+ )
742
+ );
743
+
744
+ return {
745
+ required: reasons.length > 0,
746
+ reasons,
747
+ doneLanguageBlocked: reasons.length > 0,
748
+ stopAllowedOnlyFor: reasons.length > 0 ? ['real-blocker'] : [],
749
+ nextMilestone,
750
+ repeatCount: reasons.length > 0 ? 1 : 0,
751
+ stuckRisk: null,
752
+ rescueMode: null,
753
+ wideningBlocked: false,
754
+ milestonePriority: reasons.length > 0 ? 'normal' : null,
755
+ };
756
+ }
757
+
232
758
  async function selectActiveSkills({ rootDir, promptText, commandText, targetFile, intentMode = null }) {
233
759
  const routeSignals = {
234
760
  promptRawText: String(promptText || '').toLowerCase(),
@@ -269,6 +795,10 @@ function shouldKeepRouteEntryForIntent(entry, intentMode) {
269
795
  return false;
270
796
  }
271
797
 
798
+ if (entry.id === 'code-review' && intentMode === 'implement-specific') {
799
+ return false;
800
+ }
801
+
272
802
  return true;
273
803
  }
274
804
 
@@ -313,6 +843,10 @@ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
313
843
  routeSignals.commandRawText ?? '',
314
844
  routeSignals.commandNormalizedText ?? '',
315
845
  ].join('\n');
846
+ const rawPromptSignals = [
847
+ routeSignals.promptRawText ?? '',
848
+ routeSignals.commandRawText ?? '',
849
+ ].join('\n');
316
850
 
317
851
  if (
318
852
  skillId === 'testing-quality'
@@ -321,6 +855,14 @@ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
321
855
  return 1;
322
856
  }
323
857
 
858
+ if (
859
+ skillId === 'code-review'
860
+ && /\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(rawPromptSignals)
861
+ && !/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(rawPromptSignals)
862
+ ) {
863
+ return -4;
864
+ }
865
+
324
866
  return 0;
325
867
  }
326
868
 
@@ -371,13 +913,13 @@ function deriveIntentMode({ promptText = '', commandText = '', targetFile = null
371
913
  }
372
914
 
373
915
  if (concreteTask) {
374
- if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|login|timeout)\b/.test(lower)) {
916
+ if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|timeout)\b/.test(lower)) {
375
917
  return 'debug-specific';
376
918
  }
377
- if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(lower)) {
919
+ if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(raw)) {
378
920
  return 'review-specific';
379
921
  }
380
- if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature)\b/.test(lower)) {
922
+ if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(raw)) {
381
923
  return 'implement-specific';
382
924
  }
383
925
  }
@@ -409,7 +951,7 @@ function hasConcreteTaskSignal(lower, raw, targetFile, { taskQueueNext = false }
409
951
  return true;
410
952
  }
411
953
 
412
- 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)
954
+ 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)
413
955
  || /\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);
414
956
  }
415
957
 
@@ -23,6 +23,8 @@ cat <<'FALLBACK'
23
23
  - Auto-activate matching project-local skills from prompt + tool/file signals.
24
24
  - Reuse existing code. Use yarn. Run tests after code changes.
25
25
  - Scope-based verification: match verify effort to change scope.
26
+ - If a routed lane still needs `pull-indexed-context` or still has completion debt, treat that as internal continuation work, not a stopping point.
27
+ - Do not end with “done/applied/fixed” language while write/verification evidence is still missing unless a real blocker is found.
26
28
  - If broad verification gets blocked by a Claude hook, follow the routed targeted order or ask for explicit full-suite confirmation.
27
29
  === END ===
28
30
  FALLBACK
@@ -8,6 +8,8 @@
8
8
  - Auto-activate matching project-local skills from prompt + tool/file signals; end users should not need to know skill names.
9
9
  - After routing skill + context, prefer verification derived from related tests/context bundle before broad test runs.
10
10
  - Follow routed verification policy: auto-run targeted checks when localized, widen in order for shared/risky scope, and ask before blanket broad runs when no related test evidence exists.
11
+ - Nếu route còn `pull-indexed-context`, coi đó là bước nội bộ để tiếp tục chứ không phải điểm dừng; đọc xong context thì tiếp tục edit/verify ngay khi an toàn.
12
+ - Không được kết thúc bằng “done/applied/fixed” khi còn thiếu write-evidence hoặc verification-evidence, trừ khi có blocker thật.
11
13
  - If Claude blocks a broad verification command, do not fight the hook blindly — use the targeted indexed lane first or get explicit confirmation for a full-suite run.
12
14
  - Nếu mơ hồ hoặc risk cao (security/migration/auth/data-loss/uninstall-core), escalate đọc sâu hơn hoặc hỏi 1 câu clarify ngắn.
13
15
  - Docs là navigation aid, source code là ground truth. Nếu mâu thuẫn → source thắng, update docs trước khi tiếp tục.