# 处方 — 产品 frame 的背景**不得留 Figma 默认纯白**：一个默认值同时冒充两种设计意图

- 日期：2026-09-02 · 出处：[`reports/2026-09-02-light-side-precision-diagnosis.md`](../reports/2026-09-02-light-side-precision-diagnosis.md)
- 量具：[`probes/detect-frame-theme/diagnose-light-side.mjs`](../probes/detect-frame-theme/diagnose-light-side.mjs)（4 MH + 4 MN 内建控制，exit 0）
  · 真值来自 [`render-ground-truth.mjs`](../probes/detect-frame-theme/render-ground-truth.mjs)（11 臂故障注入全 PASS）
- 面 pin：**6 个产品 Figma 文件、2253 个 frame**，逐文件 `version` 见读数 JSON
  （Micro-Apps `2386732632827891307` · Config-T `2392721135254754987` · LCD `2394096213066862534`
  · PP-2025-2026 `2391117033705822315` · MH-2026 `2394456942532330031` · Media Service UR `2385995199832356872`）
- DS pin：`0c75580b`（⚠️ DS 侧执行时按当时 HEAD 重核行号）
- 执行方：**待定** —— lab 按 `AGENTS §1.1` 可直接写 DS master，但本处方含一条**面向设计师的流程规则**
  与一笔**存量整改排期**，⇒ 🔴 **规则文本与整改范围应由 owner 拍，lab ⛔ 不代拍**
- **执行状态**：🟢 **§2 规则 + §3 闸已执行**（lab 2026-09-02 直接落 DS master `2026ba1f`，按 `AGENTS §1.1`）
- **执行状态重取**：@`d07be8e9`（2026-09-11 §30 全量重取，量具 `metrics/proposal-execution-status-refresh.mjs`）—— 落地物现取仍在（规则在 `docs/internal/mockup-conventions.md`；闸 `scripts/audit-mockup-default-white-bg.mjs` + `scripts/mockup-rules/default-white-bg.mjs`）。⬜ §4 存量整改**仍未做** ⇒ **结论不变**。
  · ⬜ **§4 存量整改未做**（要设计师排期，⛔ lab 不代拍）

---

## 0. 🔴 一句话 + 承诺指标（⛔ 写在改动之前，`AGENTS §1.1`）

**`#FFFFFF` 是 Figma 新建 frame 的默认填充，所以「白色背景」这个信号同时代表
「设计师选了白底」和「设计师从没碰过这个 frame」—— 两者在 API 里长得一模一样。**
实测：**2253 个产品 frame 里 380 个（16.87%）的 `fills[0]` 恰好是纯白**，
其中 **175 个整屏其实是深色**（背景画在子图层上）。

### 0.1 🔴 先声明选面口径（`AGENTS §2.4`：凡引用一个分母，必须带口径名）

**【W 选面口径】** = 6 个产品文件里、**每个 page 或 Section 的每一个直接子 `FRAME`**
（Figma REST `?depth=4`，⛔ 不递归更深；`SECTION` 嵌套 `SECTION` 里的 frame ⛔ 不在面内）。
在这个口径上，面 = **2275**，渲染量得到 **2253**。

⚠️ 🔴 **DS 侧挂闸时若用别的选面口径，`376` 这个基线复现不出来** ——
届时**必须在 DS 自己的口径上重新冻结基线**，并把口径名写进闸的头注释。
⛔ 不许拿本处方的 376 去对一个不同口径的读数（`AGENTS §2.4` 实证：同一批脚本按两种口径是 66 与 82）。
lab 侧的选面实现见 [`scan-real-surface.mjs`](../probes/detect-frame-theme/scan-real-surface.mjs) 的 `collectContainers()`。

### 0.2 承诺指标（分母固定、逐节点、⛔ 能失败）

| # | 指标 | 定义（分母 = 固定的 **2253 个 frame【W 选面口径】**，⛔ 不随本改动变化）| 基线 | 承诺 |
|:-:|---|---|--:|---|
| **P1** | 违反数 | `fills[0]` 恰为纯白 **且** 未绑变量的产品 frame 数 | **376** | 🔴 **shrink-only：⛔ 只准降、不准升** |
| **P2** | 方向错判数 | `#{ detectFrameTheme 判 light ∧ 渲染像素真值为 dark }` | **175** | ~~**当 P1 ≤ 40 时，P2 ≤ 20**~~ 🔴 **2026-09-03 作废 ⇒ 见 §0.3** |

