# ai-ds-lab 设计文档

- 初版：2026-08-21
- 修订：2026-08-24（v2 — 评审整合）
- 状态：待用户复核
- 作者：Nancy + Claude

> ⚠️ **第一轮实测已推翻本文若干处数字与推论，见文末 §16 勘误。** 正文保持原样不改写——
> 勘误只登记「哪一句错了、被哪份报告的哪个数推翻」，证据留在报告里，不在此处复制第二份。

> **v2 修订摘要**：`rule-hit` 方法论纠错（数据源 + 挂载筛 + 反事实）、baseline 主键改三元组、新增噪声地板、指标 4→7、新增「多 AI 中立」为一等约束、新增被测对象实测数据与归档处置原则、retrospection 自动化提炼方案、执行计划重写。
> **v2 的两项实证结论**：13 个 mockup gate 处于「未执行」状态（§10.4）；归档物不可一刀切删除，须三分法处置（§11）。
> v1 全文保留在 git history（`1f36923`）——本项目主张「不留双份」，此处即自我遵守。

---

## 1. 这是什么

一个**独立于 TVU DS 的外部评测台 + 方法论仓库**，用来分析和优化「AI 消费 / AI 维护的产品设计系统」。

一句话定位：**它是 DS 的体检科，不是 DS 的一部分。**

### 1.1 为什么需要它

TVU DS 实测状态（2026-08-24，commit `71ac2711`）：

| 项 | 实测值 |
|---|---|
| `scripts/*.mjs` | 99 个 |
| 其中 `audit-*.mjs` | 66 个 |
| `docs/` md 文件 | 327 份 / **94,830 行** |
| `prepublishOnly` gate 链 | **39 步纯串行** |
| 存量 HTML 产物 | 625 份 |

也就是说：**不缺 gate，也不缺规则。** 多轮优化仍"不尽如意"的病因判断是——

> 规则/gate 在持续增生，但缺少一个站在 DS 之外的、**结果层**度量，来回答"这一轮改动到底有没有把产出质量推上去"。

DS 现有 gate 绝大多数是**规则层**（这条写法合不合规），缺**结果层**（AI 在无人纠正下产出的整体质量与稳定性）。lab 补的就是这一层，并顺带把 gate 体系本身变成被审计对象。

> **修正（v2）**：DS 并非完全没有元层。已存在 `audit-rule-inventory`、`audit-gate-mount-declaration`、`audit-gate-ci-parity`、`audit-rule-load-map`、`audit-rule-number-collision` 五个 meta-gate。它们审的是**规则的形式一致性**（有没有挂载、编号撞不撞、CI 是否对齐），仍不回答**规则有没有用**。lab 补的是后者。

### 1.2 非目标（明确不做）

- 不做第二套设计系统，不产出组件
- 不替代 DS 现有 gate（复用它们，但标注裁判来源）
- 不追求全自动流水线（v1 是半驱动）
- 不做通用开源框架；结构上支持多 subject，v1 只服务 `tvu-ds`

---

## 2. 架构决策

| # | 决策 | 选择 | 理由 |
|---|---|---|---|
| D1 | 项目定位 | 外部评测台 + 方法论 | 需要能重跑的数字，而不只是经验文档 |
| D2 | 取样方式 | 半驱动：人/subagent 触发生成，台子打分 | 能测收敛率（需重复生成），又不用搭自动化 agent 基础设施 |
| D3 | 判据来源 | 混合：结果层指标 lab 自写；DS gate 作参考且本身受审 | 独立性与成本的平衡；避免盲区与 DS 完全重合 |
| **D4** | **AI 中立性** | **规则声明与执行载体分离；lab 与 DS 均不绑定单一 AI** | **DS 要能被 Claude / Codex / 其他模型共同消费；绑定单家会让「规则质量」和「某家 AI 的适配度」混为一谈** |
| **D5** | **归档出口** | **归档目录必须有删除出口，不得只进不出** | **无出口的归档池是增生的结构性来源（实测：`_archive` 已达 29,537 行）** |

各决策的完整取舍见 `docs/decisions/`。

---

## 3. 项目形态

- 路径：`~/Documents/AICoding/VS_Code/ai-ds-lab/`
- 独立 git repo。~~先只在本地，是否推远端后续再定~~ → **2026-08-24 已定：推 Gitea `ux-team/ai-ds-lab`**，
  与 DS 同一个团队真源、同为公开（DS 实测 `private: false`）。理由：lab 的结论本来就是给 DS 团队看的，
  且无远端 = 单点（第一、二轮全部实测产物只存在一台机器上）。
  ⚠️ **随之而来的一条约束**：报告里那些「计数可靠、归因不可靠」的边界登记从此要挡团队误读，
  不再只是给自己看的备注 —— §16 各条勘误与各报告的「覆盖边界」章节是这道防线，不得省略。
- 技术栈：Node ESM（`.mjs`），零构建。与 DS 同源，adapter 可直接 `import` DS 的 audit 脚本
- 与 DS 的耦合方式：`subjects/tvu-ds/subject.json` 记录 DS 仓库路径 + pin 的 commit sha

**方案取舍**：曾考虑放进 DS 仓库子目录 / pnpm workspace 双包，均否决——要评的恰恰是"DS 的规则体系本身有没有问题"，裁判和被告不能同居；且 DS 仓库已经很重，不该再涨。

**AI 中立约束（D4）**：lab 的 `metrics/`、`adapters/` 必须是纯 Node，不依赖任何 AI harness 的特有能力。lab 可以被任何 AI 驱动，也可以被人手工驱动。

---

## 4. 目录结构

```
ai-ds-lab/
├── README.md              # 这是什么 + 怎么跑一轮
├── AGENTS.md              # lab 自己的规则（中立文件名，见 §9）
├── docs/
│   ├── specs/             # 本设计文档
│   ├── framework/         # dimensions / metrics / diagnosis / anti-patterns
│   ├── decisions/         # ADR：为什么这么评
│   └── sessions/          # 对话沉淀 YYYY-MM-DD-<topic>.md
├── subjects/tvu-ds/       # subject.json：DS 路径、pin 的 commit、gate 清单
├── tasks/tvu-ds/          # golden task set（冻结 + 版本化，见 §7.5）
├── runs/                  # 每次生成的产物 + run.json
├── metrics/               # 自写的结果层打分脚本（纯 Node）
├── adapters/              # 调用 DS gate 的桥接层
├── judges/                # 评委契约与 prompt 模板（模型无关，见 §9.3）
├── proposals/             # 处方：结构化改进建议（含实验证据）
├── experiments/           # worktree 实验记录与指标对比
├── baselines/             # 指标快照 JSON，按三元组分线（见 §5.2）
└── reports/               # 人读报告，带与上一次的 diff
```

> `CLAUDE.md` 改名 `AGENTS.md`：lab 自己也要守 D4。若某 harness 只认特定文件名，用 symlink 或生成物指向 `AGENTS.md`，真源只有一份。

---

## 5. 核心数据流（半驱动）

```
tasks/ 定义任务
  → 人 / subagent 在任一 AI 中跑 N 次，产物落 runs/<runId>/
  → metrics/ 扫 runs 打分（T1 脚本 + T2 异构陪审团 + T3 人工）
  → 写 baselines/<线>/<date>.json
  → 生成 reports/<date>.md（对比同线上一次：哪个指标动了）
```

### 5.1 run 元数据（可比性的前提）

每个 `runs/<runId>/run.json` 必须记录：

| 字段 | 说明 |
|---|---|
| `subject` | 被测对象，如 `tvu-ds` |
| `subjectSha` | DS 仓库的 commit sha（**缺失则该 run 作废**） |
| `taskId` | golden task 编号 |
| `taskSetVersion` | golden set 版本（见 §7.5） |
| `repeatIndex` | 第几次重复 |
| `model` | 生成所用模型的完整标识（厂商 + 型号 + 版本） |
| `promptHash` | prompt 内容哈希 |
| **`contextManifest`** | **cwd、实际加载的规则文件清单及各自 hash、harness 类型与版本** |
| `timestamp` | 真实系统时钟 |

**为什么要 `contextManifest`**：`promptHash` 只保证 prompt 一致，但 AI 的实际上下文还包括入口规则文件、按需加载的文档、harness 注入的内容。同一 prompt 在不同 session/harness 下注入内容可能不同。没有它，`convergence` 测到的是 session 状态噪声，不是 DS 的稳健性。

**执行纪律**：每次 run 必须在**全新会话、固定 cwd** 下执行。

### 5.2 baseline 主键：三元组，不是单一 sha

**v1 的漏洞**：只 pin 了 `subjectSha`。但产出质量 = f(**DS 质量**, **模型能力**, **prompt/上下文构造**)，DS 只是其中一项。而模型是外部不可控、会静默更新的——**这比 DS 漂移危险得多：DS 的变化你自己知道，模型的变化你不知道**。

**规则**：

```
baseline 主键 = (subjectSha, modelId, promptTemplateVersion)
```

任一分量变化即另起一条基线线，**不允许跨线直接 diff**。

**模型重锚定协议**：模型换代时，用新旧模型各跑一次**同一个 pinned DS 版本**，量出纯模型带来的偏移量，作为后续解释差异的校正项。没做重锚定就跨模型比较的结论一律作废。

---

## 6. 三层判定

| 层 | 谁判 | 判什么 | 特性 |
|---|---|---|---|
| **T1 脚本** | 纯代码，不用 agent | token 合规、逃逸值、结构规则、gate 命中 | 零成本、秒级、100% 确定 |
| **T2 异构陪审团** | 3 个**不同厂商/型号**的 agent 投票 | 组件语义选择、命名与意图匹配 | 中成本、可并行、有方差 |
| **T3 人工** | Nancy | 视觉品质、交互合理性、**T2 的分歧项** | 高成本，只花在必要处 |

### 6.1 T2 的三条铁律

