# Task 4 对账 —— 处方 1 §5.3「失败可定位」+ Task 5 代价读数

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

## 1. 造故障形态（⛔ 都改闸的输入，不改闸脚本）

| 步 | 闸 | 造法 | 故障态先证（⛔ 不带管道单跑） |
|---|---|---|---|
| **#1** | `audit:icon-fill-currentcolor` | `arrow.ts` 一处 `fill="currentColor"` → `"#dbdbdb"` | `EXIT=1`（Task 2 已证） |
| **#39** | `audit:figma-env-single-source` | `scripts/audit-mockup-colors.mjs` 末尾加一行非注释 `export const __faultInjection = process.env.FIGMA_PERSONAL_ACCESS_TOKEN` | `EXIT=1`，逐字 `❌ S2 audit-mockup-colors.mjs:345 直读 Figma 凭据` |

⚠️ 第 39 步判据有 `COMMENT_LINE_RE` 排除注释行 ⇒ 必须是**非注释**行才命中。

## 2. 预注册对账 —— **2 HIT / 0 MISS**

| # | 预测 | 结果 |
|---|---|---|
| **T4-P1** | 第 1 步与第 39 步同时失败 ⇒ 摘要**恰好两条**，每条含步号+npmKey+exit+stderr 尾部 | **HIT**（4 字段中 3 个恒有，第 4 个见 §4） |
| **T4-P2** | 只 1 条失败时摘要**恰好一条**，⛔ 不得出现第二条 | **HIT** |

### 2.1 双故障摘要原文

```
❌ 2 / 39 步失败（全链已跑完，下面是**只含失败步**的摘要）：

   ── #1 audit:icon-fill-currentcolor  exit 1  (156 ms)
      (stderr 为空 —— 失败明细可能在 stdout，逐条跑该步看)

   ── #39 audit:figma-env-single-source  exit 1  (259 ms)
      |   ❌ S2 audit-mockup-colors.mjs:345 直读 Figma 凭据 —— 改用 …
      | ❌ FAIL — 1 条

   重跑单步：pnpm run audit:icon-fill-currentcolor
```

### 2.2 §5.3-b 走的是**反向**（比计划写的更强）

计划原写「还原第 39 步、只留第 1 步」——但那个场景 **Task 2 已经覆盖**（`❌ 1 / 39 步失败`）。
本 Task 改做**反向**：还原第 1 步、**只留第 39 步** ⇒ 换了一条闸验同一命题。

实测：`❌ 1 / 39 步失败` · 摘要**恰好 #39 一条** · ⛔ 无第二条 ·
「重跑单步」提示随之切到 `pnpm run audit:figma-env-single-source`
⇒ 该提示给的是**第一条失败步**的重跑命令（双故障时给 #1，单故障时给 #39）。

## 3. 🔴 本 Task 最有分量的发现 —— 摘要「stderr 尾部」栏在 **8/11 条契约闸上必空**

双故障摘要里两条**一空一满**：#1 的 stderr 栏空，#39 的有完整明细。
N=2 不足以下结论（AGENTS §2.7）⇒ 升级为**全量静态核**。

### 3.1 静态事实（11 条契约闸逐条，⛔ 只数非注释行的 stderr 写入）

| 闸 | stderr 写入点 | 失败时摘要那一栏 |
|---|---:|---|
| `audit:icon-fill-currentcolor` | **0** | **必空** |
| `audit:no-hardcoded-design-tokens` | 3 | 有明细 |
| `audit:component-no-inline-svg` | **0** | **必空** |
| `audit:figma-conformance` | **0** | **必空** |
| `audit:docs-site` | **0** | **必空** |
| `audit:docs-single-theme` | **0** | **必空** |
| `audit:component-tokens` | **0** | **必空** |
| `audit:tokenized-diff` | 1 | 有明细 |
| `audit:design-system` | **0** | **必空** |
| `audit:published-vs-code` | **0** | **必空** |
| `audit:translation-completeness` | 2 | 有明细 |

**⇒ 8/11 条零 stderr 写入 ⇒ 它们失败时，runner 摘要的「stderr 尾部」栏必然为空。**

### 3.2 计数器口径自查（`lab:N48` 推论五）

