# `detectFrameTheme` 探针（lab:L1 + D6 + D6.1）

把 DS [`mockup-conventions.md` §M23.18](../../../tvu-design-system/docs/internal/mockup-conventions.md)
里的伪代码落成**可 import 的纯函数**，并用 fixture 钉住它的行为。

| 文件 | 是什么 |
|---|---|
| `detect-frame-theme.mjs` | 被测对象。纯 Node、零依赖、无 Figma 依赖（AGENTS §5） |
| `fixtures.mjs` | 23 条 fixture（纯数据，⛔ 不 import 被测模块） |
| `detect-frame-theme.test.mjs` | 26 条钉子 = 23 条 fixture + 3 条结构断言 |
| `inject.mjs` | **判别力装置**：造故障、收红条 |
| `scan-real-surface.mjs` | **D6**：在 6 个真实产品 Figma 文件上选面、喂节点、报三态（口径 B/T 并排） |
| `png-luma.mjs` | **D6 第二量具**：PNG → luma 统计（纯 Node 解码器 + 自校准 `selfTest()`）|
| `render-ground-truth.mjs` | **D6 第三步**：渲染像素当真值，报与真源判定的一致率与差集 |
| `inject-png-luma.mjs` / `inject-scan.mjs` | 上面两个新量具各自的判别力装置 |
| `detect-frame-theme-backdrop.mjs` | **D6.1 的被测对象**：v2，补「子图层铺底」。⛔ 不改 L1 真源一个字节 |
| `measure-backdrop.mjs` | **D6.1 取数**：v1 / v2 / 像素真值三口径并排，逐条核对预注册的 5 个承诺指标 |
| `inject-backdrop.mjs` | v2 的判别力装置（断言写在**读数**上，⛔ 不写在 exit code 上） |

```bash
node --test probes/detect-frame-theme/detect-frame-theme.test.mjs   # 钉子
node probes/detect-frame-theme/inject.mjs                            # 判别力（读数落 runs/detect-frame-theme/）
```

⚠️ `node --test <目录>` 在 Node 24 下会把目录当模块加载并报 `MODULE_NOT_FOUND`
（**装置故障，⛔ 不是钉子红**，AGENTS §3.9）。要给到**文件路径**。

## 1. 🔴 这套钉子的判别力凭什么成立

⛔ **不是**「26 条全绿」—— 恒绿的钉子长得一模一样。凭的是 `inject.mjs` 的 **11 条注入臂**：
9 条把被测对象改坏（阈值严格性 ×2、阈值取值 ×2、luma 系数 ×2、兜底分支 ×1、可选链 ×1、正对照 ×1），
每条都断言**指定的钉子必红 + 指定的钉子必仍绿**；另 2 条是绿侧控制
（未改一字的副本、只改注释）必须全绿 —— 否则说明钉子是「见改就红」。
每臂先**直接 import 改坏后的模块打印实际读数**自证故障态成立，再跑钉子（§3.8 造故障口径①）。

读数：[`runs/detect-frame-theme/fault-injection.json`](../../runs/detect-frame-theme/fault-injection.json)
· 结论与逐臂表：[`reports/2026-09-02-detect-frame-theme-L1.md`](../../reports/2026-09-02-detect-frame-theme-L1.md)

## 2. 阈值边界的 fixture 是怎么选的

伪代码是 `luma > 0.85` / `luma < 0.25`（**都是严格不等号**），所以必须有「**恰好等于阈值**」
的 fixture 才能钉住这一点。但灰阶取巧不行 —— IEEE double 下：

```
gray(0.85) → luma 0.8499999999999999   ⛔ 不等于 0.85
gray(0.25) → luma 0.24999999999999997  ⛔ 不等于 0.25，且 < 0.25 ⇒ 会被判 dark
```

⇒ 用穷举搜出**实算恰等于阈值**的三元组：`{r:0.505, g:1, b:0.9825}` = 0.85、
`{r:0, g:0.248, b:0.916}` = 0.25。两条都断言 `unknown`。

## 3. ⛔ 这套东西**不**证明什么（边界，⛔ 不是 TODO）

1. **⛔ 不证明真实 mockup 面上这个器可用。** fixture 是构造的 Paint 对象。
   三态在真实面上如何分布 = **D6**，要 Figma REST，lab 结构上做不了。
