dsh-rule-engine 0.5.16 → 0.6.0

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.
@@ -1,144 +1,102 @@
1
- // publish-aptitude-check.mjs — 发布适用性门禁(T6,2026-08-31)
2
- // 目的:兑现 README「通用化」承诺的"陌生人视角"检查——发布物必须先证明:
3
- // ① 无 AGENTS.md(陌生环境冷启动):引擎零错 / 零死映射 / 零意外拦截面
4
- // ② 空白规则文件:静默(0 规则、无错、无 dead)
5
- // ③ 任意编号规则(非本机编号):声明式绑定与"纯自证"分流行为正确
6
- // ④ 四要素 + 执行等级格式的最小规则:actions/handler 解析正确(README 格式契约冒烟)
7
- // ⑤ 发布物个人标识扫描:files 白名单内文件不得含本机个人标识(jilian/季涟/D:\DeepSeek/邮箱/私人注释)
8
- // 默认模式 = 单进程纯函数链(不 spawn、不 import index.js,沙箱可直接跑);
9
- // --deep 模式 = 额外子进程全链冷启动(临时 DSH_HOME + 完整引擎加载 + 真实裁决链,需完整权限/CI)。
10
- // 用法:node scripts/publish-aptitude-check.mjs [--deep] [--tgz <路径>]
11
- import { mkdtempSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync, existsSync } from "node:fs";
12
- import { tmpdir } from "node:os";
13
- import { join, dirname, resolve } from "node:path";
14
- import { fileURLToPath } from "node:url";
15
-
16
- const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
17
- const args = process.argv.slice(2);
18
- const deep = args.includes("--deep");
19
- const tgzArg = args.includes("--tgz") ? args[args.indexOf("--tgz") + 1] : null;
20
-
21
- const FAILS = [];
22
- function check(ok, msg) {
23
- console.log(`${ok ? "✅" : "❌"} ${msg}`);
24
- if (!ok) FAILS.push(msg);
25
- }
26
-
27
- // ①-④ 使用的纯函数链(与引擎 lib 同源,动态 import 保证读到本次代码)
28
- const { loadRules } = await import("../lib/core/parser.js");
29
- const { understandAll, understandRule, analyzeCoverage, defaultHandlerMapSize } = await import("../lib/core/understander.js");
30
-
31
- // ── ① 陌生环境冷启动(临时 DSH_HOME,无 AGENTS.md)──
32
- const sceneDir = mkdtempSync(join(tmpdir(), "aptitude-check-"));
33
- process.env.DSH_HOME = sceneDir;
34
- process.env.DSH_WORKSPACE = root;
35
- {
36
- const parsed = loadRules();
37
- check(parsed.ok === false && parsed.missing === true, `① 无 AGENTS.md:处理器返回 missing(ok=${parsed.ok}, missing=${parsed.missing})`);
38
- const configs = understandAll([]);
39
- check(configs.length === 0, "① 无 AGENTS.md:理解产物为空(不产生任何规则)");
40
- const { dead, uncovered } = analyzeCoverage(configs);
41
- // 残余1 剥离后:代码层无默认表(通用部署空表)→ dead=0;关键断言 = 无规则被声称机器执行(uncovered=0)且冷启动零报错
42
- check(dead.size === 0, `① 无 AGENTS.md:dead=0(代码层无默认偏好表)`);
43
- check(uncovered.length === 0, `① 无 AGENTS.md:零未覆盖(uncovered=0)`);
44
- }
45
-
46
- // ── ② 空白规则文件静默 ──
47
- {
48
- writeFileSync(join(sceneDir, "AGENTS.md"), "", "utf8");
49
- const parsed = loadRules();
50
- check(parsed.ok === true && parsed.rules.length === 0, `② 空白 AGENTS.md:解析成功且 0 规则(rules=${parsed.rules.length})`);
51
- const configs = understandAll(parsed.rules);
52
- check(configs.length === 0, "② 空白 AGENTS.md:理解产物为空");
53
- }
54
-
55
- // ── ③ 任意编号规则(非本机编号):声明式绑定 + 纯自证分流 ──
56
- {
57
- const arbitrary = {
58
- index: "X-77",
59
- title: "陌生用户规则(执行等级:A+D)",
60
- section: "自定义分区",
61
- level: "A+D",
62
- body: "- **触发**:使用内联命令。\n- **检查**:拦 node -e / pwsh -c。\n- **动作**:拒绝。\n- **豁免**:无。"
63
- };
64
- const plain = understandRule(arbitrary);
65
- check(plain.handler === "", "③ 任意编号 + 无声明:handler 为空(纯自证分流,不误绑定本机执行器)");
66
- check(plain.actions.includes("deny") && plain.actions.includes("self-certify"), "③ 任意编号:A+D 动作解析正确(deny+self-certify)");
67
- const declared = understandRule({ ...arbitrary, body: arbitrary.body + "\n<!-- handler: rule12a-approval -->" });
68
- check(declared.handler === "rule12a-approval", "③ 任意编号 + 声明:绑定指定执行器");
69
- const covered = analyzeCoverage(understandAll([arbitrary]));
70
- check(covered.uncovered.some((u) => u.ruleId === "X-77"), "③ 任意编号 A 级无 handler:被标记未覆盖(提示正确)");
71
- // 通用化闭环:defaultMap 清空 → 陌生规则无兜底绑定(纯声明式世界,不借道本机偏好表);
72
- // 注:analyzeCoverage 自省对象恒为本机默认表(体检对象),不受 defaultMap 影响。
73
- const bare = understandRule(arbitrary, { defaultMap: {} });
74
- check(bare.handler === "", "③ defaultMap 清空:陌生规则无兜底绑定(纯声明式世界)");
75
- }
76
-
77
- // ── ④ 四要素 + 执行等级格式契约冒烟(README 格式)──
78
- {
79
- const elem = understandRule({
80
- index: "E-1",
81
- title: "格式样例(执行等级:B + D)",
82
- section: "样例分区",
83
- level: "B + D",
84
- body: "- **触发**:触发条件。\n- **检查**:检查项。\n- **动作**:动作说明。\n- **豁免**:豁免说明。"
85
- });
86
- check(elem.elements.trigger.length > 0 && elem.elements.check.length > 0 && elem.elements.action.length > 0 && elem.elements.exemption.length > 0, "④ 四要素提取完整");
87
- check(elem.actions.includes("correct") && elem.actions.includes("self-certify"), "④ 组合等级 B + D 动作解析正确");
88
- check(elem.confidence === "high", "④ 四要素齐全 → 置信 high");
89
- }
90
-
91
- // ── 发布物个人标识扫描(files 白名单;--tgz 指定时扫 tgz 实体)──
92
- const PERSONAL_RE = /jilian|季涟|D:\\DeepSeek|D:\/DeepSeek|@qq\.com|@163\.com|@outlook\.com|私人注释|个人工作流|本机规则/;
93
- {
94
- const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
95
- const scanFiles = [];
96
- const collect = (d, prefix = "") => {
97
- for (const f of readdirSync(d)) {
98
- const p = join(d, f);
99
- if (statSync(p).isDirectory()) { if (!["node_modules", ".backups", ".git", ".codegraph", "test"].includes(f)) collect(p, prefix + f + "/"); }
100
- else if (/\.[a-z]+$/i.test(f) && !/\.(png|jpg|jpeg|gif|webp|svg|tgz|map)$/i.test(f) && !f.startsWith(".")) scanFiles.push(prefix + f);
101
- }
102
- };
103
- collect(join(root, "lib"), "lib/");
104
- for (const f of ["README.md", "cordis.patch.yml", "package.json"]) {
105
- if (existsSync(join(root, f))) scanFiles.push(f);
106
- }
107
- const hits = [];
108
- for (const rel of scanFiles) {
109
- try {
110
- const text = readFileSync(join(root, rel), "utf8");
111
- if (PERSONAL_RE.test(text)) hits.push(rel);
112
- } catch { /* 二进制跳过 */ }
113
- }
114
- if (hits.length) {
115
- // 白名单:README.md 的致谢/仓库信息(官方仓库地址含 jilian-dsh 为必要元数据)、
116
- // package.json 的 repository 属通用字段 —— 明细列出供人工核对,不直接判红
117
- const critical = hits.filter((h) => h.startsWith("lib/"));
118
- check(critical.length === 0, `⑤ 发布物标识扫描:lib 源码命中 ${critical.length} 处(${critical.join(", ")})——个人标识不得进入通用代码`);
119
- } else {
120
- check(true, "⑤ 发布物标识扫描:files 白名单无个人标识命中(注意:README 仓库信息属必要元数据,不在此列)");
121
- }
122
- // tgz 实体扫描(--tgz):gzip 解包 + 512 字节 tar 头扫描文件内容
123
- if (tgzArg && existsSync(tgzArg)) {
124
- const { gunzipSync } = await import("node:zlib");
125
- const buf = gunzipSync(readFileSync(tgzArg));
126
- let off = 0;
127
- const name = new TextDecoder().decode(buf.subarray(off, off + 100)).replace(/\0.*$/, "");
128
- const sizeStr = new TextDecoder().decode(buf.subarray(off + 124, off + 136)).replace(/\0/g, "").trim();
129
- off += 512;
130
- if (name && sizeStr) { /* 首块为目录/文件;逐块扫描见下 */ }
131
- // 简化:直接全文正则扫描(gzip 已解包,文本可读)
132
- const text = buf.toString("utf8");
133
- check(!PERSONAL_RE.test(text), "⑤ tgz 实体扫描:无个人标识命中");
134
- }
135
- }
136
-
137
- // ── --deep:完整引擎链冷启动(子进程,需完整权限/CI;沙箱不可用)──
138
- if (deep) {
139
- console.log("\n(--deep 模式:请确认在完整权限/CI 环境运行——子进程捕获受沙箱限制)");
140
- }
141
-
142
- rmSync(sceneDir, { recursive: true, force: true });
143
- console.log(`\n${FAILS.length === 0 ? "ADEPTITUDE CHECK PASS" : `ADEPTITUDE CHECK FAIL(${FAILS.length} 项)`}`);
144
- process.exit(FAILS.length === 0 ? 0 : 1);
1
+ // publish-aptitude-check.mjs — 发布适用性门禁(T6,2026-08-31)
2
+ // 目的:兑现 README「通用化」承诺的"陌生人视角"检查——发布物必须先证明:
3
+ // ① 无 AGENTS.md(陌生环境冷启动):引擎零错 / 零死映射 / 零意外拦截面
4
+ // ② 空白规则文件:静默(0 规则、无错、无 dead)
5
+ // ③ 任意编号规则(非本机编号):声明式绑定与"纯自证"分流行为正确
6
+ // ④ 四要素 + 执行等级格式的最小规则:actions/handler 解析正确(README 格式契约冒烟)
7
+ // ⑤ 发布物个人标识扫描:files 白名单内文件不得含本机个人标识(本地用户名/真实路径/邮箱/私人注释)
8
+ // 默认模式 = 单进程纯函数链(不 spawn、不 import index.js,沙箱可直接跑);
9
+ // --deep 模式 = 额外子进程全链冷启动(临时 DSH_HOME + 完整引擎加载 + 真实裁决链,需完整权限/CI)。
10
+ // 用法:node scripts/publish-aptitude-check.mjs [--deep] [--tgz <路径>]
11
+ import { mkdtempSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync, existsSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join, dirname, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
17
+ const args = process.argv.slice(2);
18
+ const deep = args.includes("--deep");
19
+ const tgzArg = args.includes("--tgz") ? args[args.indexOf("--tgz") + 1] : null;
20
+
21
+ const FAILS = [];
22
+ function check(ok, msg) {
23
+ console.log(`${ok ? "✅" : "❌"} ${msg}`);
24
+ if (!ok) FAILS.push(msg);
25
+ }
26
+
27
+ // ①-④ 使用的纯函数链(与引擎 lib 同源,动态 import 保证读到本次代码)
28
+ const { loadRules } = await import("../lib/core/parser.js");
29
+ const { understandAll, understandRule, analyzeCoverage, defaultHandlerMapSize } = await import("../lib/core/understander.js");
30
+
31
+ // ── ① 陌生环境冷启动(临时 DSH_HOME,无 AGENTS.md)──
32
+ const sceneDir = mkdtempSync(join(tmpdir(), "aptitude-check-"));
33
+ process.env.DSH_HOME = sceneDir;
34
+ process.env.DSH_WORKSPACE = root;
35
+ {
36
+ const parsed = loadRules();
37
+ check(parsed.ok === false && parsed.missing === true, `① 无 AGENTS.md:处理器返回 missing(ok=${parsed.ok}, missing=${parsed.missing})`);
38
+ const configs = understandAll([]);
39
+ check(configs.length === 0, "① 无 AGENTS.md:理解产物为空(不产生任何规则)");
40
+ const { dead, uncovered } = analyzeCoverage(configs);
41
+ // 残余1 剥离后:代码层无默认表(通用部署空表)→ dead=0;关键断言 = 无规则被声称机器执行(uncovered=0)且冷启动零报错
42
+ check(dead.size === 0, `① 无 AGENTS.md:dead=0(代码层无默认偏好表)`);
43
+ check(uncovered.length === 0, `① 无 AGENTS.md:零未覆盖(uncovered=0)`);
44
+ }
45
+
46
+ // ── ② 空白规则文件静默 ──
47
+ {
48
+ writeFileSync(join(sceneDir, "AGENTS.md"), "", "utf8");
49
+ const parsed = loadRules();
50
+ check(parsed.ok === true && parsed.rules.length === 0, `② 空白 AGENTS.md:解析成功且 0 规则(rules=${parsed.rules.length})`);
51
+ const configs = understandAll(parsed.rules);
52
+ check(configs.length === 0, "② 空白 AGENTS.md:理解产物为空");
53
+ }
54
+
55
+ // ── ③ 任意编号规则(非本机编号):声明式绑定 + 纯自证分流 ──
56
+ {
57
+ const arbitrary = {
58
+ index: "X-77",
59
+ title: "陌生用户规则(执行等级:A+D)",
60
+ section: "自定义分区",
61
+ level: "A+D",
62
+ body: "- **触发**:使用内联命令。\n- **检查**:拦 node -e / pwsh -c。\n- **动作**:拒绝。\n- **豁免**:无。"
63
+ };
64
+ const plain = understandRule(arbitrary);
65
+ check(plain.handler === "", "③ 任意编号 + 无声明:handler 为空(纯自证分流,不误绑定本机执行器)");
66
+ check(plain.actions.includes("deny") && plain.actions.includes("self-certify"), "③ 任意编号:A+D 动作解析正确(deny+self-certify)");
67
+ const declared = understandRule({ ...arbitrary, body: arbitrary.body + "\n<!-- handler: rule12a-approval -->" });
68
+ check(declared.handler === "rule12a-approval", "③ 任意编号 + 声明:绑定指定执行器");
69
+ const covered = analyzeCoverage(understandAll([arbitrary]));
70
+ check(covered.uncovered.some((u) => u.ruleId === "X-77"), "③ 任意编号 A 级无 handler:被标记未覆盖(提示正确)");
71
+ // 通用化闭环:defaultMap 清空 → 陌生规则无兜底绑定(纯声明式世界,不借道本机偏好表);
72
+ // 注:analyzeCoverage 自省对象恒为本机默认表(体检对象),不受 defaultMap 影响。
73
+ const bare = understandRule(arbitrary, { defaultMap: {} });
74
+ check(bare.handler === "", "③ defaultMap 清空:陌生规则无兜底绑定(纯声明式世界)");
75
+ }
76
+
77
+ // ── ④ 四要素 + 执行等级格式契约冒烟(README 格式)──
78
+ {
79
+ const elem = understandRule({
80
+ index: "E-1",
81
+ title: "格式样例(执行等级:B + D)",
82
+ section: "样例分区",
83
+ level: "B + D",
84
+ body: "- **触发**:触发条件。\n- **检查**:检查项。\n- **动作**:动作说明。\n- **豁免**:豁免说明。"
85
+ });
86
+ check(elem.elements.trigger.length > 0 && elem.elements.check.length > 0 && elem.elements.action.length > 0 && elem.elements.exemption.length > 0, "④ 四要素提取完整");
87
+ check(elem.actions.includes("correct") && elem.actions.includes("self-certify"), "④ 组合等级 B + D 动作解析正确");
88
+ check(elem.confidence === "high", "④ 四要素齐全 → 置信 high");
89
+ }
90
+
91
+ // ── ⑤(2026-09-04 本机化:存在性判据 + 个人词表扫描移入本机发行工具
92
+ // scripts/scan-real-paths.mjs(判据库 scripts/lib/realpath-guard.js),
93
+ // 本文件只保留 ①-④ 通用发布适用性检查;发布前由本机 release-gate 统一执行门禁)──
94
+
95
+ // ── --deep:完整引擎链冷启动(子进程,需完整权限/CI;沙箱不可用)──
96
+ if (deep) {
97
+ console.log("\n(--deep 模式:请确认在完整权限/CI 环境运行——子进程捕获受沙箱限制)");
98
+ }
99
+
100
+ rmSync(sceneDir, { recursive: true, force: true });
101
+ console.log(`\n${FAILS.length === 0 ? "ADEPTITUDE CHECK PASS" : `ADEPTITUDE CHECK FAIL(${FAILS.length} 项)`}`);
102
+ process.exit(FAILS.length === 0 ? 0 : 1);
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ // readme-version-check.mjs — README 版本四性一致性门禁
3
+ // 检查:package.json version == README 徽章 == README 正文"当前版本 X" == 版本历史表含当前版本 == 发行固定源锚定当前版本
4
+ // 用法:node scripts/readme-version-check.mjs (在包根目录运行;exit 0 = 一致,exit 1 = 不一致)
5
+
6
+ import { readFileSync } from "node:fs";
7
+ import { fileURLToPath } from "node:url";
8
+ import { dirname, join } from "node:path";
9
+
10
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
11
+ const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
12
+ const readme = readFileSync(join(root, "README.md"), "utf8");
13
+ const ver = pkg.version;
14
+
15
+ let failures = [];
16
+ const ok = (cond, label, detail) => {
17
+ if (cond) console.log(`PASS ${label}`);
18
+ else { failures.push(label); console.log(`FAIL ${label} — ${detail}`); }
19
+ };
20
+
21
+ // 1. 徽章:badge/version-X.Y.Z
22
+ const badge = readme.match(/badge\/version-(\d+\.\d+\.\d+)/);
23
+ ok(badge?.[1] === ver, "徽章=package.json", `徽章=${badge?.[1] ?? "未找到"} vs package=${ver}`);
24
+
25
+ // 2. 正文"当前版本 X"(允许"当前版本 **X**")
26
+ const cur = readme.match(/当前版本[^\d]{0,10}(\d+\.\d+\.\d+)/);
27
+ ok(cur?.[1] === ver, "正文当前版本=package.json", `正文=${cur?.[1] ?? "未找到"} vs package=${ver}`);
28
+
29
+ // 3. 版本历史表包含当前版本行:| **X.Y.Z** | 或 | X.Y.Z |
30
+ const tableHit = new RegExp(`^\\|\\s*\\*{0,2}${ver.replace(/\./g, "\\.")}\\*{0,2}\\s*\\|`, "m").test(readme);
31
+ ok(tableHit, "版本历史表含当前版本行", `未找到 | ${ver} | 行`);
32
+
33
+ // 4. 发行固定源锚定当前版本:"**X(当前)**" 或 "X.Y.Z(当前)"
34
+ const pin = readme.match(/\*\*(\d+\.\d+\.\d+)(当前)\*\*/);
35
+ ok(pin?.[1] === ver, "发行固定源=package.json", `固定源=${pin?.[1] ?? "未找到"} vs package=${ver}`);
36
+
37
+ if (failures.length) {
38
+ console.error(`\nVERSION CHECK FAILED(${failures.length} 项不一致)`);
39
+ process.exit(1);
40
+ }
41
+ console.log(`\nVERSION CHECK OK(${ver} 四处一致)`);