# Q1 · B1 批预注册 —— 走 `audit-handoff-deliverable-sections.mjs` 的那一批

> 上游裁定：[`2026-09-08-q1-unclassified-48-classification.md`](2026-09-08-q1-unclassified-48-classification.md)
> ⛔ 判据先写死再动手。⛔ 本批**刻意只做形态完全相同的那些**（照第一批的纪律）。

---

## 0 🔴 先更正我自己 3 小时前写的分档 —— **5 处**

裁定文件把 8 段划进 B1。**逐条回活源核判据串之后，只有 5 段真属于这一批**。
⚠️ 这不是「判据被放宽了」，是**判据面被我数错了** —— 我按「有个 handoff 落点」就划了 B1，
而这条闸的真判据形态是「**规则 §Acceptance 用引号圈定了一个必须落盘的段名**」（头注释逐字）。

| 段 | 我原判 | 活源逐字 | 更正 |
|---|---|---|---|
| `#M23.18` | B1 | L987「起手 **M48 self-check 清单**必含一行…」⇒ 落点是 self-check 清单，**不是 handoff** | **改判待核**：先量 M48 self-check 落哪，⛔ 本批不收 |
| `#M32.3` | B1 | L2008「handoff / wrap-up **能回答**：…（贴一次调用样例）」⇒ 引号里是**样例内容**，不是段名 | **改判 B1′**（形态近似但不同型），单独论证，⛔ 本批不收 |
| `#M43.2` | B1 | L3027「handoff 截图含每列 zoom-in（或显式声明哪些列做了 audit + audit 结果）」⇒ **无引号段名** | **改判 B2** |
| `#M42.2` | B1 | L3140「post-clone 必跑 direct children enumeration…结果入 handoff」⇒ **无引号段名** | **改判 B2** |
| `#M36` | **B2** | L2252「handoff doc 必含 **"reused file-local components"** 段」⇒ 引号圈定 + handoff 落点 | **改判 B1** ✅（我漏了）|

⇒ **B1 真实成员 = 5 段**：`#M21.1` · `#I2` · `#M32` · `#M36` · `#M36.1`
⇒ 裁定文件的分档更正为 **A0 2 · B1 5 · B2 37 · B3 4**（合计 48 ✅，已程序化对账）。

⚠️ **这一次值得记**：被我拿去当依据的「交接」是**我自己 3 小时前写的那份**。
「照交接字面动手前先推演」这条纪律，**对自己写的交接同样成立** ——
裁定表的「档」列是我读完原文后的归类，而判据串是否符合闸的形态，
必须回闸的头注释与规则原文再核一次。⛔ 别把自己的归类当活源。

---

## 1 本批判据串（⛔ 逐字取自活源，均已单跑 grep 命中）

| 段 | Σ | 活源行 | 判据串（逐字） | 机制 |
|---|---:|---|---|---|
| `design-process#M21.1` | 3 | `design-process.md:1324`　`建 component 起手必声明："derive from \`<source_element_id>\`" 或 "from-scratch with rationale"` | `derive from` | ~~`naRe`~~ ⇒ **`altPhrases`**（实测 `naRe` 表达不了，见 §3 的 🔴）—— 规则自己给了「或」的另一支 `from-scratch with rationale` |
| `mockup-conventions#I2` | 4 | `mockup-conventions.md:1326`　`收尾 handoff 的机检块里**同时**出现 \`sectionInternalOverlap\` 与 \`sectionToSectionOverlap\` 两个数` | `sectionInternalOverlap` + `sectionToSectionOverlap` | **`traceLines`**（照 `IA-FIRST` 那条「两段都要、缺哪个点名哪个」）|
| `mockup-conventions#M32` | 3 | `mockup-conventions.md:1950`　`handoff doc 必含一行 "Buttons used: \`<component name>\` variants [\<list\>]"` | `Buttons used:` | `phrase`（与 `M35` 的 `Affordance:` 同形）|
| `mockup-conventions#M36` | 3 | `mockup-conventions.md:2252`　`handoff doc 必含 "reused file-local components" 段` | `reused file-local components` | `phrase` |
| `mockup-conventions#M36.1` | 4 | `mockup-conventions.md:2299`　`handoff doc 的 "reused file-local components" 段（M36 已要求）逐项对得上走查表` | **同上** | 同一条 SECTIONS ⇒ **一条判据覆盖 2 段** |

⛔ **反引号 / 星号不进判据** —— 规则只圈了短语、没规定强调形态。要求 `` ` `` 或 `**`
与本闸头注释已封的「升格成 `^#{1,6}\s+.*Reference probe`」是同一种发明格式。

⛔ **`#M21.1` 的「或」不是「加豁免」** —— 它表达的是规则**自己写的**二选一
（既有先例：`M35` 的 `Affordance: N/A`、`M42` 的 clone skip 条款）。
⚠️ 落地时实测发现 `naRe` 承载不了它，改用新增的 `altPhrases`，见 §3 那条 🔴。
⚠️ 但要如实登记一处**不对称**：`M35` 的 N/A 是「本轮不适用」，
而 `M21.1` 的第二支是「**本轮选了另一条路**」—— 两者都合法，闸都放行，
⇒ ⛔ 别把这条闸的绿读成「都 clone 了既有件」。

