@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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to UKit are documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 1.4.2 - 2026-05-10
8
+
9
+ ### Added
10
+
11
+ - Added a quality-first internal orchestration ladder with seven execution layers: `tiny-fix`, `local-fix`, `local-build`, `find-cause`, `shared-edit`, `map-impact`, and `review-release`.
12
+ - Added structured route continuity state so installed helpers and hooks can carry `completionState`, `continuationState`, and stuck-lane rescue signals across turns instead of forgetting unfinished execution debt.
13
+ - Added orchestration/runtime config defaults and Codex adapter metadata for the internal advisor/orchestrator contract while keeping the teammate workflow centered on `ukit install`.
14
+
15
+ ### Fixed
16
+
17
+ - Kept context routing and context-mode hints internal so UKit no longer surfaces `pull-indexed-context`, `resolve-context`, or `mode=LITE/FULL` in the main route prose for ordinary work.
18
+ - Preserved the structured route state (`nextActionType`, `helperHint`, `contextMode`) for internal orchestration, while removing the user/model-facing leakage that was nudging sessions into micro-step stop-and-wait behavior.
19
+ - Hardened implementation routing and installed agent guidance so explicit implement/apply/fix requests keep moving through bounded read → edit → verify flow instead of stopping after read-only inspection.
20
+ - Escalated repeated unfinished returns with `repeatCount`, `stuckRisk`, and `rescueMode` so UKit can rescue a stuck lane instead of quietly looping the same partial milestone.
21
+ - Fixed the installed `route-task` continuation handoff so previous route summaries are carried through correctly during rescue escalation.
22
+
23
+ ### Tests
24
+
25
+ - Added/updated route, hook, reinject, install-pipeline, and package-artifact coverage for internal helper hiding, continuation rescue escalation, and the expanded orchestration contract.
26
+
7
27
  ## 1.4.1 - 2026-05-09
8
28
 
9
29
  - Completed cleanup and 1.4.1 template alignment.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngockhoale/ukit",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Install/update an index-first AI workspace for Claude Code, Antigravity, OpenAI Codex, and OpenCode.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -49,7 +49,7 @@ export function buildDefaultRuntimeConfig(overrides = {}) {
49
49
  const safeOverrides = isPlainObject(overrides) ? overrides : {};
50
50
 