⚠️ **P1 = 376，⛔ 不是 380**：纯白 `fills[0]` 共 **380** 个，其中 **4 个已绑变量 ⇒ 不算违反**（且那 4 个 100% 判对）。
🔴 **P2 的 175 条方向错判，全部落在 P1 那 376 个里** —— 绑变量的白 0 条、非纯白浅色 0 条。
⇒ **P1 与 P2 不是两个独立的量：P1 是 P2 的完整来源面。** 这正是本处方敢承诺 P2 的依据。

#### 判否规则（🔴 这两条都能失败）
- P1 在任何一次闸运行中**高于上一次冻结值** ⇒ 闸红，处方判「反向」。
- ~~P2 在 P1 已降到 ≤ 40 之后**仍 > 20** ⇒ 🔴 **本处方的因果模型是错的**，
  按 `spec:§13.1` 逐字「若处方大多被采纳但指标没动 → 错的是 lab 的诊断模型，该改 lab 而不是改 DS」。~~
  🔴 **2026-09-03 作废，⛔ 留痕不删 —— 前件在 owner 的排期裁定下不可达，见 §0.3。**

⚠️ **为什么 P2 的分母是「固定的 2253」而不是「light 桶大小」**：
D6.1 刚踩过这个坑 —— 当时把 must-not-regress 写成条件比例 `P(真值=dark | 判=dark)`，
**分母由被测对象自己决定**，改动一搬桶、分子没多一个错、比例照样掉
（见 [`reports/2026-09-02-detect-frame-theme-D61.md`](../reports/2026-09-02-detect-frame-theme-D61.md) §2）。
⇒ **承诺指标里凡出现比例，先问「这个分母，被改动动得了吗？」动得了就不许当承诺。**

### 0.3 🔴 P2 承诺的订正（2026-09-03，`lab:N94`；⛔ 留痕不删上文 —— 改的是 lab 自己写的一句指标）

⚠️ **⛔ 这不是复议 owner 的「只做 175 条」排期裁定**（那条不复议，见 §4 排期裁定）。
改的是**处方自己写的那条 P2 承诺** —— 它在那条裁定下**结构上打不响了**。
与 `AGENTS §1.1` 订正段同形（那次也是 lab 订正 lab 自己写的一句推理，⛔ 不是复议 owner）。

#### ① 旧承诺的前件不可达 ⇒ 恒真空判（`AGENTS §2.16` 的第二头）

`175 + 105 + 96 = 376`，而 owner 裁定「其余 105 / 96 / 4 不动」⇒ **P1 只会降 175**：

| 口径 | P1 现值 | 175 条全改完后 | 触发线 |
|---|--:|--:|--:|
| 【DS 闸口径】2435 扫描（现行 shrink-only 基线）| **446** | ≥ **271** | ≤ 40 |
| 【W 选面口径】分母 2253（本表 P1 的定义口径）| **376** | **201** | ≤ 40 |

⇒ **两个口径都差一个数量级** ⇒ 「P1 ≤ 40」这个前件**永假** ⇒ 那条判否规则**永远不会被执行**。
⚠️ 它长得像一条严格的承诺，实际是一条**不能失败的控制** —— 与 `M-GATE-FALSIFIABILITY`、
与 D6.3 阻碍①（[`docs/2026-09-03-dsr1a-preregistration.md`](../docs/2026-09-03-dsr1a-preregistration.md) §0.2）**同一个病**。

#### ② 光把前件换成「175 条改完」还不够 —— 那是构造保证

owner 批的 **175 条正好就是 P2 的全体成员**（上文 §0.2 逐字「P2 的 175 条方向错判，全部落在 P1 那 376 个里」）。
⇒ 把 P2 的成员逐个改掉、再去量 P2，**降是算术，⛔ 不是读数**。
⇒ 按 D6.3 阻碍① 立下的同一条药方：**必须双侧** —— 只报「有害错判少了」会给「改成中段灰」留出路。

#### ③ 🔴 新承诺（⛔ 取数前写死，⛔ 取数后不得调）

**触发条件**（⛔ 不再是「P1 ≤ 40」）：以重测时点为准，本批 175 条中 `fills[0]` 已不再是
「精确纯白 ∧ 未绑变量」的那个子集记作 **M**（= 已改完的）。承诺在 **M** 上判读。

