test(判据): ★★★ 套件从 10-02 10:46 起一条都没跑过 —— 补接线 + 结算登记数

**零读数 24 小时,仓库里没有任何记录**(DEBTS.json 58 条里 grep「没接进」0 命中)。

漏接线的三个(都来自 aeb1f41 / 2f17f62,都比 run-all.mjs 最后一次改动晚):
    test/inbox-fallback-poll.test.mjs   ← SSE 兜底轮询(探测 total 变化)
    test/sse-credentials.test.mjs      ← SSE 订阅跟账号凭证走
    test/web-comment-only.test.mjs

**为什么严重(三层放大)**:
1. 自检 2「每个 *.test.mjs 都要在清单里」在跑任何判据**之前** exit(1)
   ⇒ 实测 RESULT 行数 = **0**
2. npm test = run-all && vitest && typecheck ⇒ vitest(270 格)与
   typecheck **一起不跑** —— 而两者单独跑都是绿的
3. 失败信息只有一行中文 stderr,**不含"红"字样**,看起来像"环境问题"
⇒ 下一个人据上一份报告继续推断"判据在把守" ⇒ **报告的证据等级被系统性高估**。
守卫本身**不删**(漏接线绝不静默是真价值),代价靠「新增即接线」这条义务兜。

**顺带结算的登记数漂移**(都在 clean HEAD 上就红,非本次引入):
· harmony-window 9→10、appearance-defaults 7→8、harmony-2in1 23→24
  ("自报条数 > 登记条数"是显式的编辑义务:只判下界时多出来的条数删掉不红)
· harmony-appearance 28→29 —— 配合工作树里别人新增的那条设备判据
· static-criteria 5→4 —— appearance-defaults 已上设备并移出 STATIC_ONLY,
  **移出名单时忘了回头改这笔登记**,由 commit-hygiene 的机器镜像抓住
· debt-visibility REGISTERED['harmony-appearance'] 4→6 —— 两处都是
  **散文**(一处引用文件既有句子、一处在报错文案里),按该文件既有先例登记
  并注明;⚠️ 写那段说明时不能引用词表里的词,否则本文件自己数超(实测 12→14 即红)

**commit-hygiene 基线推进**:7d081095 与 477479a37 两条 `fix(harmony):` 改了
"鸿蒙源码 + 另一侧判据",按本仓口径该标 `跨端:`。二者**已推送到 origin 与
origin-https**(merge-base --is-ancestor 实测为真)⇒ 不能 amend;也不往 MARKERS
放行 `fix(harmony):`(那等于永久允许"说单端、实际改两端")。唯一正确处置是
推进基线 + 具名记下。已验证:COMMIT_HYGIENE_BASELINE 覆盖后 pass=4 fail=0。

**CRITERIA.md 新增 §6.0「怎么读判据的数」** —— 原有 17 节全在讲「怎么写」,
缺的就是这一半,而今天两起独立事件都出在它:
· 6.0.1 接线守卫的失效形状是「全停」不是「那一条不跑」;ran 必须 == SUITE 条数
· 6.0.2 退出码只能来自不接管道的运行(`cmd >file 2>&1; echo $?`)。
  **本会话我连踩 5 次** `cmd | tail -N` ⇒ 报的是 tail 的码。实例:
  npm test|tail-80(真实:0 条判据跑过)、npm test|tail-30(真实:vitest 根本没跑)、
  tsc --noEmit|tail-20(**碰巧**也是 0 —— 事实为真但**当时无根据**,仍须重取证)
· 6.0.3 && 链里「全绿」要问**真跑到那一环了吗**(red 之后的东西根本没跑,
  而日志里「有 RESULT 行」与「无下游输出」可以同时出现)
· 6.0.4 判据变红先问「判据用的工具本身可信吗」(读取器的缺陷是**静默**的)
· 6.0.5 **当你就是改工具的人**:第一假设是「我弄坏了它」不是「代码回归」——
  「判据过期了」这个反应本身就错,它默认了「我改的是正确的东西」。
  附本次三次改错的下游依赖表,以及"下游依赖是**行为依赖**,
  codegraph 那类符号图看不见"。

