# 复盘 2026-08-28-f（第六十八轮）—— **纠正动作也需要判据：一句「问题不存在」比原来那个问题活得更久**

> ⚠️ **本文原定编号 `-e`，落地时撞车改 `-f`** —— 并行线同日先推了 `2026-08-28-e-a-gate-that-succeeds-invalidates-its-own-fixture.md`。
> 这是 memory `fetch-check-master-before-continuing-inflight-task` 的教科书形态：**命名空间的当前状态活源是
> `origin/master`，不是本地文件**。取号时本地已 `fetch` 过，但那次 fetch 早于对方的 push ⇒ **fetch 的有效期
> 比一轮工作短**。⛔ 下次取复盘编号：`git fetch && ls` 紧挨着做，别隔着一整段工作。

> **范围**：只记可复用 pattern。逐条实证（四条独立证据 / 97 处三档分布 / 变量名与两 mode 值）
> 在 [[INFRA-F107]] `#### 二次订正` 与 [STATUS-CHANGELOG](../STATUS-CHANGELOG.md) 第六十八轮，**本文不复述**。
> 直接前作：[2026-08-28-d](./2026-08-28-d-nobody-asked-if-the-problem-was-real.md)（本轮推翻的正是它的结论；
> 它的 pattern 反而因本轮成立得更强）。

---

## 一 · 新 pattern：**推翻一个结论之后，没有人问那个推翻本身是不是真的**

上一轮的成果是一句**否定**：「问题本身不存在，10 条 FAIL 全是假阳」。它当时具备的凭据比原 entry 还多：

| 上一轮有的 | 上一轮没有的 |
|---|---|
| 读了 Figma 活源（`get_variable_defs`）| **对那条否定本身跑一次判据** |
| 逐像素采样渲染图（背景占 89.1%）| |
| 读了 raw 的 `fills` 逐字 | |
| owner 亲自视觉否决作背书 | |
| 写进了 5 处文档 + 1 处闸豁免表 | |

实测：那句否定**只对 2/10 成立**。另外 8 条是真 code 债，被连带宣布成假阳。

🔑 **结构**：纠正动作天然带着一层道德优势 —— 它刚刚抓到了一个错，所以**没人再去查它**。
而它和被它纠正的那个结论一样，只是一个断言。**「我们刚发现前面错了」是最不容易被要求出示判据的一句话。**

⇒ **判别问句**：**「我这次推翻，它的判据是什么？如果我错了，会长什么样？」**
本轮的答案本该是「若某个节点其实有绑定，那两个 expected 就是合法的」—— 这个反面一问就能问出来，
而它恰好是真的。

⚠️ **别把本条读成「少纠正」** —— 上一轮那次纠正救回了一个错误修复。要的是**给纠正也配判据**，不是别纠正。

---

## 二 · 最可执行的一条：**「现取判据命令」写成散文，等于触发器 S 第二行不存在**

[[INFRA-F107]] 的现取判据字段逐字是：

> 「`node -e` 扫报告求「同节点同字段两主题 expected 不同 ∩ raw `boundVariables` 为空」的配对数（本轮实测 77 / 其中 FAIL 20）」

这不是命令，是**对一条命令的描述**。后果有两层，第二层才是贵的：

1. 没人跑得动它 ⇒ [触发器 S](../../meta-rules.md) 第二行结构上跑不动 ⇒ 那一行只能填「抄 entry 里的数」，
   而这正是该触发器 §违反检测 第二条逐字禁止的。
2. 🔴 **它引用的字段名在它指的文件里不存在** —— `boundVariables` 在 685 份 `figma-data/raw/components/*.json`
   里出现 **0 次**（那是 Figma REST/Plugin API 的字段名；`figma-sync/extract.mjs:98` 读它之后写进 raw 的
   字段叫 **`v`**）。⇒ 「100% 全部 `boundVariables=null`」**对任何集合都恒真**。
   **一个恒真的读数被当成了 100% 的证据。**

⇒ **把它写成真命令的那一刻就抓到了错**（`node scripts/f107-theme-expected-binding-triage.mjs`，
用真字段 `v` 重测：97 处分叉里 93 处**有**绑定）。这条 10 分钟的工作推翻了一个跑了一整轮的结论。

🔑 **要记的**：[盘点报告 §三 1](../_reports/2026-08-28-active-backlog-inventory.md) 量出「只有 7/32 条 entry
自带现取判据命令」，并建议「⛔ 别全量补写，下次碰到哪条补哪条」。**本轮是那条建议的第一次兑现，
当场就有回报。** ⇒ 碰到一条没有可跑判据的 entry，**先把判据写出来再动手**，那 10 分钟不是开销。

---

## 三 · 一个可复用的怀疑信号：**「100% 全部 X」而 X 是个字段名**

`100% 全部 boundVariables=null` 这种读数有个结构特征：**分子和分母同时由「读一个字段」决定**。
若那个字段不存在，读数恒为 100%，而它长得和真结论一模一样。

⇒ **看到「100% / 全部 / 无一例外 + 某个字段名」时，先问一句「这个字段在我读的那些文件里出现过吗」**
（一行 `grep -rc '<字段名>' <目录>` 就够）。同 memory `regression-pass-needs-fault-proof` 那条
「绿档必须钉一个只有真跑过才有的读数」—— 这里要钉的是**只有字段真存在才可能出现的读数**：
本轮的对照读数是「15468 个 paint 里 11040 个带 `v`（71.4%）」，**不是 100%，所以它有区分力**。

⚠️ 反过来也要防：真字段也可能恒空。所以钉的是**区分力**（有些命中、有些不命中），不是「字段存在」。

---

## 四 · 验证过的好方法：**活源读对了，结论仍可以错 —— 因为只读了「它没有什么」那一半**

上一轮 `get_variable_defs(1408:17150)` 的返回里**同时包含**两条信息：

- **没有** `UX/Brand/Match,Hover` ⇒ 背景没绑那个变量 ✅ 被读到了，结论正确
- **有** `UX/Brand/Brand` ⇒ 该节点**在用**一个随主题变的变量 ❌ **没被读**

上一轮找的是「那个 dark 值从哪来的」，所以只看了缺什么。**同一次工具调用里就躺着反证。**

🔑 **要记的**：读活源拿到一份清单时，**正反两面各问一遍** ——「它没有什么」（我原本要查的）
与「**它有什么是我没预期的**」。后者不需要新的工具调用，只需要多读一遍已经拿到的输出。
⇒ 这条比「再去读一次活源」便宜得多，且本轮实证它足够。

---

## 五 · 顺带：一个自己刚犯又自己抓到的同型错

本轮写那个 triage 脚本时，判「变量是否随主题变」用的是 `vals.Dark !== vals.Light`。
而语义层（`Color Type/*`）的 mode 值是 `{alias: VariableID:…}` **对象** ⇒ 两个不同对象 `!==` 恒真
⇒ 会把「两主题同值的 alias」也印成「随主题变」。**又是一个恒真判据**，与 §三 同型，相隔十分钟。

抓到它靠的不是复核，是**输出长得不对**（满屏 `Dark=[object Object] ⇒ 随主题变`）。
⇒ **让判据把它比较的两个值一起印出来**，恒真就会自己露出来；只印结论则不会。
（修法 = 递归解 alias 到 primitive 再比，脚本里已注明「⛔ 别直接比两个 mode 值」。）
