---
share_worthy: yes
audience: UX, Dev
one_liner: owner 一句「没看出区别」暴露了对照组选错——我给的材料回答的是「对齐了吗」，他问的是「改了什么」；同轮另外三次判断靠对照组站住了脚。
evidence: ff53e16d, 4bdee739, e1731e5e, docs/internal/backlog.md INFRA-F145 (c)
---

# 对照组决定了你回答的是哪个问题（2026-09-14 下半场）

> 本轮三个 commit：`ff53e16d`（render gate 导航竞态有限重试）·
> `4bdee739`（新闸 `audit:demo-disabled-style`）· `e1731e5e`（INFRA-F145 (c) 第二批）。
> 叙述在 [STATUS-CHANGELOG](../STATUS-CHANGELOG.md)，⛔ 本文不复述做了什么。

## 0. 一句话

**四个关键判断全部落在「拿什么当对照」上。选对的三次站住了脚，选错的那一次被 owner 一句话戳穿。**

---

## 一 · 选错的那次：材料回答了一个没人问的问题

我给 owner 的视觉签核材料是 **Vue 侧 vs React 侧并排**。理由当时听起来很充分：
这批改动的目标就是「React 侧对齐 Vue 侧」，Vue 侧一直是 DS 组件 ⇒ **两列看起来一样 = 对齐成功**。

owner 的回复是一句：

> 「前后没看出来有什么明显区别。」

**他问的是「改了什么」，我给的是「对齐了吗」。** 这两个问题需要**不同的对照组**：

| 问题 | 对照组 | 我给了吗 |
|---|---|---|
| 改了什么？ | **改动前 vs 改动后** | ❌ 没有 |
| 对齐了吗？ | Vue 基准 vs React 现状 | ✅ 给了 |

补上 before 之后，结论**必须分两组答**，而这正是原材料掩盖掉的东西：

- **Tabs / DateTime**：原生方框 checkbox ☐ → DS 滑动开关，**区别一眼可见**。
  owner 看不出来，纯粹因为没有 before 图。
- **Notification**：高度 35→32px + 圆角 + 内边距，**区别确实小**。

> 🔑 **可复用的自检点**：**交付视觉材料前，先把 owner 会问的那句话写出来，再挑对照组。**
> 「我想证明什么」和「他想知道什么」经常不是一件事 —— 我想证明「对齐了」，
> 他想知道「这次改动值不值」。后者需要 before，前者不需要。
> ⛔ 别拿「上一轮复盘说过 before 列不可靠」当免做的理由：
> [[2026-09-14-when-my-own-instrument-is-the-defect]] §一③ 说的是
> **「别拿一份更早的构建当对照」**，不是「不要 before 列」。我把那条教训用过头了。

---

## 二 · 选对的三次（同一个动作，三个场景）

三次都是同一个反射：**结论出来之前，先问「有没有一个已知答案的样本能验它」。**

### ✅ A. 「那 2px 是我引入的吗」→ 找一个与本次无关、但同型的页面

Tabs 两侧 label 差 2px（Vue `<button class="switch">` 40×20 · React `<tvu-switch>` 40×22）。
我本可以用机制推理说服自己「CE host 没有 `:host` 尺寸声明，所以不是我的锅」——**那只是推断**。

实际做的：拿 **Progress 页**量同一个量。那页两侧**早就都**走 `DemoRenderer` 的 DS Switch 分支、
与本次改动完全无关。读数：**40×20 vs 40×22，差值一模一样**。

⇒ 「既有的 CE host 差异（[[INFRA-F66]] 形态）」从我的说法变成了实证事实。

### ✅ B. 「棘轮从 40 掉到 39 是我干的吗」→ 把嫌疑对象移开再测一次

提交时 `audit:gate-regression-ratchet` 报「少了 1 条，BASELINE 必须跟着缩」。
最省事的做法是照它说的把 BASELINE 改成 39 —— **那会替并行 session 记他们的账，并在他们提交时撞车**。

先退回我改过的闸脚本重测：**仍是 39** ⇒ 不是我的改动。
再扫工作树，发现一个并行 session 刚建的 untracked 文件 `tests/audit-rule-load-map-cli.test.ts`
（它给 `audit-rule-load-map` 补了回归面 ⇒ 那条闸离开分子）。
把它临时移开重测：**`BASELINE 40 · 现取 40 · delta +0` PASS**。

⇒ 归因坐实，BASELINE 该由他们缩。

