# 2026-07-31（session M）· 探针测不出自己的盲区 —— F88 分段化 + F87 峰值下界冒烟

> 本篇只留**通用经验**。F88 的技术细节与数字在 [`specs/2026-07-31-infra-f88-visual-gate-coverage-design.md`](../../superpowers/specs/2026-07-31-infra-f88-visual-gate-coverage-design.md)（§5b 是实现期订正，动那条闸前必读）；F87 的判据与注入结果在 backlog entry。
> 交付：`f9a7e6bd`（F88 分段闸 + 392 张 baseline）· `b3bdc724`（F87 残余① peer 下界冒烟闸）· `1d235b3e`（SoT 订正）· `ca2db271` / `9b630972`（F88 spec）· `20ec62ce`（v1.2.0 收口）。

---

## 1. 稳定性探针会漏掉「换了路径就漂」这一整类脆弱性

前一轮（session K）为 F88 写过一个稳定性探针：同一页连拍两轮、比 sha1，用来判「分段截图稳不稳」。结论是「32 页里只有 chart 一段抖动，且 4 帧内收敛」——**这个结论本身没错，但它给了一个错的安全感**。

落地时撞到两件探针完全没预见的事：

1. 分段截图会滚动页面 → 第一版把两张 viewport 图放在各自主题的分段循环之前，于是 light 那张在页底拍成，**静默改写了 33 张 owner 已签的 baseline**。
2. 吸顶 `header.docs-topbar`（`sticky` / `z-index: 20`）会被**叠拍进 section**，且叠加位置随滚动落点变（同一段实测差约 11 000 像素）。

为什么探针看不见：**它两轮都走同一条代码路径、滚动落点完全相同**。它验证的是「同一套动作重复做，结果一致吗」，而真正的脆弱点是「动作顺序变了，结果还一致吗」。后者不在它的判据里。

**可复用的判断**：写「稳定性/幂等」探针时，除了重复同一路径，还要**故意换一条路径**做同一件事（换顺序、换起点、换进入方向）。两条路径结果一致才叫稳定；只测一条得到的是「可重复」，不是「稳定」。

---

## 2. 故障注入的第一步是断言故障态成立 —— 本轮两次注入是空的

为验「折叠线以下的改动会被抓到」，我注入过两次都**没有真正制造出故障**：

1. 把 `.api-table th` 的 `font-weight` 从 600 改 700 → 闸没红。不是闸瞎，是**该字族下两个字重渲染相同**。
2. 改 `docs.css` 里 `.api-table th` 的 `color` → computed style 仍是原值。dev server 确实吐出了我的改动（`grep` 到 `ff0000`），但 `ButtonPage.vue` 有一条**特异性更高的 scoped 同名规则**把它覆盖了。

两次都是「闸绿」，如果不去核故障态，就会得出「闸没用」或（更糟）「闸挺好，我验过了」。第三次改到真正生效的那处、先用 `getComputedStyle` 断言 `rgb(255, 0, 0)` 成立，闸才如期红。

**可复用的判断**：注入后先**独立断言故障态存在**（读 computed style / 打真请求 / 读回文件），再看闸。这条已在 memory `regression-pass-needs-fault-proof` 里，本轮是它在**样式层**的两个新形态：字重被字族吃掉 · scoped 规则覆盖全局规则。

---

## 3. 闸的产物 ≠ 审阅材料（owner 纠正）

我把 392 张新 baseline PNG 交给 owner 说「请签核」，被直接反问「为什么要生成那么多图片给我查看？我需要可点击的交互链接」。

这是对的：PNG 是**闸的比对基准**，不是给人看的东西。owner 要判的是「站点长得对不对」，那就该在**真站**上判——真站可交互、可切主题、可用右侧 CONTENTS 跳段，比看静态裁剪图更接近真相。改成「优先级表 + 页面链接 + 一句『第一屏你已签过，往下都是新纳入的』」之后，审阅一次就过了。

