# DS-R1a 预注册（第 3 份）—— 量法与判读规则写在实现之前（`lab:DS-R1a`）

> 🔴 **本文件必须在写任何判据实现代码之前 commit。** 依据 `AGENTS §1.1` 订正段。
> ⛔ 取数后本文件一字不改（要改只能另起一份并留痕）。
>
> ⚠️ **这是第 3 份预注册，但它 ⛔ 不是 D6.3。** D6.3（副臂 `v3-strict` 接闸）**本轮 ⛔ 不做**，
> 原因见 §0.2。⛔ 别把本份读成 D6.3 的替代或前置。

- **立项依据**：owner 2026-09-03 在三条候选（D6.3 / D6.4 / DS-R1a）中**选定 DS-R1a**。
- **被验对象**：处方 [`proposals/2026-09-02-mockup-default-white-bg.md`](../proposals/2026-09-02-mockup-default-white-bg.md) **§3.1**
  那条**二级判据**（`luma 差 > 0.5`）。
- **它回答哪个目前未知的问题**（⛔ 立项理由不得是「确定有产出」）：
  **那条二级判据自己的 precision 是多少 —— 即「它标出来的节点里，有多少真的是主题判反的」。**
  ⚠️ 两个方向都可能，⛔ 不是构造保证（见 §3 推演③：它的误报源是一类完全合法的设计形态）。
- **谁消费这个读数、他在乎哪个方向的错**（🔴 `D6.2 §3` 那条教训的兑现，⛔ 不得跳过这一问）：
  消费者 = **决定 DS-R1a 能否从 warn 转 fail 的那个人**。转 fail 后闸会红、挡住设计师提交
  ⇒ 他在乎的是 **误报**（标了但其实没问题）⇒ ⛔ 不是 recall、⛔ 不是总命中数。
  ⇒ **precision 正是他要的那个量** —— 本份与 D6.2 不同，指标与消费者需求在此对齐，且这一句是查过才写的。

---

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

```
登记记的：  「§3.1 二级判据以 warn 挂一轮 ⬜ 未做 —— precision 未测」（处方 §6:248 逐字）
现取的：    DS 侧 grep luma/secondary/二级 → 唯一命中是 MH3 注释「整屏 luma 0.086」，
            与判据无关 ⇒ 对方 ⛔ 未实现
前提还成不成立：成立。D62 报告 §8.1 已把「81.8% 是相邻量、分子分母都不同」钉死
            ⇒ 阻塞理由（precision 未测）原样成立
它还该做吗：该做。DS `2026ba1f..b5a5bc57` 共 9 个 commit，碰 mockup-conventions 的两条是
            M54/M55（多断点响应式、多方案比选），⛔ 未碰 default-white-bg 判据文件；
            该脚本 6 处「刻意」注释全与本项无关 ⇒ ⛔ 无裁定不做
lab 自己验过没：⛔ 没干过。grep 关键措辞在 lab 仓 12 处命中，全是「未做/未测」登记，无一处已实现
```

### 0.1 🔴 本份是**测量型**，⛔ 不是判定型 —— 但它照样必须能失败

本份 ⛔ 不产出「达成 / 未达成」，产出一个 **precision 估计**。
⇒ 「能失败」不落在达标线上，落在 **§2 的判读规则**上：
**转 fail 的支持线在取数前写死，⛔ 取数后不许调。** 不写死，取完数就可以事后说
「81% 挺高的，可以转」或「81% 不够」—— 那才是一条不能失败的控制。

### 0.2 ⛔ 本份不做 D6.3，理由登记

D6.3（给副臂 `v3-strict` 另起预注册接 D7）**本轮 ⛔ 不做**。两条独立理由：

1. 🔴 **交接指定的承诺指标单独用是构造保证。** 两臂**只在 T3 档可能不同**（D6.2 预注册 §6.1 `MH3`
   已断言），且副臂在 T3 判 `unknown`（→ navy，**无害**）、主臂回落 v1（**可能有害**）
   ⇒ **「副臂有害错判 ≤ 主臂」在任何面上恒成立**，⛔ 不是读数。
   ⇒ D6.3 若只承诺方向性危害，它**不能失败** —— 与 D6.2 预注册 §2.2 末条给自己堵的
   `M-GATE-FALSIFIABILITY` 同族。它要写，必须**双侧**：有害错判的**绝对数/率**（盲面上未知）
   **＋一条 `unknown` 上限**（否则「全判 unknown」恒过；D6.2 实测副臂 `unknown` 346 vs 主臂 213）。
