From 788d7ccb201f371b4327ac66ba97189e265dabff Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sat, 19 Sep 2026 12:51:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E6=9D=A1=E6=95=B0?= =?UTF-8?q?=E7=99=BB=E8=AE=B0=E6=A0=A1=E9=AA=8C**=E5=8F=AA=E5=9C=A8"?= =?UTF-8?q?=E7=BB=BF"=E7=9A=84=E9=82=A3=E6=9D=A1=E8=B7=AF=E4=B8=8A**=20?= =?UTF-8?q?=E2=80=94=E2=80=94=20=E6=96=87=E4=BB=B6=E8=B6=8A=E7=BA=A2?= =?UTF-8?q?=EF=BC=8C=E5=AE=83=E7=9A=84=E7=99=BB=E8=AE=B0=E6=95=B0=E8=B6=8A?= =?UTF-8?q?=E6=B2=A1=E4=BA=BA=E5=AE=88=EF=BC=88=E5=81=87=E7=BB=BF=E6=96=B9?= =?UTF-8?q?=E5=90=91=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 设备在场时跑全套,顺手核了每个文件的『登记条数 vs 实际条数』,发现: narrow-layout 登记=64 实际=88 exit=0 绿 ⇒ 校验生效(报了) nav-merge 登记=8 实际=9 exit=0 绿 ⇒ 校验生效(报了) background 登记=43 实际=44 exit=0 绿 ⇒ 校验生效(报了) harmony-presets 登记=5 实际=6 exit=0 绿 ⇒ 校验生效(报了) harmony-admin 登记=22 实际=27 exit=1 ★ 红 ⇒ 校验**走不到**(grep 0 命中) 根因:这条校验原来写在**最后一个 `else`**("退出码 0"那条路)里,而 `r.status !== 0` 会**先在 `else if` 里 reds.push 并跳过它**。 ⇒ **文件越红,它的条数登记越没人守** —— 假绿方向: harmony-admin 那多出来的 5 条判据**不在"被删会红"的保护内**, 而它恰好是红的 ⇒ **只要它一直红,缺口就一直是隐形的**; 等它修绿那天校验才第一次生效,那时多出来的几条可能早被删了。 (同族:`unlisted`/`blind`/`baseline=` 的结论到不了 `verdict`,§16.1。) ★ 而且这个缺口**已经真的吃过一次**:`c523c21`("邮件详情与「我的」页 1:1 对齐") 给 harmony-admin **+5 条判据**(它自己的 commit message 就写着 "+5"), **却没同步登记数**,而当时该文件是红的 ⇒ 5 条新判据至今裸奔。 修法:把条数校验移进 `else { … }`(红绿都跑;先 push 退出码红,再判条数)。 ⚠️ broken(崩了/一条条数都没自报)**不在这里**判 —— `diedWithoutReporting` 已吃掉它, 再叠一条"没找到自报条数"只是噪音:**"判据没答 ≠ 判据答错了"**(§17)。 并做那个"显式、可复核的编辑":登记数 22 → 27(和 c523c21 欠下的那 5 条对齐)。 变异验证: · 红的 harmony-admin 登记数改 99 ⇒ 报『自报 27 条 < 登记的 99 条』✓ · 改成 27(对齐)⇒ **不报条数**、只剩"退出码 1" ✓ · 修后全套:red=10(harmony-admin 那条从"走不到"变成"报出来"后 + 对齐登记数又收回,净额 0),另 4 个绿文件的条数不符**照旧照报** ✓ `CRITERIA.md` 新增通用规则:**校验写在哪条分支上,决定它保护谁。** 凡"出错时要额外检查 X"的守卫,先问:**这条分支真红的时候,它还跑得到吗?** --- client/electron/test/CRITERIA.md | 37 ++++++++++++++++++++++++++++++++ client/electron/test/run-all.mjs | 27 +++++++++++++++++++---- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/client/electron/test/CRITERIA.md b/client/electron/test/CRITERIA.md index 64c0d91..219d3e9 100644 --- a/client/electron/test/CRITERIA.md +++ b/client/electron/test/CRITERIA.md @@ -487,6 +487,43 @@ console.log('主题:通过'); // 有输出 那正是要看见的事(顺手删两条判据、某条被跳过、`check` 实现被改坏)。 代价:**故意删判据时要同步改数字**(这是一次显式的、能被复核的编辑,可以接受)。 +### 条数登记校验**曾经只在"绿"的那条路上**(dsh 2026-09-19 设备在场时发现) + +上面那条校验原来写在**最后一个 `else`** 里("退出码 0"那条路)。而 `r.status !== 0` +会**先**在 `else if` 里 `reds.push(...)` 并**跳过**它 ⇒ **文件越红,它的条数登记越没人守。** + +**实测**(同刻、设备在场、一次全套): + +``` +test/narrow-layout.test.mjs: 登记=64 实际=88 exit=0 绿 ⇒ 校验生效(报了) +test/nav-merge.test.mjs: 登记=8 实际=9 exit=0 绿 ⇒ 校验生效(报了) +test/background.test.mjs: 登记=43 实际=44 exit=0 绿 ⇒ 校验生效(报了) +test/harmony-presets.test.mjs: 登记=5 实际=6 exit=0 绿 ⇒ 校验生效(报了) +test/harmony-admin.test.mjs: 登记=22 实际=27 exit=1 ★ 红 ⇒ 校验**走不到**(0 命中) +``` + +⇒ ★ 这是本仓那条母规则的又一例:**"判据在,但走不到"**, +而且方向是**假绿**:`harmony-admin` 那多出来的 5 条判据**不在"被删会红"的保护内**, +而它恰好是红的 ⇒ **只要它一直红,这个缺口就一直是隐形的**; +等它被修绿那天校验才第一次生效,那时多出来的几条**可能早被删了**。 +(同族:`unlisted`/`blind`/`baseline=` 的结论到不了 `verdict`,§16.1。) + +**修法**:把条数校验移进 `else { … }`(红绿都跑;先 push 退出码红,再判条数)。 +⚠️ 但 **broken(崩了/一条条数都没自报)不在这里判** —— `diedWithoutReporting` +已经把它归入 broken,再叠一条"没找到自报条数"只是噪音: +**"判据没答 ≠ 判据答错了"**(§17)。 + +**修后实测**:`harmony-admin` 那一段现在报 +`自报 27 条 > 清单里登记的 22 条 —— 新加的那几条不在"被删会红"的保护内`,`red` 10→11。 +**变异**:把红的 `harmony-admin` 登记数改成 `99` ⇒ 报 `自报 27 条 < 登记的 99 条` ✓; +改成 `27`(对齐)⇒ **不报条数**、只剩"退出码 1" ✓。 + +★ **通用规则**: + +> **校验写在哪条分支上,决定它保护谁。** +> 凡"出错时要额外检查 X"的守卫,先问:**这条分支真红的时候,它还跑得到吗?** +> —— 把校验放在"一切正常"的那条路上,等于**只在没出事时守着**。 + 计数必须写在 **`check()` 内部**:写在调用点或靠扫源码,"实现被换空"就看不见了 —— 上面那个反例的 `pass` 会是 0,正是靠这一条才有分辨力。 diff --git a/client/electron/test/run-all.mjs b/client/electron/test/run-all.mjs index 7f13964..ec7ed35 100644 --- a/client/electron/test/run-all.mjs +++ b/client/electron/test/run-all.mjs @@ -109,7 +109,7 @@ const SUITE = [ ['test/criteria-hygiene.test.mjs', [], 6], // 用户管理页(P4c 同批):动作↔服务端调用同名 / 门禁只认严格 admin / // 启停只发 status / 「受限」徽标口径 / 页面零写死色值 / 接线(纯逻辑真被调用) - ['test/harmony-admin.test.mjs', ['--experimental-strip-types', '--no-warnings'], 22], + ['test/harmony-admin.test.mjs', ['--experimental-strip-types', '--no-warnings'], 27], // P4c 图片上传:阈值与两档策略 / 失败必带原因 / 退档判定只有一处 / // release 都 await / 解码按目标尺寸 / multipart 字段名 / 上传后重新同步 ['test/harmony-imageprep.test.mjs', ['--experimental-strip-types', '--no-warnings'], 30], @@ -513,8 +513,27 @@ for (const [file, flags, expected] of SUITE) { '\n 常见成因:语法/标识符错(`X is not defined`)、import 写错、编译不过、' + '\n **按相对路径读文件但 cwd 不对**(`ENOENT: … open \'src/index.css\'`)。' + '\n 用于变体验证时:broken **不算这次变异有效**,要重做。'); - } else if (r.status !== 0) reds.push(`${file}(退出码 ${r.status})`); - else { + } else { + if (r.status !== 0) reds.push(`${file}(退出码 ${r.status})`); + /* + * ★★ 「条数登记」校验必须在**这里**(红绿都跑),**不能留在下面那个 `else` 里**。 + * + * 实测(dsh 2026-09-19,设备在场时跑全套发现的): + * `test/harmony-admin.test.mjs` **登记 22 条、实际 27 条**(差 5 条, + * 那 5 条**不在"被删会红"的保护内**),而**它当时是红的**(1 fail)⇒ + * 走的是上面 `退出码 1` 那条分支 ⇒ **这条校验一次都没被评估**: + * `grep '自报.*harmony-admin' dev.log` ⇒ **0 命中** + * (同时刻另有 4 个**绿的**文件条数不符,它们**全都正常报了** ⇒ + * 说明不是校验写错,是**它在红的那条路上到不了**。) + * + * ★ 这正是本仓反复消的那个形状:**"判据在,但走不到"** —— + * 而且方向是**假绿**:一个文件越红,它的登记数越没人守; + * 等到它被修绿那天,那条校验才第一次生效 —— 那时"多出来的几条"可能早被删了。 + * (同族:`unlisted`/`blind`/`baseline=` 的结论到不了 `verdict`,§16.1。) + * + * ⚠️ 但 broken(崩了/没自报)**不在这里**判:`diedWithoutReporting` 已把它吃掉, + * 再叠一条"没找到自报条数"只是噪音 ——"判据没答 ≠ 判据答错了"(§17)。 + */ /* * ★ 这里**只保留"没自报条数"的那条红**;**任何累加都不在这里做** —— * 投影之后累加根本不作为语句存在,上面那条 push 出来的记录是唯一的信息来源。 @@ -582,8 +601,8 @@ for (const [file, flags, expected] of SUITE) { } } } - /* + * ─── 投影:把 `records` 算成那四个计数器(**这里是它们唯一的"来处"**)─── * * ★ 为什么是这个形状(pi 2026-09-15 建议,我采纳并实测):