> 🔑 **附带一条操作纪律**：为提交而临时挪走别人的文件时，**归还前必须先检查原位是否已有新文件**
> —— 若有就绝不覆盖（他们可能在这几十秒里重建了它）。本轮写成了显式分支，
> 归还后 `shasum` 核对逐字节原样。

### ✅ C. 「这张 before 图真的是 before 吗」→ 给 before 档加一条只有 before 才成立的断言

取 before 要把 3 个 `.tsx` 退回 HEAD。风险是 **vite HMR 没跟上，截出来的「before」其实是 after** ——
而那种错**长得正好就像「没区别」**，是最难发现的一种。

所以 before 档额外断言：**目标元素里必须真的还有原生控件**
（`input[type="checkbox"]` / `button.try-button`），断言不过直接 throw、⛔ 不出图。

> 这和上一轮 §二 A「量具自带已知向量自测」是同一个形状：
> **让读数自己证明自己处在你以为的那个状态里。**

---

## 三 · 上一轮的做法，这一轮兑现了

### ✅ S0 已知向量自测当场抓到我自己写的缺陷

新闸 `audit:demo-disabled-style` 内置了一段 fixture（5 种禁用态书写形态 + 诱饵），
要求提取器精确命中预期集合，否则当场 FAIL。

**第一次跑就红了**：切出 9 条选择器、应为 10 条。根因是我的切分器只按花括号深度判，
于是 **`@media` 块内的规则整批漏掉** —— 而真仓库里
`playground/docs/docs.css:1444` 那条 `.framework-switch--disabled` **恰恰就在 `@media` 里**。

没有 S0，这条闸会带着一个静默盲区上线，而且**扫 78 个文件报 0 命中**，
长得和「真的干净」一模一样。

> 这同时是「判据只对真源的**一种书写形态**灵敏」的第三个实例
> （前两个：`--disabled` modifier vs `:disabled` 伪类 · `boundVariables` vs `v`）。

### ✅ 分诊结论允许推翻原提案的形态

(c) 原文是「demo 面禁止手写 `<button>`，可上 lint 规则」。两批分诊完的结论是
**换 12 处、留 34 处**，并因此**反对**那条 lint。

收窄后的判据（「手写控件不许自己定义禁用态样式」）落地当天豁免 **1 行** ——
与被否方案的 **22 行**是同一个量下的天壤之别。⇒ 这个数字本身就是「收窄对了」的凭据。

---

## 四 · 一句差点写进交付材料的错话

给 owner 解释 Notification 那组的价值时，我本要写：

> 「`.try-button` 当初就是照 DS Button 抄的副本，所以长得像正是问题所在。」

写之前去核了一眼 —— **是假的**。`.try-button` 是一套独立的通用 demo 控件样式
（`--line-light` 边框 / `--bg-layer2` 底 / 6px 圆角）。真正照抄 DS Button 的是
**第一批删掉的 `.form-demo-button`**（注释逐字 `"Matches Button.vue's own ghost/gray1 disable instead"`）。

两者都在 demo CSS 里、都和按钮有关、都与 09-04 那个 bug 沾边 —— **所以特别容易串**。

⇒ 订正后，Notification 那组只剩**一条**硬理由（双框架 parity），
我把这个减弱**如实写进了给 owner 的材料**，并明说「你可以单独否掉这一组」。

> 🔑 这条属于 [[feedback_review-result-vs-rationale]] 的自用版：
> **「结论对」不等于「理由真」。** 那 4 处该不该换的结论没变，
> 但如果我把那句假因果写进交付材料，下一个人会拿它当事实继续推。

---

## 五 · 留给下一轮的

1. **INFRA-F145 (b)** owner 拍**暂缓**（不是否掉）。重开条件已写进 entry：
   `audit:demo-disabled-style` 上闸后**仍然抓到同类 bug**，且形态是**真 DS 组件内部**
   （`src/`，本闸射程之外）取了别族 token。
2. **`audit:demo-disabled-style` 的 hover 那一半没做** —— `:hover` 共 23 处 / 5 文件，
   但「控件的 hover」与「卡片/链接的 hover」要分开数，那层结构判断没做。
   ⛔ 别直接把 `:hover` 加进现有正则。
3. **DemoRenderer 的 3 处通用控件**（`range`/`text`/`cycle`）—— 换它们要改 descriptor 契约、
   影响所有用它的组件页，**独立立项**，不在 F145 内。
4. **[[INFRA-F66]] 的 2px**（CE host 缺 `:host` 尺寸声明）—— 本轮量到并归了因，
   属组件层，**刻意没顺手改**（[[feedback_root-cause-scope-creep]]）。