1. **评委必须异构。** 三票来自不同厂商/型号的模型。**同构模型的三票是相关的，不是独立的，会系统性低估方差**——这既是可移植性要求（D4），也是统计有效性要求。
2. **评委也要被测。** 抽样 20% 的样本让陪审团重复判 2 次，记录评委自身方差。
   - **判据不是「评委方差 > 被测方差就作废」**（v1 写法有缺陷：被测方差趋近 0 时，指标越好越容易被判作废）。
   - 改为：**评委方差 < 最小可检测效应（MDE，见 §6.2）的 1/3**，否则该指标本轮结论不采信。
3. **不接受文字总结。** agent 只输出结构化裁决 JSON（`verdict` + 证据定位 + `confidence`），禁止"通过 / 完成 / N 项 pass"式断言；判定 agent **拿不到生成时的上下文**（黑盒判）。

### 6.2 噪声地板与最小可检测效应（v2 新增）

`first-shot` 和 `convergence` 都是随机变量的统计量。**第一轮该做的第一件事不是拿 baseline，是测噪声地板。**

```
固定 (subjectSha, modelId, promptTemplateVersion) 三元组不变
  → 同一 prompt 跑 N≥5 次
  → 量出各指标自身的重复测量方差
  → 该方差定义 MDE（最小可检测效应）
```

**MDE 的作用**：后续任何"改进"必须超过 MDE 才算真的改进。没有它，§8.1 闭环最后一步"baseline diff 说话"说的可能全是噪音。

**注意区分两种方差**：生成过程方差（被测系统固有噪声，通常更大）≠ 评委方差（T2 的判定噪声）。两者都要测，用途不同。

### 6.3 T2 的成本控制

3 评委 × 抽样复判 → 若每样本都复判，10 task × 3 重复 = 180 次调用，与 §15「单轮 10 分钟内可重跑」直接冲突。

**约束**：T2 只用于 **T1 原理上无法判定**的维度（组件语义选择、命名与意图匹配）。文案合规、状态完整度等大部分可下沉 T1。方差校验按 20% 抽样。

---

## 7. v1 指标

### 7.1 指标表

| 指标 | 算什么 | 回答什么问题 | 层 | 轮次 |
|---|---|---|---|---|
| `escape-rate` | 产物中字面色值/间距占比 + Top-N 逃逸值清单 | DS 最该补的洞是哪几个 | T1 | **第一轮** |
| `context-cost` | 注入 token 量 / gate 数与运行时长 / 规则文档行数 / 规则冲突数 | 规则是否在纯增加成本 | T1 | **第一轮** |
| `first-shot` | 单次产出、无人纠正下的合规比例 | 易用性硬数字 | T1+T2 | 第二轮 |
| `convergence` | 同任务 N 次之间组件选择/结构的差异 | 稳健性硬数字 | T1+T2 | 第二轮 |
| `rule-hit` | 各 gate 的命中次数、误报、0 命中项 | 哪些 gate 在干活 | T1 | **需前置改造，见 §7.3** |
| `actionability` | gate 失败后 AI 无额外提示的一次修复成功率 | 报错文案值不值 | T1+T2 | 第二轮 |
| `portability` | 同一 task 跨不同 AI 的产出质量差 | 规则是否绑定了某家 AI | T1+T2 | 第三轮 |

### 7.2 `context-cost`（v2 新增，第一轮就做）

**为什么加**：lab 的立项动机是治理规则增生，但 v1 的 4 个指标没有一个测增生本身。实测 DS docs 已达 **94,830 行 / 327 份**。这些吃 AI 的上下文预算，而预算被吃掉本身会拉低产出质量，形成正反馈：

```
加规则治质量 → 挤占上下文 → 质量下降 → 再加规则
```

**分量**（全部 T1，`wc` 级零成本）：

| 分量 | 量什么 |
|---|---|
| 注入 token 量 | 一次典型生成任务实际加载进上下文的 DS 文档总 token |
| gate 数 / 总运行时长 | 66 个 audit 的规模与摩擦成本 |
| 规则文档总行数 | 327 份 / 94,830 行的趋势 |
| 规则冲突数 | 互相矛盾的规则对（先人工，后脚本） |

**它驱动什么决策**（§13 第 4 条自查）：`context-cost` 上升而 `first-shot` 不升 → 证明规则在纯增加成本不产生收益 → 触发精简。

### 7.3 `rule-hit` 的正确做法（v2 重大纠错）

v1 的写法有**两层错误**，若照做会让 lab 的第一份处方就是删掉一批有效的 gate——**危害大于什么都不做**。

**错误 1：数据源错。** v1 说"用存量产物离线跑 `rule-hit`"。但 **gate 拦截发生在生成/提交过程中，最终产物里看不到被拦下的东西**。625 份存量 HTML 全是已纠正的终版，拿它算命中率是系统性归零。

正确数据源是 gate 的**执行历史**。实测现状：

- [gates.yml](../../../tvu-design-system/.github/workflows/gates.yml) 是 `workflow_dispatch` only，且注释写明 CI 与发布已迁至 Gitea（`product-demo.tvustream.com`）
- `gh run list` 对该 remote 返回 404
- 常态执行点实际是 `scripts/release.mjs` 的 prepublishOnly 链

→ **`rule-hit` 的数据源尚未落实。** 前置动作二选一：
  - (a) 确认 Gitea run 日志的留存期与程序化拉取方式；或
  - (b) **给 gate 加轻量命中埋点**（`{ruleId, hit, evidence}` 追加 JSONL）——这是前向采集，不是历史数字

**推荐走 (b)，并与 §11.2 的 gate 合并同时做**：合并成「引擎 + 规则模块」后每条规则天然带 id，埋点几乎免费。**所以顺序是：先合并 → 埋点 → 再测命中率**，不能反过来。

> ### ⚠️ 以上三段已被 2026-08-24 实测推翻（勘误 E12 / E13），改为：
>
> - **(a) 不可行，但错在 Gitea 而非日志留存**：Gitea **1.22.6** 的 API 根本没有 actions run/log 路由（N13）。
> - **(c) 一条原文没列的路径成立**：`.github/workflows/ci.yml` 是 `on: push` 的活 CI，
>   其日志可程序化收割出**每条闸的曝光与拦截**——**不经过合并、也不经过埋点**。
>   已落为 `adapters/ds-ci-gate-history.mjs`，第一份读数见
>   `reports/2026-08-24-round2-preconditions.md` §1.5（377 条 run / 38 条闸 / **只有 4 条曾拦下过**）。
>   ⚠️ 它是**有偏子样本**（`paths:` 过滤）且**窗口已于 2026-08-14 关闭**（N14）。
> - **(b) 的形态要改**：不是「给每条闸加埋点」，而是「**统一 stdout 输出契约**」——
>   闸本来就在打输出，38 条里已有 8 条是结构化 JSON（N11）。⇒ 第二轮 **7b**。
> - **顺序纪律改为**：`rule-hit` 的真前置是**第 9 步（解 fail-fast）**，不是第 7 步（合并）。

**错误 2：推论错（幸存者偏差）。** "0 命中 = 摩擦，该删"不成立。**一个 gate 0 命中，很可能恰恰因为它有效**——它连同其规则文档已让 AI 不再产生那类违规，是威慑生效。按 0 命中删除，违规就回来了。

**硬规则**：

> **0 命中的 gate 不得直接提删除 proposal。** 必须先做反事实实验：在 DS worktree 里把该 gate **连同其对应的规则文档一起**摘除，重跑生成，观察违规率是否回升。回升 = 威慑有效，保留；不回升 = 确认冗余，可提 proposal。

**交叉判据**：`rule-hit`（事后拦截）与 `discoverability`（事前触达，§12）联合判定。**判定前必须先过挂载筛（见下表第一行）**——

| 挂载 | 曝光 | 拦截 | 触达 | 结论 | 处方 |
|---|---|---|---|---|---|
| **无** | — | — | — | **未执行**：规则与脚本都在，但没有执行点 | **接上执行点**，不是删。实测 **11** 条 mockup gate 在此类（原写 13，见勘误 E2；§10.4） |
| 有 | **被截断** | 0 | — | **⚠️ 上场了但没轮到**：`&&` fail-fast 让它排在失败点之后（勘误 N10） | **改链条结构（§14 第 9 步）**，不是接执行点、也不是反事实实验。⛔ 分母没修好之前不得进入下面三行的判定 |
| 有 | 满 | 0 | 0 | 死规则 | 可删（仍需反事实实验确认） |
| 有 | 满 | 0 | 高 | 威慑生效 | 保留 |
| 有 | 满 | 高 | 低 | 规则有效但难发现 | 改可发现性，别改规则 |

**顺序纪律**：先盘挂载，**再盘曝光**，最后才谈命中。
把未挂载的 gate 当成"0 命中所以无用"是最容易犯的错——**它们是从没上场，不是上场没拦住**；
而被 fail-fast 截断的是**上场了但没轮到**。⚠️ **这三类在朴素的「0 命中」口径下长得一模一样。**

> **`hit` 与 `blocked` 也要分开。** 实测存在「每次都报 findings、一次都不拦」的闸
> （`audit:no-hardcoded-design-tokens`，377/377 报、0 拦，`noMatch` 按设计只报不拦）。
> ⇒ 采集时两者分列，⛔ 不得合并成一个 `hit` 布尔（勘误 N15）。

### 7.4 及格线

**不预设及格线**。第一轮建立 baseline 与 MDE；及格线在拿到真实分布之后再定。

### 7.5 golden set 的冻结与版本化（v2 新增）

**冲突**：task 要取自真实需求（不虚构），但真实需求随时间变，而 baseline 要跨月对比**同一个** set。

**规则**：

- golden set **冻结并版本化**（`taskSetVersion`），新需求进入「候选池」
- 每季度做一次 set 升级
- **升级当轮新旧 set 各跑一遍做桥接**，否则 set 一换历史 baseline 全废

---

## 8. 只读边界与优化闭环

lab 的"只读"约束对象是**真源**，不是禁止改进。三层：

| 层 | 允许什么 | 理由 |
|---|---|---|
| **DS 真源（main）** | lab **永不直接写** | 裁判与执行者合一会导致"为了数字好看而改 DS"；且并行 session 写同一仓库必撞 |
| **实验隔离（git worktree）** | lab **可以改**——做实验性改动，验证"这么改指标会不会变好" | 处方必须可验证。用完即弃，不推不合 |
| **处方交接** | lab 产出结构化 proposal，由 DS 侧 session 在真源上正式执行 | 保持单向：诊断 → 处方 → 执行 → 复检 |

