@ngockhoale/ukit 3.4.48 → 3.4.49

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,13 @@
2
2
 
3
3
  All notable changes to UKit are documented here.
4
4
 
5
+ ## 3.4.49 - 2026-10-19
6
+
7
+ **Conditional consumer verification intent closure.**
8
+ - Fix a live Completion Gate false write obligation: a verification request ("Mục tiêu: xác nhận …") whose source repair/release steps apply only if a defect is found, plus a disposable test-project setup, was classified CODE_IMPLEMENTATION and demanded write-evidence even after a real `npm test` exit 0. The classifier now recognizes a declared verification goal with a narrowly structured conditional repair branch and `xác nhận` as a verify lead; unconditional trailing orders, quoted goals and non-repair conditionals still block.
9
+ - Safety preserved: when the conditional repair branch is actually taken (an owned in-project substantive source write), the evaluator re-activates the release-publication demand; explicit publish prohibitions dominate. Classifier copies (src, resolver, hook) are pinned by a mirror parity test.
10
+ - Reproduced through the npm 3.4.48 consumer router/record/Stop lifecycle with real native exits; no receipt fabrication, task reassignment or ledger reset. Direct `node --test` recognition is unchanged. Verified on macOS/Node 22 only; OMP live attribution UNVERIFIED; no paid OMP smoke or CI dispatch.
11
+
5
12
  ## 3.4.48 - 2026-10-19
6
13
 
7
14
  **Completion evidence closure and reproducible multi-host installation.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngockhoale/ukit",
3
- "version": "3.4.48",
3
+ "version": "3.4.49",
4
4
  "description": "Install/update an index-first AI workspace for Claude Code, OpenAI Codex and omp (Oh My Pi).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -199,12 +199,22 @@ function nativeOperationGoal(domain, actionable, text) {
199
199
  // Vietnamese negators scope to the sentence end as well. Noun uses of verbs ("write/verification advisory", "each proposed
200
200
  // fix", "state change") name things, not orders. Neutralised spans become blanks, never new orders.
201
201
  const NON_ORDER_VERBS = 'implement|apply|update|modify|add|create|ship|deliver|fix|refactor|remove|delete|rename|change|write|build|make|install|run|deploy|execute|edit|restart|sua|viet|chinh|them|tao|xoa';