| 项 | 查法 | 结果 |
|---|---|---|
| **容器类型** | 同时数 `console.error` 与 `process.stderr.write` | ✅ 两种都数 |
| **标签形态** | 排除注释行（`^\s*(//|\*|/\*)`） | ✅ |
| **字段路径** | 脚本路径从 `package.json#scripts` 正则取，取不到即抛 | ✅ fail closed |
| **截断** | 读整文件，⛔ 非 `head` | ✅ |
| **「是不是失败明细」** | **抽验那 3 条的 stderr 内容** | ✅ **确实是失败明细** —— `f.file:f.line f.property: f.value → var(…)` / `finding.key: finding.message` / `❌ tokenized-diff: N drifted file(s)` |

### 3.3 ⛔ 这**不是**「DS 漏了」——是**已登记的代价**，本轮给它加了个数

[`scripts/run-gate-chain.mjs:249`](../../..) 逐字：
```js
if (r.stderrTail.length === 0) console.error('      (stderr 为空 —— 失败明细可能在 stdout，逐条跑该步看)')
```
⇒ runner **显式判断并打提示**，⛔ **不是静默留空、不是没想到**。

⇒ **同时也不是契约强制的**：契约 v1 只要求 **JSON 打到 stdout**，⛔ 不禁止 stderr 输出 ——
证据就是那 3 条契约闸**两边都写**。⇒ **这是实现习惯的分裂，不是契约的约束。**

⇒ **lab 的增量 = 那个数**：处方 §3.3 第 1 条「失败要能定位」在 **8/11 条契约闸上打折**
（必须再跑一次那步才能看到明细）。**DS 知道这个情形存在，但（据 commit body 与 runner 注释）
没有量化过它覆盖多少条闸。**
⇒ 两条可选修法，⛔ lab 不替 DS 选：
① 契约 v1 加一条「失败明细同时写 stderr」；② runner 在 stderr 为空时截 **stdout** 尾部。

## 4. §5.3 四字段的实际可得性

处方 §5.3 要求摘要含「**步号 + npmKey + exit + stderr 尾部**」：

| 字段 | 可得性 |
|---|---|
| 步号 | ✅ 恒有（`#1` / `#39`） |
| npmKey | ✅ 恒有 |
| exit | ✅ 恒有（另附耗时，超出处方要求） |
| **stderr 尾部** | ⚠️ **8/11 条契约闸上恒空**（见 §3），非契约闸有 |

⇒ **判 ✅（带限定）**：三个字段恒满足，第四个字段的**空值是被显式处理并提示的**，
⛔ 不判 FAIL，但**必须带着那个 8/11 一起报**。

## 5. Task 5（处方 1 §5.5-b）的代价读数 —— 已在 Task 2 一并取到

| 口径（`wt-fault`，同一把尺 `/usr/bin/time` real，各 4 次丢弃第一次） | 均值 | σ |
|---|---:|---:|
| (a) 全绿全链 | **13,465 ms** | 102 |
| (b) 第 1 步失败全链（**改造后**语义） | **13,530 ms** | 73 |
| (c) 「改造前失败即停」**等价口径** = 第 1 步单步 | **157 ms** | — |

- **fail-fast 改造的代价 = (b) − (c) = +13,373 ms = 86.2 倍**（最坏情况：第 1 步就失败）
- (b) − (a) = **+65 ms** < 合并 **2σ=177 ms** ⇒ 失败 run 与全绿 run **统计上等耗时**
  ⇒ 代价的上界就是「全链跑满」，⛔ 不随失败位置变化
- ⚠️ (c) 是**等价口径模拟**，⛔ **不是旧 runner 实跑**（旧形态 = 39 步顶层 `&&`，第 1 步非 0 即停）
- ⛔ 全部带环境名 **`wt-fault`**（`lab:E47`）；⛔ 不与 DS 主工作树或 lab adapter 逐步口径并池

⇒ **处方 §5.5「⛔ 不许只报收益」的要求已满足**：收益（分母恒定 39）与代价（+13.4 s / 86 倍）并排。

## 6. 还原

第 39 步故障 `git checkout -- scripts/audit-mockup-colors.mjs` ·
第 1 步故障 `git checkout -- src/icons/catalog/generated/arrow.ts`
⇒ `dirty=0` · runner `EXIT=0` · `本轮 39 步全部执行 · 闸 37 条` ✅