2. **面不够了。** `ds-health-mockup-targets.json` 的 `sourceLabels` 共 11 项，`product-file` **9** 个：
   8 个正是 H 面那 8 个，第 9 个（`Media Service/UR`）**本身就在 `targets` 里**（= W 面，已污染）
   ⇒ **14 个已知产品文件全部被看过**。D6.3 要换新面需先去 Figma 发现未登记文件，
   那是一件独立的、**可行性未验证**的事。

⇒ 上述两条**登记在此**，⛔ 不是本份的产出，⛔ 也不构成对 D6.3 的否决。

---

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

| 对象 | 处置 |
|---|---|
| `detect-frame-theme.mjs`（v1）/ `-backdrop.mjs`（v2）/ `-tiered.mjs`（v3）| 🔴 **⛔ 一个字节都不改。** 三者的注入读数与 D6/D6.1/D6.2 判定要保持可复核 |
| `render-ground-truth.mjs` / `png-luma.mjs` | 🔴 **复用且 ⛔ 不改** |
| `scan-holdout-face.mjs` | 🔴 **复用且 ⛔ 不改**（它的 `surfacePin` 钉 8 个文件、带自己的注入读数）|
| **判据真源**（新）`probes/detect-frame-theme/secondary-luma-gap.mjs` | 本份被验对象。⛔ 纯判据，⛔ 自身不含取数逻辑 |
| **量具**（新）`probes/detect-frame-theme/measure-dsr1a.mjs` | 在既有缓存上跑判据 + 算 precision + 内建控制 |

🔴 **本份 ⛔ 不发任何新的 Figma 请求。** 面与像素真值全部复用已落盘读数（§5）。
⇒ 面 ⛔ 不可能漂 —— 这是本份相对 D6.2 少掉的一类风险，同时也是它的一个边界（§7）。

---

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

### 2.1 precision 的分子分母（🔴 带口径名，`AGENTS §2.4`）

设**判据定义域** `D` = 面内满足「`fills[0]` 存在 ∧ `type === 'SOLID'` ∧ 有 `color`」的 frame。
⚠️ **`D` 之外的节点 ⛔ 不进任何分母**（判据对它们无从计算）——
这是**结构性排除**，必须与读数一起报（🔴 `D6.2 §1.1` 那条教训的兑现：
「`M1 = 0` 的结构性分母是 20，⛔ 不是 895」）。

| 记号 | 定义 |
|---|---|
| `F` | **被标出**的节点 = `D` 中判据判「可疑」的 |
| `F∩GT` | `F` 中**有像素真值**的（⛔ 只有 1472 个节点渲染过，见 §5）|
| **分子** | `F∩GT` 中 **`v1(node) ≠ pixelVerdict`** 的节点数 |
| **分母** | `|F∩GT|` |
| **precision** | 分子 / 分母，口径名 =【**H 面 · W 口径 · 渲染子面**】|

- 🔴 「错」的定义**逐字沿用 D6.2**（判定 ≠ 像素真值，`unknown` 算错），⛔ 不新定义。
  但 `unknown` 与反向错判**分开报**（两者性质不同）。
- 🔴 **同时报第二个口径【加固口径】**：`F` 中**主判据（处方 §3 那条）放行**的子集上的 precision。
  ⚠️ 这才是「加固」的真实价值 —— 二级判据存在的理由是抓主判据抓不到的绕过。
  两个口径**并排报、各带名字**，⛔ 不许只报好看的那个。

### 2.2 🔴 判读规则（⛔ 取数前写死）

- **R1（功效地板）**：若 `|F∩GT| < 20` ⇒ **precision 判「N 不足、⛔ 不可判读」**，
  ⛔ 不得报点估计当结论、⛔ 不得据此说 DS-R1a 可转或不可转。
  🔴 **这条是本轮最该防的复发**：D6.1 §6.3（light 桶只剩 7）与 D6.2 §1.1（M1 分母 20）
  **已连续两轮栽在同一形态上**，两次都是事后才发现分辨率地板比达标余量还大。
- **R2**：precision **必须报 Wilson 95% 置信区间**，⛔ 不许只报点估计（小样本下点估计骗人）。
- **R3（🔴 转 fail 的支持线，取数前写死）**：
  **`precision` 的 Wilson 95% CI 下界 ≥ 0.90 ⇒ 本轮读数「支持转 fail」；否则 ⛔ 不支持。**
  - **0.90 的出处**（⛔ 不出自 H 面）：这是一条会让 CI 红、挡住设计师提交的闸；
    DS 现行消化误报的机制是 `EXEMPTIONS` 白名单且 **shrink-only（只准变短）**
    ⇒ 每一个误报都要人工加白名单、且加了就难撤。0.90 = 「每 10 个红里最多 1 个要人工豁免」。
  - ⚠️ 🔴 **这是 lab 定的先验，⛔ 不是实测出处，owner 可否决。** 登记为先验、写在取数前、
    **⛔ 取数后不许调**。