| # | 量 | 定义（分母 = **M**）| 基线 | 承诺 |
|:-:|---|---|--:|---|
| **P2a** | 仍判反 | `#{ m ∈ M : detectFrameTheme 判 light }` | 175 | 并入 P2a+P2b |
| **P2b** | 判不出 | `#{ m ∈ M : detectFrameTheme 判 unknown }` | 0 | 并入 P2a+P2b |
| 🔴 **P2a+P2b** | **改完仍未判对** | `#{ m ∈ M : detectFrameTheme 判定 ≠ dark }` | **175** | 🔴 **≤ 20** |

⚠️ **上表「基线」列是反事实值** —— 基线时 `M = ∅`，那一列读作「若 175 条全在 `M` 里且一条没改，各量是多少」。
⛔ 别把它读成一个已取到的读数。

⚠️ **必须并排报 `|M| / 175`**；**`|M| = 0` ⇒ 记「未开工」，⛔ 不记「通过」** ——
否则「永远没改完」会变成一条新的恒真空判，那正是本节要治的病，⛔ 别原地复发一次。

**为什么承诺落在和上，⛔ 不只落在 P2a 上**：这 175 条的像素真值**全是 dark**（实测中位数 0.086–0.246），
所以 `dark` 是唯一正确答案，`unknown` 与 `light` **同样算没修好**。
只承诺 P2a ⇒ 设计师把 fill 改成中段灰即可让 P2a 归零而屏幕照样没修好。

🔴 **新形态蕴含旧承诺，⛔ 不是把标准调松**：旧承诺是「全面 P2 ≤ 20」，
而全面 P2 改完后 = P2a + （本批之外新出现的）；下条 ④ 实测后者检出力≈0
⇒ `P2a+P2b ≤ 20` **蕴含** 旧的「全面 P2 ≤ 20」，且额外堵掉了 unknown 那条出路。

#### ④ ⚠️ 边界读数（⛔ 不作承诺 —— 实测检出力太弱，登记而不高估）

**P2c** =「本批 175 条**之外**新出现的方向错判」（面 = 2253 − |M|）。
它本该用来检验 §0.2 那句因果模型「**P1 是 P2 的完整来源面**」，但实测它几乎打不响：

| `opaqueFrac` 分档 | 条数 | 改 fill 会不会动渲染像素 |
|---|--:|---|
| ≥ 0.999 | **167**（95.4%）| **不动** —— 背景子层本就铺满 ⇒ 真值不会变 |
| 0.9 – 1.0 | **8**（4.6%）| 动一点 —— 但像素中位数 0.086–0.246，离 dark 阈值很远 |

⇒ **95.4% 的条目改完不会让任何像素真值移动** ⇒ P2c ≈ 0 同样是构造保证。
⇒ 🔴 **登记为边界读数：照报，⛔ 不作承诺，⛔ 不得拿它 ≈ 0 去宣称因果模型被验证。**

#### ⑤ 🔴 由此登记一条**能力丧失**（⛔ 不掩盖 —— 这是本次订正最贵的一句）

**owner 的「只做 175 条」裁定，使「P1 是 P2 的完整来源面」这条因果模型在本轮之后不可再检验。**
它只在**基线那一次**被测过（P2 ⊆ P1；绑变量的白 0 条、非纯白浅色 0 条）。
⇒ ⛔ **此后不得再引用 P2 的改善去宣称该因果模型成立** —— 改完 175 条后 P2 降下来，
测的是**处方开出的动作有没有兑现效果**（`spec:§13.1` 因子 2 的本义），
⛔ **不是**「诊断模型被验证」。两件事，⛔ 别合并。

⚠️ 这不使因子 2 不可判读：**P1 shrink-only 仍是活的、能失败的**（2026-09-03 实测 6 文件全绿、446 = 446），
新的 `P2a+P2b ≤ 20` 也能失败。丧失的**只是**那条模型证伪条款。

---

## 1. 实测证据

### 1.1 「深色准、浅色不准」这个说法本身要先纠正

| 态 | 桶 | precision | **recall** | base rate | **lift** |
|---|--:|--:|--:|--:|--:|
| `light` | 409 | 33.50% | **99.28%** | 6.13% | **5.47×** |
| `dark` | 1728 | **99.02%** | 86.99% | 87.31% | **1.13×** |

**一个恒回 `dark` 的傻瓜预测器 precision 就有 87.31%** ⇒ `dark` 的 99% 绝大部分是类先验白送的。
⇒ 病不在「判不出浅色」，在**浅色侧过度开火**：判了 409 个，真的只有 138 个。

### 1.2 误差集中在一个精确值上