### 8.1 优化闭环

```
lab 发现问题（带证据）
  → worktree 实验改动，重跑指标 → 证明改法有效（改善量 > MDE）
  → 落成 proposal（实验证据 + 验收指标）
  → DS session 在真源执行
  → lab pin 新 commit sha，重跑同一 golden set
  → baseline diff 说话：真变好 / 没动 / 变差
```

前几轮"不尽如意"的结构性原因即最后一步缺失：改完没有复检，只能凭手感。有了闭环，"如意"成为可判定事件。

### 8.2 proposal 结构

每条 proposal 必须包含：证据（run 引用 + 定位）、影响面、**失败成本加权**、建议改动、验收指标（改完重跑哪个指标、期望方向、**期望改善量需 > MDE**）。

---

## 9. 多 AI 可移植性（v2 新章 · D4）

DS 要能被 Claude Code、Codex 及其他 AI 共同消费。核心原则：

> **规则声明一次，用机器可读的中立格式；各家 AI 的入口文件退化成薄适配层。**

### 9.1 现有耦合点（实测）

| 耦合 | 说明 |
|---|---|
| `CLAUDE.md` | Codex 用 `AGENTS.md`、Gemini 用 `GEMINI.md` |
| `docs/superpowers/plans/` 21,054 行 | superpowers 是 Claude Code 的 skill 体系 |
| prepublishOnly 第 17 步 `lint-skills` | skills 是 Claude 特有概念 |
| `.claude/` 目录 | harness 私有 |

**已经做对的部分要保住**：66 个 gate 本身是中立的——Node CLI + 退出码，任何 AI 都能 `node scripts/x.mjs` 读结果。**不要让 gate 的调用依赖某个 harness。**

### 9.2 目标结构

| 层 | 内容 | 谁消费 |
|---|---|---|
| **规则真源** | `rules/*.yaml` —— 每条：`id` / 约束描述 / 判定 gate / 适用范围 / 严重度 | 机器 |
| **入口适配层** | `CLAUDE.md`、`AGENTS.md`、`GEMINI.md` 全部改为**生成物**，从真源渲染 | 各家 AI |
| **判定契约** | gate 统一输出结构化 JSON（建议直接用 SARIF，生态现成） | 任何 AI |

配一个 gate 审「入口文件与规则真源同步」。加一家 AI 只需加一个渲染模板，不重写规则。

### 9.3 lab 自身的中立要求

1. **评委是契约，不是某家模型。** `judges/` 定义输入（样本 + 维度 + 输出 schema）与输出（结构化裁决 JSON）。谁当评委可替换。
2. **陪审团必须异构**（§6.1 铁律 1）——同时满足可移植性与统计独立性。
3. **`metrics/`、`adapters/` 纯 Node**，不依赖任何 AI harness。
4. **`portability` 指标**：同一 task 跨不同 AI 的产出质量差。差异大 → 规则依赖了某家 AI 的特有机制 → 该改写成中立表达。

---

## 10. 被测对象的实测结构性问题（v2 新章）

2026-08-24 对 `71ac2711` 的实测。这些是**第一轮开跑前已经确定的**发现，不需要等指标。

### 10.1 docs 构成

| 目录 | 行数 | 占比 |
|---|---|---|
| `internal` | 36,226 | 38%（其中 `_generated` 4,885） |
| `_archive` | 29,537 | 31% |
| `superpowers` | 21,054 | 22% |
| 合计 | **94,830** | 327 份 |

体积分布：>2000 行 4 份、1000–2000 行 13 份、500–1000 行 31 份、200–500 行 72 份、<200 行 209 份。

**最大的 4 份**：`asset-inventory.md` 4,097 / `STATUS-CHANGELOG.md` 3,951 / `mockup-conventions.md` 3,238 / `_generated/a11y-contrast-report.md` 2,891。

**判断**：
- `_generated/*`（4,885 行）是报告不是规则，不该在 docs 树里给 AI 读 → 移到 `reports/` 或 gitignore
- `STATUS-CHANGELOG.md`（3,951 行）是流水账，同上
- `asset-inventory.md`（4,097 行）看名字是**数据清单**。**数据不该是 md** → 改 JSON，AI 查询便宜一个数量级，且可被 gate 校验。凡"清单/表格"性质的 md 都该降级为数据文件

### 10.2 gate 结构

- 66 个 `audit-*.mjs`，共 17,985 行
- 最大聚类：**13 个 `audit-mockup-*`**，各自 glob + 解析**同一批 625 份 HTML**（binding-fidelity 3 次 fs/glob、handoff-evidence 3 次、html-conformance 3 次…）→ 同一批文件被解析 13 遍
- 其他聚类：render 4、docs 类 5、token 类 5、figma 3、contract 3

**合并原则：合并执行，不合并规则。** 规则条数减少就是放松约束，不是优化。

```
audit-mockup.mjs           # 一个引擎：扫一次、解析一次、共享 DOM
  └─ rules/
       binding-fidelity.mjs
       bilingual-spacing.mjs
       geometry-consistency.mjs   # …13 条规则模块
```

每条规则保留独立 id 与独立开关（`--rule=binding-fidelity`）。**规则粒度不变，进程 13 → 1。**

> 这个模式 **DS 自己已示范过**：`audit-icon-naming-rules.mjs category-enum|leaf-kebab` 就是一个引擎两条规则。只是没推广。

### 10.3 gate 执行顺序

`prepublishOnly` 是 **39 步纯串行 `&&`**，顺序是历史追加序不是优化序。同主题分散：docs 类散在 7/8/20/21/22，token 类散在 9/10/11/12/37，figma 类散在 6/15/39。

改进（按可立即实施程度排）：

1. **分组并行**：绝大多数步骤无依赖。按资源分三组——纯文件扫描（高度并行）/ 需要 build / 需要 playwright（`a11y-non-contrast`、`docs-overflow` 最贵，单独一组放最后）。纯免费加速。
2. **同主题聚拢**：既能共享解析，也让失败定位集中。
3. **按 (成本, 命中率) 重排**：便宜且常命中的最前，fail-fast 平均反馈时间下降。命中率需 `rule-hit` 数据，但**成本现在就能测**（给每个 gate 计时），可先按成本排。

### 10.4 已查证：13 个 mockup gate 处于「未执行」状态

13 个 mockup gate **一个都不在 prepublishOnly 的 39 步里**。已查证原因，**不是漏了，是有意为之 + 一个 DS 自己承认的未完成项**：

| 证据 | 出处 |
|---|---|
| 审的对象不同：mockup gate 审**消费产品的 Figma 产物**，需 Figma REST API + per-task `fileKey`/`nodeId` 参数，与「发布 DS 组件库」不是一回事 | `CONVENTIONS-OVERVIEW.md:182`：「`audit:consumer-mockup` is **not** in the CI template by default … fits better as a manual pre-PR command or a `workflow_dispatch` job」 |
| DS 自己标记为未达成熟度目标 | `V1_RELEASE_CHECKLIST.md:187`：「⚠️ **L3-L4** — scripts exist and run on demand, but **NOT yet wired as a CI gate** that runs automatically on each consumer mockup change. Open question: is L5 = 'consumer products' CI runs it'?」 |

**对 lab 的意义（改变了 §7.3 的判据设计）**：这暴露出一个 v2 初稿没覆盖的类别——**规则存在、脚本存在、但没有执行点**。`rule-hit` 会把它们测成 0 命中，而处方既不是"删"也不是"保留"，而是**接上执行点**。

**且这个缺口正落在最痛的地方**：mockup 是 UX 产出的主战场，13 个 gate 一个都不自动跑。**这很可能是"多轮优化不尽如意"的一个直接原因**——UX 侧的规则从未被强制执行过。

→ 判据表见 §7.3，新增第四类。第一轮应优先量化该类的规模（DS 已有 `audit-gate-mount-declaration` 这个 meta-gate，可直接复用做挂载盘点）。

---

## 11. 归档物的处置原则（v2 新章 · D5）

### 11.1 为什么不是简单删除

实测证据（这些推翻了「归档 = 无用」的直觉判断）：

| 发现 | 数据 |
|---|---|
| `_archive` 是 gate 的**目标状态** | `audit-plan-lifecycle.mjs` 中 `archiveDir: 'docs/_archive/superpowers/plans'` 是配置项；DS 已有纪律「全仓 0 引用后 `git mv` 到 `_archive`」 |
| 引用它的是**核心现行文档** | 36 个 markdown 真链接 + 78 处裸路径，来源含 `PROJECT_GOAL.md`、`meta-rules.md`、`working-principles.md`、`code-conventions.md`、`design-process.md` |
| 内含 **7,471 行复盘** | `retrospection` 53 份——现行规则的**出处** |
| 6 个脚本读它 | plan-lifecycle、claim-vs-livesource、figma-env-single-source 等 |

直接删的后果：破坏 `audit-plan-lifecycle` 语义、36 个死链触发 `audit-stale-anchors`、丢失规则论证依据。

### 11.2 三分法处置

矛盾在于：**有价值为何叫 `_archive`？没价值为何不删？** 病灶是三种角色混在一个目录里。

| 角色 | 实测量 | 处置 |
|---|---|---|
| **决策依据**（`retrospection` 7,471 行 + 被核心文档引用的部分） | ~1 万行 | **不是归档，是现行资产**。提炼成 ADR 上移 `docs/decisions/`。提炼是有损压缩——7,471 行里真正的决策结论可能仅数百行。**提炼完原件才可删** |
| **gate 状态数据**（`_archive/superpowers/plans` 25 份 15,974 行） | 15,974 行 | 不能删，但**是数据不是文档**。移出 docs 树，gate 配置跟改 |
| **纯流水账**（`_reports` 16 份 2,490 行、0 引用部分） | ~3 千行 | **真删**，git 兜底 |

### 11.3 根因与规则

