@ngockhoale/ukit 2.3.21 → 2.3.23
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
|
@@ -2,6 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to UKit are documented here.
|
|
4
4
|
|
|
5
|
+
## 2.3.23 - 2026-09-13
|
|
6
|
+
|
|
7
|
+
Self-install no longer clobbers UKit's own canonical docs. Running `ukit install` inside the UKit
|
|
8
|
+
source repository (e.g. the global CLI invoked from the repo) overwrote `docs/PROMPT_CACHING.md` —
|
|
9
|
+
the canonical 368-line ruleset — with the distilled 127-line copy shipped under `templates/docs/`,
|
|
10
|
+
because the manifest item is `overwrite_with_backup` and self-install went undetected.
|
|
11
|
+
|
|
12
|
+
- **Self-install detection.** `buildInstallPlan` now recognizes an install targeting the UKit repo
|
|
13
|
+
by two independent signals: the templates resolving inside the install target (the repo has a
|
|
14
|
+
`templates/` directory, the global CLI does not), or the target `package.json` naming
|
|
15
|
+
`@ngockhoale/ukit`. This covers the common case where the global CLI runs inside the repo, which
|
|
16
|
+
the path-containment check alone misses.
|
|
17
|
+
- **Docs downgraded to seed-only.** On a detected self-install, every `overwrite_with_backup`
|
|
18
|
+
entry whose target lives under `docs/` is downgraded to `skip`: a missing doc is seeded, but an
|
|
19
|
+
existing canonical doc is never overwritten. Downstream projects are unaffected — their docs
|
|
20
|
+
refresh on `ukit update` as before.
|
|
21
|
+
- **Verification.** New `buildInstallPlan` regressions cover the containment signal, the
|
|
22
|
+
package-name signal, and the unchanged downstream path; the full suite stays green.
|
|
23
|
+
|
|
24
|
+
## 2.3.22 - 2026-09-13
|
|
25
|
+
|
|
26
|
+
Consultation-question routing fix — pure questions no longer fabricate mutation debt that the
|
|
27
|
+
completion gate then enforces against an answer turn. Observed live the same day: two Vietnamese
|
|
28
|
+
consultation questions (no English signal word, no target file) scored every candidate mode ≤ 0,
|
|
29
|
+
the mode ladder's upward bias resolved the find-cause/shared-edit tie to `shared-edit`, and the
|
|
30
|
+
gate blocked the answer turn six times ("missing write-evidence / verification-evidence") until
|
|
31
|
+
the continuation cap released it.
|
|
32
|
+
|
|
33
|
+
- **Consultation questions carry no completion contract.** A question-shaped prompt (trailing `?`,
|
|
34
|
+
a leading English or ASCII-folded Vietnamese interrogative, or a `là gì / được không` tail) with
|
|
35
|
+
no implement/review/debug/failure/impact signal and no target file routes `informational` — the
|
|
36
|
+
same no-mutation-debt lane delivery-only requests already use. Applied to
|
|
37
|
+
`src/index/taskRouting.js`, the standalone router `route-task.mjs`, and the hook-embedded
|
|
38
|
+
routing copy in `skill-router.sh` (live twins byte-identical).
|
|
39
|
+
- **Leading interrogatives override action words.** The router's signal regexes are English-only,
|
|
40
|
+
so Vietnamese how-questions mentioning an action ("Làm sao mà đo được khi tôi cài hệ thống
|
|
41
|
+
này…?") kept a mutation contract. A leading interrogative (làm sao / thế nào / tại sao / phải
|
|
42
|
+
làm gì / …) now marks the prompt consultative even when it contains implement verbs
|
|
43
|
+
(sửa / thêm / cài / cài đặt / …) — asking HOW something is done is not ordering the action
|
|
44
|
+
performed. Question-phrased edit orders ("bạn sửa giúp tôi… không?") keep their write contract.
|
|
45
|
+
- **Verification.** New routing regressions lock the two incident prompts to `informational` with
|
|
46
|
+
empty contract evidence, and lock both guard directions (English signal-word questions and
|
|
47
|
+
question-phrased Vietnamese edit orders stay in their lanes); full suite green.
|
|
48
|
+
|
|
5
49
|
## 2.3.21 - 2026-09-13
|
|
6
50
|
|
|
7
51
|
C16 release — the prompt-caching ruleset turns from research into shipped guidance, and the
|
package/package.json
CHANGED
package/src/core/buildPlan.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { selectManifestItems } from '../manifest/selectItems.js';
|
|
4
4
|
import { renderTemplateString } from '../render/renderTemplate.js';
|
|
5
|
+
import { UKIT_PACKAGE_NAME } from './update.js';
|
|
5
6
|
|
|
6
7
|
const BINARY_TEMPLATE_EXTENSIONS = new Set([
|
|
7
8
|
'.ttf',
|
|
@@ -44,6 +45,52 @@ function safeResolve(basePath, relativePath) {
|
|
|
44
45
|
return resolvedPath;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
function isPathInside(parentPath, childPath) {
|
|
49
|
+
const relative = path.relative(parentPath, childPath);
|
|
50
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isDocsTarget(targetPath, projectRoot) {
|
|
54
|
+
const relative = path.relative(projectRoot, targetPath);
|
|
55
|
+
return relative === 'docs' || relative.startsWith(`docs${path.sep}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Detect `ukit install` running against the UKit source repository itself. Two independent
|
|
59
|
+
// signals: the templates resolve inside the install target (the repo has a templates/ dir, the
|
|
60
|
+
// global CLI does not), or the target's package.json declares UKIT_PACKAGE_NAME (the path check
|
|
61
|
+
// misses the common case where the global CLI runs inside the repo).
|
|
62
|
+
async function isSelfInstall({ templatesRoot, projectRoot }) {
|
|
63
|
+
if (isPathInside(projectRoot, templatesRoot)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const raw = await fs.readFile(path.join(projectRoot, 'package.json'), 'utf8');
|
|
68
|
+
return JSON.parse(raw)?.name === UKIT_PACKAGE_NAME;
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// In the UKit source repo, docs/ holds canonical sources that deliberately diverge from the
|
|
75
|
+
// shipped copies under templates/ (docs/PROMPT_CACHING.md is the full ruleset while
|
|
76
|
+
// templates/docs/ ships a distilled version). The refresh-on-update strategy would silently
|
|
77
|
+
// clobber those canonical files, so downgrade docs overwrites to skip: seed a missing doc, but
|
|
78
|
+
// never overwrite one that already exists.
|
|
79
|
+
async function downgradeSelfInstallDocOverwrites(planEntries, { templatesRoot, projectRoot }) {
|
|
80
|
+
if (!(await isSelfInstall({ templatesRoot, projectRoot }))) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const entry of planEntries) {
|
|
85
|
+
if (entry.type === 'link' || entry.mergeStrategy !== 'overwrite_with_backup') {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (isDocsTarget(entry.targetPath, projectRoot)) {
|
|
89
|
+
entry.mergeStrategy = 'skip';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
47
94
|
async function readTemplateFile(filePath, { binary = false } = {}) {
|
|
48
95
|
if (binary) {
|
|
49
96
|
return fs.readFile(filePath);
|
|
@@ -263,6 +310,8 @@ export async function buildInstallPlan({
|
|
|
263
310
|
}
|
|
264
311
|
}
|
|
265
312
|
|
|
313
|
+
await downgradeSelfInstallDocOverwrites(planEntries, { templatesRoot, projectRoot });
|
|
314
|
+
|
|
266
315
|
return {
|
|
267
316
|
selectedItems,
|
|
268
317
|
entries: planEntries,
|
package/src/index/taskRouting.js
CHANGED
|
@@ -452,6 +452,47 @@ function deriveExecutionMode({
|
|
|
452
452
|
&& !scores.smallFixSignal
|
|
453
453
|
&& !scores.sharedRisk
|
|
454
454
|
&& !targetFile;
|
|
455
|
+
// A pure consultation question ("Rồi việc kế tiếp của tôi phải làm gì…?", "how do I
|
|
456
|
+
// measure this on another machine?") asks for an answer, not a repository change. The
|
|
457
|
+
// signal regexes are English-only, so a Vietnamese (or signal-free English) question
|
|
458
|
+
// with no target file scores every candidate mode ≤ 0 and the ladder's upward bias
|
|
459
|
+
// resolves the 0-score find-cause/shared-edit tie to shared-edit — fabricating
|
|
460
|
+
// write+verification debt that a text answer can never satisfy, after which the
|
|
461
|
+
// completion gate blocks the answer turn until the continuation cap releases it.
|
|
462
|
+
// Consultation questions carry no completion contract instead. A question that also
|
|
463
|
+
// carries an implement/review/debug/failure signal keeps its lane; targeted questions
|
|
464
|
+
// (explicit file target) keep their contract so a Vietnamese edit order against a
|
|
465
|
+
// named file stays enforced.
|
|
466
|
+
const trimmedPromptText = String(promptText || '').trim();
|
|
467
|
+
const foldedPromptText = trimmedPromptText
|
|
468
|
+
.toLowerCase()
|
|
469
|
+
.normalize('NFD')
|
|
470
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
471
|
+
.replace(/\u0111/g, 'd');
|
|
472
|
+
const leadingInterrogative = /^(?:what|how|which|when|where|who)\b/.test(foldedPromptText)
|
|
473
|
+
|| /^(?:lam sao|the nao|nhu the nao|nhu nao|vi sao|tai sao|khi nao|bao gio|bao nhieu|o dau|phai lam gi|lam gi|lam cach nao)\b/.test(foldedPromptText);
|
|
474
|
+
const questionShapedPrompt = /\?\s*$/.test(trimmedPromptText)
|
|
475
|
+
|| leadingInterrogative
|
|
476
|
+
|| /\b(?:la gi|duoc khong)\s*$/.test(foldedPromptText);
|
|
477
|
+
// Implement verbs (Vietnamese included — the English-only scores above cannot see
|
|
478
|
+
// them) mark an action order even when phrased as a question ("ban sua giup toi…?" is
|
|
479
|
+
// an edit order, not a consultation). A leading interrogative overrides them:
|
|
480
|
+
// "Lam sao ma do duoc khi toi cai…?" asks HOW something is done, it does not order
|
|
481
|
+
// the action performed.
|
|
482
|
+
const consultationImplementWords = /(?<![A-Za-z0-9_])(?:implement|apply|update|modify|add|create|ship|deliver|fix|refactor|remove|delete|rename|change|write|build|make|install|run|deploy|execute|sửa|thêm|tạo|xóa|đổi|thay thế|cập nhật|viết|chạy|cài)(?![A-Za-z0-9_])/.test(trimmedPromptText);
|
|
483
|
+
const consultationOnlySignal = questionShapedPrompt
|
|
484
|
+
&& (leadingInterrogative || !consultationImplementWords)
|
|
485
|
+
&& scores.editCertainty === 0
|
|
486
|
+
&& !scores.implementSignal
|
|
487
|
+
&& !scores.reviewSignal
|
|
488
|
+
&& !scores.debugSignal
|
|
489
|
+
&& !scores.failureSignal
|
|
490
|
+
&& !scores.impactSignal
|
|
491
|
+
&& !scores.buildSignal
|
|
492
|
+
&& !scores.directTransformSignal
|
|
493
|
+
&& !scores.smallFixSignal
|
|
494
|
+
&& !scores.sharedRisk
|
|
495
|
+
&& !targetFile;
|
|
455
496
|
|
|
456
497
|
if (
|
|
457
498
|
releaseVerificationContinuation
|
|
@@ -460,7 +501,7 @@ function deriveExecutionMode({
|
|
|
460
501
|
return 'review-release';
|
|
461
502
|
}
|
|
462
503
|
|
|
463
|
-
if (deliveryOnlySignal) {
|
|
504
|
+
if (deliveryOnlySignal || consultationOnlySignal) {
|
|
464
505
|
return 'informational';
|
|
465
506
|
}
|
|
466
507
|
|
|
@@ -1652,7 +1652,38 @@ const { pathToFileURL } = require('url');
|
|
|
1652
1652
|
return 'review-release';
|
|
1653
1653
|
}
|
|
1654
1654
|
|
|
1655
|
-
|
|
1655
|
+
const trimmedPromptText = String(promptText || '').trim();
|
|
1656
|
+
const foldedPromptText = trimmedPromptText
|
|
1657
|
+
.toLowerCase()
|
|
1658
|
+
.normalize('NFD')
|
|
1659
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
1660
|
+
.replace(/\u0111/g, 'd');
|
|
1661
|
+
const leadingInterrogative = /^(?:what|how|which|when|where|who)\b/.test(foldedPromptText)
|
|
1662
|
+
|| /^(?:lam sao|the nao|nhu the nao|nhu nao|vi sao|tai sao|khi nao|bao gio|bao nhieu|o dau|phai lam gi|lam gi|lam cach nao)\b/.test(foldedPromptText);
|
|
1663
|
+
const questionShapedPrompt = /\?\s*$/.test(trimmedPromptText)
|
|
1664
|
+
|| leadingInterrogative
|
|
1665
|
+
|| /\b(?:la gi|duoc khong)\s*$/.test(foldedPromptText);
|
|
1666
|
+
// Implement verbs (Vietnamese included — the English-only scores above cannot see
|
|
1667
|
+
// them) mark an action order even when phrased as a question ("ban sua giup toi…?" is
|
|
1668
|
+
// an edit order, not a consultation). A leading interrogative overrides them:
|
|
1669
|
+
// "Lam sao ma do duoc khi toi cai…?" asks HOW something is done, it does not order
|
|
1670
|
+
// the action performed.
|
|
1671
|
+
const consultationImplementWords = /(?<![A-Za-z0-9_])(?:implement|apply|update|modify|add|create|ship|deliver|fix|refactor|remove|delete|rename|change|write|build|make|install|run|deploy|execute|sửa|thêm|tạo|xóa|đổi|thay thế|cập nhật|viết|chạy|cài)(?![A-Za-z0-9_])/.test(trimmedPromptText);
|
|
1672
|
+
const consultationOnlySignal = questionShapedPrompt
|
|
1673
|
+
&& (leadingInterrogative || !consultationImplementWords)
|
|
1674
|
+
&& scores.editCertainty === 0
|
|
1675
|
+
&& !scores.implementSignal
|
|
1676
|
+
&& !scores.reviewSignal
|
|
1677
|
+
&& !scores.debugSignal
|
|
1678
|
+
&& !scores.failureSignal
|
|
1679
|
+
&& !scores.impactSignal
|
|
1680
|
+
&& !scores.buildSignal
|
|
1681
|
+
&& !scores.directTransformSignal
|
|
1682
|
+
&& !scores.smallFixSignal
|
|
1683
|
+
&& !scores.sharedRisk
|
|
1684
|
+
&& !targetFile;
|
|
1685
|
+
|
|
1686
|
+
if (deliveryOnlySignal || consultationOnlySignal) {
|
|
1656
1687
|
return 'informational';
|
|
1657
1688
|
}
|
|
1658
1689
|
|
|
@@ -1832,7 +1832,38 @@ function deriveExecutionMode({
|
|
|
1832
1832
|
return 'review-release';
|
|
1833
1833
|
}
|
|
1834
1834
|
|
|
1835
|
-
|
|
1835
|
+
const trimmedPromptText = String(promptText || '').trim();
|
|
1836
|
+
const foldedPromptText = trimmedPromptText
|
|
1837
|
+
.toLowerCase()
|
|
1838
|
+
.normalize('NFD')
|
|
1839
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
1840
|
+
.replace(/\u0111/g, 'd');
|
|
1841
|
+
const leadingInterrogative = /^(?:what|how|which|when|where|who)\b/.test(foldedPromptText)
|
|
1842
|
+
|| /^(?:lam sao|the nao|nhu the nao|nhu nao|vi sao|tai sao|khi nao|bao gio|bao nhieu|o dau|phai lam gi|lam gi|lam cach nao)\b/.test(foldedPromptText);
|
|
1843
|
+
const questionShapedPrompt = /\?\s*$/.test(trimmedPromptText)
|
|
1844
|
+
|| leadingInterrogative
|
|
1845
|
+
|| /\b(?:la gi|duoc khong)\s*$/.test(foldedPromptText);
|
|
1846
|
+
// Implement verbs (Vietnamese included — the English-only scores above cannot see
|
|
1847
|
+
// them) mark an action order even when phrased as a question ("ban sua giup toi…?" is
|
|
1848
|
+
// an edit order, not a consultation). A leading interrogative overrides them:
|
|
1849
|
+
// "Lam sao ma do duoc khi toi cai…?" asks HOW something is done, it does not order
|
|
1850
|
+
// the action performed.
|
|
1851
|
+
const consultationImplementWords = /(?<![A-Za-z0-9_])(?:implement|apply|update|modify|add|create|ship|deliver|fix|refactor|remove|delete|rename|change|write|build|make|install|run|deploy|execute|sửa|thêm|tạo|xóa|đổi|thay thế|cập nhật|viết|chạy|cài)(?![A-Za-z0-9_])/.test(trimmedPromptText);
|
|
1852
|
+
const consultationOnlySignal = questionShapedPrompt
|
|
1853
|
+
&& (leadingInterrogative || !consultationImplementWords)
|
|
1854
|
+
&& scores.editCertainty === 0
|
|
1855
|
+
&& !scores.implementSignal
|
|
1856
|
+
&& !scores.reviewSignal
|
|
1857
|
+
&& !scores.debugSignal
|
|
1858
|
+
&& !scores.failureSignal
|
|
1859
|
+
&& !scores.impactSignal
|
|
1860
|
+
&& !scores.buildSignal
|
|
1861
|
+
&& !scores.directTransformSignal
|
|
1862
|
+
&& !scores.smallFixSignal
|
|
1863
|
+
&& !scores.sharedRisk
|
|
1864
|
+
&& !targetFile;
|
|
1865
|
+
|
|
1866
|
+
if (deliveryOnlySignal || consultationOnlySignal) {
|
|
1836
1867
|
return 'informational';
|
|
1837
1868
|
}
|
|
1838
1869
|
|