- **R4（⛔ 防「不能成功」）**：若点估计 ≥ 0.90 但 CI 下界 < 0.90 ⇒ 判
  **「点估计支持、N 不足以支持」**，并**报出还需多少 N** 才能让下界够到 0.90。
  ⚠️ 一条只能失败、不能成功的规则与一条不能失败的规则**同样是病**，R4 堵这一侧。

### 2.3 ⛔ 不算数的几种形态（提前堵掉）

- ⛔ 拿 D6.2 的 **81.8%** 替本份的 precision。分子分母都不同（D62 报告 §8.1 已钉死）。
- ⛔ 拿【H 面 · W 口径】的 precision 替【**DS 闸口径**】（全深度、2435 扫描 / 446 违反）报。
  三个口径是三个不同的数（`AGENTS §2.4`）。本份产出**只**是前者。
- ⛔ 取数后调阈值 `0.5`。它来自处方字面（2026-09-02 写定，早于 H 面被扫），
  ⛔ 不是对着这个面选的 —— 调了就作废。
- ⛔ 只报 `F∩GT` 的 precision 而不报 `|D|`、`|F|`、`|F∩GT|` 三个数。
  分母链条断一环，读者就无法判断分辨率地板。
- ⛔ 把「支持转 fail」读成「已转 fail」。本份 ⛔ 不改 DS 真源（§7）。

---

## 3. 动手前的推演三问（⛔ 不许跳过）

**① 这个器的输出有没有可能不是我预期的那个值？—— ✅ 可能，且两个方向都可能。**
- **可能标出极少**（若「绑白变量却真是深色」这种绕过形态在 H 面上罕见）⇒ R1 触发、不可判读。
- **也可能标出极多**：🔴 **判据字面 ⛔ 没有「frame fill 必须是纯白」这个前置，⛔ 也没限定方向。**
  它对**任何** frame fill 与铺底色 luma 差 > 0.5 都标 —— 包括「frame fill 深、铺底浅」的反向情形。
  ⇒ 字面定义域**比它的立项语境（绕过主判据）宽得多**。⇒ §4.2 拆臂。
⇒ 有信息量，⛔ 不是构造保证。

**② 交接引的原文有没有说过相反的话？—— ⚠️ 有两处，且都改变了做法。**
- **处方 §3.1 逐字建议的是「先以 warn 形态挂一轮**收数据**」** —— 即它指的路是
  **在 DS 闸上收真实数据**，⛔ 不是「在 lab 离线量」。
  ⇒ 两者的面不同：**DS 闸口径是全深度 payload（2435/446），H 面是 8 个 hold-out 文件**。
  ⇒ 🔴 本份量的是一个**离线代理**，⛔ 不是处方指的那件事。
  **登记为本份的显式偏离**（代价：外推性差），换来的是快、可控、⛔ 不惊动设计师。
- **处方 §3 主判据刻意「精确纯白 ⛔ 不近似」**，理由逐字是「本闸要抓的就是**那一个默认值**」。
  而二级判据 ⛔ 没有这个限定 ⇒ **两条判据的哲学不同**（主判据抓默认值，二级判据抓视觉不一致）。
  ⇒ ⛔ 别把主判据的收窄哲学套到二级判据上，那会静默改掉被验对象。

**③ 交接判为「病」的东西，会不会正是唯一信号源？—— ⚠️ 是，而且这正是本轮要量的那个未知。**
被标为「可疑」的是「frame fill 与铺底 luma 差大」。**有一类完全合法的设计形态长这样**：
一个 frame 自己是深色底，里面放一张**铺满的浅色卡片 / 图片 / 弹窗** —— frame fill 就是真实背景，
铺满的子层是**内容**。⇒ 这条判据的误报源正在这里，
⇒ 🔴 **precision 可能很低，而那是读数、⛔ 不是缺陷。**

---

## 4. 🔴 判据形态在取数前写死（⛔ 取数后调参 = 另起一次预注册）

### 4.1 逐字沿用既有定义（⛔ 不重新定义）

- **luma** = `0.299r + 0.587g + 0.114b` —— 🔴 **直接 import v1 的公开常量**，⛔ 不复制字面量
  （复制了会在真源改动时静默分岔；这条逐字沿用 D6.2 预注册 §4.3）。