**`_archive` 是个没有出口的池子。** DS 的归档纪律只写到「0 引用 → 移进 `_archive`」就停了，没有下一步，所以只进不出，必然单调增长到 29,537 行。**这是增生的结构性来源，比"文件太大"本质。**

补完最后一环即可，不用发明新规则，只是把 DS 已有判据（入站引用数）再推一级：

> **0 引用 且 归档满 N 期 → 删除**

**判断标准（可用于任何文档）**：

> **这份内容，你还希望 AI 读到吗？**
> 希望 → 它是现行资产，提炼进现行文档，别叫归档。
> 不希望 → 删，git history 兜底。
>
> **「既不希望 AI 读到、又不敢删」就是囤积，没有第三种正当状态。**

git history 确实保留一切，但**git 里的东西不会被检索到**——AI 不会 `git log` 去找规则依据。所以"留在 git history"实质等于"决定不再使用它"。这个决定对流水账正确，对复盘错误。**因此答案是分类，不是统一的删或移。**

### 11.4 retrospection 的自动化提炼（无人工审核）

7,471 行 / 53 份复盘，逐份人读不现实。**方案：优先用确定性判据，AI 只处理残差，DS 已有的 gate 当安全网。**

> **为什么不是"全交给 AI 判"**：AI 判定是有方差的文字判断，而 lab 自己的 §13 铁律就禁止"完成/通过"式断言。能用脚本确定的就不要用 AI——这既更可靠，也更便宜。

**实测可锚定信号**（53 份中）：

| 信号 | 命中份数 | 可否脚本判定 |
|---|---|---|
| 被现行文档引用（入站引用数） | **18 / 53** | ✅ 纯脚本 |
| 提到 `§` 规则号 | 30 | ✅ 对照现行规则清单 |
| 提到 `F<n>` 编号 | 20 | ✅ 对照 tracker 状态 |
| 提到 `audit-*.mjs` 脚本名 | 12 | ✅ 对照 `scripts/` 现存文件 |
| 统一 `TL;DR` 段结构 | 抽样确认存在 | ✅ 可程序化抽取 |

**分类流水线**：

```
S1 脚本分类（零 AI）
   ├─ 有入站引用            → live-source：提炼成 ADR，原件提炼后删
   ├─ 0 引用 + 提到的 gate/§规则/F编号 现存且 active
   │                        → superseded：结论已固化成可执行 gate，原件即过程记录 → 删
   └─ 0 引用 + 无可锚定物   → 进 S2
S2 AI 判定（仅残差，估约 10 份）
   └─ 输出结构化 JSON：{ verdict, 证据定位, confidence }，禁止文字总结
S3 提炼（对 live-source）
   └─ 抽 TL;DR + 决策结论 → ADR，有损压缩：7,471 行的结论量级预计仅数百行
```

**安全网（替代人工审核）**——三层，全部确定性：

1. **DS 已有的 gate 就是审核者**：分批删除，每批后跑 `audit-stale-anchors`（审死锚点）+ `audit-plan-lifecycle`（审归档纪律）。删错了 gate 立刻红。
2. **先在 worktree 全量演练**，指标无退化再上真源（§8）。
3. **git 可逆**，且删除是唯一一类"错了能瞬间全量回滚"的操作。

**提炼时专门捞的一类内容**：最早一份 retrospection（`2026-04-28-design-system-audit.md`）的起点就是「Codex 生成时存在 AI 语义化痕迹」——**说明 DS 从立项起就是多 AI 场景，retrospection 里可能已存有跨 AI 差异的一手观察**。这是 `portability`（§12.3）的历史证据，提炼时应单独归集，不要和普通决策混在一起。

---

## 12. 审核维度全景

### 12.1 第一层（原有）

稳健 / 可用 / 易用。

### 12.2 第二层（v1 已列）

可观测性、规则可判定率、真源漂移、可演进性、上下文经济学、失败成本加权、心智模型对齐。

### 12.3 第三层：AI 消费特有（v2 新增）

| 维度 | 问什么 | 驱动什么决策 |
|---|---|---|
| **`portability` 可移植性** | 同一 task 跨不同 AI 的产出质量差多少 | 差异大 → 规则依赖了某家 AI 的特有机制，改写成中立表达 |
| **`discoverability` 可发现性** | **规则存在 ≠ AI 找得到。** 94,830 行里的一条规则，AI 生成时会读到它吗 | 与 `rule-hit` 交叉判定 gate 存废（§7.3 表） |
| **`actionability` 报错可行动性** | gate 失败时，报错能否让 AI **自主**修好 | 低分 gate → 改报错文案。**投入产出比最高的一类改进**：改几行文案，不动任何规则 |
| **`repair-convergence` 修复收敛性** | AI 修 gate 失败时会不会引入新违规（打地鼠） | 修复轮数分布 + 引入新违规比例。规则间隐性冲突会先在此暴雷 |
| **`cold-start` 冷启动成本** | 零上下文的新会话，要读多少文档才能达到合格产出 | **把"该精简哪些文档"从主观判断变成可测量**：逐步增加注入文档看 `first-shot` 何时饱和，饱和点之后的可移出默认加载 |

> **`discoverability` 与「规则可判定率」的区别**：后者问"这条规则能否机器判"，前者问"AI 生成时会不会读到它"。前者是 gate 的性质，后者是文档组织的性质。

---

## 13. 防止 lab 自己变成第二个增生现场

写进 `AGENTS.md`，作为 lab 的硬规则：

1. lab **只读** DS 真源，实验只在 worktree
2. 每份 baseline 必须记全三元组主键（§5.2），否则不可比，直接作废
3. 每个判据必须标注**裁判来源**（lab 自写 / DS gate）
4. **新增指标前必须先写清"它会驱动什么决策"**，写不出来就不加
5. agent 判定必须结构化输出 + 异构陪审团 + 方差校验（§6.1）
6. **lab 的文档同样适用 §11.3 的归档出口规则**——不得只进不出
7. **lab 自身受元指标考核**（见 13.1）

### 13.1 lab 自己的成功标准（v2 新增）

v1 定义了 DS 的"如意"如何判定，却没定义 lab 的。三个月后跑了 6 轮、出了 20 条 proposal，凭什么判断 lab 值得继续维护？

**元指标**：

```
proposal 采纳率 × 采纳后指标实际改善的命中率
```

**若处方大多被采纳但指标没动 → 错的是 lab 的诊断模型，该改 lab 而不是改 DS。**

这是对第 4 条的补完：第 4 条只防新增，不防**已有判断模型是错的**。

---

## 14. 执行计划（v2 重写）

### 14.1 顺序为何如此

docs 精简先做才有 `context-cost` 的干净基准——这半条仍成立。

> ⚠️ 原文另半条「**gate 合并先做才有近乎免费的 `rule-hit` 埋点**」**已作废**（勘误 E13）。
> 实测 `rule-hit` 的历史读数可直接从 CI 日志收割，**不经过合并、也不经过埋点**。
> ⇒ 第 7 步与 `rule-hit` 之间没有依赖，两者可并行。真正与 `rule-hit` 有依赖的是**第 9 步**：
> `prepublishOnly` 的 `&&` fail-fast 让失败 run 里排在后面的闸曝光为 0，
> **分母不修好，命中率就不可比**（见 N10）。

### 第一轮（约一天）

| # | 动作 | 产出 | 归属 |
|---|---|---|---|
| 1 | **gate 挂载盘点**（§10.4）：复用 `audit-gate-mount-declaration`，逐个标注 66 个 gate 有无执行点 | **「未执行」gate 清单**——预期至少含 13 个 mockup gate | lab（只读） |
| 2 | 归档三分法处置（§11.2）：`_generated` / `STATUS-CHANGELOG` 移出，流水账删除 | docs 减约 1.1 万行 | DS session 执行 |
| 3 | **retrospection S1 脚本分类**（§11.4）：算入站引用 + 对照 gate/§规则/F编号 存活 | **53 份的三分类表 + 残差清单** | lab 出表，DS session 执行删除 |
| 4 | `escape-rate` 扫 625 份存量产物 | **Top-N 逃逸值清单** | lab |
| 5 | `context-cost` 首次快照 | **上下文成本基线** | lab |
| ~~6~~ | ~~噪声地板测量（§6.2）：固定三元组跑 N≥5~~ **→ 已移至第二轮（勘误 E9）**，仅 T1 那半在第一轮做完 | 两个 T1 指标 MDE=0（实测） | lab |

**第一轮交付：未执行 gate 清单 + Top-N 逃逸值 + 上下文成本快照 + MDE（部分）。**

> 第 1 项被提到最前，因为它**不需要任何新基础设施**（DS 已有 meta-gate 可复用），却可能直接解释"为什么 UX 侧规则一直不生效"。性价比高于其余各项。

> **预期下调**：v1 承诺的"哪些 gate 从没拦住过东西"**拿不到**——`rule-hit` 数据源尚未落实（§7.3）。不要把它算进第一轮。

### 第二轮

**顺序已按第一轮实测重排**（勘误 E9 / E13），依赖关系写在右列——**不满足依赖就换来一个不可比的数**。