202
+ // Declared verification purpose ("Mục tiêu: xác nhận ...", "Goal: verify ..."). Under that declaration a
203
+ // conditional repair/release branch ("Nếu phát hiện lỗi ... sửa ...", "Neu co source fix ... publish") is a
204
+ // branch the check MAY take, not an unconditional order, and disposable test-project setup ("Tạo project thử
205
+ // nghiệm độc lập") is scaffolding of the check. Unconditional orders in the same request keep blocking.
206
+ const VERIFICATION_GOAL = /(?:^|\n)[ \t*#_-]*(?:muc tieu|goal|purpose|objective)\s*:[^\n]*\b(?:xac nhan|verify|confirm|kiem tra|validate|double-?check|re-?run)\b/;
207
+ const CONDITIONAL_BRANCH = /(?:^|\n|\.\s+)[ \t]*(?:\d+[.)]\s*)?(?:neu\s+(?:phat hien\s+(?:false-block|loi|bug|defect)|co\s+(?:source fix|sua doi))|if\s+(?:(?:a\s+)?(?:bug|defect|failure)\s+is\s+found|(?:you\s+)?find\s+(?:a\s+)?(?:bug|defect)))\b[^\n]*?(?=\.\s+|$|\n)/g;
208
+ const GOAL_LABEL = /\b(?:muc tieu|goal|purpose|objective)\s*:/g;
209
+ const DISPOSABLE_SETUP = /\b(?:tao|create)\s+(?:mot\s+)?(?:project|du an)\s+(?:thu nghiem|test|disposable|doc lap)\b(?:\s+(?:doc lap|independent))?/g;
202
210
  function neutralizeNonOrders(text) {
203
- return String(text)
211
+ let source = String(text).replace(/```[\s\S]*?```/g, ' ').replace(/^[ \t]*>.*$/gm, ' ');
212
+ if (VERIFICATION_GOAL.test(source)) source = source.replace(GOAL_LABEL, ' ').replace(CONDITIONAL_BRANCH, '\n').replace(DISPOSABLE_SETUP, ' ');
213
+ return source
204
214
  .replace(new RegExp(`\\b(?:do\\s+not|don't|dont|must\\s+not|never|shall\\s+not)\\s+[^.;\\n]*?(?=(?<!,)\\s+(?:and|va)\\s+(?:${NON_ORDER_VERBS})\\b|\\s+(?:but|nhung|then|however)\\b|[.;\\n]|$)`, 'g'), ' ')
205
215
  .replace(new RegExp(`\\b(?:khong|chua)\\s+[^.;\\n]*?(?=\\s+(?:nhung|tuy nhien)\\b|[.;\\n]|$)`, 'g'), ' ')
206
216
  .replace(/"[^"\n]*\s[^"\n]*"|`[^`\n]*\s[^`\n]*`|\u201c[^\u201d\n]*\u201d/g, ' ')
207
- .replace(/\b(?:neu\s+(?:phai|can|thieu|khong du|bang chung chua du)|if\s+(?:needed|necessary|required|evidence is insufficient))\b[^.;\n]*/g, ' ')
217
+ .replace(/\b(?:neu\s+(?:phai|can|thieu|khong du|bang chung chua du)(?![^.;\n]*\b(?:restart|deploy|xoa|delete|remove|cancel)\b)|if\s+(?:needed|necessary|required|evidence is insufficient))\b[^.;\n]*/g, ' ')
208
218
  .replace(/[^.;\n]*\b(?:nao|gi|sao|the nao|bao nhieu)\b[^.;\n]*\?/g, ' ')
209
219
  .replace(/\bchinh\s+(?:xac|sach)\b|\b(?:phuong an|cach|de xuat|ban)\s+(?:sua|fix)\b|\b(?:diem|cho|muc|phan)\s+(?:phai|can|nen)\s+(?:sua|fix)\b/g, ' ')
210
220
  .replace(/\bwrite\s*\/\s*verification\b|\bverification\s*\/\s*write\b|\bwrite\s+(?:or|and)\s+verification\b/g, ' ')
@@ -244,7 +254,7 @@ function declaredReadOnly(promptText, releasePolicy) {
244
254
  // "template/install", "source change") as mutation orders. Order is structural: a request whose
245
255
  // command-position verbs are verification verbs, with no explicit order (hasExplicitOrder) and no
246
256
  // change-leading clause, orders no change - it owes verification evidence, not a write.
247
- const VERIFY_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:verify|re-?run|confirm|validate|double-?check)\b/;
257
+ const VERIFY_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:verify|re-?run|confirm|xac nhan|validate|double-?check)\b/;
248
258
  const CHANGE_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:make|rebuild|redo|rewrite|restore|revert|correct|clean|patch|resolve|ensure|run(?!(?=[^.]*\b(?:tests?|suite|gate|checks?|lint|typecheck|pilot|smoke|e2e|verif\w*|vitest|jest|audit)\b)))\b/;
249
259
  export function isVerificationOnlyRequest(promptText) {
250
260
  const plain = String(promptText ?? '').normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[đĐ]/g, 'd').toLowerCase()