| `fills[0]` | 个数 | 真 light | 真 dark | precision |
|---|--:|--:|--:|--:|
| **`#ffffff`（精确纯白）** | **380** | 109 | **175** | **28.7%** |
| 其余全部浅色值（`#f7fff5` 17 · `#e1eefd` 5 · `#f6f6f6` 3 · `#f8f8f8` 2 · `#fbf4ea` · `#d9d9d9`）| **29** | 28 | **0** | **96.6%** |

再切一刀 —— 同为纯白：

| | 个数 | precision |
|---|--:|--:|
| fill **绑了变量**（明确选了白）| **4** | **100.0%** |
| fill **未绑变量** | **376** | **27.9%** |

**对照组（深色侧）**：`fills[0]` 摊在 **25 个不同色值**上，前 8 个（每个 150+ 个样本）precision 全在 **98.6–100%**；
整个 1728 个 dark 里**方向错判只有 1 个**。precision < 90% 的两个色值（`#111111` 8 个 / `#292929` 5 个）
**全部是 `dark → 像素 unknown`**，是量具中段模糊，⛔ 不是判反。

⇒ **深色摊在 25 个「设计师挑的值」上；浅色 92.9% 压在同一个「默认值」上。**

### 1.3 逐条目视核过的两个样本（⛔ 不只有统计）

| 节点 | frame 自身 `fills` | 真正铺底的是什么 | 渲染 median |
|---|---|---|--:|
| `0054ib…/1834:300` `About/Status 1` | 单个纯白 SOLID | 子 `RECTANGLE` 铺满 `#252525`（**已看截图，整屏深色**）| 0.192 |
| `0054ib…/9359:3` `BEFORE - Online (baseline)` | 单个纯白 SOLID | 子 VECTOR，**名字逐字叫 `page_bg`** | 0.086 |

---

## 1.4 🔴 DS 早已在**同一个病**上立过规则 —— 但管的是另一层（先例，⛔ 不是已覆盖）

`mockup-conventions.md` §M23.18.1 逐字：

> **禁写死单一值、禁留 Figma 默认白画布**：白画布是异类
> （实证 2026-07-22 V4-1827 起初白画布 `#FFF`，全库 68 page 仅 2 个白）；起手必探测适配。

⇒ **DS 自己已经认定「Figma 默认白 = 异类、必须显式设」** —— 本处方是把**同一条原则往下推一层**：

| | 对象 | 已有规则 | 已有闸 |
|---|---|---|:-:|
| page 层 | `page.backgrounds[0].color` | ✅ §M23.18.1 | ✅ `audit-mockup-colors.mjs:45` `covers-acceptance: …#M23.18.1 [7]` |
| **frame 层** | **产品 frame 自身 `fills[0]`** | ⛔ **无** | ⛔ **无** |

🔴 **⛔ 别把这读成「已覆盖」**：两者管的是不同对象、不同 API 字段。
但它意味着本处方 **⛔ 不需要新立一条原则**，只需要把既有原则的适用面补全 ——
论证成本因此低很多，且 §M23.18.1 那条的**实证形态与本处方完全同构**
（「全库 68 page 仅 2 个白 ⇒ 白是异类」 vs 「29 个非纯白浅色 28 个判对、380 个纯白只有 28.7%」）。

---

## 2. 🔴 建议入册的规则文本（可直接贴，落 `docs/internal/mockup-conventions.md`）

建议挂在 **§M23.18 之下**（它是唯一消费画布主题探测的规则），编号建议 **`§M23.18.2`**
（⚠️ ⛔ 别用裸 `§M-BG`：`mockup-conventions.md:18` 自陈 H5 层标题 `M23.18.1` 这类正则抓不到，
新起 H5 会重复那个已知盲区 —— **⛔ lab 不代拍层级，DS 侧按自己的编号纪律定**）。

