# H 格预注册：实证 `--probe` 在 G2 上的盲区 —— 把 §28.7 第 3 条那条**结构推理**变成**实跑读数**

**跑前落盘。⛔ 事后不改假设、不改判定规则；改了就在报告里逐条标「事后改」并说明理由。**

- 立档时间：2026-08-27
- **入口**：用户在本 session 起手给的三个候选活里**明确挑了**
  「候选 2 + 顺手改候选 1」——
  - **候选 2**（主活）：`docs/round2-status.md:2264` 逐字登记的未覆盖项 ——
    > 「⛔ **`--probe` 假绿那条未实证** —— 只核了 `inScopeIds` 的构造式；要实证需造一个**同宽不等高**的
    > fixture 走离线纯函数路径。」
  - **候选 1**（顺手）：`proposals/2026-08-27-designqa-gate-gap.md:96` 的「**已实测**」措辞，
    按 `AGENTS.md` 第 23 条推论五改为「转引自 + 标 sha + 现取值」。
- ⚠️ **本格严守 lab `§8` 单向纪律**：只出读数 / 订正 lab 自己的产物，**⛔ 不改 DS 一个字节**。
  ⚠️ 且 `§8` 口径已在 §28.7 订正：**单向 = 流程方向（lab 不执行），⛔ 不是「lab 不给修法」**；
  真边界是 **⛔ 不裁任何一条设计规则的内容**。
- **交付定义（跑前写死）**：
  1. **必须给出 H1–H8 的实跑读数**，每条落进下方预先写好的档。
  2. 实测结论**必须回写**进 `proposals/2026-08-27-geometry-gate-denominator-defects.md`
     （§3.1 第 5 点 + §5.4「未实跑」那条）—— 🔴 **该处方已交 DS 执行，登记面必须与实测一致**
     （第 22 条推论一：判出新事实要**当场回改处方正文**，⛔ 不能只写进报告和 status）。
  3. 候选 1 的措辞回改**落在同一次交付里**。

---

## 1. 被验命题（拆档，⛔ 不合并）

| 半 | 命题 | 性质 |
|---|---|---|
| **①** | 同宽不等高的 sibling state frame **落不进** `collectStateFrameGroups` 的组 ⇒ G2 分母塌 | **可实测事实** ⇒ lab 判 |
| **②** | 该态下整跑 **exit 0 印 ✅ pass**，而一处真实的跨帧宽度不一致**从未被测** | **可实测事实** ⇒ lab 判 |
| **③** | `--probe <该帧内的 control id>` **不触发** `:797` ⇒ 对策通道给假绿 | **可实测事实** ⇒ lab 判 |
| **④** | 🔴 **本格新增**：`inScopeIds` **从不含任何 state frame 的 id**（即使成组成功）⇒ `--probe` 对 G2 **全域**不可断言，⛔ 不只是「在同宽不等高这一态上失效」 | **可实测事实** ⇒ lab 判 |
| **⑤** | 「同宽不等高的帧**还算不算**同一 feature 的状态变体」 | 🔴 **应然判断** ⇒ ⛔ **lab 不裁**（§28.7 已定：DS 立场是「等高是前置不变量」，且被 `tests:151` 锁定） |

⛔ **不验 / 不裁**：G2 该不该改判据 · 等高不变量该不该保留 · `--probe` 该不该扩到 frame 面
（后者是**工程修法**，可给建议，但「该不该做」由 DS / owner 拍）。

---

## 2. 起手实测（**非假设** —— 写本预注册之前已看到的，⛔ 不算本格验证结果）

### 2.0 三条起手纪律

**① 基线实核**（⛔ 不信交接文字）：