@@ -1881,12 +1881,22 @@ function nativeOperationGoal(domain, actionable, text) {
1881
1881
  // Vietnamese negators scope to the sentence end as well. Noun uses of verbs ("write/verification advisory", "each proposed
1882
1882
  // fix", "state change") name things, not orders. Neutralised spans become blanks, never new orders.
1883
1883
  const NON_ORDER_VERBS = 'implement|apply|update|modify|add|create|ship|deliver|fix|refactor|remove|delete|rename|change|write|build|make|install|run|deploy|execute|edit|restart|sua|viet|chinh|them|tao|xoa';
1884
+ // Declared verification purpose ("Mục tiêu: xác nhận ...", "Goal: verify ..."). Under that declaration a
1885
+ // conditional repair/release branch ("Nếu phát hiện lỗi ... sửa ...", "Neu co source fix ... publish") is a
1886
+ // branch the check MAY take, not an unconditional order, and disposable test-project setup ("Tạo project thử
1887
+ // nghiệm độc lập") is scaffolding of the check. Unconditional orders in the same request keep blocking.
1888
+ const VERIFICATION_GOAL = /(?:^|\n)[ \t*#_-]*(?:muc tieu|goal|purpose|objective)\s*:[^\n]*\b(?:xac nhan|verify|confirm|kiem tra|validate|double-?check|re-?run)\b/;
1889
+ const CONDITIONAL_BRANCH = /(?:^|\n|\.\s+)[ \t]*(?:\d+[.)]\s*)?(?:neu\s+(?:phat hien\s+(?:false-block|loi|bug|defect)|co\s+(?:source fix|sua doi))|if\s+(?:(?:a\s+)?(?:bug|defect|failure)\s+is\s+found|(?:you\s+)?find\s+(?:a\s+)?(?:bug|defect)))\b[^\n]*?(?=\.\s+|$|\n)/g;
1890
+ const GOAL_LABEL = /\b(?:muc tieu|goal|purpose|objective)\s*:/g;
1891
+ const DISPOSABLE_SETUP = /\b(?:tao|create)\s+(?:mot\s+)?(?:project|du an)\s+(?:thu nghiem|test|disposable|doc lap)\b(?:\s+(?:doc lap|independent))?/g;
1884
1892
  function neutralizeNonOrders(text) {
1885
- return String(text)
1893
+ let source = String(text).replace(/```[\s\S]*?```/g, ' ').replace(/^[ \t]*>.*$/gm, ' ');
1894
+ if (VERIFICATION_GOAL.test(source)) source = source.replace(GOAL_LABEL, ' ').replace(CONDITIONAL_BRANCH, '\n').replace(DISPOSABLE_SETUP, ' ');
1895
+ return source
1886
1896
  .replace(new RegExp(`\\b(?:do\\s+not|don't|dont|must\\s+not|never|shall\\s+not)\\s+[^.;\\n]*?(?=(?<!,)\\s+(?:and|va)\\s+(?:${NON_ORDER_VERBS})\\b|\\s+(?:but|nhung|then|however)\\b|[.;\\n]|$)`, 'g'), ' ')
1887
1897
  .replace(new RegExp(`\\b(?:khong|chua)\\s+[^.;\\n]*?(?=\\s+(?:nhung|tuy nhien)\\b|[.;\\n]|$)`, 'g'), ' ')
1888
1898
  .replace(/"[^"\n]*\s[^"\n]*"|`[^`\n]*\s[^`\n]*`|\u201c[^\u201d\n]*\u201d/g, ' ')
1889
- .replace(/\b(?:neu\s+(?:phai|can|thieu|khong du|bang chung chua du)|if\s+(?:needed|necessary|required|evidence is insufficient))\b[^.;\n]*/g, ' ')
1899
+ .replace(/\b(?:neu\s+(?:phai|can|thieu|khong du|bang chung chua du)(?![^.;\n]*\b(?:restart|deploy|xoa|delete|remove|cancel)\b)|if\s+(?:needed|necessary|required|evidence is insufficient))\b[^.;\n]*/g, ' ')
1890
1900
  .replace(/[^.;\n]*\b(?:nao|gi|sao|the nao|bao nhieu)\b[^.;\n]*\?/g, ' ')
1891
1901
  .replace(/\bchinh\s+(?:xac|sach)\b|\b(?:phuong an|cach|de xuat|ban)\s+(?:sua|fix)\b|\b(?:diem|cho|muc|phan)\s+(?:phai|can|nen)\s+(?:sua|fix)\b/g, ' ')
1892
1902
  .replace(/\bwrite\s*\/\s*verification\b|\bverification\s*\/\s*write\b|\bwrite\s+(?:or|and)\s+verification\b/g, ' ')
@@ -1905,7 +1915,7 @@ function hasExplicitOrder(clauses) {
1905
1915
  // "template/install", "source change") as mutation orders. Order is structural: a request whose
1906
1916
  // command-position verbs are verification verbs, with no explicit order (hasExplicitOrder) and no
1907
1917
  // change-leading clause, orders no change - it owes verification evidence, not a write.
1908
- const VERIFY_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:verify|re-?run|confirm|validate|double-?check)\b/;
1918
+ const VERIFY_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:verify|re-?run|confirm|xac nhan|validate|double-?check)\b/;
1909
1919
  const CHANGE_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:make|rebuild|redo|rewrite|restore|revert|correct|clean|patch|resolve|ensure|run(?!(?=[^.]*\b(?:tests?|suite|gate|checks?|lint|typecheck|pilot|smoke|e2e|verif\w*|vitest|jest|audit)\b)))\b/;
1910
1920
  function isVerificationOnlyRequest(promptText) {
1911
1921
  const plain = String(promptText ?? '').normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[đĐ]/g, 'd').toLowerCase()
