# D6.3 预注册（第 4 份）—— 副臂 `v3-strict` 在**盲面**上的双侧读数（`lab:D6.3`）

> 🔴 **本文件必须在写任何取数代码之前 commit。** 依据 `AGENTS §1.1` 订正段。
> ⛔ 取数后本文件一字不改（要改只能另起一份并留痕）。

- **立项依据**：owner 2026-09-03（第二轮）在带推荐的方案中**同意推荐**，选定 D6.3。
- **被验对象**：`probes/detect-frame-theme/detect-frame-theme-tiered.mjs` 导出的
  **`detectFrameThemeTieredStrict`（副臂 `v3-strict`）**。
- **它回答哪个目前未知的问题**（⛔ 立项理由不得是「确定有产出」）：
  **副臂在 H 面上量到的「有害错判 3 / 1472」，能不能外推到一个它没被看过的产品面。**
  ⚠️ 两个方向都可能：副臂靠「T3 退成 `unknown`」换来低危害，而**T3 档占比是面的性质**
  ⇒ 换个面就可能既不低也不稳，⛔ 不是构造保证（详见 §2.4）。
- **谁消费这个读数、他在乎哪个方向的错**（🔴 `D6.2 §3` 那条教训的兑现，⛔ 不得跳过这一问）：
  消费者 = **决定 D7 接哪个臂进 `variant-axis` 闸的那个人**。他同时在乎两侧：
  ① **有害错判**（判 `light` 而真 `dark` ⇒ 深色页套浅色皮肤，用户可见）；
  ② **`unknown` 不能多到闸失去判读力**（全判 `unknown` ⇒ 闸恒绿、白挂）。
  ⇒ **单侧承诺在此结构上必然是构造保证**（§0.1 ①），双侧才是他要的那个量。

---

## 0. 接手纪律（`AGENTS §20` 四行块 + `§22` 第五行，⛔ 不许跳过）

```
登记记的：  「D6.3 副臂接闸第 3 份预注册 ⏸ 未动，两条阻碍原样成立」
            （open-items §12.7 逐字；阻碍原文在 dsr1a 预注册 §0.2）
现取的：    ① 阻碍② 实测**被推翻** —— Figma 上 51 project / 234 文件 / 未登记 220
              （open-items §13.3，量具 enumerate-figma-face.mjs，MH1 过）
            ② 阻碍① 原样成立 —— 是设计题，本份 §0.1 当场解掉
前提还成不成立：**一半不成立**。阻碍②（面不够了）的前提已假；阻碍①（单侧=构造保证）仍真
它还该做吗：该做，但**要改一处读法**。DS `scripts/audit-mockup-variant-axis.mjs:30` 逐字
            「要覆盖它，前置是把 `detectFrameTheme` 落成真实现 —— 属独立立项」、
            `scripts/mockup-rules/variant-axis.mjs:8` 逐字「`detectFrameTheme` 只有文档伪代码、零实现」
            ⇒ 🔴 **D7 的前置 ⛔ 不只是 D6.3（选哪个臂），还有「判据落进 DS 成真实现」这一件独立立项。**
            ⇒ 本份 ⛔ 不承诺解决 D7，只解决「选哪个臂」这一半（§6 边界②）
lab 自己验过没：⛔ 没在盲面上验过。H 面上那组 3 vs 21 是**事后观察列**，
            D6.2 报告逐字「⛔ 不许直接拿本轮副臂的事后观察去接闸（那是对着测试集选臂）」
            ⇒ H 面对副臂而言**已是看过的测试集** ⇒ 必须换面，⛔ 不得在 H 面重跑
```

### 0.1 🔴 两条阻碍的处置（⛔ 这一节是本份能否成立的前提）

**① 阻碍①（单侧承诺 = 构造保证）—— 本份这样解**

`dsr1a` 预注册 §0.2 逐字断言：两臂**只在 T3 档可能不同**，且副臂在 T3 判 `unknown`（无害）、
主臂回落 v1（可能有害）⇒ **「副臂有害错判 ≤ 主臂」在任何面上恒成立** ⇒ ⛔ 不是读数。
本份复核了这条断言，**它成立**：`resolveTiered` 是两臂共用的唯一分档步，
`detectFrameThemeTiered` 与 `…Strict` 的源码差**只有 T3 一行**（前者回落真源、后者返回 `unknown`）。