---

## 2 落地要动两处，第二处是本批的真代价（⛔ 与第一批同）

亲验：本闸 `--all` 在 DS 仓的分母 = **1 份**
`templates/consumer-product/docs/handoffs/_handoff.template.md`，
它含 `Reference probe` / `clone source probe table` / `CRUD self-check` / `Affordance:` /
`PRD examples scoped` / `Cardinality self-check` / `IA 树` / `Selection model`，
**不含**本批任何一个新短语。

⇒ 只加判据 ⇒ 模板当场红 ⇒ 全仓提交被拦。
⛔ **正解不是放宽判据，是把 4 段加进模板**（照既有 `## 3x. <段名> (<规则>)` 形态）。

⚠️ **这不是新的摩擦取舍**（⛔ 不进 C 档）：四条规则的 Acceptance **本来就写着「必含」**，
闸只是让一个既有要求变得可见 ⇒ 边际代价 = 模板多 4 个待填段。修法唯一 ⇒ B 档。
🔴 **⚠️ 这一段的第二半在落地时被推翻了，见 §4.1** —— 我原本照上一轮写「消费仓不装 githooks」，
而 `templates/consumer-product/.githooks/` 里**带着这条闸的可执行镜像**
⇒ 射程比这里写的宽。⛔ 别照这一段的原话对外描述本批。

⚠️ **`#M32` 与 `#I2` 都没有 N/A 逃生口** —— 规则原文没给 skip 条款，
⛔ 我不许发明一个（那就是放宽判据）。仓内已有先例：模板 `3a. Reference probe` 段
逐字写着「⛔ 本段**无 N/A 逃生口**」。⇒ 照那个形态写。

---

## 3 判据（⛔ 跑之前写死；每条要两侧）

### P1 · 5 段各自会红并**单独**点名
拿缺该段的 handoff 文本喂 `findMissingSections`：
- 缺 `derive from` 且无 `from-scratch with rationale` ⇒ 恰好 1 条，`id` = `M21.1`
- 有 `from-scratch with rationale`（`derive from` 缺）⇒ **0 条**

#### 🔴 P1 那条疑点已实测 —— `naRe` **不成立**，判据面缺了一块

预注册原写「用既有 `naRe` 机制」。**动手前先实测（⛔ 不假设）**：

```
findMissingSections 的既有语义（scripts/audit-handoff-deliverable-sections.mjs:192-195）：
  if (!lower.includes(s.phrase)) { missing.push(s); continue }   ← phrase 缺 ⇒ 当场判缺
  if (!s.traceLines) continue
  if (s.naRe && s.naRe.test(body)) continue                       ← naRe 只在这里被读

探针：把 M35 的 `Affordance:` 整串删掉、只留 `N/A — 本轮未新增`
  ⇒ 仍报缺 M35   ⇒ naRe 救不回 phrase 缺失
```

⇒ `naRe` 是「**跳过 traceLines 检查**」的机制，**不是 phrase 的替代**。
⇒ 按纪律先问「是判据错了，还是判据面缺了一块」——**是后者**：
M21.1 逐字是两个引号短语的**或**关系，而这条闸没有任何字段能表达 `phrase` 层的 OR。

**⇒ 判据面补一块：新增 `altPhrases`**（`phrase` 或任一 `altPhrases` 命中即算写了）。
⛔ **这不是放宽判据**：判据仍然是「规则引号里那两个短语，至少写一个」，逐字取自活源；
在补这个字段之前，闸**根本无法表达**这条规则，只能把它误判成「必须写 `derive from`」。
⚠️ 附带义务（⛔ 不许省）：
- 既有 6 条 SECTIONS **一个字未动**，`altPhrases` 缺省 ⇒ 走原路径 ⇒ 行为零变化（要有阴性对照）
- 造故障验 `altPhrases` 真在起作用（拆掉它 ⇒ 只有 M21.1 那条转红）
- 缺 `sectionInternalOverlap` ⇒ 恰好 1 条，`id` = `I2`
- 缺 `sectionToSectionOverlap`（前者在）⇒ 恰好 1 条，`id` 逐字为 `I2:sectionToSectionOverlap`
  （⛔ 不连坐 —— 照 `IA-FIRST` / `M35` 已有的「缺哪行点名哪行」）
- 缺 `Buttons used:` ⇒ 恰好 1 条，`id` = `M32`
- 缺 `reused file-local components` ⇒ 恰好 1 条，`id` = `M36-REUSE`