51
51
  return mergeObjects({
52
- version: '1.4.1',
52
+ version: '1.4.2',
53
53
  agent: 'claude-code',
54
54
  autonomy: {
55
55
  level: 'balanced',
@@ -112,6 +112,59 @@ export function buildDefaultRuntimeConfig(overrides = {}) {
112
112
  advisorEnabled: true,
113
113
  maxAdvisorCalls: 3,
114
114
  },
115
+ orchestration: {
116
+ enabled: true,
117
+ orchestratorModel: 'claude-sonnet-4-6',
118
+ advisorEnabled: true,
119
+ contracts: {
120
+ 'tiny-fix': {
121
+ maxReadPasses: 0,
122
+ maxContextPulls: 0,
123
+ verificationPolicy: 'minimal-or-targeted',
124
+ completionRule: 'never-claim-done-without-write',
125
+ delegationPolicy: 'disallow',
126
+ },
127
+ 'local-fix': {
128
+ maxReadPasses: 1,
129
+ maxContextPulls: 1,
130
+ verificationPolicy: 'targeted-if-covered',
131
+ completionRule: 'require-write',
132
+ delegationPolicy: 'disallow',
133
+ },
134
+ 'local-build': {
135
+ maxReadPasses: 2,
136
+ maxContextPulls: 1,
137
+ verificationPolicy: 'targeted-if-covered',
138
+ completionRule: 'require-write',
139
+ delegationPolicy: 'disallow-by-default',
140
+ },
141
+ 'find-cause': {
142
+ maxReadPassesBeforeReassess: 3,
143
+ verificationPolicy: 'root-cause-then-targeted',
144
+ completionRule: 'never-claim-fixed-without-write-and-verification',
145
+ delegationPolicy: 'allow-specialized-debug-lane',
146
+ },
147
+ 'shared-edit': {
148
+ maxReadPasses: 2,
149
+ maxContextPulls: 2,
150
+ verificationPolicy: 'targeted-then-widen-on-risk',
151
+ completionRule: 'require-write-and-verification',
152
+ delegationPolicy: 'allow-qualified-sidecar',
153
+ },
154
+ 'map-impact': {
155
+ maxReadPasses: 3,
156
+ maxContextPulls: 3,
157
+ verificationPolicy: 'impact-first-then-targeted-then-widen-on-risk',
158
+ completionRule: 'require-impact-evidence-before-edit-claim',
159
+ delegationPolicy: 'allow-impact-sidecar',
160
+ },
161
+ 'review-release': {
162
+ verificationPolicy: 'evidence-first',
163
+ completionRule: 'report-findings-not-implementation',
164
+ delegationPolicy: 'allow-review-sidecar',
165
+ },
166
+ },
167
+ },
115
168
  memory: {
116
169
  enabled: true,
117
170
  autoCapture: true,
@@ -278,6 +331,17 @@ export function validateRuntimeConfig(config) {
278
331
  pushPositiveNumberError(errors, config.router.maxAdvisorCalls, 'router.maxAdvisorCalls');
279
332
  }
280
333
 
334
+ if (!isPlainObject(config.orchestration)) {
335
+ errors.push('orchestration must be an object.');
336
+ } else {
337
+ pushBooleanError(errors, config.orchestration.enabled, 'orchestration.enabled');
338
+ pushNonEmptyStringError(errors, config.orchestration.orchestratorModel, 'orchestration.orchestratorModel');
339
+ pushBooleanError(errors, config.orchestration.advisorEnabled, 'orchestration.advisorEnabled');
340
+ if (!isPlainObject(config.orchestration.contracts)) {
341
+ errors.push('orchestration.contracts must be an object.');
342
+ }
343
+ }
344
+
281
345
  if (!isPlainObject(config.memory)) {
282
346
  errors.push('memory must be an object.');
283
347
  } else {
@@ -52,6 +52,21 @@ 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 executionMode = deriveExecutionMode({
63
+ promptText: normalizedPrompt,
64
+ commandText: normalizedCommand,
65
+ targetFile: normalizedTarget,
66
+ intentMode,
67
+ taskType: inferredTaskType,
68
+ executionScores,
69
+ });
55
70
  const preservedPrompt = normalizedPrompt || String(lastExplicitUserPromptText || '').trim();
56
71
  const degradedWarnings = [];
57
72
  let contextResult = null;
@@ -128,11 +143,14 @@ export async function deriveTaskRoute({
128
143
  taskType: inferredTaskType,
129
144
  intentMode,
130
145
  autonomyLevel,
146
+ executionScores,
147
+ executionMode,
131
148
  },
132
149
  contextRecommendation,
133
150
  verificationRecommendation,
134
151
  nextAction,
135
152
  });
153
+ const approachSelector = routeSummary?.approachSelector ?? null;
136
154
 
137
155
  return {
138
156
  activeSkills,
@@ -145,7 +163,10 @@ export async function deriveTaskRoute({
145
163
  taskType: inferredTaskType,
146
164
  intentMode,
147
165
  autonomyLevel,
166
+ executionScores,
167
+ executionMode,
148
168
  },
169
+ approachSelector,
149
170
  contextRecommendation,
150
171
  verificationRecommendation,
151
172
  nextAction,
@@ -186,6 +207,21 @@ export function buildRouteSummary({
186
207
  const compactHelperLane = nextAction?.type === 'pull-indexed-context'
187
208
  && typeof contextRecommendation?.command === 'string'
188
209
  && contextRecommendation.command.trim();
210
+ const executionMode = routingContext.executionMode ?? null;
211
+ const executionScores = routingContext.executionScores ?? null;
212
+ const approachSelector = buildApproachSelectorResult({
213
+ executionMode,
214
+ executionScores,
215
+ });
216
+ const executionContract = buildExecutionContract(executionMode);
217
+ const completionState = buildCompletionState({
218
+ executionMode,
219
+ verificationRecommendation,
220
+ });
221
+ const continuationState = buildContinuationState({
222
+ nextActionType: nextAction?.type ?? null,
223
+ completionState,
224
+ });
189
225
  const helperHint = compactHelperHint(
190
226
  compactHelperLane
191
227
  ? contextRecommendation?.command
@@ -204,7 +240,6 @@ export function buildRouteSummary({
204
240
  editGuardHint ? `editGuard=${editGuardHint}` : null,
205
241
  delegationRecommendation?.hint ? `delegate=${delegationRecommendation.hint}` : null,
206
242
  policyMode ? `policy=${policyMode}` : null,
207
- contextMode ? `mode=${contextMode}` : null,
208
243
  ].filter(Boolean).join(' | ');
209
244
 
210
245
  return {
@@ -213,6 +248,12 @@ export function buildRouteSummary({
213
248
  preferredOrder,
214
249
  policyMode,
215
250
  editGuardHint,
251
+ executionMode,
252
+ executionScores,
253
+ approachSelector,
254
+ executionContract,
255
+ completionState,
256
+ continuationState,
216
257
  intentMode: routingContext.intentMode ?? null,
217
258
  delegateHint: delegationRecommendation?.hint ?? null,
218
259
  nextActionType: nextAction?.type ?? null,
@@ -229,6 +270,327 @@ function deriveContextMode(taskType) {
229
270
  return null;
230
271
  }
231
272
 
273
+ function deriveExecutionScores({
274
+ promptText = '',
275
+ commandText = '',
276
+ targetFile = null,
277
+ intentMode = null,
278
+ taskType = null,
279
+ } = {}) {
280
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
281
+ const sharedRisk = isSharedImpactFile(targetFile);
282
+ const directTransformSignal = /\b(change|replace|set|rename|convert|swap)\b/.test(raw) || /\bchange\s+.+\s+to\s+.+\b/.test(raw);
283
+ const smallFixSignal = /\b(adjust|tweak|patch|fix|modify|update|apply)\b/.test(raw);
284
+ const buildSignal = /\b(build|create|add|implement|feature)\b/.test(raw);
285
+ const debugSignal = intentMode === 'debug-specific' || /\b(root cause|debug|triage|flaky|investigate|why)\b/.test(raw);
286
+ const reviewSignal = intentMode === 'review-specific' || /\b(review|audit|verify|release readiness)\b/.test(raw);
287
+
288
+ let editCertainty = 0;
289
+ if (directTransformSignal) {
290
+ editCertainty = 3;
291
+ } else if (smallFixSignal) {
292
+ editCertainty = 2;
293
+ } else if (buildSignal) {
294
+ editCertainty = 1;
295
+ }
296
+
297
+ let investigationNeed = 0;
298
+ if (debugSignal) {
299
+ investigationNeed = 3;
300
+ } else if (/\b(failing|broken|error|crash|timeout)\b/.test(raw)) {
301
+ investigationNeed = 2;
302
+ }
303
+
304
+ let blastRadius = 0;
305
+ if (sharedRisk) {
306
+ blastRadius = 3;
307
+ } else if (taskType === 'non-trivial' || taskType === 'shared-simple') {
308
+ blastRadius = 2;
309
+ } else if (!targetFile) {
310
+ blastRadius = 1;
311
+ }
312
+
313
+ let verificationBurden = 1;
314
+ if (reviewSignal || sharedRisk) {
315
+ verificationBurden = 3;
316
+ } else if (debugSignal || taskType === 'non-trivial') {
317
+ verificationBurden = 2;
318
+ }
319
+
320
+ let ambiguity = 0;
321
+ if (!targetFile) {
322
+ ambiguity = 2;
323
+ } else if (buildSignal && !directTransformSignal && !smallFixSignal) {
324
+ ambiguity = 1;
325
+ }
326
+
327
+ return {
328
+ editCertainty,
329
+ investigationNeed,
330
+ blastRadius,
331
+ verificationBurden,
332
+ ambiguity,
333
+ };
334
+ }
335
+
336
+ function deriveExecutionMode({
337
+ promptText = '',
338
+ commandText = '',
339
+ targetFile = null,
340
+ intentMode = null,
341
+ executionScores = {},
342
+ } = {}) {
343
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
344
+ const scores = executionScores;
345
+ const explicitReviewLead = /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw);
346
+
347
+ if (intentMode === 'review-specific' || explicitReviewLead) {
348
+ return 'review-release';
349
+ }
350
+
351
+ if (scores.blastRadius >= 3 && (scores.ambiguity >= 2 || scores.investigationNeed >= 2)) {
352
+ return 'map-impact';
353
+ }
354
+
355
+ if (scores.blastRadius >= 3 || isSharedImpactFile(targetFile)) {
356
+ return 'shared-edit';
357
+ }
358
+
359
+ if (scores.investigationNeed >= 3) {
360
+ return 'find-cause';
361
+ }
362
+
363
+ if (
364
+ scores.editCertainty >= 3
365
+ && scores.ambiguity === 0
366
+ && scores.blastRadius === 0
367
+ && scores.verificationBurden <= 1
368
+ ) {
369
+ return 'tiny-fix';
370
+ }
371
+
372
+ if (
373
+ /\b(build|create|add|implement|feature|summary card)\b/.test(raw)
374
+ && scores.investigationNeed === 0
375
+ && scores.blastRadius < 3
376
+ ) {
377
+ return 'local-build';
378
+ }
379
+
380
+ if (scores.editCertainty >= 2 && scores.investigationNeed === 0 && scores.blastRadius === 0) {
381
+ return 'local-fix';
382
+ }
383
+
384
+ return 'local-build';
385
+ }
386
+
387
+ function buildExecutionContract(executionMode = null) {
388
+ if (!executionMode) {
389
+ return null;
390
+ }
391
+
392
+ const contracts = {
393
+ 'tiny-fix': {
394
+ maxReadPasses: 0,
395
+ maxContextPulls: 0,
396
+ verificationPolicy: 'minimal-or-targeted',
397
+ completionRule: 'never-claim-done-without-write',
398
+ delegationPolicy: 'disallow',
399
+ completionEvidence: ['write-evidence'],
400
+ },
401
+ 'local-fix': {
402
+ maxReadPasses: 1,
403
+ maxContextPulls: 1,
404
+ verificationPolicy: 'targeted-if-covered',
405
+ completionRule: 'require-write',
406
+ delegationPolicy: 'disallow',
407
+ completionEvidence: ['write-evidence'],
408
+ },
409
+ 'local-build': {
410
+ maxReadPasses: 2,
411
+ maxContextPulls: 1,
412
+ verificationPolicy: 'targeted-if-covered',
413
+ completionRule: 'require-write',
414
+ delegationPolicy: 'disallow-by-default',
415
+ completionEvidence: ['write-evidence'],
416
+ },
417
+ 'find-cause': {
418
+ maxReadPassesBeforeReassess: 3,
419
+ verificationPolicy: 'root-cause-then-targeted',
420
+ completionRule: 'never-claim-fixed-without-write-and-verification',
421
+ delegationPolicy: 'allow-specialized-debug-lane',
422
+ completionEvidence: ['write-evidence', 'verification-evidence'],
423
+ },
424
+ 'shared-edit': {
425
+ maxReadPasses: 2,
426
+ maxContextPulls: 2,
427
+ verificationPolicy: 'targeted-then-widen-on-risk',
428
+ completionRule: 'require-write-and-verification',
429
+ delegationPolicy: 'allow-qualified-sidecar',
430
+ completionEvidence: ['write-evidence', 'verification-evidence'],
431
+ mirrorConsistencyRequired: true,
432
+ },
433
+ 'map-impact': {
434
+ maxReadPasses: 3,
435
+ maxContextPulls: 3,
436
+ verificationPolicy: 'impact-first-then-targeted-then-widen-on-risk',
437
+ completionRule: 'require-impact-evidence-before-edit-claim',
438
+ delegationPolicy: 'allow-impact-sidecar',
439
+ completionEvidence: ['impact-evidence', 'write-evidence', 'verification-evidence'],
440
+ mirrorConsistencyRequired: true,
441
+ },
442
+ 'review-release': {
443
+ verificationPolicy: 'evidence-first',
444
+ completionRule: 'report-findings-not-implementation',
445
+ delegationPolicy: 'allow-review-sidecar',
446
+ completionEvidence: ['verification-evidence'],
447
+ },
448
+ };
449
+
450
+ return contracts[executionMode] ? { ...contracts[executionMode] } : null;
451
+ }
452
+
453
+ function buildApproachSelectorResult({
454
+ executionMode = null,
455
+ executionScores = null,
456
+ } = {}) {
457
+ if (!executionMode) {
458
+ return null;
459
+ }
460
+
461
+ const executionContract = buildExecutionContract(executionMode);
462
+ const policyByMode = {
463
+ 'tiny-fix': {
464
+ riskLevel: 'minimal',
465
+ contextPolicy: 'confirm-target-only',
466
+ },
467
+ 'local-fix': {
468
+ riskLevel: 'local',
469
+ contextPolicy: 'bounded-local',
470
+ },
471
+ 'local-build': {
472
+ riskLevel: 'local',
473
+ contextPolicy: 'bounded-with-related-files',
474
+ },
475
+ 'find-cause': {
476
+ riskLevel: 'investigation',
477
+ contextPolicy: 'trace-then-bounded-context',
478
+ },
479
+ 'shared-edit': {
480
+ riskLevel: 'shared',
481
+ contextPolicy: 'bounded-with-related-tests',
482
+ },
483
+ 'map-impact': {
484
+ riskLevel: 'wide',
485
+ contextPolicy: 'impact-map-first',
486
+ },
487
+ 'review-release': {
488
+ riskLevel: 'release',
489
+ contextPolicy: 'evidence-review-only',
490
+ },
491
+ };
492
+ const selectedPolicy = policyByMode[executionMode] ?? {
493
+ riskLevel: 'local',
494
+ contextPolicy: 'bounded-local',
495
+ };
496
+
497
+ return {
498
+ executionMode,
499
+ executionScores: executionScores ? { ...executionScores } : null,
500
+ riskLevel: selectedPolicy.riskLevel,
501
+ contextPolicy: selectedPolicy.contextPolicy,
502
+ verificationPolicy: executionContract?.verificationPolicy ?? null,
503
+ completionRule: executionContract?.completionRule ?? null,
504
+ };
505
+ }
506
+
507
+ function buildCompletionState({ executionMode = null, verificationRecommendation = null } = {}) {
508
+ if (!executionMode) {
509
+ return null;
510
+ }
511
+
512
+ const contract = buildExecutionContract(executionMode);
513
+ const missingEvidence = [...(contract?.completionEvidence ?? [])];
514
+ const requiresVerification = missingEvidence.includes('verification-evidence');
515
+ if (
516
+ requiresVerification
517
+ && verificationRecommendation
518
+ && !(verificationRecommendation.commands?.length || verificationRecommendation.fallbackCommands?.length)
519
+ ) {
520
+ missingEvidence.push('verification-plan');
521
+ }
522
+
523
+ let reason = 'completion evidence is still required';
524
+ if (['tiny-fix', 'local-fix', 'local-build', 'shared-edit'].includes(executionMode)) {
525
+ reason = 'implement request has not produced an edit yet';
526
+ } else if (executionMode === 'review-release') {
527
+ reason = 'review/release evidence is still required before final claim';
528
+ } else if (executionMode === 'map-impact') {
529
+ reason = 'impact evidence is still required before safe completion claim';
530
+ }
531
+
532
+ return {
533
+ claimAllowed: false,
534
+ missingEvidence,
535
+ reason,
536
+ };
537
+ }
538
+
539
+ function buildContinuationState({
540
+ nextActionType = null,
541
+ completionState = null,
542
+ } = {}) {
543
+ const reasons = [];
544
+ const missingEvidence = unique(completionState?.missingEvidence ?? []);
545
+
546
+ if (nextActionType === 'pull-indexed-context') {
547
+ reasons.push('pending-bounded-context');
548
+ }
549
+
550
+ if (nextActionType === 'read-skill-instructions') {
551
+ reasons.push('pending-skill-read');
552
+ }
553
+
554
+ if (missingEvidence.includes('write-evidence')) {
555
+ reasons.push('missing-write-evidence');
556
+ }
557
+
558
+ if (missingEvidence.includes('verification-evidence') || missingEvidence.includes('verification-plan')) {
559
+ reasons.push('missing-verification-evidence');
560
+ }
561
+
562
+ if (missingEvidence.includes('impact-evidence')) {
563
+ reasons.push('missing-impact-evidence');
564
+ }
565
+
566
+ const nextMilestone = reasons.includes('pending-bounded-context')
567
+ ? 'complete-bounded-context-then-continue'
568
+ : (
569
+ reasons.includes('missing-write-evidence')
570
+ ? 'produce-write-evidence'
571
+ : (
572
+ reasons.includes('missing-verification-evidence')
573
+ ? 'produce-verification-evidence'
574
+ : (
575
+ reasons.includes('missing-impact-evidence')
576
+ ? 'produce-impact-evidence'
577
+ : null
578
+ )
579
+ )
580
+ );
581
+
582
+ return {
583
+ required: reasons.length > 0,
584
+ reasons,
585
+ doneLanguageBlocked: reasons.length > 0,
586
+ stopAllowedOnlyFor: reasons.length > 0 ? ['real-blocker'] : [],
587
+ nextMilestone,
588
+ repeatCount: reasons.length > 0 ? 1 : 0,
589
+ stuckRisk: null,
590
+ rescueMode: null,
591
+ };
592
+ }
593
+
232
594
  async function selectActiveSkills({ rootDir, promptText, commandText, targetFile, intentMode = null }) {
233
595
  const routeSignals = {
234
596
  promptRawText: String(promptText || '').toLowerCase(),
@@ -269,6 +631,10 @@ function shouldKeepRouteEntryForIntent(entry, intentMode) {
269
631
  return false;
270
632
  }
271
633
 
634
+ if (entry.id === 'code-review' && intentMode === 'implement-specific') {
635
+ return false;
636
+ }
637
+
272
638
  return true;
273
639
  }
274
640
 
@@ -313,6 +679,10 @@ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
313
679
  routeSignals.commandRawText ?? '',
314
680
  routeSignals.commandNormalizedText ?? '',
315
681
  ].join('\n');
682
+ const rawPromptSignals = [
683
+ routeSignals.promptRawText ?? '',
684
+ routeSignals.commandRawText ?? '',
685
+ ].join('\n');
316
686
 
317
687
  if (
318
688
  skillId === 'testing-quality'
@@ -321,6 +691,14 @@ function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
321
691
  return 1;
322
692
  }
323
693
 
694
+ if (
695
+ skillId === 'code-review'
696
+ && /\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(rawPromptSignals)
697
+ && !/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(rawPromptSignals)
698
+ ) {
699
+ return -4;
700
+ }
701
+
324
702
  return 0;
325
703
  }
326
704
 
@@ -374,10 +752,10 @@ function deriveIntentMode({ promptText = '', commandText = '', targetFile = null
374
752
  if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|login|timeout)\b/.test(lower)) {
375
753
  return 'debug-specific';
376
754
  }
377
- if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(lower)) {
755
+ if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(raw)) {
378
756
  return 'review-specific';
379
757
  }
380
- if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature)\b/.test(lower)) {
758
+ if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(raw)) {
381
759
  return 'implement-specific';
382
760
  }
383
761
  }
@@ -409,7 +787,7 @@ function hasConcreteTaskSignal(lower, raw, targetFile, { taskQueueNext = false }
409
787
  return true;
410
788
  }
411
789
 
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)
790
+ 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
791
  || /\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
792
  }
415
793
 
@@ -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.