diff --git a/client/electron/test/criteria-hygiene.test.mjs b/client/electron/test/criteria-hygiene.test.mjs index 807c7e6..0138c18 100644 --- a/client/electron/test/criteria-hygiene.test.mjs +++ b/client/electron/test/criteria-hygiene.test.mjs @@ -777,3 +777,45 @@ test('★ 非门禁工具(deploy/*.sh)必须有发现路径(至少一处 + ' 会让工具从"强制被调用"掉到"零引用",而**上一条判据正好不再覆盖它** ——\n' + ' 改名这个合规动作本身制造了一个无判据的盲区。'); }); + +/* + * ★★ 同一断言在两处各有一份副本时,**两份都要有守**(pi `7ec0044a` 指出,我验证)。 + * + * 背景:⑤b("已装二进制 = 当前 HEAD")这句话出现在**两个文件**里 —— + * · `deploy/check-deploy-drift.mjs` 的 `negative` 常量(+ 它的自检格) + * · `deploy/redeploy-gateway.sh` 的注释与 `ok` 文案(§7) + * 而**只有前者有自检**。实测: + * grep '已装二进制 = 当前 HEAD' 全仓 ⇒ **只命中 redeploy-gateway.sh 那一行** + * redeploy-gateway.sh 的 `--self-check` 出现次数 = **0** + * ⇒ 同一个动作、同一句"不覆盖"的声明,**一处有守、一处没有**。 + * + * ⇒ 判据: **两份负向清单必须同时存在,且都点名同一对失败类(脏树 / 手工替换)**。 + * 为什么不能只查一份: 删掉 shell 那一份、或它漂成只写一项,**没有任何东西会红** —— + * 而读者看到的 `ok` 行正是 shell 那份(那是部署时**打在屏幕上**的那份)。 + * ⚠️ 这与上一条(发现路径)同族: 都防"**声明的副本**没有守卫"。 + * 区别: 上一条防"没人知道工具存在",这一条防"**声明漂了没人知道**"。 + */ +test('★ ⑤b 的负向清单在 JS 与 shell 两处都有副本,两份都必须点名同一对失败类', () => { + const JS = join(REPO_ROOT, 'deploy', 'check-deploy-drift.mjs'); + const SH = join(REPO_ROOT, 'deploy', 'redeploy-gateway.sh'); + + // 每一份都必须同时点出这两类(正是它们**不被**该判据覆盖) + const CLASSES = ['脏树', '手工替换']; + const missing = []; + for (const [name, p] of [['check-deploy-drift.mjs', JS], ['redeploy-gateway.sh', SH]]) { + const src = prose(p); + for (const c of CLASSES) { + if (!src.includes(c)) missing.push(`${name} 未点名「${c}」`); + } + } + + assert.deepEqual(missing, [], + `⑤b 的负向清单有副本没写全(**副本漂了,而它恰好是屏幕上看的那份**):\n` + + ` ${missing.join('\n ')}\n` + + ' ⇒ 这两类的**唯一**呈现处就是这两份文案;漏掉一类 ⇒ 读者会把绿读成"没问题":\n' + + ' · 脏树构建(含未提交代码,只披露不判红)\n' + + ' · 部署后被手工替换/修改(只看内嵌 revision)\n' + + ' ⚠️ 为什么两处都要守: `check-deploy-drift.mjs` 有 `--self-check`,\n' + + ' 而 `redeploy-gateway.sh` **没有** ⇒ 只守前者的话,删掉/写漂 shell 那份不会红,\n' + + ' 而 shell 那份是**部署当时打印 `ok` 行**所用的文本。'); +}); diff --git a/client/electron/test/run-all.mjs b/client/electron/test/run-all.mjs index 9487042..c9f88d9 100644 --- a/client/electron/test/run-all.mjs +++ b/client/electron/test/run-all.mjs @@ -137,8 +137,8 @@ const SUITE = [ ['test/debt-visibility.test.mjs', [], 1], ['test/commit-hygiene.test.mjs', ['--experimental-strip-types', '--no-warnings'], 4], // 判据目录自身的卫生:读文本必须走 test/lib/read.mjs 的具名入口; - // 以及非门禁工具必须有**发现路径**(躲开 check-* 约定改名 ⇒ 否则零引用无人知) - ['test/criteria-hygiene.test.mjs', [], 8], + // 非门禁工具必须有**发现路径**;⑤b 负向清单两处副本都要点名同一对失败类 + ['test/criteria-hygiene.test.mjs', [], 9], // 用户管理页(P4c 同批):动作↔服务端调用同名 / 门禁只认严格 admin / // 启停只发 status / 「受限」徽标口径 / 页面零写死色值 / 接线(纯逻辑真被调用) ['test/harmony-admin.test.mjs', ['--experimental-strip-types', '--no-warnings'], 31],