| # | 动作 | 依赖 / 为什么在这个位置 |
|---|---|---|
| 7 | mockup gate 合并为引擎 + 规则模块（§10.2），**并接上执行点** | ⚠️ **立项理由已重写**，见 E10 与 `reports/2026-08-24-round2-preconditions.md` §2。原文「省掉 13 遍 HTML 解析」与「省进程启动」两条都不成立（后者实测只值 0.06%）；真实收益是**同一份 350–400 MB Figma payload 从抓 8 遍降到 1 遍**，而这是「让一条 140s 的闸有可能被接上执行点」的前提 |
| 7b | **统一闸的 stdout 输出契约**（新增） | 独立于第 7 步。实测 38 条在链闸里只有 8 条吐 JSON、3 条能读出 findings 计数（N11）。**这才是 §7.3 方案 (b) 该做的事**——不是给每条闸加埋点，而是把已有的 8 条示范推广成契约 |
| 8 | retrospection S2（AI 判残差）+ S3（live-source 提炼 ADR），走 §11.4 三层安全网 | 前置（S1 分类）第一轮已交付 |
| 9 | gate 分组并行 + 按成本重排（§10.3），**并解掉 `&&` fail-fast 的遮蔽** | **`rule-hit` 的真正前置**（不是第 7 步）。链条不改成「全跑完再汇总」，失败 run 里靠后的闸曝光恒为 0，命中率分母不可比（N10） |
| 10 | ~~补 10–15 条 golden task~~ → **从零冻结 3 条**，`taskSetVersion: v1` | ⚠️ **已落地（2026-08-25），但范围改了**，见勘误 **E25–E28**。「补」预设已有 set（实测无）；「10–15 条」无成本依据（生成侧调用 spec 从未计价）。⇒ 条数由 6′ 的成本**上界**倒推 = 3 条，与 DS 自己 P0 计划 §P2 的「2-3 个任务」独立吻合。产出：`tasks/tvu-ds/v1/` + `metrics/golden-set-freeze.mjs` |
| **6′** | **噪声地板测量（§6.2）：固定三元组跑 N≥5** ——从第一轮移入 | **必须夹在 10 与 11 之间**：它要 golden task（⇒ 在 10 之后），又是 `first-shot`/`convergence` 的前置（⇒ 在 11 之前）。E9 只说了后半条。⚠️ 起手第一件事是裁决「subagent 算不算 §5.1 要的『全新会话』」——3 条 × N=5 = **15 个干净会话**是真门槛，不是 token 数 |
| 11 | 开启 `first-shot` / `convergence` / `actionability` | 依赖 6′ 的 MDE 阈值 |

### 第三轮

| # | 动作 |
|---|---|
| 12 | 对**已挂载**的 0 命中 gate 逐个做反事实实验（§7.3）。**前置「积累足够 `rule-hit` 数据」已满足**——历史读数已收割（`adapters/ds-ci-gate-history.mjs`，窗口 2026-06-11 → 08-12）。⚠️ 但候选名单要等第 9 步修好 fail-fast 分母后重取，否则会把 N10 的「被遮蔽」误当成「0 命中」 |
| 13 | `cold-start` 实验，确定文档饱和点 |
| 14 | 规则真源中立化（§9.2），开启 `portability` |

---

## 15. 风险

| 风险 | 缓解 |
|---|---|
| lab 变成又一个维护负担 | 指标增加需过 §13 第 4 条；lab 自身受 §13.1 元指标考核 |
| 评委噪音被误读为 DS 退步 | 异构陪审团 + MDE 阈值校验（§6.1） |
| **改进量淹没在噪声里** | **先测 MDE，改善量必须 > MDE 才算数（§6.2）** |
| DS 快速演进导致 baseline 不可比 | 三元组主键 + pin sha |
| **模型静默更新导致 baseline 不可比** | **三元组主键 + 模型重锚定协议（§5.2）** |
| **0 命中 gate 被误删** | **反事实实验 + 与 `discoverability` 交叉判定（§7.3）** |
| golden task 不代表真实需求 | task 取自真实需求；冻结 + 季度版本桥接（§7.5） |
| **规则绑定单一 AI 而不自知** | **`portability` 指标 + 规则真源中立化（§9）** |
| 半驱动的人工触发成本过高 | 单轮控制在 10 分钟内可重跑；T2 收窄到 T1 判不了的维度（§6.3） |

---

## 16. 勘误（2026-08-24 第一轮实测）

**登记原则**：只记「哪一句错了 / 被什么推翻 / 出处」。**不在此复制证据**——证据在 `reports/`，
本项目主张「不留双份」（见文首 v2 摘要），勘误表自己也守这条。

| # | 本文原话 | 实测 | 出处 |
|---|---|---|---|
| E1 | §10.4「DS 已有 `audit-gate-mount-declaration` 这个 meta-gate，**可直接复用做挂载盘点**」 | **不能**。该闸头注释自陈「不检查声明的真假，只检查有没有声明」，且 DS 已实证 3 例假自声明。挂载盘点须直接解析挂载产物 | `reports/2026-08-24-gate-mount-inventory.md` §1 |
| E2 | §10.4「13 个 mockup gate **一个都不在**」 | **11 个未执行、2 个真挂载**（`handoff-evidence` / `html-conformance`）。分界线是「要不要外部凭据」，不是「是不是 mockup」 | 同上 §4.1 |
| E3 | §1.1 / §7.3 / §10.2「存量 HTML 产物 **625 份**」 | **测量假象**。pin 处 tracked 仅 **16 份**；657 份里 588 份是 30 个 git worktree 的重影 | `reports/2026-08-24-first-round-baseline.md` §1 |
| E4 | §10.2「13 个 mockup gate 解析**同一批 625 份 HTML**，同一批文件被解析 13 遍」 | **11 条打 `api.figma.com`，根本不碰 HTML**。合并仍值得做，但收益是「省 Figma API 往返 + 进程启动」，且父闸已有缓存层 ⇒ 第二轮第 7 步的 ROI 论证要重写 | 同上 §1 |
| E5 | §11.4 信号表「提到 §规则号 **30 份**」 | **11 份**。原数把 `§Sprint` / `§Pre-Phase` / `§Release` 等**散文小节引用**算成了规则号 | `reports/2026-08-24-retrospection-s1.md` §3 |
| E6 | §11.4「S2 AI 判定（**仅残差，估约 10 份**）」 | **38 份 / 5,075 行**。按严格判据 `superseded` = **0**（没有一份满足「0 引用 + 结论已固化成可执行 gate」） | 同上 §0 / §1 |
| E7 | §10.1「`_generated` 是报告不是规则 → 移到 `reports/` 或 gitignore」 | **不能整目录移**。其中 json/jsonl 是 7 个脚本（含 `release.mjs`）读写的活数据，整移会打断发布链。只有 4 份 .md 适用 | `proposals/2026-08-24-archive-triage.md` §2 |
| E8 | §10.1 各项行数（`_generated` 4,885 / `STATUS-CHANGELOG` 3,951 / `_archive` 29,537） | 5,340 / 3,889 / 28,990。量级对，具体数按 pin 重取 | `proposals/2026-08-24-archive-triage.md` §0 |
| E9 | §14 把「噪声地板测量」排在**第一轮第 6 步** | **顺序有误**。它是 `first-shot`/`convergence` 的前置，而那两个指标在第二轮；第一轮交付的两个指标是纯 T1，实测 MDE ≡ 0。建议移到第二轮第 11 步之前 | `reports/2026-08-24-first-round-baseline.md` §4.1 |
| E9b | E9 自己说的「移到第二轮**第 11 步之前**」 | **只说了一半。** 它同时要 golden task，而 golden set 冻结是第 10 步 ⇒ 正确位置是 **10 → 6′ → 11**。§14 已按此改 | `reports/2026-08-24-round2-preconditions.md` §3 |
| E10 | 第一轮 baseline §1 分桶表「打 `api.figma.com` **11 条**」（这是勘误表 E4 引用的那个数） | **10 条**，且含父闸自己。被错分的是 `audit-mockup-handoff-evidence`——它读的是本地 markdown 交付卡，既不打 API 也不读 HTML。⚠️ 它恰是「已挂载的 2 条」之一，错分掩盖了「分界线 = 要不要外部凭据」这条结论的一半证据 | `reports/2026-08-24-round2-preconditions.md` §2.1 |
| E11 | E4 记「父闸**已有缓存层**」（据此下调合并收益） | **字面属实，材料上误导。** 父闸对那份缓存**只读头部 4 KB** 取 `extractedAt` 做新鲜度分类（源码注释：「⛔ 别整份读，实测 402 MB」）；9 条子闸里只有 `library-binding` 真读缓存，而它是唯一不打 API 的。⇒ **8 条打 API 的子闸之间没有任何共享 payload 缓存**，重复是真的 | 同上 §2.3 |
| E12 | §7.3「`gh run list` 对该 remote 返回 **404**」「CI 与发布已迁至 Gitea」 | **remote 认错了。** DS 配了两个（`origin`=Gitea、`github`=GitHub），且 `origin` 带两个 push URL 双发。打 GitHub 那个正常返回 **833 条 run**。「已迁 Gitea」对**发布**成立，对 **CI 不成立**——`ci.yml` / `stale-anchors.yml` 都还是 `on: push` | 同上 §1.1 |
| E13 | §7.3 / §14.1「推荐走 (b) 埋点……**先合并 → 埋点 → 再测命中率**」 | **顺序前提不成立。** 历史命中可直接从 CI 日志收割，**不经过合并、也不经过埋点**。⇒ 第 7 步与 `rule-hit` 解耦。且 (b) 的形态该改写：不是「给每条闸加埋点」，而是「**统一 stdout 输出契约**」——闸本来就在打输出，38 条里已有 8 条是结构化 JSON。真正的前置改为**第 9 步**（不解掉 fail-fast，分母不可比，见 N10） | 同上 §1.7 |