| 项 | 实测 | 判 |
|---|---|---|
| lab HEAD | `742d17d` · `git status --porcelain` = **0 行** | ✅ clean |
| lab remote | 🔴 **只有 `origin` 一个**（`github` 不存在）· `origin/master` = `742d17d…e2de` 逐位相同 | ⚠️ 订正：「双 remote」是 **DS 仓**的属性，⛔ 不是 lab |
| DS HEAD | **`1cc95824`** —— ⚠️ **本轮起手未漂**（与 §28.7 收尾同值），交接 prompt 预测的「必然又漂」**本轮不成立** | ✅ 如实记 |
| DS dirty | **2**（`skills/shared-vocab-rules/vocabulary.md` M · `docs/internal/_generated/backlog-owner-decisions.html` ??），口径写死 `grep -v '\.claude/worktrees/'` | ✅ 既存两项 |
| 四 pin | `339a72bf` / `4a68e02d` / `71ac2711` / `c1db57b8` 各 **dirty=0**，HEAD 各等于自己的 sha | ✅ |

**② 第 22 条【第五行】「这活我自己干过没」** —— 判定面 = **`git archive 742d17d`**（= 接手时那个
commit 的树，第 23 条口径三；⛔ 不是工作树、⛔ 不含本格预注册），口径写死排 `runs/`。
判定面自证非空（`lab:N48`）：**230 文件 / 76 md**，已知锚 `geometry-consistency` 命中 **19 文件** ✅。

| 关键读数 | lab 内命中 | 判 |
|---|:-:|---|
| `inScopeIds` | **7 行 / 2 文件** | ⛔ **全是 §28.7 与处方自己写的那条结构推理**，且 `status:2264` **逐字登记了「未实证」** ⇒ **不是「已干过」，是「已登记为待做」** ✅ 第五行通过 |
| `collectStateFrameGroups` | 11 行 / 3 文件 | 全在 §28.6/§28.7/处方/README 的**缺陷 A 取证**面，⛔ 无一处是 probe 覆盖面实测 |
| `假绿` | 19 行 | 全是**别的族**（噪底空壳 · 契约 `checkedUnits` · N33 · 计数假绿），⛔ 与 G2 probe 无关 |

**③ 第 20 条第四行「它还该做吗」** —— 判定面**双口径都报**（第 23 条口径二）：

- **窄口径 = 该族四文件 @`1cc95824`**：裁定词（`刻意|为什么|重开条件|hold|不改|唯一真源|by design|intentional|invariant|deliberate|on purpose|NOT a bug|acceptable`）命中 **6 处**，逐条判：
  `:53` R1/R2 分立 · `:60` 可配置 · `:107` `ignoreNames` 默认空 · `:174` `--file` 用途 · `:146` `rowOutlierRatio` 的 escape hatch ⇒ **五处全不同族**；
  `:279` 「built **equal-height by construction** — that's the **invariant**」⇒ **这是缺陷 A 的裁定（§28.7 已定死）**，⛔ **不是**「probe 不覆盖 G2 是刻意的」的裁定。
- **宽口径 = 全 tracked 树 @`1cc95824`**：`inScopeIds` 命中 **5 行 / 2 文件**（闸 `:719/:720`…共 3 · `tests/audit-*.test.ts:236/:237`）。
  ⚠️ 导出树无 `.claude/worktrees/` ⇒ 第 23 条推论二的 basename 归并**本口径不需要**，如实记。

⇒ **DS 侧 0 处裁定说「`--probe` 不覆盖 G2 是刻意的」**，反而**三处说它该覆盖**：

| # | 出处 @`1cc95824` | 逐字 |
|:-:|---|---|
| ① | `tests/audit-mockup-geometry-consistency.test.ts:232-239` | `describe('runProbes — scope probe surface')` / `it('exposes **every classified node id** so --probe can assert reachability')` —— ⚠️ 而断言只有 `1:1`/`1:2` 两个 **control**，⛔ 对 state frame **零断言** |
| ② | `audit-*.mjs:82-84` | `--probe … assert a node you KNOW must be in scope actually reached the classifier. **Absent probe = the filters are wrong**, not "the target doesn't exist"` |
| ③ | `docs/internal/mockup-conventions.md:2995` | 「扫查脚本的**每个过滤条件**都能答『它排除了什么、为什么目标不在被排除的那部分』，且**已用一个已知必然命中的目标当探针验证过**（硬约束 5）」 |