> #### M23.18.2 — 产品 frame 的背景必须是**显式**的（2026-09-02 新增）
>
> **规则**：任何产品 frame（mockup 主体、`BEFORE ·`/`AFTER ·` 卡、Section 内主页面 frame）
> 的自身 `fills[0]` **⛔ 不得停留在 Figma 新建 frame 的默认纯白 `#FFFFFF` 且未绑变量**。三选一：
>
> 1. **深色页面** ⇒ 把 frame 自身的 fill 设成**这一屏真正的深色背景**，⛔ 不要把背景只画在子图层上；
> 2. **确实是白底的页面** ⇒ 🔴 **首选把 fill 绑到白色变量**（`UX/Grey/grey-1 #FFFFFF`）——
>    **零像素改动**，只是把「没动过」变成「明确选了白」；
> 3. 或设成一个**非纯白**的显式浅色值（`#F8F8F8` / `#F5F5F5` 等）
>    —— ⚠️ 这条**会改变像素**，⛔ 别拿它当白底页面的默认解法。
>
> **Why**：与 §M23.18.1「禁留 Figma 默认白画布」**同一条原则，只是往下推了一层**（page → frame）。
> `#FFFFFF` 是新建 frame 的默认值，所以它**同时**代表「选了白底」与「从没动过」。
> 二者在 Figma API 里完全同形 ⇒ §M23.18 的 `detectFrameTheme` 无法区分。
> 实测（2026-09-02，6 个产品文件 2253 个 frame）：留默认白的 380 个里 **175 个整屏其实是深色**，
> 而任何**非**默认白的浅色值 **29 个里 28 个判对**。
> ⇒ 这不是探测算法的问题，是**背景层没有表达意图**。
>
> **Acceptance**：新建 / 迭代 mockup page 后，M48 self-check 能答
> 「本 page 的产品 frame 自身 fill = ⟨具体色值或变量名⟩，⛔ 不是未绑变量的默认 `#FFFFFF`」。
> 闸：`audit-mockup-default-white-bg`（见 §3），**shrink-only**。

⚠️ **`**Acceptance**` 段是刻意加的** —— `2026-08-28-variant-axis-rule-gap.md` §3.1.2 那条的教训：
没有 Acceptance 段的规则**不进对位闸的分母**，等于白写。

---

## 3. 上闸（判据形态写死，⛔ 取数后不得调）

**落点建议**：作为一个模块挂进既有的 `scripts/audit-mockup-conformance.mjs`
（它已是规则模块引擎，`geometry-consistency` 即以模块形式挂上），⛔ 不必新起一条链。

**判据**：对面内每个产品 frame ——

```
违反 ⟺ fills[0] 存在
     ∧ fills[0].type === 'SOLID'
     ∧ fills[0].color 的 r/g/b **精确**等于 1     ← ⛔ 不做近似，见下
     ∧ fills[0] 未绑变量（无 boundVariables.color，且节点无 boundVariables.fills）
```

- 🔴 **「精确等于 1」是刻意的**：本闸要抓的就是「**那一个默认值**」。
  `#FFFFFE` 是设计师动过手的痕迹 ⇒ 应当放行。近似匹配会把它一起抓进来，那就抓错了对象。
- **基线冻结 = 376**（在上面那 6 个 `version` 上；纯白共 380，减去已绑变量的 4 个）。**shrink-only**：与 DS 已有的
  `webcomponents-types` 那条 `EXEMPTIONS` 同构 —— 允许列白名单，但白名单**只准变短**。
- **must-hit / must-not-hit（⛔ 内建进脚本，`AGENTS §3.2`；全部已逐个亲验过判据结果）**：

  | 类 | 节点 | 为什么 |
  |---|---|---|
  | **MH1** | `0054ib0n… / 1834:300` `About/Status 1` | 单个未绑变量纯白 fill，整屏其实是 `#252525`。本处方的头号具名事实 |
  | **MH2** | `0054ib0n… / 4867:3469` `V4/TM1100` | `fills` = [纯白, `#252525`]，**`fills[0]` 仍是未绑变量的纯白 ⇒ 必须命中** |
  | **MH3** | `0054ib0n… / 9359:3` `BEFORE - Online (baseline)` | 子图层逐字叫 `page_bg` |
  | **MN1** | `DtZcMkhN… / 192:11484` `Pay Wall` | 纯白但**已绑变量**（真值 light）⇒ 必须放行 |
  | **MN2** | `rJJjWWs5… / 790:1622` | `#f6f6f6`（**非**纯白，真值 light）⇒ 必须放行 |
  | **MN3** | 任一 `fills[0]` 为深色的 frame（如 `#1f1f1f` 那 252 个中任一）| 必须放行 |

  ⚠️ **MH2 是自查时改过来的（留痕，⛔ 不删）**：初稿把它写成 **must-not-hit**，理由是
  「它的问题是层序、⛔ 不是默认白」。**那是错的** —— 按本处方自己的判据它就是命中，
  且它本来就在那 376 里。⇒ 🔴 **这正是 `AGENTS §3.2 推论一` 说的形态：
  是那条 must-not-hit 自己失败，把我的假设打回来的。**

