# Task 3 对账 —— 处方 1 §5.2「步骤面不是第二真源」（⛔ §18.8 漏登的两格）

**环境名 `wt-fault`** · 三元组 `(c1db57b8, n/a, n/a)`

⚠️ 上轮报告 §4 表里 §5.2 那行判 **✅**，覆盖的是**命题的静态证据**
（「runner 零命中步骤名 / 解析走 `CHAIN_SOT_KEY` 单一真源」）。
proposal §5.2 的**两个 checkbox 都是动态造故障**，⛔ 与静态证据不等价。本 Task 才是它们。

## 1. 预注册对账 —— **2 HIT / 1 部分推翻**

| # | 预测 | 结果 |
|---|---|---|
| **T3-P1** | 追加第 40 步、**不改 runner 一个字** ⇒ 逐步表 **40 行** | **HIT**（双装置都报 40） |
| **T3-P2** | npm key 改成不存在 ⇒ runner **当场抛/非 0**，⛔ 不静默跳过 | **HIT**（`EXIT=2`） |
| **T3-P3** | 不存在的 key **很可能由 pnpm 报错而非 runner 检测** ⇒ fail closed 成立但责任方是 pnpm | **⚠️ 推翻一半** —— runner **有自己的前置校验**，pnpm 只是链路径的兜底（见 §3） |

## 2. §5.2-a：新增一步 ⇒ 40 行

改动 = `package.json#scripts.gate-chain` 末尾追加 ` && pnpm run audit:icon-category-enum`。

| 核验项 | 实测 |
|---|---|
| `git status` | **只有 `package.json` 一行** |
| `git diff --numstat package.json` | **`1  1`** ⇒ 只改 1 行，⛔ 格式未被整文件重写（`lab:E44` 教训） |
| `git diff --stat scripts/run-gate-chain.mjs` | **空** ⇒ **runner 一个字没改** ✅ |
| **DS runner**（被测对象） | `40 步（闸 38 · 非闸 2）` · 逐步表第 40 行 = `audit:icon-category-enum` · `EXIT=0` |
| **lab adapter**（独立第二实现） | `steps.length` = **40** · `chainSource.key` = `gate-chain` · 末步同 · 控制 **18/18** |

⇒ **两个独立实现都从 `package.json` 读出 40** ⇒ **步骤面确实是唯一真源，runner 没抄清单** ✅

⚠️ **我自己写了一次 fail-open 判定**：判控制全绿时用 `c.pass ?? c.ok ?? true` ——
真实字段是 `ok`，`c.pass` 恒 `undefined` 于是取到 `c.ok`，**结果碰巧是对的**。
但 `?? true` 的兜底意味着**字段一改名就假绿**。已改成 fail-closed
（`typeof c.ok !== "boolean"` 即抛）重判：**18 条 0 失败**。
⇒ **`lab:N48` 的一个新形态：前七例是「结果错」，这次是「结果对但装置错」——
后者更危险，因为不会被结果暴露。**

## 3. §5.2-b：坏 key ⇒ 两条路径**责任方不同**，⛔ 别合并成一句「fail closed 通过」

把第 2 步换成 `pnpm run audit:this-key-does-not-exist`（`diff --numstat` = `1 1`）：

| 路径 | 命令 | EXIT | 谁抓到的 | 何时抓到 |
|---|---|---:|---|---|
| **①** | `node scripts/run-gate-chain.mjs` | **2** | **runner 自己** | **跑任何一步之前** |
| **②** | `pnpm run gate-chain`（`&&` 链） | **1** | **pnpm** | 跑到第 2 步才炸 |

**① 的输出逐字（全部，只有这一行 —— ⛔ 没有逐步表）：**
```
✗ gate-chain-steps: 第 2 步引用了不存在的 npm key `audit:this-key-does-not-exist`
  —— fail closed， ⛔ 不许静默跳过（静默跳过 = 把一条闸从分母里悄悄摘掉）。
```

**② 的输出尾部：**
```
（第 1 步的契约 JSON 已完整输出 …… "checkedUnit": "icon-entry" ）
 ERR_PNPM_NO_SCRIPT  Missing script: audit:this-key-does-not-exist
 ELIFECYCLE  Command failed with exit code 1.
```

⇒ **三处结构性差异，⛔ 都不能合并**：
1. **时机**：runner 是**前置全量校验**（一次性发现所有坏 key）；链是**跑到那步**才炸
   ⇒ 链路径下前面的步骤已经白跑了。
2. **退出码分层**：runner 用 **`EXIT=2`** 与「闸真的失败」的 `EXIT=1` **区分开**
   ⇒ 「装置坏了」与「被测对象坏了」可由退出码分辨。
3. **理由文字**：runner 逐字写出**为什么**要 fail closed
   （「静默跳过 = 把一条闸从分母里悄悄摘掉」）—— 这正是处方的立论。

⇒ **处方 §5.2 must-not-hit 被超额满足**：处方只要求「当场抛/非 0，不静默跳过」，
实测 runner 还额外做了前置校验 + 退出码分层。
⇒ ⚠️ 但 **CI 走的是 `prepublishOnly` → runner 路径**（`EXIT=2`），
而**开发者手敲 `pnpm run gate-chain` 走的是 ② 路径**（`EXIT=1`，pnpm 兜底）
⇒ **两条路径的诊断质量不同**，如实登记，⛔ 不写成「一律 fail closed」。

## 4. 还原

`git checkout -- package.json`（两次各一回）⇒ `dirty=0` ·
还原后 runner `EXIT=0` · `本轮 39 步全部执行（曝光分母恒定 = 39）· 闸 37 条` ✅