| E14 | N11「跑满全链那条 run 的 **38 条闸**里……」 | **38 是 npm step 数，不是闸数。** 39 个顶层 step 里 38 个跑 `node *.mjs`，对应 **36 个 distinct 闸文件**（`audit:icon-category-enum` 与 `audit:icon-leaf-kebab` 共用 `audit-icon-naming-rules.mjs`）；余下 1 个 step 是 `lint:ds`（eslint，**不是闸**）—— N11 那「1 条静默」就是它 | `reports/2026-08-24-gate-output-contract.md` §5 |
| E15 | N11「**8 条**吐 JSON」 | **是下界，不是真值。** 收割器的形态判据是 `body.startsWith('{')`，一行散文前缀就会让它误判成 prose。实测 2 例反例（`audit:tokenized-diff` / `audit:translation-completeness`，各 298 条 run 全被误判）⇒ 在链默认结构化发射 **10 条** | 同上 §1 / §4.5 |
| E16 | N11「**3 条**能读出 findings 计数」 | **4 条**。且该数在 `ci-gate-history` 的 JSON 里**不可复现** —— 它只存 `reportedFindingsRuns`（计数 >0 的 run 数），读不出「可读但为 0」那类 | 同上 §5 |
| E17 | preconditions §1.7「8 条已经在打结构化 JSON（`{auditId, totals, findings[]}`）」 | **两件事被合并成一句。** 10 条发结构化输出，但**只有 3 条**是这个形状；其余 7 条各用自己的键名。⇒ 「已经是结构化」≠「已经可收割」 | 同上 §4.3 |
| E18 | §14 第 7b 步「把已有的 8 条示范推广成契约，**一处改动**，而不是 38 处侵入」 | **收割侧成立，闸侧不成立。** 在链的 25 条 `scripts/` 闸**零结构化输出、零 `--json` 开关**，要从零长出输出 —— 25 处改动，与「38 处侵入」同阶。省下的不是改动条数，是**改动性质**（在已有输出上收口 vs 另开埋点通道） | 同上 §8 |
| **E19** | §11.4 安全网第 1 层「DS 已有的 gate 就是审核者……**删错了 gate 立刻红**」 | ⛔ **实测近乎无覆盖。** 在 pin 的 worktree 里删掉 34 份 retrospection，`audit-stale-anchors` **exit 0、锚点 TOTAL 529 = baseline、一字未变**；`audit-plan-lifecycle` 只剩那 1 条 pre-existing。两个原因：① `SCAN_FILES` 逐字只有 3 个文件（`mockup-conventions` / `code-conventions` / `meta-rules`）；② 行内代码 span 里的文件路径**刻意不校验**（源码 `:197-199` 写了理由）。决定性实验：删掉 `mockup-conventions.md:3237`（**在扫描面内**）用代码 span 引用的那份存活规则 `M-DISCIPLINE` 出处，闸**仍 exit 0**。⇒ §11.4「无人工审核」的前提目前不被它依赖的 gate 支撑 | `reports/2026-08-24-retrospection-s2-s3.md` §4 |
| **E20** | §11.4 流水线图「S1 → 有入站引用 → live-source」这一步的入站判据 | **下界漏了一整类形态。** `ds-doc-graph.mjs` 原三条判据全要求「`](` 开头」或「`docs\|scripts\|src\|tests` 打头」，漏掉**行内代码 span 里的相对/部分路径**（`` `../_archive/retrospection/x.md` ``）。retrospection 面漏 3 份，其中 2 份正是核心现行文档给**存活规则 `M-DISCIPLINE`** 举的证据出处 ⇒ 按补漏前读数会被判成可无人审核删除的残差。全 `_archive` 面共 **5 份 / 762 行**从「0 入站」翻成「有入站」。⇒ S1 的 live-source **15 → 18 份**、S2 待判 **38 → 35 份**；`archive-triage` 的 `_reports` 可删量 **9 份 1,430 行 → 8 份 1,171 行** | 同上 §1 |
| **E21** | E6 记「S2 待判 **38 份 / 5,075 行**」 | **35 份 / 4,665 行**（E20 的连带修正）。量级结论（远超 §11.4 原估「约 10 份」）不变 | 同上 §0 |
| **E22** | E16 / N18「**4 条**能读出 findings 计数」 | **口径没标全（N19 同款）。** 4 条是**静态顶层键口径**（顶层出现 `totals` 或 `findings` 就算）；**运行时 `totals.findings` 整数口径只有 2 条**。差集 2 条（`audit-design-system` / `audit-no-hardcoded-design-tokens`）成因同一个：静态口径看不进 `totals` 里面。契约落地后（`4a68e02d`）两口径收敛到 **11 = 11** | `reports/2026-08-25-round2-step7-7b-delta.md` §4.2 |
| **E23** | `round2-status.md` §1「第 7 步把 10 条 mockup npm script 合并 ⇒ **链条组成变了** ⇒ 第 9 步处方到货即过期」 | **前提不成立。** 那 10 条 mockup 闸**本来就不在 `prepublishOnly` 链里**（正是第一轮点名的未执行那批）。合并前后 `prepublishOnly` **逐字未变**（39 步）。⇒ 第 9 步当时被高估了串行必要性；真正需要等的只有**成本画像**（要在合并后的树上量），不是链条组成 | 同上 §2.3 |
| **E24** | §10.3「按成本重排」（第 9 步三个动作之一） | **ROI 不成立。** 全链热态仅 **8.7 s**（N=3，σ ≈ 0.1 s），且**单条 `audit:plan-lifecycle` 占 70%（6.2 s）**。并行的理论天花板收益 ≈ 2.7 s，代价是把 39 步线性链改成并行图。⚠️ §10.3 当年建在「625 份 HTML 被解析 13 遍」这个已被 E3/E4 推翻的图景上。⇒ 第 9 步收窄为「只解 fail-fast」，理由是 N10 分母可比，**与省时间无关** | 同上 §6 |

### 16.1 新增的、v2 没覆盖的发现

| # | 发现 | 影响 |
|---|---|---|
| N1 | **两条 meta-gate 全绿的同时，17 条闸从未上过场** | 「元 gate 全绿 ≠ 规则在执行」。这是 lab 的第一条结构性发现 |
| N2 | `audit-gate-mount-declaration` 的 **46 条 `KNOWN_SILENT` 豁免表，是一份尚未被读出来的未执行嫌疑名单**（17 条未执行里 16 条在表内） | 缩表可当作接执行点的进度条 |
| N3 | `audit-mockup-geometry-consistency` **既不在父闸 `AUDITS` 数组、也无自动执行点** | mockup 家族里最弱的一条 |
| N4 | `smoke-consumer-treeshake` 是唯一 orphan（零调用点），**同时是耗时最贵的闸（冷态 35.4s）** | 接执行点时它会立刻成为 gate 链最贵单点 |
| N5 | 存在 `docs/_archive/_archive/`——**归档的归档** | §11.3「没有出口的池子」的直白实证 |
| N6 | **DS 的 GitHub remote 是活的**，`.github/workflows/ci.yml` 是 `on: push` 的双 CI 之一（自称 `INFRA-F68 gov-dual-ci gate-parity`） | §7.3 记的「CI 已迁 Gitea、`gh run list` 404」需复核；GitHub Actions 运行历史**可能是 `rule-hit` 的现成数据源**，比埋点便宜 |
| N7 | 计时**冷启动效应 3.4 倍**（46.9s → 13.8s） | §10.3「按成本重排」必须丢弃第一次测量 |
| N8 | DS 另有 `docs/internal/retrospection` **22 份 / 2,246 行**（现行、非归档），§11 未提及 | 归档出口规则（§11.3）是否也适用于它，是开放问题 |
| N9 | DS 的闸**能否被 `import` 不一致**：`audit-gate-ci-parity` / `audit-stale-anchors` 有 main guard，`audit-gate-mount-declaration` 没有（import 即执行 + `process.exit`） | 它 export 了 `classifyHead` 却不可复用 ⇒ export 形同装饰。属 §9 可移植性问题 |

### 16.2 第二轮前置实测新增（2026-08-24 · `reports/2026-08-24-round2-preconditions.md`）

| # | 发现 | 影响 |
|---|---|---|
| **N10** | **第三类「没上场」：fail-fast 遮蔽。** `prepublishOnly` 是 39 步串行 `&&`，失败 run 里排在失败点之后的闸当次一条都不跑。实测 164 条失败 run **平均 9.1 条闸没跑到**，跑到比例中位 56%、最低 26%（通过 run 恒 100%） | **§7.3 判据表要加一行。** 现表只分「未挂载」与「已挂载 0 命中」，这类是「已挂载、但在失败 run 里没机会上场」，处方是**改链条结构（第 9 步）**，既不是接执行点也不是反事实实验。⚠️ 三类在朴素「0 命中」口径下长得一模一样 |
| **N11** | **闸的 stdout 输出契约不统一**：跑满全链的 38 条闸里 **8 条吐 JSON、仅 3 条能读出 findings 计数、29 条散文、1 条静默** | 「日志天然就是埋点」只对 8/38 成立 ⇒ §7.3(b) 改写为第二轮 **7b：统一 stdout 输出契约**（推广已有的 8 条示范，一处改动，非 38 处侵入） |
| **N12** | **规则增生第一次有了时间序列**：CI 日志里每条 run 自述当次链条长度，**16（06-11）→ 38（08-12）→ 39（pin），两个半月 2.4 倍**；其中 07-24 → 08-03 十天加了 9 条 | `context-cost`（§7.2）第一轮只有静态快照，这条给出**增生速率**——正是「加规则 → 挤上下文 → 质量降 → 再加规则」那条正反馈的直接观测 |
| **N13** | **团队真源 CI 的执行史取不到**：Gitea **1.22.6** 的 swagger 全表里 `actions` 命名空间只有 runners / secrets / variables，**无任何 run / task / job / log 路由**（鉴权已通过，是版本限制） | `rule-hit` 只能拿 GitHub 这半边，而 `ci.yml` 带 `paths:` 过滤 ⇒ **语料是有偏子样本**。要拿全量得升 Gitea |
| **N14** | **GitHub Actions 窗口已于 2026-08-14 关闭**：该仓 224 条 run 报 "job was not started（付款失败 / 额度）"，最后一条真执行是 08-12 | 收割到的是**历史语料，不是活数据源**。且这正是 `gates.yml` 头注释记的「私有仓 Actions 计费反复卡住」同一个根因 |
| **N15** | **DS 每次 CI 都看见 escape-rate 那批裸值，每次都按设计放行**：`audit:no-hardcoded-design-tokens` 在 377/377 条 run 里都报 findings、**0 次拦下**——源码里 `exactMatch` 阻断、`noMatch` 只报不拦，头注释逐字把「999px pill radius」列为不拦的例子 | 与第一轮 `escape-rate` 的 Top 逃逸值（`999px` 32 次、真源无此值）**独立对上**。⇒「该补进标尺还是该拦掉」是一个**悬置的决策**，不是漏 |

### 16.3 第二轮第 7 / 7b 步实测新增（2026-08-24 · `reports/2026-08-24-gate-output-contract.md`）