- **「铺满该 frame 的子图层」** = `visible !== false` ∧ `(opacity ?? 1) === 1` ∧
  `absoluteBoundingBox` **几何包含** frame 的（容差 **0.5px**）—— 逐字沿用 v2/v3 的「合格铺底」定义。
- **「最顶层」** = 按画序 `children[n-1]` 最顶 → `children[0]` 最底，取**第一个**合格者。
- **阈值 `0.5`** —— 逐字取自处方 §3.1，⛔ 不调（§2.3）。

### 4.2 🔴 字面有两处歧义 ⇒ 并排多臂测（⛔ 不预设哪个对）

⚠️ 这是 `D6.2 §3` 那条「两臂设计买到的读数」的推广应用：**字面有歧义就别预设，同批多一列几乎零成本。**

| 维度 | 臂 | 口径 |
|---|---|---|
| **递归** | **N（字面）** | 只看**直接子图层**的最顶层铺满者，⛔ 不递归（处方字面没提递归）|
| | R | 递归探 3 层（v3 的 T2 口径）|
| **方向** | **B（字面）** | **双向**：\|luma(fill) − luma(铺底)\| > 0.5（处方字面是「相差」）|
| | D | 只标「fill 亮 ∧ 铺底暗」（= 立项语境：抓默认白绕过）|

🔴 **字面臂 = `N × B`** —— 它是 DS-R1a 要采纳的那个，**precision 以它为准**。
其余三臂（`N×D` / `R×B` / `R×D`）是**并排的敏感性分析**，回答「DS 采纳时若收窄口径，precision 会不会更好」。
⚠️ 四臂**在同一次取数、同一个面、同一份像素真值上**算出，⛔ 不分四次跑。
⛔ **不许取数后改判「字面臂其实该是另一个」** —— 那是对着测试集选臂（`D6.2 §8` 逐字禁止的形态）。

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

① 定义域 `D` 排除「无 `fills[0]` / 非 SOLID / 无 color」的 frame —— 判据对它们无从计算（§2.1）。
② precision 只在**渲染子面**（1472）上可算，⛔ 不是全部 4425。
③ ⛔ 不做半透明合成、⛔ 不做颜色空间转换、⛔ 不看 `clipsContent`（沿用 v3 边界）。
④ 本份 ⛔ 不量 recall（要 recall 得知道「所有真错判」，那是另一件事）。
⑤ 🔴 **本份 ⛔ 不改 DS 真源、⛔ 不挂闸。** 产出是一个读数 + 一条支持/不支持的判读。

---

## 5. 面与口径（⛔ 取数后不得更换）

**面 =【H 面 · W 口径】**，与 D6.2 **逐字同一个面**（8 个 hold-out 产品文件，`surfacePin` 见
[`holdout-pins.json`](../runs/detect-frame-theme/holdout-pins.json)）。
读数复用已落盘：[`holdout-scan.json`](../runs/detect-frame-theme/holdout-scan.json)（4425 行逐节点）
· [`ground-truth-H.json`](../runs/detect-frame-theme/ground-truth-H.json)（1472 个像素真值）
· `holdout-cache/`（8 个 `depth=4` payload + subtrees，本地 677M，⛔ 已 gitignore、可由 pin 逐字重取）。

### 5.1 🔴 面污染登记（⛔ 不掩盖）

**H 面对本条判据是「参数未污染、但作者已知该面分布」** ⇒ 🔴 **降级为推导面**。逐条：

| 项 | 判 |
|---|---|
| 阈值 `0.5` 是否对着 H 面选的 | **否** —— 处方 2026-09-02 写定，早于 H 面被扫 |
| 判据形态是否对着 H 面选的 | **否** —— 逐字取自处方 |
| 作者是否已知 H 面分布 | 🔴 **是** —— 已知 1658 精确纯白、T2 429、T2 正确率 81.8% 等 |

⇒ **precision 报为【H 面 · W 口径 · 渲染子面】的估计**，
⛔ **不得外推到【DS 闸口径】**，⛔ 不得称为「无偏估计」。
⚠️ 这与 D6.2 用 H 面的地位**不同**：那轮 H 面是**盲面**（承诺指标写在取数前、作者不知分布），
本轮它已不是。**⛔ 别把 D6.2 报告里「H 面是干净面」那句话搬过来用。**

---

## 6. 量具与控制（`AGENTS §3.2`：每条判据配 must-hit + must-not-hit，内建进脚本）

### 6.1 内建控制（签名一变当场抛）

