Files
MailUI4Agents/client/electron
JianFeeeee a023286158 修复: 守着锚的那张表**自己没人守** —— CAUSAL = [] 与基线逐字相同,层 7 的攻击被完整放回(层 8)
pi 2026-09-18 报的层 8。**成立,我复现,读数与它逐字相同。**

## 一、缺陷:我上一条那句"表本身也被真跑锚住了"**只对了一半**

被守的是"表里的锚文本在源码里唯一",而 **"表里该有几条"没有任何东西在问**。实测:

```
CAUSAL = [](整表清空)      ⇒ red=10,与基线**逐字相同**、因果 RED 0 条
只留 1 对(删掉另外 3 对)    ⇒ red=10,那三条自检**从此失去因果锚**、无任何提示
CAUSAL = [] + 假 ok×13        ⇒ red=10、因果/空壳 RED **0 条**
  ⇒ **层 7 的攻击被完整放回来了,而代价从"改函数体"变成"删掉表里一行"。**
```

★ 根因:**锚点不能落在"被守对象的自述"上**(层 7),
而这里是 **"守着锚的那张表自己的内容"没有锚** —— 上一轮是"判据自述",这一轮是"**锚表的自述**"。
★ 新轴:**守护者自己进入了被守集合** —— 前几层问的都是"**被守的东西**怎样",
层 8 问的是"**守它的东西**怎样"。

## 二、修法:覆盖面必须有一个**不在表里**的来源

用**两个**,都是已有的、不必新写表:
· ① `contractEntries` —— `CRITERIA.md` 契约行(**外部文件**,与层 5 **同一个锚点**;
  一个外部来源同时守两件事);
· ② `SELFTESTS` 的**键**(源码结构)—— 让"新加了一条自检却没配因果对"也能被抓。

⚠️ **空真的坑**:只写"每个 `CAUSAL` 项都对应一个真自检"(反向)**挡不住 `[]`** ——
`[]` 恰好满足那个空真("每个"在空集上恒真)。⇒ 必须**正向**要求覆盖。
★ 与本仓那句同族:**"判据存在" vs "判据在路径上"**;这里是 **"表非空" vs "表够长"**。

## 三、⚠️ 我实现时踩的坑:**两个命名空间别混**(被我自己刚写的判据当场抓住)

`wired` 装的是**函数名**(`probeSelfTest`),`CAUSAL`/契约行装的是**旗标名**(`probe-selftest`)。
我第一版拿 `wired` 比 `CAUSAL` ⇒ **5 条全报"幽灵名"、另 5 条全报"没有因果锚"**
(一次红 2 条、方向相反)。⇒ **判据没错,是我把两套名字当成了一套。**
修法是改用 `SELFTESTS` 的键(旗标名,与 `CAUSAL` 同命名空间)。
★ 教训:**同一个东西在两个地方有两套名字时,"名字相等"不是可传递的。**

同一次还抓出第二个我自己的 bug:第 5 对的锚文本在 JS 源码里只写了 `\d`,
运行时会**塌成 `d`** ⇒ 锚文本永远匹配不上(唯一性报 0,而不是 1)。
★ 这两处都是**新判据抓新判据**——判据互相盯着是有效的。

## 四、补第 5 对(`mutants-line-selftest`)

层 7 时表里只有 4 对,`mutantsLineSelfTest` 一直没有因果锚。
★ 我实测了三个候选锚:`未列入清单`(7 处)、`baseline-stale`(22 处)改坏后 **rc=0**
⇒ **它不读那两处**(那是别的函数在用);只有读 `RESULT` 行那个正则改坏后 rc=1。
⇒ 这也是"**关系表写错就会被真跑抓住**"的又一次实例。

## 五、变异验证(都已还原)

| 变异 | 结果 |
|---|---|
| **层 8(a)** `CAUSAL = []` | **红**:`这些自检**没有因果锚**:…(要求 5 条,因果表只有 0 对)` |
| **层 8(b)** 只留 1 对 | **红**(缺 4 条) |
| **层 8(c) 最尖**:`CAUSAL=[]` + 假 ok×13 | **红**(修复前 0 条) |
| 幽灵名(CAUSAL 放不存在的自检名) | **红 3 条**(幽灵名 + 覆盖不全 + 因果判据退出码 2) |
| **方向②** 新自检 + 登记 + 契约项但**不给因果对** | **红**(`要求 6 条,因果表只有 5 对`) |
| **`CAUSAL` 5 对逐条直验**(从源码解析表,防我手抄错) | 改坏后**每条** `--only-selftest` rc=1 |
| 回归 层6 掏空 / 层7 假ok×13 / 层5 删整条 / 契约行删掉 | 各 1 条 |
| 回归 M43 / M45 / M47 / M48 / 名字级未接线 | 各 1 条 |
| 干净树假红检查 | `^RED` **0 条**、因果红 0、覆盖红 0 |

## 六、⚠️ 残留(如实登记,不声称层 8 封死)

· **多处同步删仍可静默**:把 `CAUSAL` 对 + `SELFTESTS` 登记 + CLI guard + **`--only-selftest` 快入口那一项**
  + 函数体 + 契约项**全部**删掉 ⇒ 实测 `red=9`、`^RED ` **0 条**(与基线相同)、`自检段:4 个自检`。
  ⇒ 与层 5/6/7 同形:我把成本从一次编辑抬到**五六处**,**性质没变**。
· ★ 顺带如实记一笔:**我的层 7 修法自己新增了一个引用点**(`--only-selftest` 的名字表)——
  它让删除**更贵了一点**(要一起删),但它本身也是一个新的"必须同步删的地方"。
  这不是设计意图,是副作用,写在这里以免下一轮把它当成"防线"。

## 七、验证与状态

· 五个自检单独跑全 rc=0;默认跑 `checks=462 pass=458 fail=4 skip=0 red=9 broken=0 unreported=0 verdict=red`、
  `mutants=48 ran=47 skipped=1 on_new_criteria=35 diag=none baseline=7/7✓`;正常态 `^RED ` **0 条**。
· ⚠️ 口径变化**不是**本提交造成的,是**另一个会话**的提交 `f2cddf4`
  (`harmony-deviceprobe` 期望条数 8→**11**,加了 3 条包名漂移回归锁)⇒ `checks 459→462`;
  同一次也修掉了 `harmony-nav` 的**设备占用红**(那条红曾连红 42 轮)⇒ `fail 5→4`、`red 10→9`。
  ⇒ 我这次没有引入新的红,也没有消掉别人的红。
· 本提交含 `client/electron/test/run-all.mjs`(第 5 对 + 覆盖判据)与
  `client/electron/test/CRITERIA.md`(新增 §6.1.2)。
· 提交前 `HEAD=f2cddf4`。
2026-09-18 08:44:23 +08:00
..