⇒ **该做 ✅**。且这是「**DS 自己的注释/测试就是它的反证**」这一模式的**第 2 次**（第 1 次 = 缺陷 B 的 `:696-700`）。

### 2.1 起手代码面读数（⛔ 不算验证结果）

1. `inScopeIds` @`1cc95824` = **`:719-723`**，三个来源逐字：`controls.map(c=>c.id)` ·
   `rowScan.groups.flatMap(g=>[g.id, ...g.rows.map(r=>r.id)])` · `dividers.map(d=>d.id)`
   ⇒ **构造式里没有 `collectStateFrameGroups` 的任何产物**（与登记逐位相同，行号未漂）。
2. `:797` 逐字 `if (PROBE_ID && !inScopeIds.has(PROBE_ID))` ⇒ `process.exit(2)`。
3. **controls 族的 exit 路径**：`:812-822` 的 `rowDenominatorEmpty` 只在 `probeKeys` 含 ROW 探针时为真
   ⇒ controls 族恒 `false`；`:805` `over = counts[k] > baseline[k]`；`:872-874` `over.length===0` ⇒ **exit 0 印 `✅ geometry-consistency audit pass`**。
4. `baseline` @`:116` = `{G1:0, G2:0, G3:0, R1:0, R2:0, R3:0}` · `widthTolerance` @`:112` = **1**。
5. 🔴 **G1 与 G2 ⛔ 不冗余**：`probeG1` `:309` 按 **`c.label`**（`labelOf` = 组件集名，`:223-228`）分组；
   `probeG2` `:335` 按 **`c.name`**（**图层名**）分组。
   ⇒ **同图层名 + 不同组件集 label** 的两个 control 会让 **G1 静默（每 label 各 1 项 < 2）而 G2 该命中**。
   —— 这一条决定了 fixture 的构造，**必须这样造才能把 G2 盲区与 G1 隔离**。
6. `classify` `:230-239`：`probe = \`${label} ${node.name}\`` 且模式带 `^` 锚
   ⇒ label 必须**自己**以某条 `controlPatterns` 开头才会被分类 ⇒ 取 `select box/filled` 与 `input box/default`。
7. `controlPatterns` @`1cc95824` = **`:97-104`**（键行 `:97`，六条正则 `:98-103`）。
   —— 候选 1 要用的现值；§28.6 记的 `:97@8a341f46` **仍成立**（该族四文件两 sha 间未变）。

---

## 3. 装置（⛔ 不跑该闸 CLI）

- **⛔ 不跑 CLI**：`:754` `requireFigmaToken()` 在任何 fetch 之前 ⇒ 无 live token 跑不了，**本机不具备**。
- **离线路径**：`git archive 1cc95824 | tar -x` 到 scratchpad，`import` 该闸**自己导出的纯函数**
  （`compileConfig` · `buildComponentIndex` · `collectStateFrameGroups` · `collectControls` · `runProbes`），
  喂 **lab 自造的对照 fixture**。
- **`--probe` 与 exit code 由 lab 逐行复刻**（`:797` 的集合判定 · `:805` 的 `over` · `:872-874` 的分支）
  ⇒ ⚠️ **这是复刻，⛔ 不是实跑 CLI**，报告必须标明。

### 🔴 第 24 条两栏声明（跑前写死）

```
用对方装置复现  ⇒ 量具（纯函数）是 DS 写的
用自己独立路径  ⇒ 🔴 输入是 lab 自造的对照 fixture（造故障），判的是
                 「一处已知必然存在的不一致，有没有被测到」
⇒ 本格属【右栏：造故障】，⛔ 不是【左栏：复现对方读数】——
   ⛔ 不许写成「已验证该闸正确」，只能写「该闸在这一态上漏 / 不漏」
```