| # | 发现 | 影响 |
|---|---|---|
| **N16** | **第一轮的扫描面漏了一整个目录。** `ds-gate-mount-inventory.mjs` 只扫 `scripts/`（71 条），`figma-sync/` 下另有 **14 条 `audit-*.mjs`** 从未进入 lab 任何扫描面 —— 而「默认发结构化输出」的闸**全在那里**。其中 3 条（`audit:component-attributes` / `audit:figma-naming-hygiene` / `audit:figma-vs-sot`）**无任何自动执行点**（逐个核过 `.husky/`、两套 workflow、全部 npm script：零命中；后两条唯一的父调用者 `figma-sync/sync-figma-library.mjs:338,370` 自己也只能手敲） | **「17 条未执行」在含 `figma-sync/` 的扫描面下实为 20 条。** 且第一轮登记的边界 ①（父闸→子闸只认 `script: 'x.mjs'` 形态）**实测确实漏了真边** —— `runOrAbort(label, ['figma-sync/x.mjs'])` 这种数组形态解析不出 |
| **N17** | **`--json` 能力与挂载状态反相关。** 全扫描面 10 条带 `--json` 开关的闸**全部不在链**，其中 8 条正是第一轮点出的未执行那批（mockup 家族 7 条 + `product-code`）。在链的 36 条闸里**一条都没有** | 合理解释：未挂载的闸靠人手工跑、结果要给人和工具看，所以作者给了 `--json`；在链的闸只在 CI 里串着跑，输出只给人翻日志。⇒ **7b 不存在「复用已有 `--json` 接上收割器」的便宜路径** |
| **N18** | **「结构化」不等于「可收割」。** 10 条默认结构化发射的闸里，只有 **4 条**能机械读出 findings 计数，只有 **3 条**带自证身份的 `auditId`；其余 6 条各用自己的键名（`{summary, issues}` / `{checkedAt, stats, coverage…}` / …） | 契约必须同时规定**身份**（`auditId`）与**计数**（`totals.findings`）两件事。只规定「输出 JSON」不解决问题。⇒ 验收判据是「读得出计数」，不是「吐了 JSON」 |
| **N19** | **跨报告的分母不同源且都没标口径。** `audit-*.mjs` 有两个口径：`scripts/` 口径 = 66（gate-mount-inventory 用）、全仓 tracked 口径 = 82（preconditions §4 漂移表用，= scripts 66 + figma-sync 14 + templates 2）。两份各自内部一致、都不是算错 | 并排读会以为矛盾。⇒ **AGENTS 应加一条：凡跨报告引用分母必须带口径名**（写成「66（`scripts/` 口径）」而不是「66」） |
| **N20** | **lab 自己的收割器栽在了它批评过的那类判据上。** `ds-ci-gate-history.mjs:141-149` 用 `body.startsWith('{')` 判输出形态，被「散文前缀 + JSON」骗过 2 条；本 session 写新盘点器时又用单行 `grep 'console.log(JSON.stringify'` 把跨行发射的 `audit-tokenized-diff` 判成「无结构化发射」，**是内建的 must-not-hit 控制自己失败把假设打回来的** | AGENTS §3.2「每条判据配 must-hit + must-not-hit 并内建进脚本」**第二次救回一个真错**（第一次是 CI 收割的矩阵双腿重复计数 408 vs 382）。⇒ 这条纪律的价值已有两次独立实证，不是形式主义 |

### 16.4 第二轮第 8 步（retrospection S2/S3）实测新增（2026-08-24 · `reports/2026-08-24-retrospection-s2-s3.md`）

| # | 发现 | 影响 |
|---|---|---|
| **N21** | **归档里躺着的，是 lab 后来自己重新推导的方法论。** 逐份读完 35 份后逐条对上：DS 2026-06-02 已写「覆盖率一变，数字不可跨段比较」＝lab 的 **N19 口径纪律**；2026-06-03 已写「『量错元素』既虚高也掩盖真 drift」＝lab 的 **E3 + N10**；2026-05-13 已写「L1 文本规则 = 提示，L3 hook = 触发」「文本提及 ≠ checklist 显式步骤」＝lab 的 **N1**；2026-05-27 一次 Δ=44pp 的三轮 A/B 实验已写「baseline 接近天花板则实验没有区分度」＝**§6.2 MDE**；2026-06-30 / 2026-05-11 已写「核磁盘实物别信摘要」「CI green ≠ registry 接受」＝**AGENTS §4** | 这是 §11.3「归档是没有出口的池子」第一次有了**可量化代价**：结论写下了、存进归档、归档没有出口，于是同一条方法论被重新学了一次。⇒ 对 §13.1 元指标（lab 值不值得维护）是直接支持证据 —— lab 的增量不在「发现了新方法论」，而在把它们变成了 pin 住的可复现读数 |
| **N22** | **自陈「必记入册」的约束实测没入册。** `2026-05-07` 声明 #1–#15、`2026-05-08` 声明 #16–#19，均写「新 session 必读 / 必记入册」。实测现行 `AGENTS.md:439 §plan owner 角色行为约束` 只有 **9 条**，且不含：destructive 操作前须报告+等授权、commit 前逐个 `git diff` 未 staged 的 M 文件、不下「by-design」判断（全仓非归档面 grep **零命中**）、报视觉差异前先 grep 真源 token 值。`working-principles.md` 也没有那条自陈要加的「共享组件不 emit no-op inline style」 | 「声明入册」与「真的入册」之间没有任何机械校验 ⇒ 这是 `rule-hit`（§7.3）在**规则登记**环节的同型缺口：不只「规则存在但没执行」，还有「规则宣布要存在但压根没登记」 |
| **N23** | **复盘自述的回流落点有 7/30 已失效（`relocated`）。** 规则落地后又被搬走：M11.x/M14/M15/M16/M21/M22 → `design-process.md`；M17–M20 → `figma-technical-reference.md` 改名 Q1–Q4；STATUS.md §Release wrap-up 必改项 → 拆到 `WRAP-UP.md`。⇒ 只 grep 自述落点会把「已固化、可删」误判成「会丢结论、不可删」，**结论是反的** | 任何「按文档自述的落点去核对」的判据都要支持备胎落点。这一条也说明：**自述是数据不是事实**，`retrospection-s2-claims.mjs` 这一层不可省 |
| **N24** | **归档池里装着活状态。** `process-gap-report-2026-06-11.md`（466 行）不是复盘而是诊断台账，4 条 owner-gated 决策（B3/B5/C2/C3）既无「已落」也无「砍掉」标记，仍未闭合 | §11.3 只写了「该出不出」（只进不出的池子），没覆盖**「不该进却进了」**——未闭合决策被归档，等于把活状态藏进了没人看的池子。归档出口规则应补一条**入口**判据：有未闭合决策的文件不得归档 |
| **N25** | **发现 E19/E20 的机制是「同一个量做上下两个方向的口径再对差集」**，不是 must-hit/must-not-hit。`basename 口径`（上界，含叙述性提及）− `path 口径`（下界）= 3 份，逐条追查后全是真引用；补漏后两个口径在该面上**收敛**，收敛本身就是「补完了」的证据 | 与 must-hit/must-not-hit **互补**：后者验判据自身不塌，前者验**判据的形态枚举是否穷尽**。只写控制抓不到「我压根没想到还有这种形态」。⇒ 已写进 AGENTS §3.7 |

> ⚠️ **N21 是归因，不是计数**（AGENTS §3.5）。「同一条方法论被重新学了一次」这个因果，
> 建立在「lab 第一轮/第二轮确实不知道那些归档内容」这个前提上 —— 该前提为真（S1 只做了脚本分类，
> 直到本轮第 8 步才逐份通读），但它不可被脚本复现。**可复现的部分只有「两处文字表述同形」这件事本身。**

### 16.5 第二轮第 7 / 7b 步落地后的 delta 实测新增（2026-08-25 · `reports/2026-08-25-round2-step7-7b-delta.md`）