| 控制 | 断言 | 抓什么 |
|---|---|---|
| **MH1** | 🔴 **H 面内具名事实锚点**（§6.2）必须被字面臂标出，且其 `v1 ≠ pixelVerdict` | 判据整条塌掉 |
| **MH2** | 四臂的 `F` 满足包含关系：`R×B ⊇ N×B ⊇ N×D` 且 `R×B ⊇ R×D` | 臂之间串味 / 递归实现反了 |
| **MH3** | `|F| ≤ |D| ≤ 面内节点总数`，且 `F∩GT ⊆ F` | 分母链条断裂 |
| **MN1** | ⛔ **不得**出现「被标出但 `fills[0]` 不在 `D` 里」的节点 | 定义域泄漏 |
| **MN2** | ⛔ **不得**出现 luma 差 `≤ 0.5` 却被标出的节点（⛔ 用独立第二实现重算 luma）| 判据与子判据分岔 |
| **MN3** | ⛔ **不得**出现 `backdropDepth > 3` 的铺底（R 臂递归越界）| 深度上限失效 |
| **MN4** | 🔴 **`|F∩GT| ≥ 20` 的前提断言**：不满足则**当场判 R1 不可判读并显式落盘**，⛔ 不静默出点估计 | 分辨率地板被无声跳过 |

### 6.2 🔴 H 面自己的具名事实锚点（= 待办 **D6.4**，并进本份做掉）

⚠️ **⛔ 不得复用处方 §3 那三条锚点**（`0054ib0n… / 1834:300`、`4867:3469`、`9359:3`）——
它们全属**旧的 6 文件面**，⛔ 不在 H 面内。直接套用会**逐字复刻 D6.2 §5 那次
`PIXEL_ANCHOR` 整轮空转**（`observed: null`、静默放行，形态 = `MH-not-in-face`）。

**锚点选取规则（🔴 取数前写死，⛔ 事后不许换人选）**：
1. 从 `ground-truth-H.json` 取 `opaqueFrac ≥ 0.95` 且 `pixelMedian ≤ 0.10`（明确深色）的节点；
2. 在其中取**同时被字面臂标出**的一个；
3. 🔴 **目视确认**（渲染 PNG 亲看一眼）它确实是深色页 —— ⛔ 不靠量具自证；
4. 落成 `H_FACE_ANCHOR` 常量，**若它不在面内则抛**（fail closed，⛔ 不静默放行）。

⚠️ 第 2 步意味着锚点是从「被标出者」里挑的 ⇒ 🔴 **它 ⛔ 不构成 precision 的证据**
（那是循环论证），它只证**「判据 + 像素量具这条链路没塌」**。**这一句必须进报告。**

### 6.3 故障注入（`AGENTS §3.8`：跑一遍绿不算验证，⛔ 少于 4 臂不算做过）

读数入 `runs/detect-frame-theme/dsr1a-fault-injection.json`：

| 臂 | 改坏什么 | 🔴 预期 |
|---|---|---|
| `ctrl-pristine` | 不改 | 逐字复现已落盘读数 |
| `p1-threshold-zero` | 阈值 `0.5 → 0` | `F` 应大幅扩张、precision 应劣化 |
| `p2-no-containment` | 去掉「几何包含」约束 | 铺底认定放宽 ⇒ `F` 变化；**MN2 或 MH2 应红** |
| `p3-abs-to-signed` | 双向 `|Δ|` 改成单向 | `N×B` 应逐字塌成 `N×D` ⇒ **MH2 必红** |
| `p4-anchor-out-of-face` | 把锚点换成旧 6 文件面的节点 | 🔴 **MH1 必红**（⛔ 不许静默放行 —— 这一臂专测 D6.2 §5 那个空转形态是否已堵上）|

**操作纪律**：① **先证故障态成立**（贴被改对象的实际输出，⛔ 不改完直接跑）；
② 用完 `git checkout` 还原并核 `git status` 为空；③ **注入本身也要验** —— marker 计数没涨就不算注入成功。

---

## 7. ⛔ 本份不承诺什么

- ⛔ **不承诺改 DS 真源、不承诺挂闸。** 产出是读数 + 一条按 §2.2 写死的判读。挂不挂由 owner 定。
- ⛔ **不承诺【DS 闸口径】的 precision。** 本份是【H 面】离线代理（§3 推演②）。
- ⛔ **不承诺 recall**（§4.3 边界④）。
- ⛔ **不承诺 D6.3**（理由见 §0.2），⛔ 不承诺 D7。
- ⛔ **不承诺存量 446 条整改**（DS-R1b，要设计师排期，lab 不代拍）。
- ⛔ **不改 v1 / v2 / v3 任何一个字节。**