### fixture（跑前写死，⛔ 事后不改）

两臂只差 `sf:2` 的**高度**一项，其余逐位相同（⛔ 单变量）：

| 臂 | `sf:1` | `sf:2` | 立意 |
|:-:|---|---|---|
| **Y**（对照 / 判定面非空自证） | `1228x1077` | `1228x**1077**` | 等高 ⇒ 该成组、G2 该命中 |
| **X**（被验态） | `1228x1077` | `1228x**900**` | **同宽不等高** ⇒ 疑似落不进组 |

两臂共有：父帧 `P`；`sf:1` 内含 `INSTANCE c:1` 图层名 **`Ctrl`**、`componentId=comp:A`（组件集 `select box/filled`）、`width=418`；
`sf:2` 内含 `INSTANCE c:2` 图层名 **`Ctrl`**、`componentId=comp:B`（组件集 `input box/default`）、`width=480`。
⇒ **一处真实的、肉眼可见的跨帧同名控件宽度不一致（418 vs 480，Δ62 ≫ tolerance 1）。**

---

## 4. 逐值表（**一档一判定** —— 第 21 条判据；每档只绑**一个可实测事实**）

⛔ 命题⑤（应然）**不进任何一档**。

| # | 读数 | 档 | 判定（⛔ 每档一个） |
|:-:|---|---|---|
| **H1** | 臂 Y `collectStateFrameGroups(P).length` = `n_grpY` | **a** `=1` | 等高态成组成立（预期） |
| | | **b** `=0` | fixture 造错 ⇒ 回炉，⛔ 不许据此推理 |
| | | **c** `≥2` | 有额外分组 ⇒ 先查明机制再判 |
| **H2** | 臂 Y `counts.G2` = `g2Y` | **a** `=1` | G2 判据在等高态**工作** ✅ 判定面非空自证（`lab:N48`） |
| | | **b** `=0` | G2 未命中 ⇒ 我对调用链或 fixture 读错 ⇒ 回炉 |
| | | **c** `≥2` | 查明再判 |
| **H3** | 臂 Y `counts.G1` = `g1Y` | **a** `=0` | G1/G2 隔离成立（同名不同 label 生效） |
| | | **b** `≥1` | 隔离失败 ⇒ **⛔ 不能单独论证 G2 盲区**，须报告说明并改臂 |
| **H4** | 臂 Y `inScopeIds` 含不含 `sf:1`/`sf:2` | **a** 两个都**不含** | 🔴 命题④成立：`--probe` 对 G2 **全域**不可断言 ⇒ §28.7#3 / 处方 §3.1#5 的**范围被订正为更宽** |
| | | **b** 两个都含 | 🔴 处方那条推理**错** ⇒ 当场订正为假阳 |
| | | **c** 含一个不含一个 | 查明机制再判 |
| **H5** | 臂 X `n_grpX` / `g2X` | **a** `n_grpX=0 ∧ g2X=0` | 命题①成立：分母塌（预期） |
| | | **b** `n_grpX≥1` | `WxH` 分组理解错 ⇒ 订正 |
| | | **c** `n_grpX=0 ∧ g2X≥1` | 结构上不可能 ⇒ 说明我读错调用链，先查自己 |
| **H6** | 臂 X `g1X` / `g3X` / **复刻 exit code** = `eX` | **a** `g1X=0 ∧ g3X=0 ∧ eX=0` | 🔴 命题②成立：**假绿实证** —— 印 `✅ pass` 而那处 418/480 从未被测 |
| | | **b** `g1X≥1 ∨ g3X≥1`（⇒ `eX=1`） | 🔴 **命题②不成立** —— 另一条判据接住了 ⇒ **当场推翻** §28.7#3 的假绿读法并订正处方 |
| | | **c** `g1X=0 ∧ g3X=0 ∧ eX≠0` | 我复刻的 exit 逻辑错 ⇒ 先查**自己的装置**（§18.7），⛔ 不许当结论 |
| **H7** | 臂 X 复刻 `--probe c:1`（一个「我确知必须在 scope 内」的 control） | **a** 不触发 `:797` | 命题③成立：对策通道在这一态**给绿** |
| | | **b** 触发 | 对策通道其实拦住了 ⇒ **推翻**命题③ |
| **H8** | 臂 X 复刻 `--probe sf:2`（被丢弃的**帧本身**） | **a** 触发 ⇒ exit 2 | 存在一种探针能报红，但话术是「filters are wrong」⇒ **半个**对策，需在报告里区分「报红」与「报对」 |
| | | **b** 不触发 | 完全无感 |