⇒ 🔴 **本份 ⛔ 不承诺任何「副臂 vs 主臂」的比较量**，改承诺**副臂自己的绝对数**，并配一条 `unknown` 上限：

| 侧 | 治什么 | 不写它会怎样 |
|---|---|---|
| **C1 有害错判绝对数** | 「副臂到底安不安全」 | 只报相对量 ⇒ 恒过 |
| **C2 `unknown` 上限** | 「靠全判 `unknown` 换安全」这条出路 | 副臂可以把一切退成 `unknown` 让 C1 归零 |

⚠️ 这与处方 `§0.3 ③` 那个 `P2a+P2b` 的「和」形态**同一个药方**（那次是 `light` 与 `unknown` 都算没修好），
本份是它在另一处的第二次应用。

**② 阻碍②（面不够了）—— 已被 2026-09-03 的枚举推翻**

原文逐字「14 个已知产品文件全部被看过 ⇒ 换新面需先去 Figma 发现未登记文件，那是一件**可行性未验证**的事」。
实测：**未登记文件 220 个**（open-items §13.3）。原判之所以写「未验证」，是因为两仓源码对 `team_id` **0 命中**、
REST 面确实到不了，而 team id 在 **Figma MCP `whoami`** 里
⇒ ⚠️ **「不可行」与「我没找到那条通道」是两件事。**

### 0.2 ⛔ 本份不做什么

- ⛔ **不接 D7**、⛔ 不改 DS 一个字节、⛔ 不碰任何 DS 闸。
- ⛔ **不改任何既有判据与量具**（清单见 §1）。
- ⛔ **不在 H 面上重跑副臂** —— H 面对副臂已是看过的测试集。
- ⛔ **不裁定「该接哪个臂」** —— 本份只交出盲面上的双侧读数，选臂是 owner 的（`AGENTS §8`）。

---

## 1. 被测对象与不动的东西

| 对象 | 处置 |
|---|---|
| `detect-frame-theme.mjs`（v1）/ `-backdrop.mjs`（v2）/ `-tiered.mjs`（v3 两臂）| 🔴 **⛔ 一个字节都不改**（含本份的被验对象）|
| `render-ground-truth.mjs` / `png-luma.mjs` | 🔴 **复用且 ⛔ 不改** |
| `scan-holdout-face.mjs`（H 面那把尺子）/ `scan-real-surface.mjs`（W 面那把）| 🔴 **⛔ 不改、⛔ 不复用** —— 各自钉死自己的 `surfacePin`，动它们会让已发表读数不可复核 |
| `check-face-drift.mjs` / `enumerate-figma-face.mjs`（2026-09-03 新增）| 复用且 ⛔ 不改 |
| **量具（新）** `probes/detect-frame-theme/scan-blind-face.mjs` | B 面扫描器。W 口径**逐字复用**，⛔ 自身不含任何主题判定逻辑 |
| **量具（新）** `probes/detect-frame-theme/measure-d63.mjs` | 跑两臂 + 算双侧 + 内建控制 |

🔴 **判据真源一律 `import`，⛔ 不抄副本**（`AGENTS §3.2` 推论三）。

---

## 2. 🔴 产出与判读规则（⛔ 取数后不得增删、不得换）

### 2.1 面与分母（🔴 带口径名，`AGENTS §2.4`）

**【B 面 · W 口径】**：§3 选出的盲面文件集，节点 = **page 或 SECTION 的直接子 `FRAME`**
（与 H 面 `scan-holdout-face.mjs` 边界① **逐字同一口径**）。

- `scannedN` = W 口径枚举到的 frame 总数
- `measuredN` = 其中**渲染出不透明像素、拿到像素真值**的数 ⇒ 🔴 **一切承诺的分母是 `measuredN`**
- ⚠️ 渲染失败 / 无不透明像素的节点**单独记一层**，⛔ 不并进三态（同 `render-ground-truth` 边界③）

