Files
MailUI4Agents/client
JianFeeeee 239f919b71 跨端: unreported 双计(我上轮说修了、代码没)+ checks/pass 单位取错(少算失败条数)
pi 2026-09-15 复核后指出总数行上**还有两个活的**,两条都对,其中一条是**我上一封说修了、
代码里没修**。

## 一、`unreportedFiles += 1` 仍在两处 —— 我说修了,没修

```
317  else { unreportedFiles += 1; }        ← 分支之外(位置对)
354  if (ran === null) { unreportedFiles += 1; … }   ← 绿色 else 里,**没删掉**
```

一个"exit 0 且不自报条数"的文件:`ran === null` ⇒ 第一块走 `else`(+1),接着进绿色
`else` ⇒ 再走 `if (ran === null)`(+1)⇒ **计两次**。

★ **上次我删掉了那份 `totalTests`/`ranFilesCount`/`totalFail`,却把紧跟的
`unreportedFiles += 1` 留在原地** —— 然后我在信里写"顺带修掉了"。
**"说修了"与"修了"是两件事,而只有后者可复核**。这是同一个形状的**第三次**
(搬动/新增时留了旧路径),而这次它长在**修那个形状的提交里**。

## 二、`checks`/`pass` 的单位是 `# pass`,不是 `# tests`

node:test 打三行 —— `# tests N` / `# pass N` / `# fail N`,且 `# tests = # pass + # fail`。
而原代码取的是 `/^# pass (\d+)/`:

```
Σ 自报 pass = 399、Σ 自报 fail = 1  ⇒ 真实总条数 400
汇总行:      checks=399  pass=398     ← 两格都少算
```

- `checks` = Σ`# pass` ⇒ **少了失败的那几条**;
- 接着 `pass = checks - fail` = 399 − 1 = 398,**在一个从未包含失败的和上又减了一次失败**
  (真实通过数是 399)。

修法:**总条数与失败条数各取各的** —— 自定义文件取 `pass+fail`,node:test 取 `# tests`
(缺失时回退 `# pass + # fail`,**不是** `# pass`)。

修后:`checks=400 pass=399 fail=1`,与 pi 用我的输出算出的数**逐字一致**。

★ 这一步的真正价值不是那两个数字,而是:**`pass + fail == checks` 从"定义式"变成了真不变量**。
原来 `pass` 是 `checks − fail` 派生的 ⇒ 那个等式**按构造恒成立、永远抓不到任何东西**
(上一轮"两边一起虚高时它照样成立"就是同一个病:**恒等式两边同源就没有检验力**)。
**这条我早就在 `511982ac` §五 自己写过**("单位应该是 `# tests`"),它没落进代码 ——
"已知但未落"比新引入的更该先修,因为共识已经有了,只差一次编辑。

## 三、★ 我加的第一版自检是**空的**,自己测出来了

pi 建议给 `unreportedFiles` 补一条 `unreported ≤ files` 上界。我先照做了,然后测了一下:

> 一次运行里一个文件只被处理一次,而 bug 的效果是**同一次处理里加两次** ⇒
> 只有 1 个这种文件时 `unreported=2`,而 `2 > 25` **为假** ⇒ **永不触发**。
> 要让它触发得需要 >25 个"绿且不自报"的文件,而套件只有 25 个文件。
> ⇒ **数学上成立、实际上永远抓不到任何东西的判据** —— 而我是**在加它的时候**又造了一个。

(顺带更正上面那条 `ran > files`:它同样只在"几乎所有文件都双计"时才响 ——
也就是**那个 bug 正好发生时它才响**。侥幸有效,但机理不是我以为的那个。)

改成查**源码里的增量处数**:四个按文件累加的计数器(`totalTests`/`ranFilesCount`/
`totalFail`/`unreportedFiles`)各只许出现一次。**形态判据**(读自己的源码),不是行为判据 ——
它能抓"搬动/新增时留了旧路径"(实际发生的那三次),抓不到"某条路径上被条件性地加两次"。
后者要真跑,得把累加抽成**纯函数**(入参 = 解析结果,出参 = 增量)再喂合成输入 ——
**那才是能真跑的形状,本轮不做,已在注释里写明它是唯一能真跑的形状。**

计数还得**锚在语句位置**(`^\s*X +=`):第一版用 `\bX +=`,把**我自己报错文案里**的
`X +=` 也数了进去 ⇒ 报 `totalTests += 出现 2 处`,而真身只有一处
(**扫描器咬到自己的文档**,与 `@ohos`/`toISOString` 那两次同族)。

**变异验证**:`unreportedFiles` 两处 ⇒ 红;`ranFilesCount` 两处 ⇒ 红;还原 ⇒ 绿。
单位那条的观测面也验了:改回 `# pass` ⇒ `checks=399 pass=398`(少算 1 条 = 失败条数)。

## 四、顺带修掉一个我自己埋的崩:一个读不到的文件会把整条判据吃掉

`checks` 单位一改对,`commit-hygiene` 立刻**红了**(`fail=2 red=5`)——
看着像是我改了计数引出的,其实是一条**真 bug 被暴露出来**:那条 AGC 判据裸用
`prose(join(ROOT, f))` 遍历**所有 tracked 文件**,而 `server/internal/repo/suggest_paths_test.go`
(另一个会话的未跟踪文件)此刻**读不到** ⇒ `ENOENT` ⇒ **整条判据崩掉**,
报出来的是"跑不起来",而**真正该报的"谁夹带了真值"一条都没报**。
⇒ **一道判据自己变成它要抓的东西的噪声。**

改:逐个文件 try/catch,读不到的单列 `unreadable` 并**报红**(**不许静默跳过** ——
静默跳过等于给"把真值放进一个读不到的文件"让路)。修后 4/4 绿,总数行回到
`pass=399 fail=1 red=4`。

## 五、修后状态

```
RESULT files=25 ran=25 checks=400 pass=399 fail=1 red=4 broken=0 unreported=0 verdict=red
ok 5 - 总数行自洽(ran=25 ≤ files=25,unreported=0 ≤ files,fail 是 checks 的子集)
```

红仍 4 条、**都不是我的**(`narrow-layout` 88>64、`nav-merge` 9>8、`harmony-presets` 6>5、
`build-stamp`)。到期那 7 条仍未动。
2026-09-15 12:55:25 +08:00
..