### 3.1 ⚠️ 这条闸的一个**已知洞**（登记，⛔ 不掩盖）

**可以这样绕过它而不解决问题**：把一个**真实背景是深色**的 frame 的白 fill **绑到 `UX/Grey/grey-1 #FFFFFF`**
⇒ 闸变绿、`detectFrameTheme` 照样判反。

⇒ 建议**同时**挂一条二级判据（⛔ 但它 ⛔ 不在本处方的承诺指标里，属加固）：

```
可疑 ⟺ frame 自身 fill 的 luma  与  「铺满该 frame 的最顶层子图层」的 luma  相差 > 0.5
```

⚠️ 这条二级判据的 precision **本轮未测** —— ⛔ 不得在未测的情况下把它设为 fail。
建议先以 **warn** 形态挂一轮收数据。

---

#### 🔴 结档裁定（2026-09-03，owner 拍板；⛔ 留痕不改上文）

**⛔ 不挂这条二级判据 —— 连 warn 都不挂。** 上面那句「建议先以 warn 挂一轮」**已被读数推翻**。

依据 [`reports/2026-09-03-dsr1a-secondary-luma-gap.md`](../reports/2026-09-03-dsr1a-secondary-luma-gap.md)
（预注册 `8f34d2b` 先于实现代码）：

| 量 | 读数 |
|---|---|
| 它自己的 precision | **0.9974** [0.9856, 0.9995]（【H 面 · 渲染子面】389 个样本）|
| 与主判据 §3 的重叠 | 🔴 **1162 / 1164 = 99.83%** ⇒ 边际价值 **2 个节点** |
| 【加固口径】N | 🔴 **1**（它唯一的立项口径）⇒ 不可判读 |
| 上面那个绕过形态的实例数 | 🔴 **H 面 0 个** · **DS 侧 2/6 文件的 992 个产品 frame 里 4 个** |
| 那 4 个里它标出几个 | 🔴 **0 个**（连铺满子层都探不到）|

🔴 **⛔ 不挂的理由不是「precision 不够」** —— 它高达 0.9974 ——
**而是它要抓的东西主判据已经全抓了，它独有的那一格是空的。**
⚠️ 那 4 个「纯白 ∧ 已绑变量」里，`DtZcMkhN… / 192:11484 Pay Wall`
**正是本处方 §3 自己的 `MN1` 具名事实**（逐字「已绑变量、真值 light ⇒ 必须放行」）
⇒ 它们本来就该放行，**⛔ 不是绕过**。

⇒ 🔴 **上面那个「已知洞」的登记改为：洞的形态成立，但实测在可查的 ~5000 个产品 frame 上
⛔ 无一实例。** 它是一个**理论洞**，⛔ 不是一个待补的洞。
⚠️ 若将来真出现该形态（例如某次批量绑变量之后），**重开条件** = 「`纯白 ∧ 已绑变量` 的计数
显著大于 0」——量法见 [`probes/detect-frame-theme/count-bound-white.mjs`](../probes/detect-frame-theme/count-bound-white.mjs)。

---

## 4. 存量整改：范围与成本（🔴 lab ⛔ 不代拍排期）

| 类 | 个数 | 该怎么改 | 改完的预期 |
|---|--:|---|---|
| 留默认白、**整屏其实是深色** | **175** | 把 frame 自身 fill 设成真正的深色背景 | `detectFrameTheme` 由判反变判对 |
| 留默认白、**整屏确实是浅色** | **105** | 🔴 **首选「绑白色变量」** —— ⛔ 别改成 `#F8F8F8` | 判定不变（本来就对），但信号变显式 |
| 留默认白、像素落中段 | **96** | 逐个看 —— ⚠️ 这批要人判，⛔ 量具答不了 | 未知 |
| 已绑变量的白 | 4 | ⛔ 不用改 | — |

#### 🔴 排期裁定（2026-09-03，owner 拍板；⛔ 留痕不改上文）

**只做 175 条**（「整屏其实是深色」那一类），其余三类**不动**：
105 条真浅色（判定本来就对）· 96 条落中段（要人判）· 4 条已绑变量（本来就该放行）。

⇒ 逐条工单已出：[`docs/2026-09-03-dsr1b-175-worklist.md`](../docs/2026-09-03-dsr1b-175-worklist.md)
（由 `probes/detect-frame-theme/make-dsr1b-worklist.mjs` 生成，带 3 条内建控制）。
🔴 **工作量高度集中**：175 条只落在 **2 个**文件上 ——
`Touch Screen` **150 条（85.7%）** · `Config T` **25 条（14.3%）**
⇒ **只需打开 2 个文件，⛔ 不是 6 个。**