### 2.2 🔴 双侧承诺（⛔ 取数前写死，⛔ 取数后不得调）

**基线率取自 H 面**（`runs/detect-frame-theme/d62.json`，`measuredN = 1472`）：
副臂有害错判 **3**、`unknown` **346**。

| # | 量 | 定义（分母 = `measuredN`）| 判否线 | 能怎么失败 |
|:-:|---|---|---|---|
| 🔴 **C1** | **有害错判** | `#{ n : v3strict 判 light ∧ 像素真值 dark }` | **≤ `ceil(0.005268 × measuredN)`** | 盲面上副臂的危害率显著高于 H 面 |
| 🔴 **C2** | **`unknown` 上限** | `#{ n : v3strict 判 unknown } / measuredN` | **≤ 0.50** | 副臂靠把一切退成 `unknown` 换安全 |

**C1 的线是怎么来的**（⛔ 不是拍的）：H 面观测 3 次事件，取 **Poisson 单侧 95% 上界**
`λ = ½·χ²₀.₉₅,₈ = ½ × 15.507 = 7.754` ⇒ 率上界 `7.754 / 1472 = 0.005268`。
⇒ C1 检验的是**「H 面那个率能不能外推」**，⛔ 不是「危害小不小」。
⇒ 若 `measuredN = 1472`，线 = **8**；即盲面上有害错判 ≥ 9 就判否。

**C2 的线是怎么来的，以及它 ⛔ 有多弱**：H 面副臂 `unknown` 率 = `346/1472` = **23.5%**。
0.50 这条线**故意放松到基线的两倍以上**，理由：`unknown` 率由 **T3 档占比**主导，而 T3 占比是**面的性质**
⇒ 卡紧会因面的正常异质性而误杀，那是「必然失败的控制」，与「不能失败的控制」一样坏。
⇒ 🔴 **C2 只堵退化情形，⛔ 不得读作「`unknown` 受控」** —— 它过了只说明副臂没有退化成全 `unknown`。

### 2.3 ⚠️ 边界读数（⛔ 不作承诺 —— 是构造保证，登记而不高估）

| 量 | 为什么 ⛔ 不能作承诺 |
|---|---|
| **M1 回退**（`v1` 判对 ∧ `v3strict` 判错）| 副臂在 T3 恒判 `unknown`，而 v1 在 T3 会给出判定 ⇒ 回退数 ≈ T3 档里 v1 判对的数 ⇒ **算术，⛔ 不是读数**（H 面 79）|
| **副臂 vs 主臂的任何差** | §0.1 ① 已证恒成立 |
| **总错判数** | 含 `unknown`，被 C2 那一侧吸收；且 D6.2 已判它不是消费者要的量 |

⇒ 三者**照报**（决策要用），⛔ 不进承诺、⛔ 不得用来改 C1/C2 的判定。

### 2.4 🔴 为什么 C1 ⛔ 不是构造保证（本份成立的关键一问）

副臂的低危害来自「T3 → `unknown`」。**但有害错判 ⛔ 不只在 T3 产生** ——
`T2` 档（探到铺底涂层）两臂**判定完全相同**，该档若把深色页的铺底探成浅色，
副臂照样判 `light` 而真值 `dark` ⇒ **C1 在 T2 档上完全暴露**。
H 面上副臂那 3 个有害错判正是这么来的（T3 已被 `unknown` 兜住）。
⇒ **盲面 T2 档的性质变了，C1 就会动** ⇒ 能失败。

### 2.5 地板（⛔ 不达则整轮判「不可判读」，⛔ 不得改判成通过）

| # | 条件 | 不达时 |
|:-:|---|---|
| **F1** | **B 面 T3 档节点数 ≥ 20** | 两臂在该面上**无差异** ⇒ 整轮「面不含被测形态、⛔ 不可判读」|
| **F2** | `measuredN ≥ 300` | 「N 不足、⛔ 不可判读」，⛔ 不得报 C1/C2 |