2. **⛔ 不证明钉子面已穷尽。** 只证明这 26 条对**这 9 种改坏**有判别力；
   「还有哪种改坏能全身而退」是开放的（§3.8：造故障验的是真值表少没少一格）。
3. ~~**⛔ 未验 Figma 的 `fills[0]` 是最底层还是最顶层 paint。**~~
   🔴 **D6 已验：是最底层**（PNG 渲染 + SVG 画序两路独立证据，见 D6 报告 §1）。
   ⇒ 「白底 + 盖深色 paint」被判 `light` 这种**方向相反的错判**在真实面上**已实际发生**。
4. ~~**⛔ 未验 D6 会不会走这份实现。**~~ 🔴 **D6 走的就是这份**（`import` 真源，⛔ 未抄副本）。

## 4. D6 的读数（2026-09-02）

`unknown` 只占 **5.5%（宽口径）/ 6.9%（严口径）** ⇒ ⛔「探测整片塌了」被证伪。
但拿**渲染像素**当真值一比，三态的可靠度差一个数量级：

| 态 | 真实面上的可靠度 |
|---|--:|
| `dark` | 262/263 = **99.6%** ✅ |
| `unknown` | 兜底 navy，18/23 恰好对 🟡 |
| `light` | 6/45 = **13.3%**，其余 **68.9% 是方向相反的错判** ⛔ |

🔴 根因是真源伪代码那行注释「或探 Top bar / page bg 图层」**点名了却没实现** ——
真实文件里那个图层真的就叫 `page_bg`。结论与推荐见
[`reports/2026-09-02-detect-frame-theme-D6.md`](../../reports/2026-09-02-detect-frame-theme-D6.md)。

```bash
# D6 三步（要 FIGMA_TOKEN；--targets 指向 DS pin 上的 ds-health-mockup-targets.json）
node probes/detect-frame-theme/scan-real-surface.mjs   --targets <pin>/docs/internal/ds-health-mockup-targets.json --out runs/detect-frame-theme/real-surface-d6.json
node probes/detect-frame-theme/render-ground-truth.mjs --reading runs/detect-frame-theme/real-surface-d6.json --face N --out runs/detect-frame-theme/ground-truth-N.json
node probes/detect-frame-theme/inject-png-luma.mjs     # 11 臂
D6_TARGETS=... D6_CACHE=... node probes/detect-frame-theme/inject-scan.mjs   # 9 臂
```

## 5. D6.1 的读数（2026-09-02）—— 判**未达成**

预注册 [`docs/2026-09-02-d61-preregistration.md`](../../docs/2026-09-02-d61-preregistration.md)
**先于任何 v2 代码 commit**。5 条承诺指标里 M2 / M3 未过 ⇒ 判定就是未达成，⛔ 未放宽任何一条。

| 指标 | 基线 | 达标线 | v2 | 判 |
|---|--:|--:|--:|:-:|
| M1 反向错判数 | 31 | ≤3 | **0** | ✅ |
| M2 dark 侧准确率 | 99.62% | ≥99% | 97.34% | ⛔ |
| M3 总体一致率 | 82.48% | ≥95% | 91.84% | ⛔ |
| M4 light 侧准确率 | 13.33% | ≥80% | 85.71%（分母只有 7）| ✅ |
| M5 unknown 数 | 23 | ≤46 | 23 | ✅ |

🔴 注入 `p2` 臂把语义**改回真源字面**（子图层只作回退）⇒ M1 回到 31、changed 回到 0，
**逐字等于 v1 基线** ⇒「按真源那个『或』字做，一条反向错判都修不掉」已是实测，⛔ 不再是推理。

结论与下一步：[`reports/2026-09-02-detect-frame-theme-D61.md`](../../reports/2026-09-02-detect-frame-theme-D61.md)

```bash
# D6.1（要 FIGMA_TOKEN；--subtree-cache 让故障注入能离线重跑同一批数据）
node probes/detect-frame-theme/measure-backdrop.mjs --scan runs/detect-frame-theme/real-surface-d6.json \
  --ground-truth runs/detect-frame-theme/ground-truth-N.json --subtree-cache <tmp>/subtree-n.json \
  --out runs/detect-frame-theme/d61-backdrop.json
D61_SCAN=... D61_GT=... D61_SUBTREE=... node probes/detect-frame-theme/inject-backdrop.mjs   # 9 臂
```