⚠️ **推演③ 登记的一处真副作用**：对那 105 个**真浅色** frame，白 fill **就是它们唯一的主题信号源**。
若改成 `#F8F8F8`/`#F5F5F5`，**像素会真的变化**（纯白 → 浅灰），那是视觉改动、⛔ 不是纯元数据整理。
⇒ 🔴 **对这一类，规则的三条出路里只有「绑变量」是零像素改动的** —— 处方 §2 的措辞已按此排序。

⇒ **要动的是 376 个 frame（175 + 105 + 96），其中 175 个是「必须改否则判反」的硬核。**
（另有 4 个纯白已绑变量 ⇒ ⛔ 不用改，合计纯白 380。）
⚠️ 按文件分布：方向错判 176 条集中在 **LCD 150 / Config-T 25 / PP 1** ⇒ **单从 LCD 一个文件下手能覆盖 85%**。

---

## 5. ⛔ 本处方不主张什么

1. ⛔ **不主张改 `detectFrameTheme` 的算法** —— 那是 lab 侧 D6.2 的事，且它的盲测面还没跑。
   本处方主张的是**上游把意图表达出来**，那样探测器根本不用猜。
2. ⛔ **不主张「Figma 的默认 frame 填充就是 `#FFFFFF`」已被实测** ——
   本处方证明的是「**这一个精确值不携带意图**」（三条旁证见诊断报告 §4 判否强度）。
   ⚠️ 若 DS 侧要把「Figma 默认值」写进规则的 Why 里，**建议先花一分钟实测**：
   新建一个 frame，读它的 `fills`。
3. ⛔ **不外推到 6 个文件之外** —— 面是 `ds-health-mockup-targets.json` 的清单，该清单自陈人工维护、漏登记即静默少测。
4. ~~⛔ **不主张 §3.1 那条二级判据可以直接 fail** —— 它的 precision 未测。~~
   🔴 **2026-09-03 已过期（`lab:N94`，⛔ 留痕不删）**：precision **已测 = 0.9974**
   （[`reports/2026-09-03-dsr1a-secondary-luma-gap.md`](../reports/2026-09-03-dsr1a-secondary-luma-gap.md)），
   且该判据已 **结档·不挂**（见 §3.1 结档裁定）—— 理由 ⛔ 不是 precision 不够，是**它独有的那格是空的**。
5. ~~⛔ **未改 DS 一个字节、⛔ 未跑 DS 任何闸**（lab 侧全程只读 pin `0c75580b` 的 worktree）。~~
   🔴 **2026-09-03 已过期（`lab:N94`，⛔ 留痕不删）**：该句只在**处方起草时点**成立。此后
   ① lab 已按 `AGENTS §1.1` 把 §2 规则 + §3 闸**直接落进 DS master**（`2026ba1f`，见 §6）；
   ② 2026-09-03 在 DS `6d41d0a6` 上**实跑过本闸**（MH-2026 单文件，违反 9 = 基线 9，真退出码 0）。

---

## 6. 执行状态

**DS commit `2026ba1f`**（2026-09-02，lab 直接落 master；起手 pin `0c75580b` → 提交时 DS HEAD `098a2d2f`）。