⚠️ F1 是本份最关键的地板：**T3 = 0 时副臂 ≡ 主臂**，一切读数都恒等，看着像「完美通过」。

---

## 3. 🔴 B 面（盲面）的选法（⛔ 取数前写死，⛔ 看到任何读数后不得调）

### 3.1 候选池

`runs/detect-frame-theme/figma-face-enumeration.json` 里 **`known === false`**，
且其 `project` **∈ 那 7 个已承载已登记文件的 project**：
`Drive` · `Media Service&UR` · `MediaHub` · `PARTYLINE` · `PRODUCER` · `TVU One` · `Usage of Sass Service`。

**为什么限定这 7 个**（⛔ 不是图省事）：这 7 个 project 里已有 14 个文件被验证为真实产品 mockup 面
⇒ 同池文件**是产品面**这一点有据可依；而其余 44 个 project 混着 FigJam、设计评议、个人 sketch，
「哪些算产品 mockup 面」**没有可执行口径** ⇒ 现在把它们纳入等于用主观判断选面。
⇒ 🔴 **代价照登记**：本份因此只测**「同产品线、不同文件」的泛化**，
⛔ **不得**把结论读成「对任意 Figma 面成立」（§6 边界①）。

### 3.2 抽样规则（机械、可复核）

1. 候选池按 **`fileKey` 字典序**升序排列（⛔ 不按名字、⛔ 不按修改时间 —— 那两者与内容相关）。
2. 逐个文件扫 W 口径结构，**累加 `scannedN`**。
3. 累计 `scannedN ≥ 4000` 时**停止纳入**（当前这个文件**整份纳入**，⛔ 不截断文件内部）。
4. 🔴 **硬上限 `scannedN ≤ 6000`**：若纳入某个文件会越过 6000，**跳过它**并继续看下一个（成本闸）。
5. 若候选池耗尽仍 `< 4000`，**照实取**并在报告里标 N 不足风险（F2 仍适用）。

⚠️ **⛔ 不许在看过任何判定/像素读数之后回头改这五条。** 选面偏倚正是 D6.2 报告禁的
「对着测试集选臂」的同族形态，只不过换成了「对着测试集选面」。

### 3.3 ⛔ 盲面纪律

- 取数前 ⛔ **不看** B 面任何 frame 的截图、fills 分布、判定分布。
- 扫描器落盘后，**⛔ 不许先看结果再决定跑不跑判定** —— 一次跑到底。

### 3.4 🔴 节点级抽样（**补写，见下方留痕**）

§3.2 只写了**文件级**纳入规则，⛔ 漏了「扫到的 frame 要渲染其中多少个」——
而渲染是本份唯一的贵操作，这个旋钮不写死，`RENDER_CAP` 就成了**事后可调的量**
（想让 C1 过就少渲染一点，Poisson 线随 `measuredN` 线性缩小）。⇒ 现在写死：

1. **`RENDER_CAP = 1500`**，**`SAMPLE_SEED = 20260903`**。
2. 抽法**逐字复用 H 面那把尺子**：固定种子 `mulberry32` + Fisher-Yates 前 k 个
   ⇒ **无放回均匀抽样**，⛔ 不分层、⛔ 不按文件配额、⛔ 不剔除任何节点。
3. `scannedN ≤ 1500` 时**全渲染**，⛔ 不抽样。

**为什么恰好是 1500**：H 面基线正是 `RENDER_CAP = 1500 → measuredN = 1472`。
C1 是把 H 面的率外推到 B 面，**两边分母同量级时这个外推最干净**
⇒ 取同一个值 ⛔ 不是图省事，是为了让 §2.2 那条线可比。

> 🔴 **补写留痕（⛔ 不掩盖）**：本节写于预注册首次 commit（`a19f61b`）**之后**、
> B 面任何取数**之前** —— 触发点是实现 `scan-blind-face.mjs` 时读 H 面尺子源码，
> 发现它有 `RENDER_CAP`/`SAMPLE_SEED` 两个写死常量而本份 §3.2 没有对应条款。
> **此刻 B 面的 `scannedN`、判定分布、像素真值一个都没取过**，⇒ 补写 ⛔ 不可能受读数影响。
> ⚠️ 但这仍是一次**预注册在实现阶段被补写**，按 `AGENTS §2.12` 照登记：
> 若日后 C1/C2 的判定被质疑，**这一节是要被单独审视的那一处**。

