# Devlog · H6:隐藏文本盲区——同色检测尝试与回退(发现记录)

> 2026-06-12 · 里程碑 H6(见 [hardening-iteration.md](../plans/hardening-iteration.md))· 状态:⏸ 回退,列为需基建的余项

## 尝试

H6 两盲区之一:同色文本(fill 色与背景同 → 人眼不可见,prompt-injection 向量)。
实现了一版**白底假设**的设备色检测:在 interpreter 追踪非描边 fill 色(随 q/Q
入栈,只认 device 色 `g`/`rg`/`k`,`sc`/`scn` 等非设备空间设 None 不判),
文本若以填充模式(render 0/2/4/6)画且 fill 各通道 ≥0.95(≈白)→ 标 `hidden`。

合成"白底白字注入"样例验证通过:可见正文保留,白字注入被标 `hidden=true`
并滤出 text/markdown,审计通道可见。单测三条(白字 hidden、黑/灰可见、
非设备色不判)全过。

## 为什么回退:白底假设在反白设计上误杀(MHS −0.05)

全语料记分牌一跑就暴露问题:**ODL MHS 0.687 → 0.636**(NID 几乎不变,因为
词级对标题占比不敏感)。根因:**反白文本**——深色表头/标题色块上的白色文字
(bialetti 财报、2305 表格表头常见)用的就是 `1 1 1 rg` 白色 fill,但背景是
深色填充矩形,**人眼完全可见**。白底假设把它们误判成隐藏,可见标题被滤出
→ MHS 掉。这违反 H6 的零误杀硬指标(误杀 = 丢可见文本,严重回归)。

收紧到"纯 1.0 才算白"无用:反白白字也是纯白 `1 1 1`。**区分"白底白字(隐藏)"
与"深底白字(可见反白)"本质上必须知道背景色**,白底假设做不到。

## 正确解(记余项):背景矩形模型

同色检测的正确深度不是"假设白底",而是**建立局部背景模型**:
- 追踪填充矩形(`re` 构造 + `f`/`F`/`B` 填充)的 bbox + 当前 fill 色;
- 文本落在某填充矩形内时,背景 = 该矩形色(最后绘制的覆盖者),否则页白;
- 同色 = `|fill − background|` 极小。

这样白底白字注入(无深色矩形 → 背景白 → 隐藏 ✓)与反白标题(深色矩形 →
背景深 → 白字 ≠ 背景 → 可见 ✓)都正确。这需要在 interpreter 增加填充矩形
追踪(z-order 取最近覆盖者),是中等基建;另一盲区**图像遮挡**(文本被其后
绘制的不透明图像完全覆盖)同样需要绘制顺序/z 追踪。两者共用"按 z-order 解析
某点最终可见色/覆盖物"的能力,适合一起做。

鉴于零误杀是硬指标、正确解需新基建且要全语料验证,本轮**不仓促上白底版**,
回退干净(interpreter.rs 复原至 H4 状态,MHS 回 0.687、22 单测绿),把 H6
列为"需背景/z-order 模型"的余项。

## 经验

- **安全特性更要先看记分牌**:隐藏文本检测的代价是误杀=丢可见文本,比漏检
  更糟;白底白字合成样例过了不等于安全,反白设计才是真实世界的反例——
  全语料 MHS 一跑立刻现形。
- **特例(白底假设)不是深度够的解**:正确解是背景模型(altitude),白底假设
  是省掉背景追踪的特例,代价是反白误杀。宁可记余项做对,不留误杀上线。
- CLAUDE.md 经验②印证:参照系口径(MHS)如实反噬了这次不够深的实现。