### P2 · 不误报
- 5 段都在的文本 ⇒ **0 条**
- 段名写在 ``` 围栏里 ⇒ 仍算缺（`stripFences` 既有语义，⛔ 本批不许改它）
- 大小写不敏感（既有语义）

### P3 · 模板补段后闸转绿，且**真的是靠模板补段转绿的**
`node scripts/audit-handoff-deliverable-sections.mjs --all` ⇒ **EXIT=0** 且 `交付物 1 份`。
**造故障**：从模板删掉 `Buttons used:` 那段 ⇒ **EXIT≠0** 且逐字点名
⇒ 证明绿不是因为判据没接上。

### P4 · 棘轮 46 → 41，BASELINE 只许收到现取值
`node scripts/audit-acceptance-gate-coverage.mjs` ⇒ EXIT=0，
`unclassified` **46 → 41**，`gated` **37 → 42**，`BASELINE_UNCLASSIFIED` 同步收到 **41**。
⛔ 不许把 BASELINE 留在 46。

### P5 · `[N]` 对账
5 行自陈：`#M21.1 [3]` · `#I2 [4]` · `#M32 [3]` · `#M36 [3]` · `#M36.1 [4]`。
**造故障**：任一行写成 `Σ+1` ⇒ `[S4]` 必须逐字印出「逐字有 N 个、自陈 N+1 个」。

### P6 · 该闸既有两份测试零回归
`tests/audit-handoff-deliverable-sections.test.ts` + `…-cli.test.ts` ⇒ 全绿。
若因 `SECTIONS` 变长而失败，**先看它断言的是什么**：
⛔ 断言「SECTIONS 恰好 N 条」⇒ 那是要改测试；
⛔ 断言判据行为 ⇒ 那是我改错了，**不许为了让测试绿而改判据**。

### P7 · 全量回归
`pnpm test` ⇒ 零新增失败（基线 3241 passed）。

### P8 · 还原后零残留
每次造故障后 `git diff` / `cmp` ⇒ 逐字一致。

---

## 4 🔴 落地时撞出的两件事（⛔ 都不在预注册里，如实补登记）

### 4.1 射程比我在 §2 写的**更宽** —— 有一份 consumer `.githooks` 镜像

全量回归红了 3 条，全在 `tests/consumer-hook-mirror-parity.test.ts`。查活源：
`templates/consumer-product/.githooks/audit-handoff-deliverable-sections.mjs`
是 DS 真源的**自包含零依赖镜像**，parity 测试拿同一组 fixture 喂两边并断言判定逐条一致。

⇒ **我 §2 那句「DS 仓分母 = 1 份模板 ⇒ 守的是模板不回退」不完整**：
模板里**已经带着**这条闸的可执行副本 ⇒ 任何**装了 `.githooks` 的消费仓**，
真 handoff 会被这 4 条新判据真的拦下。⛔ 这不是「只守模板」。

⚠️ 同时更正队列 Q1 §顺带查到 第 2 条那句「要让这一族真有牙**需往** `templates/consumer-product/.githooks/`
投放 ⇒ 独立提案」—— **投放早就做了**（handoff 那两条闸都在），
所以那条「独立提案」的前提要重新量：待投放的是 mockup 那一族，⛔ 不是 handoff 族。

⇒ 镜像已按真源逐字同步（含新增的 `altPhrases`）。
**造故障 F6**：把镜像里 `M32` 的判据串改一个字（`Buttons used:` → `Buttons use:`）
⇒ 恰好 1 条转红（「判据字符串两边逐字相同（改了短语就是改了判据）」），还原后 9/9 全绿。

### 4.2 F3 第一次设计的造故障**验不出东西** —— 分母没量

原设计：「从模板删掉 `Buttons used:` 那一段 ⇒ 闸红」。实跑 **EXIT=0**。
根因：`Buttons used:` 在模板里出现 **3 处**（小节标题 `## 3j. Buttons used: (M32)` /
填写形态示例 / 「本轮无按钮就写」那句），删一行剩两处 ⇒ 判据照样命中，**闸绿是对的**。

⇒ 改成把 3 处全部替换后才红（`缺 1 个强制段落` + 逐字点名 `[M32]`，零误伤）。
⚠️ **教训是「造故障之前先量它的分母」** —— 与上闸前先量分母是同一条纪律。
一个删不干净的造故障会给出**绿**，而那个绿会被读成「判据没接上」或更糟的「闸是好的」。

---

## 5 ⛔ 本批不能说的话

- ⛔ 不许说「handoff 族已上闸」—— DS 仓自己的分母是 **1 份模板**。
  ⚠️ 但也 ⛔ 不许照上一轮那样说「只守模板不回退」：见 §4.1，模板里带着可执行镜像，
  **装了 `.githooks` 的消费仓会真的被拦**。准确的说法是「DS 仓守模板不回退 +
  消费仓装了 hooks 的那些会被真拦」，⛔ 两半都别省。
- ⛔ 不许说「Q1 第 2 步做完了」—— 本批 5 段，其中只有 **1 段**（`#M21.1`）属 design-process。
- ⛔ 不许说「每段都有闸了」—— 每段只覆盖被点名的那**一条**；
  `[N]` 是变更探测的锚、不是覆盖度声明。
- ⛔ 不许说 `#M36` / `#M36.1` 是「两条闸」—— 是**一条判据覆盖两段**，
  它们逐字要求的是同一个 handoff 段。