---

## 4. 内建控制（`AGENTS §3.2`：签名一变当场红；fail closed）

| # | 类 | 控制 | red 时的含义 |
|:-:|---|---|---|
| **MH1** | 等价 | 🔴 **新尺子 `scan-blind-face.mjs` 在 H 面那 8 个文件上跑，三态计数必须与 `holdout-scan.json` 逐位相同** | 两把尺子口径分岔 ⇒ B 面读数不可与 H 面基线比 ⇒ 读数作废 |
| **MH2** | 像素 | **像素锚点**：`0054ib0nLmt27bC3QlGDl7 / 4867:3469` 必须判 `dark` | 渲染/luma 管线坏了（⛔ 不是被测对象坏了）|
| **MH3** | 分档 | T1/T2 档上 **`v3strict` 判定必须逐节点 ≡ `v3`** | 两臂分歧溢出了 T3 ⇒ §0.1 ① 的断言假 ⇒ 全部承诺推理失效 |
| **MH4** | 分档 | T3 档上 **`v3strict` 必须恒为 `unknown`** | 副臂实现与预注册描述不符 |
| **MN1** | 面 | `gt` 与 `scan` **逐节点对齐、孤儿 0** | 两次选面静默分岔（`§3.7` 差集里最难解释的一类）|
| **MN2** | 独立 | luma **第二份独立实现**重算，与主路一致 | 判定与真值共用了同一个错 |

🔴 **MH2 修掉 D6.2 登记的那个缺陷**：D6.2 的 `pixelAnchor` 在 H 面上**空转**
（锚点节点不在面内），其报告已登记「⛔ 不得再引用『像素量具有锚点控制』为 H 面读数背书」。
⇒ 本份把锚点节点**额外渲染一次**（⛔ 不计入 `measuredN`、⛔ 不进任何承诺分母），
控制因此**真的在跑**，且完全不碰 B 面。

---

## 5. 判读规则（🔴 取数后按此逐字执行，⛔ 不得另行解释）

| 情形 | 判定 |
|---|---|
| F1 或 F2 不达 | **不可判读** —— ⛔ 不报 C1/C2，⛔ 不得写成「副臂通过」|
| F 全达 ∧ C1 过 ∧ C2 过 | **`D63_VERDICT = 支持副臂外推`** —— 仅指「H 面那个危害率在 B 面没被推翻」|
| F 全达 ∧ C1 否 | **`D63_VERDICT = 不支持`** —— 🔴 副臂的低危害**不外推**，⛔ 不得再引用 3/1472 谈副臂安全性 |
| F 全达 ∧ C1 过 ∧ C2 否 | **`D63_VERDICT = 退化`** —— 低危害是靠全判 `unknown` 换的，⛔ 不得读作安全 |

⚠️ **「支持外推」⛔ 不等于「该接副臂」**：接闸还卡在 §0 第四行那件独立立项
（`detectFrameTheme` 在 DS 侧零实现）。**两件事，⛔ 别合并。**

---

## 6. 覆盖边界（`§3.4`：边界，⛔ 不是 TODO）

1. **射程**：只测「同产品线、不同文件」的泛化（§3.1）。⛔ 不得外推到任意 Figma 面。
2. **⛔ 不解决 D7**：DS 侧 `detectFrameTheme` 零实现是一件独立立项（§0 第四行）。
3. **口径 B 的老边界原样继承**：W 口径是 §M23.18「产品 frame」那句自然语言的**单一可执行近似**，⛔ 不是那句话本身。
4. **单次读数**：本份是一个面上的一次取数，⛔ 不是重复实验，⛔ 不得报置信区间以外的稳定性主张。
5. **C2 是弱线**（§2.2），⛔ 不得读作「`unknown` 受控」。