@@ -1096,12 +1096,22 @@ function nativeOperationGoal(domain, actionable, text) {
1096
1096
  // Vietnamese negators scope to the sentence end as well. Noun uses of verbs ("write/verification advisory", "each proposed
1097
1097
  // fix", "state change") name things, not orders. Neutralised spans become blanks, never new orders.
1098
1098
  const NON_ORDER_VERBS = 'implement|apply|update|modify|add|create|ship|deliver|fix|refactor|remove|delete|rename|change|write|build|make|install|run|deploy|execute|edit|restart|sua|viet|chinh|them|tao|xoa';
1099
+ // Declared verification purpose ("Mục tiêu: xác nhận ...", "Goal: verify ..."). Under that declaration a
1100
+ // conditional repair/release branch ("Nếu phát hiện lỗi ... sửa ...", "Neu co source fix ... publish") is a
1101
+ // branch the check MAY take, not an unconditional order, and disposable test-project setup ("Tạo project thử
1102
+ // nghiệm độc lập") is scaffolding of the check. Unconditional orders in the same request keep blocking.
1103
+ const VERIFICATION_GOAL = /(?:^|\n)[ \t*#_-]*(?:muc tieu|goal|purpose|objective)\s*:[^\n]*\b(?:xac nhan|verify|confirm|kiem tra|validate|double-?check|re-?run)\b/;
1104
+ const CONDITIONAL_BRANCH = /(?:^|\n|\.\s+)[ \t]*(?:\d+[.)]\s*)?(?:neu\s+(?:phat hien\s+(?:false-block|loi|bug|defect)|co\s+(?:source fix|sua doi))|if\s+(?:(?:a\s+)?(?:bug|defect|failure)\s+is\s+found|(?:you\s+)?find\s+(?:a\s+)?(?:bug|defect)))\b[^\n]*?(?=\.\s+|$|\n)/g;
1105
+ const GOAL_LABEL = /\b(?:muc tieu|goal|purpose|objective)\s*:/g;
1106
+ const DISPOSABLE_SETUP = /\b(?:tao|create)\s+(?:mot\s+)?(?:project|du an)\s+(?:thu nghiem|test|disposable|doc lap)\b(?:\s+(?:doc lap|independent))?/g;
1099
1107
  function neutralizeNonOrders(text) {
1100
- return String(text)
1108
+ let source = String(text).replace(/```[\s\S]*?```/g, ' ').replace(/^[ \t]*>.*$/gm, ' ');
1109
+ if (VERIFICATION_GOAL.test(source)) source = source.replace(GOAL_LABEL, ' ').replace(CONDITIONAL_BRANCH, '\n').replace(DISPOSABLE_SETUP, ' ');
1110
+ return source
1101
1111
  .replace(new RegExp(`\\b(?:do\\s+not|don't|dont|must\\s+not|never|shall\\s+not)\\s+[^.;\\n]*?(?=(?<!,)\\s+(?:and|va)\\s+(?:${NON_ORDER_VERBS})\\b|\\s+(?:but|nhung|then|however)\\b|[.;\\n]|$)`, 'g'), ' ')
1102
1112
  .replace(new RegExp(`\\b(?:khong|chua)\\s+[^.;\\n]*?(?=\\s+(?:nhung|tuy nhien)\\b|[.;\\n]|$)`, 'g'), ' ')
1103
1113
  .replace(/"[^"\n]*\s[^"\n]*"|`[^`\n]*\s[^`\n]*`|\u201c[^\u201d\n]*\u201d/g, ' ')
1104
- .replace(/\b(?:neu\s+(?:phai|can|thieu|khong du|bang chung chua du)|if\s+(?:needed|necessary|required|evidence is insufficient))\b[^.;\n]*/g, ' ')
1114
+ .replace(/\b(?:neu\s+(?:phai|can|thieu|khong du|bang chung chua du)(?![^.;\n]*\b(?:restart|deploy|xoa|delete|remove|cancel)\b)|if\s+(?:needed|necessary|required|evidence is insufficient))\b[^.;\n]*/g, ' ')
1105
1115
  .replace(/[^.;\n]*\b(?:nao|gi|sao|the nao|bao nhieu)\b[^.;\n]*\?/g, ' ')
1106
1116
  .replace(/\bchinh\s+(?:xac|sach)\b|\b(?:phuong an|cach|de xuat|ban)\s+(?:sua|fix)\b|\b(?:diem|cho|muc|phan)\s+(?:phai|can|nen)\s+(?:sua|fix)\b/g, ' ')
1107
1117
  .replace(/\bwrite\s*\/\s*verification\b|\bverification\s*\/\s*write\b|\bwrite\s+(?:or|and)\s+verification\b/g, ' ')
@@ -1141,7 +1151,7 @@ function declaredReadOnly(promptText, releasePolicy) {
1141
1151
  // "template/install", "source change") as mutation orders. Order is structural: a request whose
1142
1152
  // command-position verbs are verification verbs, with no explicit order (hasExplicitOrder) and no
1143
1153
  // change-leading clause, orders no change - it owes verification evidence, not a write.
1144
- const VERIFY_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:verify|re-?run|confirm|validate|double-?check)\b/;
1154
+ const VERIFY_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:verify|re-?run|confirm|xac nhan|validate|double-?check)\b/;
1145
1155
  const CHANGE_LEAD = /^(?:[-*+•>\d.)\s]*)(?:(?:please|hay|vui long|now|also|first|then)\s+)*(?:make|rebuild|redo|rewrite|restore|revert|correct|clean|patch|resolve|ensure|run(?!(?=[^.]*\b(?:tests?|suite|gate|checks?|lint|typecheck|pilot|smoke|e2e|verif\w*|vitest|jest|audit)\b)))\b/;