| # | 发现 | 影响 |
|---|---|---|
| **N26** | **`auditId` 与 npm key 是双射但不同字面。** 11 条上契约的在链闸里有 3 条不相等（`docs-site-readiness`↔`audit:docs-site`、`component-token-linkage`↔`audit:component-tokens`、`figma-published-vs-code`↔`audit:published-vs-code`） | 7b 处方 §4.1 只要求「一一对应且唯一」（成立），没说是否同字面。⇒ 任何按 npm key 去 join `auditId` 的下游（时间序列、`rule-hit` 归并）**必须带映射表**，⛔ 不能靠字符串相等 —— 那会**静默丢行**，而不是报错 |
| **N27** | **交叉校验的真值表少了一格。** `crossCheckExitVsTotals` 只在 `exit 0 ∧ findings 0` 那一格看 `checkedUnits`，另三格标 `—`。⇒ `findings=9 ∧ checkedUnits=0` 这种**自相矛盾**的读数不报、收割器 exit 0（造故障实测）。当前在链 3 条 findings>0 的闸，分母不受任何机械校验 | 契约立论是「没有分母的读数不可解读」，那与 findings 是不是 0 无关。⇒ 处方 `2026-08-25-crosscheck-denominator-gap.md`。**方法论**：这条 must-hit/must-not-hit **抓不到** —— 收割器自己 6 条控制全绿，因为它们测「判据按真值表跑对了没」，而缺的是**表本身少一格**。只有造故障炸得出来 ⇒ AGENTS §3.8 与 §3.2 **不可互相替代** |
| **N28** | **被测对象换了形态，lab 的量具全盲。** 7b 把 `console.log(JSON.stringify(x))` 换成 `emitGateOutput(buildGateOutput({…}))` 后，lab 静态盘点器的 9 条 `json-only` **全部塌成 `silent`** —— 若无控制，报告会写成「7b 把结构化输出从 9 条干到 0 条」这个**反向的假读数** | **AGENTS §3.2 第三次救回真错**（前两次：CI 收割器矩阵双腿重复计数 408 vs 382；单行 grep 漏跨行发射）。⇒ 推论：**量具的形态枚举会随被测对象演化而过期**。控制必须钉在「我认为这条闸应该是什么形态」，而不是「解析器跑通了没有」—— 后者在形态换代时恒真 |
| **N29** | **「文件里提到过 vs 调用位置」在"注释"这个形态上又犯了一次**：lab 反解契约模块时，函数体切片右界取「下一个 `export function`」，把下一个函数的 JSDoc 吃进了上一个的体内 —— 而那段注释逐字写着 `process.stdout.write`，于是 `buildGateOutput` 被误判成 emitter | 是 must-not-hit「解析端导出不得被算成 emitter」抓回来的（§3.2 第四次）。⇒ AGENTS §3.3 补一条操作口径：**凡文本切片判据，先去注释再切** |
| **N30** | **跑别人的 npm script 时，环境差异会伪装成被测对象的失败。** lab 耗时量具第一版用 `sh -c`，没有 pnpm 会加的 `node_modules/.bin` PATH，于是 `lint:ds` 报 **exit 127（command not found）**，读起来像「lint 挂了」 | 已加常驻控制「任何步骤 exit 127 = 量具坏了，不是被测对象坏了」。与 N20 同族：lab 又一次栽在自己批评过的那类判据上 |
| **N31** | **在链的 11 条 `figma-sync` 闸零网络**（逐条 grep `api.figma.com` / `fetch(` / `https://`，唯一命中是一句注释）。打 API 的是 mockup 家族，而它们**不在 `prepublishOnly` 链上** | ① 本轮耗时读数是**离线真值**，不是「没 token 所以走了降级路径」；② E4/E10/E11 那条「10 条打 `api.figma.com`」与 `prepublishOnly` 链**无交集** ⇒ 两处数字不可并排读（N19 口径纪律的又一实例） |
| **N32** | **N2 的「未执行嫌疑名单」第一次真的缩了**：`KNOWN_SILENT` **46 → 35**（P1→P2 缩 10 增 0，另 1 行在中间 16 个 commit 里缩掉），缩掉的正是第一轮点名的 mockup 家族 10 条；同批 `沉默 46 → 35`、`已声明 25 → 37`（D2 1 → 10） | **第一轮结论第一次被兑现成读数。** 对 §13.1 元指标（`proposal 采纳率 × 采纳后指标实际改善的命中率`）——分子首次非 0 |
| **N33** | **契约上线当天就抓出一条真·假绿**：`audit:figma-conformance` = `exit 0 · findings 0 · checkedUnits 0`。契约前它与其余 9 条长得一模一样（都是一条绿）；DS 同批立 INFRA-F139 + 具名 shrink-only 豁免 | 7b 的 ROI 直接证据：**一条存在了不知道多久的假绿，变成了有名字、有 owner、只许缩的条目** |

### 16.6 第二轮第 10 步（golden set 冻结）实测新增（2026-08-25 · `reports/2026-08-25-round2-step10-golden-set.md`）

| # | 本文原话 | 实测 |
|---|---|---|
| **E25** | §14 第 10 步「**补** 10–15 条 golden task，冻结为 `taskSetVersion: v1`」 | **两个前提都不成立。**（a）「补」预设已有 set —— lab 无 `tasks/`、无 `runs/`（§4 目录结构 L95/L96 画了它们），DS pin 上 golden 语料 **0** ⇒ 是**从零建**；（b）「10–15 条」**没有任何成本依据**，生成侧调用在 spec 里从未计价（见 E26）。⇒ 改为 **3 条**，由 6′ 的成本上界倒推，且与 DS 自己 `_plans/2026-07-24-p0-systematic-gap-fixes.md` §P2 的「2-3 个任务（列表台/表单流/数据可视化）」独立吻合 |
| **E26** | §6.2「同一 prompt 跑 **N≥5** 次」 vs §6.3「10 task × **3 重复** = 180 次调用」 | **spec 内部自相矛盾（3 ≠ 5）**，且 §6.3 那 180 次**只算了评委调用**，生成侧从未计价。补价（两个口径）：单次注入 **254,846 token**（声明规则文档集口径，上界）/ **25,142**（入口文件口径，下界），差 **10.1 倍**，收敛需第一次 run 的 `contextManifest`（先有鸡还是先有蛋）。⇒ 10–15 条 × N=5 在上界下是 **12.7 M–19.1 M token**，与 §15「单轮 10 分钟内可重跑」不相容。更硬的非 token 约束：§5.1 要求每次 run 全新会话 ⇒ 10–15 条需 **50–75 个干净会话** |
| **E27** | §7.5「task 取自真实需求（不虚构）」 | **成立，但语料不在 spec 以为的地方。** 现行交付卡目录**是空的**：`docs/internal/_design-kickoffs/` 只有 `.gitkeep`；`docs/handoffs/` tracked 只有 `.conformance/*.json` **10 份**，交付卡 `.md` 本体 **0 份**。可复现语料 = `_metrics/phase0-ledger.md`（**56 条**，ledger 日期行口径）+ `_archive/_design-kickoffs/`（2 份）+ `_plans/2026-07-24-…`。⚠️ 且该 ledger **对机器按列取数不可靠**（三处实证：内嵌竖线错列 / 文档内两张表都有 `日期` 表头 / 数据行列数不齐 9 vs 10）⇒ 出处须**人工选 + 机器校验引文可定位** |
| **E28** | §7.5「每季度做一次 set 升级」—— 只给「到季度」一个触发条件 | **触发条件不全，实测还缺三个**：① 6′ 跑出真实 `contextManifest` ⇒ 条数按实测注入量重定；② **re-pin 后 citation 定位不到**（出处被删/被搬）—— 不是假想，GT-002 的出处此刻就在 `archive-triage` 桶三待删清单上；③ grader 换了发射形态（N28）⇒ 判据绑定失效 |

| # | 发现 | 影响 |
|---|---|---|
| **N34** | **DS 自己已独立推导过 lab 的核心方法论，且任务清单都给了。** `_plans/2026-07-24-p0-systematic-gap-fixes.md`「实验效度边界」段与 lab §6.1/§6.2 **逐条同构**（n=1 不可信 / n≥5 / 盲评多评审 /「我既出题又打分」/ rubric 从真源派生 / 同一底模）；§P2 已定「2-3 个任务 × 每环境 n≥5 × 盲评」。**状态 `[ ]` 未做** | **N21 同款但更严重**：N21 那批躺在**归档**里（§11.3 没有出口的池子），这一条躺在**现行 `docs/internal/_plans/`**，是活计划、只是没人回来执行。⇒ 归档出口规则治不了这一类，「**活计划的失效**」是另一个缺口。⇒ 第 10 步接上它，不另起炉灶 |
| **N35** | **上次 3-way 实验的证据留档不可复现**：`scratch/ux-test/` 在 pin 上 tracked = **0**、工作树不存在 | n=1 那次的读数**不能作为桥接基线** ⇒ golden set v1 是真正的第一条基线。已内建成 must-not-hit（citation 不得指向 `scratch/`） |
| **N36** | **T1 grader 的校准面就是它的过拟合面。** `audit-mockup-html-conformance` 的验收硬线逐字是「对现有 2 个 `_demos/*.html` 必须 PASS」 | 那 2 份**不能同时当测试集**。⇒ 一般化：**凡「闸的校准集」与「指标的样本集」重叠，指标恒好看**。已内建成 must-not-hit |
| **N37** | **「0 入站引用 ⇒ 可删」在存在仓外消费者时结构性盲。** DS 的 `ds-doc-graph` 只扫 DS 仓内；lab 的冻结物引 `_archive/_design-kickoffs/`，而该目录正因「外部入站 = 0」被列进 `archive-triage` 桶三真删 | 已冻结的 v1 不受影响（pin 是不可变快照），但 re-pin 会 fail。⇒ **归档出口规则应补一条入口判据：判「0 入站」前先问「有没有仓外消费者」。** `archive-triage` 自留的余地（「`=0` 需再过一道」）就是这一道 |
| **N38** | **lab 第三次栽在自己批评过的判据上（N20/N30 同族）**：取被测进程 exit code 时接了管道，读到的是 `tail` 的退出码 ⇒ `fail.html` 显示成 `exit=0`，长得像「这条闸不拦」 | 去管道重测才是真值（0 / 1）。⇒ 与 N30（`sh -c` 缺 PATH ⇒ exit 127 伪装成被测对象失败）同族：**测量装置的失败伪装成被测对象的行为** |
| **N39** | **§3.3 的操作口径第一次「拦在发生前」。** must-not-hit「prompt 不得含 DS 组件名」若按整文件 grep 写，会在**干净基线**上误报 —— 干净的 GT-001 里 `PopupBox` 整文件命中 **1** 处（在 `graders[].expectedDiscrimination`，那是在描述 grader 抓什么），`prompt.text` 口径 **0** 处 | §3.3 前四个实例（E5 / N20 / N29 …）全是**事后**被控制打回来的；这是第一个**在写判据时就被规则拦住**的。⇒ 本轮补进 §3.3 的操作口径已经在还本 |

> ⚠️ **E25–E28 / N34–N39 中，唯一的归因是 N34「DS 独立推导了同一套方法论」**（AGENTS §3.5）。
> 可复现的部分只有「两处文字表述逐条同形」这件事本身；「所以 lab 的方法论不是新东西」是**推论，未测**。
> 其余各条均为计数与结构事实。

---

> ⚠️ **N26–N33 是计数与结构事实，不是归因**（AGENTS §3.5）。
> 「所以契约值得推广到 v2 那 25 条」是归因，本轮**没有测过** —— v1 覆盖的 11 条恰好都是
> 「扫本地文件产结构化报告」类（N16/N18 那条边界仍然成立）。
> v2 的 25 条里有大量「检查通过就该安静」的闸，**它们的 `checkedUnits` 值不值那份改动，是未测的开放问题**。

---

> ⚠️ **N16–N18 全部是计数，不是归因**（AGENTS §3.5）。
> 「所以 `figma-sync/` 那代闸写得更好」是归因，**不可靠** —— 也可能只是那 14 条恰好都是
> 「扫本地文件产结构化报告」这类任务，而 `scripts/` 那 25 条里有大量「检查通过就该安静」的闸。
> **要不要给一条闸上契约，判据是「它的输出会不会被机械消费」，不是「它在哪个目录」。**