| 项 | 状态 | 亲验证据 |
|---|:-:|---|
| §2 规则入册（含 `**Acceptance**` 段）| 🟢 | `docs/internal/mockup-conventions.md` **§M23.18.2**；`parseAcceptanceSections` 实测 `M23.18.2@1042 items 1` |
| §3 闸挂上 | 🟢 | `scripts/audit-mockup-default-white-bg.mjs`（判据真源）+ `scripts/mockup-rules/default-white-bg.mjs`（引擎第 12 条规则）；引擎实跑 `--rule default-white-bg` exit 0、`namedFactsChecked: true` |
| §3 基线冻结 | 🟢 | `figma-data/audit-allowlist/default-white-bg-baseline.json` —— **446 违反 / 2435 扫描**（⚠️ 见下方口径订正）|
| §3 三条 MH/MN | 🟢 **扩成 3 MH + 2 MN** | 造故障 5 臂全 PASS（下表）|
| §3 造故障验证「它会拦」| 🟢 | 读数 [`runs/ds-gates/default-white-bg-fault-injection.json`](../runs/ds-gates/default-white-bg-fault-injection.json) |
| §3.1 二级判据以 warn 形态挂一轮 | 🔴 **结档·不挂** | 2026-09-03 owner 拍板，见 §3.1 结档裁定。precision 已测 = 0.9974，但与主判据重叠 99.83%、独有那格实测 0 实例 ⇒ **影子闸** |
| §4 存量整改（175 硬核 / 446 全量）| 🟡 **已排期·只做 175** | 2026-09-03 owner 拍板，见 §4 排期裁定。工单 [`docs/2026-09-03-dsr1b-175-worklist.md`](../docs/2026-09-03-dsr1b-175-worklist.md)，集中在 2 个文件（150 + 25）|
| **P1 shrink-only 未被反向** | 🟢 起点 | 6 个文件单跑全 exit 0，违反数 = 基线数 |
| ~~**P2 在 P1 ≤ 40 时 ≤ 20**~~ | 🔴 **作废** | 前件不可达（P1 只会降 175 ⇒ ≥ 271 / 201），见 §0.3 ① |
| **`P2a+P2b ≤ 20`（改完仍未判对）** | ⬜ **未开工** | 触发条件 = 本批已改完子集 `M`；2026-09-03 实测 `\|M\| = 0` —— 承载 175 条的 2 个文件 Figma `version` 与基线冻结值逐字相同 |

### 6.1 🔴 P1 基线的口径订正（`AGENTS §2.4`：同一个量在不同口径下是不同的数）

| 口径 | 选面 | 扫描 | 违反 |
|---|---|--:|--:|
| **【lab W 选面口径】** | REST `?depth=4`，只到 page/Section 的直接子 FRAME | 2253（已渲染核验）| **376** |
| **【DS 闸口径】**（现行基线）| 全深度 payload，`page 或 SECTION 的直接子 FRAME`（含深层嵌套 Section）| **2435** | **446** |

⇒ 🔴 **闸的 shrink-only 基线以 446 为准**（`--freeze` 首跑冻结）。
**⛔ 别拿 376 去对它** —— 这正是本处方 §0.1 提前警告过的那一条，实际发生了。
P2（方向错判 175）仍是【lab W 选面口径】的数，因为它要渲染像素真值。

### 6.2 造故障读数（DS `working-principles`：跑一遍绿不算验证）

| 臂 | 改坏了什么 | 扫描 | 违反 | added | stale | 控制 | 判 |
|---|---|--:|--:|--:|--:|---|:-:|
| `ctrl-pristine` | 不改 | 584 | 281 | 0 | 0 | — | 绿 ✅ |
| `w1-new-violation` | 给一个合规深色 frame 换上未绑变量纯白 | 584 | 282 | **1** | 0 | — | 红 ✅ |
| `w2-stale-baseline` | 把基线里一条「修好」 | 584 | 280 | 0 | **1** | — | 红 ✅ |
| `c1-named-fact-mh` | 把 MH 节点 `1834:300` 的白 fill 绑上变量 | 584 | 280 | 0 | 1 | **MH-missed** | 红 ✅ |
| `c2-face-collapse` | `SECTION`→`GROUP`，选面塌 | **242** | 131 | 0 | **150** | **MH-not-in-face** | 红 ✅ |

### 6.3 ⚠️ 施工中当场撞到并修掉的两个坑（留痕，⛔ 不删）

1. 🔴 **锚点偷窃**：初版把 `##### M23.18.2` 插在 §M23.18.1 的 bullet 之后 ⇒
   **把原本归属 `M23.18.1` 的 Acceptance 段（旧 1001 行）整段顶成了 `M23.18.2`**，
   `audit-acceptance-gate-coverage` S3 当场报红。
   ⚠️ **这正是该闸头注释逐字警告过的形态**（「在同一份真源更靠前处插入任意一个 Acceptance 段，
   就把其后每一条自陈静默顶到别的段上」）。改插到那一段**之后**（`---` 与 `#### M23.19` 之间）。
   ⇒ **是别人的闸把我的错打回来的** —— §3.8「别人的闸不等于你的安全网」的反向一例：这次它拦住了。
2. **Acceptance 形态**：初版写成散文 ⇒ 核查项计 **0**，而闸自陈 `[1]` ⇒ S4 报红。改成 1 条 `- [ ]`。

⇒ 元闸复核（改完）：`audit:gate-mount-declaration` ✅ · `audit:rule-load-map` ✅ ·
`audit:acceptance-gate-coverage` ✅（`gated` 28→**29**，`unclassified` 棘轮守在 **59**）。