1146
1156
  export function isVerificationOnlyRequest(promptText) {
1147
1157
  const plain = String(promptText ?? '').normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[đĐ]/g, 'd').toLowerCase()
@@ -3532,8 +3532,32 @@ export function promptOrdersNpmPublish(state = {}) {
3532
3532
  return ordered && !prohibited;
3533
3533
  }
3534
3534
 
3535
+ // Conditional repair is not a publication order until owned project source actually changes.
3536
+ // Reuse the affirmative publication parser on the taken branch, including its negation rules.
3537
+ function conditionalRepairOrdersPublish(state, ledger, cwd) {
3538
+ if (ledger?.substantiveWrite !== true || !cwd) return false;
3539
+ const text = String(state?.routingContext?.lastExplicitUserPromptText || '')
3540
+ .normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[đĐ]/g, 'd').toLowerCase()
3541
+ .replace(/```[^]*?```|~~~[^]*?~~~/g, '').replace(/^[ \t]*>.*$/gm, '');
3542
+ if (!/(?:^|\n)[ \t*#_-]*(?:muc tieu|goal|purpose|objective)\s*:[^\n]*\b(?:xac nhan|verify|confirm|validate)\b/.test(text)) return false;
3543
+ const branch = text.match(/(?:^|\n|\.\s+)[ \t]*(?:\d+[.)]\s*)?(?:neu\s+co\s+source fix|if\s+(?:there is a\s+)?source fix)\s*:[^\n]*/)?.[0];
3544
+ if (!branch) return false;
3545
+ const writes = [...(ledger.receipts || []), ...Object.keys(ledger.bankedWrites || {}).map((file) => ({ kind: 'write', success: true, file }))];
3546
+ const projectWrite = writes.some((receipt) => {
3547
+ if (receipt.kind !== 'write' || receipt.success !== true || !receipt.file || isBookkeepingWrite(receipt.file)) return false;
3548
+ if (receipt.detail === 'bash-workspace-delta' && /^bash-workspace:/.test(receipt.file)) return true;
3549
+ const relative = path.relative(path.resolve(cwd), path.resolve(cwd, receipt.file));
3550
+ return relative && !relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative)
3551
+ && !/^(?:\.ukit|\.cache|node_modules|tests?|fixtures?|tmp)(?:[\\/]|$)/.test(relative);
3552
+ });
3553
+ if (!projectWrite) return false;
3554
+ // Explicit task-wide publication prohibitions still dominate the conditional branch.
3555
+ if (/(?:do not|never|khong|chua)\s+(?:npm\s+)?publish\b|\bno\s+(?:release|publication)\b/.test(text)) return false;
3556
+ return promptOrdersNpmPublish({ routingContext: { lastExplicitUserPromptText: branch.replace(/^.*?:/, 'Run ') } });
3557
+ }
3558
+
3535
3559
  function releasePublicationMissing(state, ledger, cwd) {
3536
- if (!promptOrdersNpmPublish(state)) return null;
3560
+ if (!promptOrdersNpmPublish(state) && !conditionalRepairOrdersPublish(state, ledger, cwd)) return null;
3537
3561
  let pkg = null;
3538
3562
  try { pkg = JSON.parse(fsSync.readFileSync(path.join(cwd || process.cwd(), 'package.json'), 'utf8')); } catch { return null; }
3539
3563
  if (!pkg?.version) return null;