### 取值域自查（第 23 条推论一 —— 反复漏的那一格）

- `n_grp` / `g1` / `g2` / `g3` ∈ ℕ ⇒ 每条都有 `=0` / `=1` / `≥2` 三档，**覆盖全** ✅
- `exit code` ∈ {0,1,2} ⇒ H6 三档覆盖 `0` / `1` / `其它` ✅
- `inScopeIds` 含否 ⇒ {都不含, 都含, 含一个} **覆盖全** ✅
- ⚠️ **H8 的取值域只有二值**（触发 / 不触发）—— 逐字确认 `:797` 是单条布尔判定，⛔ 无第三态。

### 一档一判定自查

逐档过一遍：**H1–H8 共 20 档，每档只绑一个可实测事实** ✅
⚠️ 曾差点焊在一起的是 **H6a** ——「exit 0」（技术事实）与「这是**缺陷**」（价值判断）。
⇒ 已拆：H6a 只判 **exit 0 ∧ 那处不一致未被测**；「这算不算缺陷 / 该不该改」⛔ 不在档内。

---

## 5. must-hit / must-not-hit（⛔ 少一条就是假绿）

| 类 | 条 | 为什么 |
|:-:|---|---|
| **must-hit** | 臂 Y 必须产出 `g2Y ≥ 1` | 判定面非空自证（`lab:N48`）—— 装置能出 finding，`g2X=0` 才有意义 |
| **must-hit** | 两臂的 `n_grp` / `counts` 必须由**同一份脚本、同一次进程**产出 | 排「两次跑法不同」的口径漂 |
| **must-not-hit** | 臂 X 与臂 Y 的 fixture **只许差 `sf:2.height` 一项** | 单变量；程序化 diff 自证 |
| **must-not-hit** | ⛔ 不写 DS 仓一个字节 | 全部操作在 scratchpad 导出树上；收尾复证 DS dirty 仍 = 2 |
| **must-not-hit** | ⛔ 不跑 `pnpm build` / `release` / 四 pin 上的任何构建 | 硬约束 |

---

## 6. ⛔ 本格明确不覆盖（跑前写死）

1. ⛔ **不跑该闸 CLI 本体**（需 live Figma token）⇒ probe 判定与 exit code **均为逐行复刻**。
2. ⛔ **不跑 `pnpm vitest`** ⇒ 「改了会不会转红」仍是读源码推理（§28.7#1 原样保留）。
3. ⛔ **不验 live Figma 上真实存在同宽不等高的帧** —— 本格证的是「**闸对这一态漏**」，
   ⛔ **不是**「DS 的真实文件里有这一态」。**两句话不可互相冒充**（第 23 条推论四同族）。
4. ⛔ **不裁**命题⑤（等高不变量的应然），⛔ 不裁 `--probe` 该不该扩面。
5. ⚠️ 裁定 grep 的措辞集**按措辞取数，⛔ 不保证穷尽**（第 21 条推论一）。