**可复用的判断**：需要人签核时，先问一句「**我给的是判断依据，还是我这条链路的中间产物？**」。产物是给机器比对的；人要的是能操作的真实现场 + 一份告诉他「该看哪、为什么是这些」的清单。顺带一条量化：把「要看什么」按客观量排序（本轮用「新纳入闸的像素面积」）比按字母序/文件序有用得多——前 6 页就覆盖了 39% 的新增面积。

---

## 4. entry 里的「候选解」是上一轮的推断，动手前先量

F88 entry 原文写「候选解 ① 按 demo 卡分段…①是最贴近视觉回归本意的」，还写了个前置「但要先解决卡的稳定标识问题」。实测两条都不成立：

- `.docs-demo-card` 只覆盖 **15.5%** 像素面却要 **530** 张 baseline，而 `.docs-section` 是 **93.4%** / 374 张 —— 两个轴同时更差。原因：demo 卡只由 `DemoRenderer` 发射，**33 页里 20 页一个都没有**。这是「按使用中枚举而非按结构枚举」（meta-rules 反模式 #6）在选型阶段的形态。
- 「卡的稳定标识」根本不是障碍：187 个 section 逐页核，页内标题两两不重复、无一无标题。

还有一条更值得记的：entry 说「若把死掉的 `viewport 1280×900` 改成真值，66 张 baseline 全部作废需重签」——这句让「顺手的小事」看起来很贵。实测发现**根本不必改成真值**：分段是元素级截图、与 viewport 高度无关，所以正解是**删掉那行死配置**，成本为零。

**可复用的判断**：entry 里的候选解、前置条件、成本估计都是**上一轮的转述与预测**（memory `entry-restatement-is-secondhand`）。接手时先花半小时把候选解各自的**客观量**测出来（覆盖率、产物数、耗时），再排序；很可能会推翻上一轮的首选，也可能发现被标贵的那步其实免费。

---

## 5. 工具面：`git commit -- <path>` 对 untracked 文件不生效（本轮踩两次）

本仓库的硬纪律是 `git commit -F <msg> -- <显式路径>`（防并行 session 串味）。但**该形式只提交已跟踪文件的工作树内容**：新文件必须先 `git add`。本轮两次踩到——

1. 提 F88 spec（新文件）时报 `pathspec … did not match any files`，**大声失败**，立刻发现。
2. 提 392 张 baseline（全是新文件）时，`git add` 漏了，commit **静默只含 3 个代码文件**、显示 "3 files changed" 就过去了。因为还没 push，`git add` 后 `--amend` 修回（并核到 395 files / 392 PNG）。

第 2 种是危险形态：**它不报错**。若当时直接 push，落地的会是「代码已改、baseline 没进」的组合，CI 会因「每页至少一张分段 baseline」判据红——虽然会被发现，但要多绕一轮。

另附一条 zsh 差异：`git add -- $VAR` 里未加引号的变量**不会被词分割**（与 bash 不同），整串被当成一个路径。写批量路径要显式列出或用数组。

**可复用的判断**：commit 后核 `git show --name-only | wc -l` 与预期文件数，别只看 "N files changed" 那行的语气。

---

## 6. 本轮做对的（同等重要，别只囤教训）

- **先量后拍**：owner 给的是「量清楚覆盖面能提到多少 + 重签规模，出方案再动手」。照此执行的结果是推翻了 entry 的首选解、并把重签规模从「66 + 394 全签」压到「只签新增」——因为量的过程中才发现「分段可以是增量而非替换」。
- **把「不动」也当成一个方案去量**：死掉的 viewport 行，最后的正解是删除而非修正。
- **收敛判据照抄本仓已有先例**：分母 fail closed（照 `audit-demo-css-page-scope.mjs` 的 S3）· 豁免表 shrink-only（照 S2）· 闸自印覆盖面（照 `audit:framework-api-floor`）。没有一条是新发明的机制，所以不用重新论证它对不对。
- **subagent 的完成声明先亲验再用**：F87 那条冒烟路是外包实测的，回报说「可行 + 带故障对照」。我自己复跑时，第一次阴性对照的 exit 1 其实是**「文件不存在」的假失败**（脚本没拷到阴性目录），补上脚本重跑才拿到真故障（`useId` 链接期抛）。若照抄它的结论，等于拿一个假的阴性对照当证据。