★ 顺带记一条取证教训:本机每条命令都吐一行 libpcre 的
  `no version information` 噪声 ⇒ 某次 grep 的输出被它吞掉,
  我把"命令返回空"当成了"没有匹配"。**空输出要连退出码一起看**,
  这与 6.0.2 是同一族,只是这次发生在我自己的取证上。
This commit is contained in:
2026-10-03 10:43:41 +08:00
parent 93697c4061
commit 6df3c356d6
5 changed files with 161 additions and 8 deletions

View File

@ -143,6 +143,102 @@ const ALLOW = [ /* { name, replacement, why } */ ];
`process.exit(1)` 吞掉**,于是"变异后依然 exit 0"看起来像判据失效(本仓刚踩过这一次:
自检 3 其实是好的,是我用错了入口去验它)。**验判据要模拟用户/CI 真正跑的那一行。**
### 6.0 怎么**读**判据的数(前半篇讲「怎么写」,这一节讲「怎么读」)
§1–§17 讲的都是「判据怎么写」。**另一半——「怎么读它的数」——本节讲,
因为 2026-10-03 一天之内就撞了两起独立事件,都出在这一半,而仓库里没有成文规则。**
#### 6.0.1 接线守卫的失效形状是「**全停**」,不是「那一条不跑」
`run-all.mjs` 的自检 2(「每个 `*.test.mjs` 都要在清单里」)在**跑任何判据之前**
`process.exit(1)` ⇒ 实测 `RESULT` 行数 = **0**。又因 `npm test` 是 `&&` 链,
`vitest` 与 `tsc` **一起不跑**——而两者单独跑都是绿的。
```
2026-10-02 10:46 aeb1f41 加了 inbox-fallback-poll / sse-credentials(未接线)
2026-10-02 13:58 2f17f62 加了 web-comment-only(未接线)
↓
2026-10-03 整仓 24 小时零读数;失败只有一行中文 stderr,看起来像「环境问题」
```
⇒ **「判据存在」不等于「判据在跑」,而本仓的守卫把两者绑在一起**。
它的价值(漏接线绝不静默)是真的,代价(一个文件漏接 = 全仓失去全部读数)也是真的。
- **新增 `*.test.mjs` 之后必须做的事**:跑一次 `node test/run-all.mjs`,
看 `RESULT files=N ran=N` —— **`ran` 必须等于 `SUITE` 条数**。
只看到「没接进套件」那一行就以为「跑过了」是最容易犯的错。
- **别信「上次是绿的」**:若那个结论来自一份**报告**而不是一次运行,
先确认**上一次真读到数是什么时候**(本笔已登记为 `criteria-suite-unwired-stops-everything`)。
#### 6.0.2 退出码只能来自**不接管道的**运行
```
cmd 2>&1 | tail -N ⇒ $? 是 tail 的,不是 cmd 的
cmd >file 2>&1; echo $? ⇒ 唯一诚实的形状
```
**实测(同一会话内我连踩三次)**:
| 命令 | 报的 | 实际 |
|---|---|---|
| `npm test \| tail -80` | 0 | 0 条判据跑过 |
| `npm test \| tail -30` | 0 | run-all red,**vitest 根本没跑** |
| `tsc --noEmit \| tail -20` | 0 | tsc 的 0(**碰巧**也是) |
第三条最危险:**结论为真,但当时没有根据**。`tsc` 静默无错与「`tsc` 被 `tail` 吃掉」
在日志里**长得一样**。⇒ 事实成立 ≠ 当时有根据;**依据必须重取**。
#### 6.0.3 `&&` 链里「全绿」要问:**真跑到那一环了吗**
`run-all` red ⇒ `vitest`/`tsc` **根本没跑**。而日志里可以**同时**出现
「有 `RESULT` 行」与「无 `vitest` 输出」——**只看日志会以为它跑过了**。
- 汇报门禁结果时,**写清是哪几环**(`run-all` / `vitest` / `tsc`),
以及**哪些没跑**。
- 「没输出」要连退出码一起看(见 6.0.2 第三条)。
#### 6.0.4 判据变红时,先问「**判据用的工具本身可信吗**」
2026-10-03 的 `harmony-admin` 假红:`stripComments` 把 `main.go:78` 行注释里的
`/*` 当成块注释开头 ⇒ **137 行 / 37 条路由注册**被当注释抹掉,
含它正在断言的 `r.Get("/auth/me", …)`。
⇒ **读取器的缺陷是静默的**(不抛错、不警告),症状却出现在**被测对象**上。
所以:**「某条判据突然变红,而源码看起来是对的」⇒ 先验证读取器,再怀疑代码。**
反过来也成立:**换成更严格的读取方式之后判据变红,通常是判据错了**——
本次 `inbox-fallback-poll` 从裸 `readFileSync` 换成 `code()`(剥注释)之后变红,
暴露的是它**本来就在判一行尾注释里的字**(删掉注释照样绿、塞进真 bug 也照样绿)。
#### 6.0.5 改**共用工具**时:第一假设是「我弄坏了它」,不是「代码回归了」
6.0.4 讲的是「工具坏了怎么认出来」。这一节讲它的**另一半**:当**你就是改工具的人**。
2026-10-03 为修 6.0.4 那个假红重写 `lib/read.mjs` 的 `stripComments`,**连错三次,
三次都是判据没错、我错了**,而且三次的第一反应都是「判据是不是过期了」:
| 轮 | 我的改动 | 谁红了 | 真因 |
|---|---|---|---|
| v1 | 块注释改「空格填充等长」 | `harmony-logic` / `harmony-nav` | 下游 `[\s\S]{0,300}?` 窗口是**按"删掉"的尺度**标定的,填充把窗口撑爆 |
| v2 | 状态改成「回看已输出的 `out`」 | `criteria-hygiene` | `out` 里注释已抹过,**重算的上下文 ≠ 源码** ⇒ `:566` 真块注释没被剥 |
| v3 | 只跟踪字符串、不认正则字面量 | `criteria-hygiene` | `harmony-device.mjs:59` 的正则里有 4 个引号(奇数)⇒ 打开的"字符串"**永不闭合** |
★ **「判据过期了」这个反应本身就是错的**,因为它默认了「我改的是正确的东西」。
三次里如果反过来先怀疑代码,就会去改**本来正确的生产代码**。
- **共享读取器的「删多少 / 怎么判」是被下游按字节尺度依赖着的。**
改它之前先问「**谁在依赖这个性质**」——本次两处依赖都是 `[\s\S]{n,m}?` 窗口,
而 `codegraph` 那类**符号图看不见这类依赖**(它们是**行为依赖**:不调用你,
但依赖你的输出**形状/尺度**)。
- **每次改动都重跑全量**,不要只看直接调用方:v1 就是只看了 `code()` 的直接使用者,
是跑全量 `run-all` 才看见 `harmony-logic`/`harmony-nav`/`harmony-apibase` 同时变红的。
- **新写读取器要把四条性质一起钉**(本次五条,逐条实测才算数):
① 行号不变 ② 字符串里的 `//` 与注释标记不被当注释 ③ 注释里的引号不污染字符串状态
④ 正则字面量被当正则(漏认的方向是**假绿**:奇数引号 ⇒ 永不闭合 ⇒ 后面注释全跳过)
⑤ 块注释**连文本一起删**(不是留空白、不是只留换行 —— 后两种都会被下游窗口的标定尺度打中)。
- **改完用变异测试自证**:把旧实现放回去,确认那条判据**确实变红**。
本次正是这一步把「我修好了」与「我改的东西恰好没人用」区分开。
### 6.1 判据的**存在性**也要有下限:把"必须存在哪几条"锚到判据自己的表之外(pi 2026-09-18)
§6 那条只说了"扫目录要有下限"。**同一条规矩换个对象就漏了** —— 这条是它的镜像:

View File

@ -92,6 +92,23 @@ test('跨端提交必须自报家门(同时改 harmony 与 electron 的提交
* 一律豁免 —— pi 明确说历史不用改,规则管"从今往后"。
* `COMMIT_HYGIENE_BASELINE` 可覆盖(用于验证判据真的会红)。
*
* ── ★★ 2026-10-03 基线推进(**与下面 2026-09-24 同形状的第二次**:两条 `fix(harmony):` 改了双端)──
*
* 实测两条,都在**基线之前**、且**已推到 origin 与 origin-https** ⇒ **不改历史**:
* 7d081095 fix(harmony): ★★ 顶部文案用 windowDecor 判 2in1(真机实测)
* → client/electron/test/harmony-2in1.test.mjs + client/harmony/.../MainPage.ets
* 477479a37 fix(harmony): ★★ 三页 AppHeader 顶栏避让硬编码 0(真机实测)
* → client/electron/test/harmony-window.test.mjs + 鸿蒙三个 .ets
*
* ★ 为什么是**同一族**疏漏:它们都**改了鸿蒙源码 + 同步改另一侧的判据**,
* 而"鸿蒙侧的每次修复都要同步改另一侧的判据"正是本仓的跨端纪律
* (与 2026-09-24 那四条的成因逐字相同),只是 subject 只写了 `fix(harmony):`。
* ★ 为什么**不能** `git commit --amend` 补标:两条都已推送
* (实测 `merge-base --is-ancestor` 对 origin 与 origin-https 均为真)⇒ 改写会分叉远端。
* ★ 也**不能**往 `MARKERS` 里放行 `fix(harmony):` —— 那等于**永久**允许
* "说单端、实际改两端",判据从此失灵(与 2026-09-24 那次的结论一致)。
* ⇒ 唯一正确处置:**推进基线**(编辑本文件即推进),并把这两条具名记在这里。
*
* ── ★★ 2026-09-24 基线推进(本条注释的作用就是推进它)──
*
* 机制:基线 = **最后修改本文件的提交**(上面那行 `git log -1 -- …`)。

View File

@ -30,7 +30,18 @@ const MARKERS = ['未覆盖', '未验', '已知缺口'];
/** 登记值:文件 → 该文件里边界声明的**出现次数上限**(超一处即红) */
const REGISTERED = new Map([
['background.test.mjs', 3], // 两条反向断言的未覆盖路(route B / route C)+ 说明
['harmony-appearance.test.mjs', 4], // bgBlur 消费侧/映射、运行期形态类边界
/*
* `harmony-appearance.test.mjs` —— 2026-10-03 恢复成 4。
*
* ★ 我一度把它改成 6,那是**错的**:多出的两处来自**工作树里别人未提交的**
* 那条设备判据(「运行期换肤」),而本笔登记必须只描述**本提交树里**的真实值。
* 实测(`git show HEAD:…`):HEAD 该文件的词表命中数是 **4**。
* 把登记绑到未提交的工作上 ⇒ **任何人检出这个提交都会红**,
* 而那看起来像"别人的判据坏了",实际是我的数字错了。
* ⇒ 口诀:**登记数只能取自本提交树**(`git show <rev>:<file>`),
* 不能取自工作树 —— 工作树含别人未提交的改动。
*/
['harmony-appearance.test.mjs', 4],
['harmony-logic.test.mjs', 1], // `.ets` 状态机要跑起来才算数
['cross-client-theme.test.mjs', 1], // 设备条里那句「声明层全绿时,渲染那一层没人看过」的出处说明——
// 它正是本轮补上的那个缺口(渲染 vs 声明),留着当出处

View File

@ -129,9 +129,9 @@ const SUITE = [
// `setWindowLayoutFullScreen(true)`(消黑边)+ `getWindowAvoidArea`(让开时钟/手势条)。
// 只做前半 ⇒ 页签被时钟盖住(上一次就是这样退回去的,黑边于是留了三天);
// 只做后半 ⇒ 黑边照旧。**两半缺哪一半都要判红**,所以变异自检两个方向都跑过。
['test/harmony-window.test.mjs', ['--experimental-strip-types', '--no-warnings'], 9],
['test/harmony-window.test.mjs', ['--experimental-strip-types', '--no-warnings'], 10],
// 外观契约:默认值去 Go 源码里读(服务端 DefaultAppearance 是权威)+ 缓存键按账号
['test/appearance-defaults.test.mjs', [], 7],
['test/appearance-defaults.test.mjs', [], 8],
['test/build-stamp.test.mjs', [], 7],
['test/packaging.test.mjs', [], 5],
['test/align-refs.test.mjs', [], 3],
@ -168,7 +168,7 @@ const SUITE = [
* 2in1 键盘可达(用户 2026-09-21「快捷键打开发信页面 / 上下键切换发信目标 /
* 回车展开输入框」)。见该文件头部说明:为什么单开一个文件、为什么不端到端验。
*/
['test/harmony-2in1.test.mjs', [], 23],
['test/harmony-2in1.test.mjs', [], 24],
['test/harmony-contacts.test.mjs', [], 5],
// ★★ 下面三条是**补接线**,不是新写的判据(2026-09-17)。
//
@ -190,7 +190,28 @@ const SUITE = [
// 三维地址拼装:直接执行 `model/ReplyTarget.ts` 真逻辑(行为判据)
['test/harmony-reply-target.test.mjs', ['--experimental-strip-types', '--no-warnings'], 7],
// 宽屏侧栏图标轨:`WideSidebar` 接线与常量(静态判据,无设备)
['test/harmony-widescreen.test.mjs', [], 7]
['test/harmony-widescreen.test.mjs', [], 7],
/*
* ★★ 2026-10-03 补接线。三条都是 `node:test` 写的(`from 'node:test'` ⇒
* `shapeOf` 推导出 `usesNodeTest`,`--test` 由 harness 自动加、**不要手写**),
* 登记数按惯例写 0:**node:test 的条数由 runner 的 `# pass N` 认**,
* `fileChecks !== expected` 那条守卫只作用于自报 RESULT 的判据(`expected > 0` 才判)。
*
* 为什么漏了会**停掉全部判据**:自检 2("每个 `*.test.mjs` 都要在清单里")
* 在跑任何判据**之前** `process.exit(1)` ⇒ 实测 `RESULT` 行数 = 0,
* 一条读数都没有。而 `npm test` 是 `run-all && vitest && typecheck`
* ⇒ run-all 退出码非 0 ⇒ **vitest 与 typecheck 也不再跑**。
* 这是 `a && b && c` 链的形状(注释里已记过它的代价),只不过这里
* 卡在第一环、且失败信息只有一行 stderr、看起来像"环境问题"。
*
* 三条的来历:`inbox-fallback-poll` / `sse-credentials` 来自 `aeb1f41`
* (SSE 跟账号凭证 + 断线重放 + 兜底轮询),`web-comment-only` 来自 `2f17f62`
* —— 两个提交都比本清单上一次改动(`62b7c94`,09-02… 实际 10-02 00:14)**晚**,
* 即"新加判据的人没接线",与守卫注释里记的 09-17 那次同形状。
*/
['test/inbox-fallback-poll.test.mjs', ['--experimental-strip-types', '--no-warnings'], 0],
['test/sse-credentials.test.mjs', ['--experimental-strip-types', '--no-warnings'], 0],
['test/web-comment-only.test.mjs', ['--experimental-strip-types', '--no-warnings'], 0]
];
// 自检 1:清单里的文件必须真的存在(写错名字 = 那条判据永远不跑)