# 规则清单镜像闸扩展（S7 指针化文档反枚举）Implementation Plan

> ✅ **Status：2026-09-03 全部 5 个 Task 已执行并落 master**（撰于 08-12，隔 22 天执行）。
> commit 链：`2d2dc11e`（Task 1 · OVERVIEW 指针化）→ `5dc190bf`（Task 2 · 三份 SKILL + 两处 legacy 指针）
> → `cb8c4e57`（Task 3 · S7 判据 + 单测 + harness）→ `652f9aec`（Task 4 · hook 触发面）→ Task 5 收尾登记。
> ⛔ **本文件现在是历史记录，别再当待办执行** —— 要复用的是下方 **§执行期订正** 的 10 条，
> 其中 **4 条是 plan 对「闸会不会红」的预判失准**（§3 / §5 / §10 / 以及 §4 的锚点漂移）。
> 勾选框刻意保持未勾：本仓 `audit:plan-lifecycle` 头注释逐字写着「勾选率已证与完成度无关」，
> 判据是**可达性**不是勾选率 —— 真实执行状态以本行与 §执行期订正 为准。

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 把 2026-08-03 那轮 `audit:rule-inventory`（INFRA-F58）扫描面漏掉的两类无闸 stale 镜像 —— `docs/internal/CONVENTIONS-OVERVIEW.md` 的规则 ID / 场景枚举表、`skills/*/SKILL.md` 的规则 ID 枚举句 —— 指针化清零，并给闸新增 S7 判据（受管指针化文档出现「规则 ID 枚举形态」即红），让这类镜像不能无声回潮。

**Architecture:** 先清零后上闸（顺序刻意：闸挂在 pre-commit 上、触发面含闸脚本自身，先上闸会被自己的红挡住提交）。Task 1-2 把 4 份受管文档降为指针（概念解释保留，逐条 ID 枚举删除）；Task 3 在 `scripts/audit-rule-inventory.mjs` 内新增 **denominator-free** 的 S7（行级 ≥3 个裸 id + 表格级连续 ≥3 行 ID 打头两种形态，token 系列共用 `scripts/lib/rule-ids.mjs` 真源 + `US-\d+` 场景号），沿用 `audit-layout-tokens.mjs` 可测范式；Task 4 扩 pre-commit 触发面并全链验绿；Task 5 收尾登记。

**Tech Stack:** Node ESM（`.mjs`）audit 脚本 · vitest · husky pre-commit（L4 条件触发）· `prepublishOnly`（L5，已含 `audit:rule-inventory`，无需改）· 纯 Markdown 编辑

## 判据真源（owner 裁定记录 · 2026-08-12）

- **立项裁定（owner 已同意）**：INFRA-F58 2026-08-03 那轮治了 5 处「规则清单镜像 stale」，但扫描面漏了两处今天实测 stale 的无闸副本：① `CONVENTIONS-OVERVIEW.md` §3.1/§3.2 的 M/R-rule 枚举表（自标「reflects the 2026-05-18 consolidation」）与 §6 的 US-1…US-6 场景表（活源已有 US-7）；② `skills/tvu-design-mockup/SKILL.md` 等的规则 ID 枚举句。grep 实证：CONVENTIONS-OVERVIEW 在 `scripts/` `.husky/` `.gitea/` 里 0 命中 = 无闸；`lint-skills.mjs` 只校验链接文件存在，不校清单内容。
- **默认方向（owner 未对子选项逐项拍板，按此执行）**：**镜像指针化而非补齐**。OVERVIEW 两张 ID 枚举表与 §6 US 表收缩为「概念解释（umbrella / legacy stub / mirror pair 的定义可留）+ 指向活源的指针」，逐条 ID 枚举删除；SKILL.md 枚举句同理改指针。然后扩 `audit-rule-inventory.mjs`：受管镜像文件中出现「规则 ID 枚举模式」即红。
- **判据的逃逸方向分析（选型依据）**：「禁枚举」被规避的两条路都是想要的流向 —— ① 把内容写进活源（mockup/code-conventions 自身，那里有 `audit:rule-load-map` 与 rule-inventory S3 守着）；② 改写成 `§` 引用形态（引用 ≠ 清单，与 S4 同一裁定，prescriptive 引用合法且有信息量）。
- **⛔ owner 可推翻点（推翻后的改法一并登记）**：
  1. **指针化 vs 补齐上闸**：若 owner 改判「补齐 + 派生同步」，则 OVERVIEW 两表改为从活源派生的受管 inventory —— 需把现有 INVENTORIES 机制从「H2 级 + 引用块形态」扩到「H2–H4 rule heading 级 + 表格形态」，S7 不做。工程量更大且再造一处镜像面（活源 67 个 heading 的表没人会读，OVERVIEW 的价值本来就在概念层），故不作默认。
  2. **S7 注册面 = 4 份**（OVERVIEW + tvu-design-mockup / tvu-design-code / role-ux 三份 SKILL.md）。`design-walkthrough` / `design-qa-loop` 的规则 checklist 是**走查工作产物**（prescriptive、删了丢功能），`tvu-design-pipeline:30` 的 3-ID 行是 stage 路由引用，均不注册。owner 可推翻扩大注册面（代价：那几份要先按 S7 判据改写）。
  3. **`US-\d+` 计入 S7 token 系列**（护住 §6 场景表那类漂移）。若 owner 认为场景号不属规则编号治理面，删 `S7_TOKEN_SERIES` 里的 `|US-\\d+` + 对应单测即可。
  4. **顺手修正两处阈值以下的 stale 单 ID 指针**（`consumer-product-conventions/SKILL.md:173-174` 指向已迁移/已 stub 的 M2 / R1）。闸管不住（1-2 个 id = 引用），纯 factual fix，owner 可砍。

## Global Constraints

- **提交纪律（并行 session 共享 git index）**：一律 `git commit -F <msg文件> -- <显式路径列表>`（`-F` 在 `--` 之前；msg 文件用 Write 工具写到 scratchpad），**提交后紧跟 `git reset -- <同一批路径>`**。pre-commit 闸链（vue-tsc + 全量 vitest + 十余条 audit）可能 >2min，**commit 用后台跑**；遇到 `.git/index.lock` = 并行 session 正在提交，**等待重试，不是故障**。当前工作树有别的 session 的 dirty/deleted 文件（`playground-dist/` 大批 D、`AGENTS.md`/`STATUS.md` M 等）——**不碰、不 stage、不消化**。
- **改闸必须沿用 `audit-layout-tokens.mjs` 可测范式**：纯函数 export + `IS_MAIN` guard（本脚本已有，形态是 `process.argv[1]` resolve 比对）+ vitest 单测 + **CLI 故障注入证明能转红**——造含 ID 枚举的假镜像内容喂进受管文件（注册表是硬编码路径，「喂假文件」的通道就是临时注入受管文件、验完 `git restore` + sha256 逐字节复原），断言 exit 1 且 stderr **指名文件与行号**。故障注入的探针 ID 用 `M900 / M901 / M902`——已核 4 份受管文件零命中（2026-08-12），执行日重核；**探针不得与真实规则 ID 撞名**。
- **新判据落地当天抓到的存量违例（OVERVIEW / SKILL）在本计划内清零**——指针化就是清零动作。**不留 EXEMPTIONS 豁免表条目，也不新增任何 `rule-inventory-ok` 行内标记**（那是给未来历史引用留的逃生口，本轮存量没有一条配得上它）。
- **计划里不写死「今天实测的缺失 ID 清单」当执行判据**——执行时以闸自己的输出为准（entry/plan 的转述是二手源）。本计划引用的行号同理：全部用**唯一字符串锚点**定位（Edit old_string），行号仅作导航提示。
- **硬规则 #10（活源优先）**：每个编辑步骤动手前先 `grep` 锚点串确认还在（并行 session 可能已动过）；锚点找不到 → 停下 re-Read 该文件再定位，不准凭本计划的引文硬改。
- **不碰 `.vue` / `.css` / `.html`**：本计划产出物 = 5 份 `.md` + 1 个 `.mjs` + 1 个 lib `.mjs` + 1 个 `.test.ts` + `.husky/pre-commit`。视觉门（`VISUAL_COMMIT_APPROVED`）不适用，**也不得自设**。
- **闸类验收三问**：造故障看它变红（两种形态各一次）· 空/畸形输入 fail closed（受管文件读不到 → S7 红，有单测钉）· 数清调用面（CLI `pnpm audit:rule-inventory` / pre-commit L4 / prepublishOnly L5 / vitest import，Task 4 全部过一遍）。PASS 本身不是证据。

## 现状实测（2026-08-12 计划撰写时对活源核过；执行期一律以当日活源与闸输出为准）

- **基线绿**：`pnpm audit:rule-inventory` PASS（S1-S6；design-process 25/25 · domain-tvu 12/12 · S4 12/12 · S6 6/6 · 豁免 0）。
- **stale 坐实**：`extractRuleEntries(mockup-conventions.md)` 得 **67** 个 rule heading（含子号/umbrella 段），OVERVIEW §3.1 表只列 13 条——仅顶层就漏 M36/M37/M38/M39/M40/M41/M42/M43/M44/M45/M46/M47/M52/M-DISCIPLINE 之外的… 不逐条列了，**差集以指针化 commit 的 diff 与闸输出为证**。§6 表停在 US-6，活源 `mockup-conventions.md:145` 已有 US-7（`:149` 起 4 阶段子流程）。
- **SKILL 枚举面**（grep 全部 `skills/*/SKILL.md` 实测）：`tvu-design-mockup/SKILL.md`（frontmatter `:5`、硬规则行 `:14`、`:61` 裸 M48/M49、§工作期间约束摘要 `:100-115` 整段、回流段 `:132`）；`tvu-design-code/SKILL.md`（`:14` 15 个 R 号、`:80` 4 个）；`role-ux/SKILL.md`（`:46-47`，且内容 stale——M3/M4/M7 已迁 domain-tvu、M11–M20 已迁 design-process、R1 已 stub）。其余 skills 为走查 checklist / stage 路由 / ≤2 id 引用，不属「内容镜像」（见判据真源 ⛔2）。
- **挂载现状**：`.husky/pre-commit:257` rule-inventory 触发 regex **不含** OVERVIEW 与任何 SKILL.md；`prepublishOnly` 已含 `audit:rule-inventory`（无需改）；Gitea `pr-checks` 无 paths 过滤跑 prepublishOnly（L5 兜底已在）；GitHub `ci.yml` paths 不扩（owner 2026-08-11 已拍，backlog gov-dual-ci 段）。
- **指针目标锚点在**：`mockup-conventions.md:12` `## 🤖 AI 读取指引`、`code-conventions.md:13` 同名、`mockup-conventions.md` `## Task Entry Modes`（US-7 行 `:145`）、`code-conventions.md:116` `## Task Entry Modes`。`pnpm rule:next` 存在（`package.json:109`）。
- **changeset 惯例**：闸初版 commit `85a4bf4f`（.husky + 5 docs + script + test + plan）**无 .changeset 文件**；`.changeset/` 里现存条目全是库消费面（token/组件）变更 → **本计划不写 changeset**，跟随惯例。
- **无 fragment 依赖**：全仓无 `CONVENTIONS-OVERVIEW.md#...` 链接 → 改它的 heading 不产生 stale-anchors 孤儿。`lint-skills.mjs` 只校验 SKILL.md 里 markdown 链接的目标文件存在。

### 枚举探针（Task 1-2 的工作辅助，非验收判据）

```bash
# 过近似探针：列出所有含规则 ID/US 号形态的行，人工逐行判「≤2 个裸 id 且非 ID 打头表行」。
# §-前缀、`M2-M9`/`US-1..6` 区间写法也会被它打出来（那些是合法形态）——最终裁决权在 Task 3 的闸输出。
grep -nE 'M-[A-Z][A-Z-]+|R-[A-Z][A-Z-]+|M[0-9]|R[0-9]+|C[0-9]|I[0-9]|Q[0-9]|US-[0-9]' <file>
```

## 非目标

- 不动 `design-walkthrough` / `design-qa-loop` / `tvu-design-pipeline` 的 prescriptive 规则引用（判据真源 ⛔2）。
- 不动 `code-conventions.md:543` 的「US-1..6」措辞——那是活源文件自治的内容问题（code 侧是否长出 US-7 是 owner 内容决策），且区间写法本就在 S7 边界之外。
- 不给 OVERVIEW 上 INVENTORIES（S3 型 inventory 块）义务；不改 GitHub `ci.yml` paths；不重构 S4/S6 既有判据。

---

## Task 1: 指针化 CONVENTIONS-OVERVIEW.md

**产出**：`docs/internal/CONVENTIONS-OVERVIEW.md` 无任何规则 ID 枚举形态；概念层（umbrella / legacy stub / mirror pair / 实证 / 拍板）原样保留。

- [ ] **1.1 前置核验**：`pnpm audit:rule-inventory` 确认基线绿；`git status` 确认 `docs/internal/CONVENTIONS-OVERVIEW.md` 无别的 session 的未提交改动（有 → 停，报 owner）。
- [ ] **1.2 Status 行去日期快照**。Edit（old 串唯一）：

  old:
  ```
  > **Status**: reflects the 2026-05-18 consolidation (M-COLOR / M-INTEGRITY umbrellas, R15 library-first umbrella).
  ```
  new:
  ```
  > **Status**: living map —— 规则清单与场景全集不在本文枚举（见 §3 / §6 的活源指针）；只在概念层（umbrella / legacy stub / mirror pair 的定义）变化时才需要动本文件。
  ```
- [ ] **1.3 §3 整段替换**（从 `## 3. Rule Quick Reference` 起，到 `## 4. How rules emerge + flow` 之前的 `---` 为止，含 §3.1/§3.2/§3.3 三张表全删）。替换为：

  ```markdown
  ## 3. Rule families & where the lists live

  Rules are split into two families:

  - **M-rules** = Mockup rules — apply when producing Figma artifacts. Live in [`mockup-conventions.md`](./mockup-conventions.md).
  - **R-rules** = Code rules — apply when writing code that consumes the TVU library. Live in [`code-conventions.md`](./code-conventions.md).

  IDs are stable. When a rule is absorbed into an umbrella, the original ID is kept as a **legacy stub** (an anchor that redirects to the new home) so external references don't break. Business-domain rules and process rules were migrated wholesale to `domain-tvu.md` / `design-process.md` — the migration maps live in the conventions files themselves.

  **本文件不复述规则 ID 清单**（此前 §3.1/§3.2 两张表自标「2026-05-18 consolidation」，2026-08-12 实测对活源大面积 stale —— 一份清单散在 N 处必漂，机理见 `scripts/audit-rule-inventory.mjs` 头注释；本文件自身受 S7 判据看守，枚举回潮会红）。清单活源：

  | 要什么 | 去哪读 | 机械保证 |
  |---|---|---|
  | M-rules 全集 + 按场景路由 | [`mockup-conventions.md`](./mockup-conventions.md) 顶部 §🤖 AI 读取指引 | `pnpm audit:rule-load-map`（每条 rule 必在路由表） |
  | R-rules 全集 + 按场景路由 | [`code-conventions.md`](./code-conventions.md) 顶部 §🤖 AI 读取指引 | 同上 |
  | umbrella 吸收关系 / legacy stub 落点 | 各 conventions 文件内该 umbrella 条目与 stub anchor | `pnpm audit:stale-anchors`（anchor 零孤儿） |
  | 跨 M/R 镜像关系（mirror pair，如 M31 ↔ R14） | 各规则条目内的 cross-reference 字段 | — |
  ```
- [ ] **1.4 §5.3 audit-product-code 行的 Rule 格降为指针**。Edit（old 串唯一）：

  old: `R1 (icons via R15 §icons) / R2 / R16`
  new: `R2 / R16 等（完整规则映射见脚本头注释 —— 闸挂载真源是脚本自声明）`
- [ ] **1.5 §6 整段替换**（从 `## 6. Common usage scenarios (US-1 … US-6)` 起，到 `## 7. Contributing a new rule` 之前的 `---` 为止）。替换为：

  ```markdown
  ## 6. Common usage scenarios (US-N)

  Both rule families classify tasks with one usage-scenario taxonomy (**US-N**). Each rule's "Task Entry Modes" table tells you whether that rule fires for a given scenario.

  **场景全集不在本文枚举**（此前这张表停在 US-6，活源早已长出 US-7 Redesign legacy product —— 同一种镜像漂移）。活源：

  - 场景定义 + 每场景入口流程 → [`mockup-conventions.md`](./mockup-conventions.md) §Task Entry Modes
  - code 侧场景表（含 4a/4b 拆分）→ [`code-conventions.md`](./code-conventions.md) §Task Entry Modes

  US-4（Figma frame → code mirror）is the most failure-prone scenario — the visual-diff protocol and the code→token mapping rule exist because of it.
  ```
- [ ] **1.6 §7 两处**。Edit ①（old 串唯一）：

  old: `3. **Pick the next free ID** — \`grep\` both Active and Resolved entries; pick max+1 (see project memory \`feedback_backlog-id-collision.md\`).`
  new: `3. **Pick the next free ID** — \`pnpm rule:next\`（从 origin/master 现算，防并行分支撞号 —— 2026-08-11 PR #9 实证）.`

  Edit ②（old 串唯一）：

  old: `5. **Check for umbrella fit** — If your rule is close to M-COLOR or M-INTEGRITY or R15, prefer adding a sub-section rather than a new top-level ID.`
  new: `5. **Check for umbrella fit** — If your rule is close to an existing umbrella (e.g. M-COLOR), prefer adding a sub-section rather than a new top-level ID.`
- [ ] **1.7 §8 两处**。Edit ①：

  old: `Most rules describe **WHERE** to search/find (e.g., M32/R15 library priorities). M35 / R13 describe **HOW** to think before searching (affordance category trace).`
  new: `Most rules describe **WHERE** to search/find (library-first priorities). The affordance-search mirror pair (M35 ↔ R13) describes **HOW** to think before searching (affordance category trace).`

  Edit ②：

  old: `4. **Annotation vs Product UI separation** — M23 / M33 / R16 split: annotation = bilingual (designer-facing); product UI = device language only (end-user-facing).`
  new: `4. **Annotation vs Product UI separation** — M33 and its code mirror R16 split: annotation = bilingual (designer-facing); product UI = device language only (end-user-facing).`
- [ ] **1.8 §9 Glossary 三处**。Edit ①：

  old: `(e.g. M-COLOR absorbed M10/M25/M26)`
  new: `(e.g. M-COLOR absorbed the legacy color rules)`

  Edit ②：

  old: `(M31↔R14, M32↔R15, M33↔R16, M35↔R13)`
  new: `(e.g. M31↔R14; 完整对照见各规则条目的 cross-reference 字段)`

  Edit ③：

  old: `listing which US-1…US-6 scenarios trigger that rule`
  new: `listing which US-N scenarios trigger that rule`
- [ ] **1.9 自查**：跑上方「枚举探针」grep 过一遍本文件剩余命中行，逐行确认 ≤2 个裸 id 且无 ID 打头的连续表行（`§` 前缀与区间写法放行）。
- [ ] **1.10 Commit**（后台跑；msg 用 Write 写到 scratchpad）：

  ```bash
  git commit -F <msg文件> -- docs/internal/CONVENTIONS-OVERVIEW.md
  git reset -- docs/internal/CONVENTIONS-OVERVIEW.md
  ```
  message: `docs(conventions-overview): 规则/场景枚举表降为指针 —— 自标 2026-05-18 的镜像实测对活源 stale（INFRA-F58 S7 前置清零）`

## Task 2: 指针化三份 SKILL.md + 两处 stale 单 ID 指针修正

**产出**：`skills/tvu-design-mockup/SKILL.md`、`skills/tvu-design-code/SKILL.md`、`skills/role-ux/SKILL.md` 无枚举形态；`consumer-product-conventions/SKILL.md` 两处 legacy 指针改对。

- [ ] **2.1 mockup SKILL frontmatter**。Edit（old 串唯一）：

  old: `跳步会漏 M0/M23/M32/M35 等所有 mockup 硬规则`
  new: `跳步会漏 M0 等所有 mockup 硬规则`
- [ ] **2.2 mockup SKILL 团队导览行**。Edit（old 串唯一）：

  old: `（推荐**先读这个**了解整体）`
  new: `（概念层导览，人类 onboarding 向；AI 起手仍按下方起手协议读真源，不以导览代替）`
- [ ] **2.3 mockup SKILL 硬规则行整行替换**。Edit：

  old:
  ```
  - 硬规则 — `${CLAUDE_PLUGIN_ROOT}/docs/internal/mockup-conventions.md`（含 M0/M1 + umbrella `M-COLOR` / `M-INTEGRITY` + M23/M23.6/M24/M29/M31/M32/M33/M35 + legacy M10/M25/M26/M27/M28/M30/M34 anchor stubs；具体编号见 OVERVIEW Quick Reference）
  ```
  new:
  ```
  - 硬规则 — `${CLAUDE_PLUGIN_ROOT}/docs/internal/mockup-conventions.md`（**规则全集与路由的活源**：顶部 §🤖 AI 读取指引 即清单，`pnpm audit:rule-load-map` 机械保证零漏；本 skill 不复述编号）
  ```
- [ ] **2.4 mockup SKILL 起手协议两处**。Edit ①（old 串唯一）：

  old: `含 M48/M49 指针`
  new: `含 §M48/§M49 指针`

  Edit ②（Step 6 首行；old 串唯一）：

  old: `6. **Task classify** — 按 conventions 的 Task Entry Modes 表把任务归到 US-1..6，不确定则问用户：`
  new: `6. **Task classify** — 按 conventions 的 Task Entry Modes 表归类（场景全集见该表），不确定则问用户：`

  并在 Step 6 的 `- **US-6** Review/Audit …` 行之后**新增一行**（补 US-7 路由缺口，指活源不复述流程；⚠️ 行内只留 1 个裸场景号，别写成「回到 US-1/US-3」——那会凑满 3 token 被 S7 自己判红）：
  ```
     - **US-7** Redesign legacy product → 走 `mockup-conventions.md §Task Entry Modes` 的对应子流程（4 阶段、每阶段 STOP 等拍板），拍板后才回到常规 greenfield / 增改路径
  ```
- [ ] **2.5 mockup SKILL §工作期间约束摘要 整段替换**（从 `## 工作期间约束摘要（按类别查真源）` 起，到 `## 回流入口（生成翻车 → 新规则）` 之前为止）。替换为：

  ```markdown
  ## 工作期间约束（查真源，不在此复述）

  active M-rule 全集、按主题分组、迁出与 legacy stub 映射，活源都在
  [`mockup-conventions.md`](../../docs/internal/mockup-conventions.md) 顶部 **§🤖 AI 读取指引**
  （Quick Reference + 触发路由表；`pnpm audit:rule-load-map` 机械保证零漏）。
  本 skill 曾在此维护一份按主题分组的编号摘要，2026-08-12 实测已对活源 stale
  （与 CONVENTIONS-OVERVIEW 同病：摘要即镜像，镜像必漂），故收成本指针；
  枚举回潮由 `pnpm audit:rule-inventory` S7 看守。
  ```
- [ ] **2.6 mockup SKILL 回流段**。Edit（old 串唯一）：

  old: `2. 优先尝试**并入既有 umbrella**（M-COLOR / M-INTEGRITY / M32 / M35 等）作为 sub-clause；只在没有合适 umbrella 时才续 M36 / M37 等新编号`
  new: `2. 优先尝试**并入既有 umbrella**（如 M-COLOR）作为 sub-clause；只在没有合适 umbrella 时才续新编号（下一个可用号用 \`pnpm rule:next\` 取，防并行撞号）`
- [ ] **2.7 code SKILL 硬规则行整行替换**。Edit：

  old:
  ```
  - 硬规则 — `${CLAUDE_PLUGIN_ROOT}/docs/internal/code-conventions.md`（含 R0/R2/R3/R4/R5/R6/R7/R8/R9/R10/R12/R13/R14/R15/R16 + legacy R1 anchor stub；具体编号见 OVERVIEW Quick Reference）
  ```
  new:
  ```
  - 硬规则 — `${CLAUDE_PLUGIN_ROOT}/docs/internal/code-conventions.md`（**规则全集与路由的活源**：顶部 §🤖 AI 读取指引 即清单，`pnpm audit:rule-load-map` 机械保证零漏；本 skill 不复述编号）
  ```
  另 `grep -n "推荐.*先读" skills/tvu-design-code/SKILL.md`：若其团队导览行也有「推荐先读」措辞，按 2.2 同款改法处理；没有则跳过。
- [ ] **2.8 code SKILL 回流段**。Edit（old 串唯一）：

  old: `2. 优先尝试**并入既有规则**作为 sub-clause（R15 涵盖所有 UI library-first；R13 涵盖 affordance search；R8/R12 涵盖 audit script enforcement）；只在没有合适归宿时才续 R17+ 新编号`
  new: `2. 优先尝试**并入既有规则**作为 sub-clause（如 R15 涵盖所有 UI library-first —— 先对照 §🤖 AI 读取指引 找归宿）；只在没有合适归宿时才续新编号（\`pnpm rule:next\`）`
- [ ] **2.9 role-ux SKILL 两行替换**（内容本身已 stale：M3/M4/M7 已迁 domain-tvu、M11–M20 已迁 design-process、R1 已 stub —— 执行前 `grep -n "^#\{2,4\}.*M3 \|^#\{2,4\}.*M11" docs/internal/mockup-conventions.md` 亲验为 0 命中再动手）。Edit：

  old:
  ```
  - `mockup-conventions.md` — M3 状态色（红=Live / 绿=Preview / 灰=Inactive）/ M4 monitoring dark only / M7 control surface 视觉权重 / M11–M20 plan-owner 方法论
  - `code-conventions.md` — R0 Phase 0 mapping / R1 库实证 / 真源关系（Figma 100% / code 60% sync）
  ```
  new:
  ```
  - [`mockup-conventions.md`](../../docs/internal/mockup-conventions.md) — mockup 侧约束全集（状态色 / monitoring 主题等 domain 规则已迁 [`domain-tvu.md`](../../docs/internal/domain-tvu.md)，plan-owner 方法论已迁 [`design-process.md`](../../docs/internal/design-process.md)；各文件清单见其头部读取指引 / inventory 块）
  - [`code-conventions.md`](../../docs/internal/code-conventions.md) — code 侧约束全集 + 真源关系（Figma 100% / code 60% sync）
  ```
- [ ] **2.10 consumer-product-conventions 两处 legacy 指针（判据真源 ⛔4，verify-then-edit）**：
  - 先验：`grep -nE "^#{2,4}.*\bM2\b" docs/internal/mockup-conventions.md`（预期 0 命中）+ `grep -nE "^#{2,4}.*\bM32\b" docs/internal/mockup-conventions.md`（预期命中 library-first heading）。**若 M2 仍是活 heading → 本步整个跳过，保持原文**。
  - Edit ①：old `- 组件复用：\`docs/internal/mockup-conventions.md\` M2 / \`docs/internal/code-conventions.md\` R15` → new `- 组件复用（library-first）：\`docs/internal/mockup-conventions.md\` M32 / \`docs/internal/code-conventions.md\` R15`
  - Edit ②：old `- 图标取 \`dist/icons/svg/\`：\`docs/internal/code-conventions.md\` R1` → new `- 图标取 \`dist/icons/svg/\`：\`docs/internal/code-conventions.md\` R15 §icons（R1 为 legacy stub）`
- [ ] **2.11 本地预验**（commit 时 hook 还会跑，先跑省一轮返工）：`pnpm lint-skills && node scripts/audit-rule-load-map.mjs && node scripts/audit-artifact-routing.mjs`，三绿。再对三份 SKILL.md 跑「枚举探针」grep 逐行自查（同 1.9 标准）。
- [ ] **2.12 Commit**（后台跑）：

  ```bash
  git commit -F <msg文件> -- skills/tvu-design-mockup/SKILL.md skills/tvu-design-code/SKILL.md skills/role-ux/SKILL.md skills/consumer-product-conventions/SKILL.md
  git reset -- skills/tvu-design-mockup/SKILL.md skills/tvu-design-code/SKILL.md skills/role-ux/SKILL.md skills/consumer-product-conventions/SKILL.md
  ```
  message: `docs(skills): SKILL.md 规则 ID 枚举句收成指针（mockup/code/role-ux）+ 修两处 legacy 指针（INFRA-F58 S7 前置清零）`

## Task 3: 闸扩展 —— S7 判据 + 单测 + CLI 故障注入

**产出**：`scripts/lib/rule-ids.mjs`（系列抽取，行为不变）、`scripts/audit-rule-inventory.mjs`（S7）、`tests/audit-rule-inventory.test.ts`（新增两个 describe）。

- [ ] **3.1 rule-ids.mjs 抽出系列常量（行为零变化）**。把

  ```js
  export const RULE_HEADING_RE =
    /^#{2,4}\s+\*{0,2}(M-[A-Z][A-Z-]+|R-[A-Z][A-Z-]+|M\d+(?:\.\d+)?|R\d+S?|C\d+|I\d+|Q\d+)\b/
  ```
  改为：

  ```js
  /** 规则编号的系列形态（无锚点、无捕获）。第 4 个调用面 = audit-rule-inventory S7（行内 token）。 */
  export const RULE_ID_SERIES =
    'M-[A-Z][A-Z-]+|R-[A-Z][A-Z-]+|M\\d+(?:\\.\\d+)?|R\\d+S?|C\\d+|I\\d+|Q\\d+'

  export const RULE_HEADING_RE = new RegExp(`^#{2,4}\\s+\\*{0,2}(${RULE_ID_SERIES})\\b`)
  ```
  并在文件头注释的调用面清单里补一行 `audit-rule-inventory.mjs —— S7 指针化文档反枚举（行内 token 形态）`。
- [ ] **3.2 验证重构零回归**：`grep -rln "rule-ids" tests/ | xargs npx vitest run` 全绿（rule-load-map / rule-number-collision / rule-next 等既有消费面）。
- [ ] **3.3 写失败测试**。`tests/audit-rule-inventory.test.ts` import 块加三个名字（Edit：在 `findSelfDeclaredChains,` 之后插入 `findPointerizedEnumerations,` 一行，在 `EXEMPTIONS,` 之后插入 `POINTERIZED_DOCS,\n  MIN_ROWS_FOR_TABLE,`），文件末尾追加：

  ```ts
  describe('findPointerizedEnumerations — S7', () => {
    it('单行 ≥3 个裸 id → inline 命中（不要求提到目标文件名 —— 与 S4 的关键差异）', () => {
      const hits = findPointerizedEnumerations('本文件规则含 M900 / M901 / M902 三条。')
      expect(hits).toHaveLength(1)
      expect(hits[0]).toMatchObject({ kind: 'inline', ids: ['M900', 'M901', 'M902'] })
    })

    it('1-2 个 id 的引用不命中（引用 ≠ 清单，阈值与 S4 同源）', () => {
      expect(findPointerizedEnumerations('M31 ↔ R14 是 mirror pair 的例子。')).toEqual([])
    })

    it('§ 前缀 id 不计数（prescriptive 引用形态放行，同 S4 裁定）', () => {
      expect(findPointerizedEnumerations('读 §M900 + §M901 + §M902 三节。')).toEqual([])
    })

    it('US-N 场景号计入 token（US-7 漂移的病面）', () => {
      expect(findPointerizedEnumerations('场景有 US-1 / US-2 / US-3。')[0].ids).toEqual(['US-1', 'US-2', 'US-3'])
    })

    it('连续 ≥3 行首列 ID 的表格 → table 命中（每行只 1 个 id 也拦 —— OVERVIEW §3.1 的真实形态）', () => {
      const md = ['| ID | Rule |', '|---|---|', '| **M900** | a |', '| M901 | b |', '| `M902` | c |'].join('\n')
      const hits = findPointerizedEnumerations(md)
      expect(hits).toHaveLength(1)
      expect(hits[0]).toMatchObject({ kind: 'table', line: 3, rows: 3 })
    })

    it('2 行 ID 打头的表不命中（阈值以下）', () => {
      expect(findPointerizedEnumerations('| M900 | a |\n| M901 | b |')).toEqual([])
    })

    it('首列非 ID（脚本路径 / 文案）的表不命中 —— §5.3 audit 脚本表的合法形态', () => {
      const md = ['| scripts/a.mjs | M900 audit |', '| scripts/b.mjs | M901 audit |', '| scripts/c.mjs | M902 audit |'].join('\n')
      expect(findPointerizedEnumerations(md)).toEqual([])
    })

    it('表格 run 被非 ID 行截断 → 各段不够数', () => {
      const md = ['| M900 | a |', '| M901 | b |', '| 说明 | x |', '| M902 | c |'].join('\n')
      expect(findPointerizedEnumerations(md)).toEqual([])
    })

    it('rule-inventory-ok 行内标记放行（与 S4 同一 escape；本计划存量清零未使用它）', () => {
      expect(findPointerizedEnumerations('M900 / M901 / M902 <!-- rule-inventory-ok: 假设的历史引用 -->')).toEqual([])
    })

    it('已知边界回归钉：ASCII 连字符与 .. 区间是 0 token（形态代理非语义判定）', () => {
      expect(findPointerizedEnumerations('迁出：M2-M9 / M11-M22；场景 US-1..6。')).toEqual([])
    })

    it('真仓库回归钉：受管指针化文档非空且 0 命中', () => {
      for (const rel of POINTERIZED_DOCS) {
        const text = read(rel)
        expect(text.length).toBeGreaterThan(0)
        expect(findPointerizedEnumerations(text)).toEqual([])
      }
    })
  })

  describe('evaluate — S7 编码', () => {
    const okInv = { id: 'x', target: 't.md', liveIds: ['A'], inventory: { found: true, ids: ['A'] } }
    const base7 = { inventories: [okInv], mirrors: [], chains: [], exemptions: [] }

    it('S7: 枚举回潮 → 红并点名 file:line', () => {
      const f = evaluate({
        ...base7,
        pointerized: [{ file: 'docs/internal/CONVENTIONS-OVERVIEW.md', missing: false, hits: [{ line: 49, kind: 'table', rows: 13, ids: ['M900'] }] }],
      }).failures
      const s7 = f.filter((x) => x.code === 'S7')
      expect(s7).toHaveLength(1)
      expect(s7[0].message).toMatch(/CONVENTIONS-OVERVIEW\.md:49/)
    })

    it('S7: 受管文件读不到 → fail closed（注册表 stale 也要红，不是 skip）', () => {
      const f = evaluate({ ...base7, pointerized: [{ file: 'gone.md', missing: true, hits: [] }] }).failures
      expect(f.map((x) => x.code)).toContain('S7')
    })

    it('S7 注册面含 4 份已指针化文档', () => {
      for (const f of [
        'docs/internal/CONVENTIONS-OVERVIEW.md',
        'skills/tvu-design-mockup/SKILL.md',
        'skills/tvu-design-code/SKILL.md',
        'skills/role-ux/SKILL.md',
      ]) {
        expect(POINTERIZED_DOCS).toContain(f)
      }
    })

    it('表格阈值 ≥3 行（与 MIN_IDS_FOR_MIRROR 同刻度：1-2 是引用/示例，3 起是清单）', () => {
      expect(MIN_ROWS_FOR_TABLE).toBe(3)
    })
  })
  ```
- [ ] **3.4 跑确认失败**：`npx vitest run tests/audit-rule-inventory.test.ts` —— 新增用例全 fail（import 缺名字报错也算 fail），既有用例不受影响。
- [ ] **3.5 最小实现**。`scripts/audit-rule-inventory.mjs`：

  ① import 行补：
  ```js
  import { RULE_ID_SERIES } from './lib/rule-ids.mjs'
  ```
  ② 在 `EXEMPTIONS` 定义之后新增（常量 + 函数）：
  ```js
  /** S7 受管「已指针化」文档：清单已收成指针，此后不得再长出规则 ID 枚举形态。
   *  ⚠️ 注册面是手工清单（同 S4/S6 的 ⚠️）：新指针化一份导览/skill 要手工加进来，
   *  并同步 .husky/pre-commit 的 rule-inventory 触发 regex（漏挂 L4 时 L5 prepublishOnly / Gitea CI 兜底）。 */
  export const POINTERIZED_DOCS = [
    'docs/internal/CONVENTIONS-OVERVIEW.md',
    'skills/tvu-design-mockup/SKILL.md',
    'skills/tvu-design-code/SKILL.md',
    'skills/role-ux/SKILL.md',
  ]

  /** 表格连续 ≥ 这么多行首列是 ID 才算「枚举表」（与 MIN_IDS_FOR_MIRROR 同刻度）。 */
  export const MIN_ROWS_FOR_TABLE = 3

  // S7 token 系列 = 规则编号（共用真源 rule-ids.mjs）+ US-N 场景号（US-7 漂移实证 2026-08-12）。
  const S7_TOKEN_SERIES = `${RULE_ID_SERIES}|US-\\d+`
  const S7_TOKEN_RE = new RegExp(`(?<![\\w.-])(?:${S7_TOKEN_SERIES})(?![\\w.-])`, 'g')
  const S7_CELL_RE = new RegExp(`^(?:${S7_TOKEN_SERIES})(?![\\w.-])`)

  /** 一行里的裸 token（§ 前缀 = 引用不计，判据与 findEnumerationMirrors 同款）。 */
  function bareIdsOnLine(line) {
    const ids = new Set()
    for (const m of line.matchAll(S7_TOKEN_RE)) {
      if (!line.slice(Math.max(0, m.index - 3), m.index).includes('§')) ids.add(m[0])
    }
    return [...ids]
  }

  /**
   * S7：受管指针化文档里的「规则 ID 枚举形态」。两种形态，每种都能单独红：
   *   inline —— 单行 ≥ minIds 个不同裸 id（S4 是「同行还要提到目标文件名」的行级谓词，
   *             正是它扫不到 OVERVIEW 的枚举表 —— 表格每行只 1 个 id 且不提文件名）；
   *   table  —— 连续 ≥ minRows 行的表格行，首列（剥 `*` 与反引号后）以 id 打头。
   * denominator-free：不对照活源分母，纯形态判定 —— 假 id / phantom（发明过「M2」）同样拦。
   * ⚠️ 已知边界（形态代理，非语义判定，同 S4 的 ⚠️）：ASCII 连字符区间（M2-M9）与
   *   `..` 区间（US-1..6）因整词边界不计 —— 拦的是枚举形态的自然漂移，不是恶意规避。
   */
  export function findPointerizedEnumerations(mdText, { minIds = MIN_IDS_FOR_MIRROR, minRows = MIN_ROWS_FOR_TABLE } = {}) {
    const lines = mdText.split('\n')
    const hits = []
    lines.forEach((line, i) => {
      if (ALLOW_RE.test(line)) return
      const ids = bareIdsOnLine(line)
      if (ids.length >= minIds) hits.push({ line: i + 1, kind: 'inline', ids })
    })
    let run = []
    const flush = () => {
      if (run.length >= minRows) {
        hits.push({ line: run[0].line, kind: 'table', rows: run.length, ids: run.map((r) => r.id) })
      }
      run = []
    }
    lines.forEach((line, i) => {
      if (!/^\s*\|/.test(line) || ALLOW_RE.test(line)) return flush()
      const cell = (line.split('|')[1] ?? '').replace(/[`*]/g, '').trim()
      const m = cell.match(S7_CELL_RE)
      if (m) run.push({ line: i + 1, id: m[0] })
      else flush()
    })
    flush()
    return hits.sort((a, b) => a.line - b.line)
  }
  ```
  ③ `evaluate` 签名加参并在 `chains` 循环之后插入 S7 块：
  ```js
  export function evaluate({ inventories = [], mirrors = [], chains = [], exemptions = EXEMPTIONS, pointerized = [] } = {}) {
  ```
  ```js
    for (const p of pointerized) {
      if (p.missing) {
        failures.push({
          code: 'S7',
          message: `${p.file}：受管指针化文档读不到 —— 注册表 stale（文件被移/改名？），fail closed`,
        })
        continue
      }
      for (const h of p.hits) {
        failures.push({
          code: 'S7',
          message:
            h.kind === 'table'
              ? `${p.file}:${h.line} 规则 ID 枚举表回潮（连续 ${h.rows} 行首列 ID：${h.ids.join(' / ')}）—— 本文件已指针化，清单活源在规则文件自身（§AI 读取指引 / §Task Entry Modes）`
              : `${p.file}:${h.line} 单行枚举 ${h.ids.length} 个规则 ID（${h.ids.join(' / ')}）—— 降为指针，或确属 prescriptive 引用则改 § 形态`,
        })
      }
    }
  ```
  ④ `main()` 里 `chains` 收集之后、豁免命中判定之前插入：
  ```js
    const pointerized = []
    let pointerizedFileCount = 0
    for (const doc of POINTERIZED_DOCS) {
      const text = readOrNull(doc)
      if (text === null) {
        pointerized.push({ file: doc, missing: true, hits: [] })
        continue
      }
      pointerizedFileCount++
      pointerized.push({ file: doc, missing: false, hits: findPointerizedEnumerations(text) })
    }
  ```
  `evaluate` 调用加 `pointerized`；覆盖面打印在 S6 行后加：
  ```js
    console.log(`  S7 扫描面   : ${pointerizedFileCount}/${POINTERIZED_DOCS.length} 份已指针化文档（单行 ≥${MIN_IDS_FOR_MIRROR} 裸 id · 表格连续 ≥${MIN_ROWS_FOR_TABLE} 行 ID 打头；区间写法不计 —— 形态代理非语义判定）`)
  ```
  FAIL footer（`真源形态：…` 那两行 console.error）后补一行：
  ```js
    console.error('  已指针化文档（S7）：概念解释可留，ID 枚举一律降为指针；受管清单 = POINTERIZED_DOCS。')
  ```
  ⑤ 头部大注释「判据」清单补一行 `S7 已指针化文档不得再长出 ID 枚举形态（行级 + 表格级，denominator-free）`，并在 Why 段追加：
  ```
  //   2026-08-12 扩展（S7）：08-03 的 S4 是「同行提到目标文件 + ≥3 个已知 id」的行级谓词，
  //   扫不到两类真实存在的镜像：① CONVENTIONS-OVERVIEW.md §3.1/§3.2 的 ID 枚举表（每行 1 个
  //   id、不提目标文件名；自标 2026-05-18，实测对 67-heading 的活源只列 13 条，§6 场景表缺
  //   US-7）；② skills/*/SKILL.md 正文的枚举句。治法同型：这些文档降为指针（概念可留），
  //   S7 按文件级禁枚举形态回潮。缺项差集不在此复述 —— 以指针化 commit 的 diff 为证。
  ```
- [ ] **3.6 跑确认过**：`npx vitest run tests/audit-rule-inventory.test.ts` 全绿（若「真仓库回归钉」红 → 那是 Task 1-2 漏改的存量枚举，**按测试/闸点名的 file:line 回去改那几行文档**，不改测试不改阈值，改完重跑；这就是「以闸自己的输出为准」）。
- [ ] **3.7 真仓库 CLI 跑**：`pnpm audit:rule-inventory` → 期望 PASS 且覆盖面打印含 `S7 扫描面 : 4/4`。有 S7 红同 3.6 处理。
- [ ] **3.8 CLI 故障注入 ①（inline 形态）**——先核探针无撞名：`grep -n "M90[0-2]" docs/internal/CONVENTIONS-OVERVIEW.md skills/tvu-design-mockup/SKILL.md skills/tvu-design-code/SKILL.md skills/role-ux/SKILL.md` 零命中。然后：

  ```bash
  F=docs/internal/CONVENTIONS-OVERVIEW.md
  git diff --quiet -- "$F" || { echo "该文件有未提交改动，先停"; exit 1; }
  SHA=$(shasum -a 256 "$F" | cut -d' ' -f1)
  printf '\n临时故障探针（勿提交）：本行枚举 M900 / M901 / M902\n' >> "$F"
  pnpm audit:rule-inventory; echo "exit=$?"     # 期望 exit=1，输出含 [S7] + CONVENTIONS-OVERVIEW.md:<行号>
  git restore "$F"
  [ "$SHA" = "$(shasum -a 256 "$F" | cut -d' ' -f1)" ] && echo RESTORED-BYTE-IDENTICAL
  pnpm audit:rule-inventory; echo "exit=$?"     # 期望回 exit=0
  ```
- [ ] **3.9 CLI 故障注入 ②（table 形态，且证明 skills/ 路径真在扫描面）**：

  ```bash
  F=skills/role-ux/SKILL.md
  SHA=$(shasum -a 256 "$F" | cut -d' ' -f1)
  printf '\n| M900 | 探针 |\n| M901 | 探针 |\n| M902 | 探针 |\n' >> "$F"
  pnpm audit:rule-inventory; echo "exit=$?"     # 期望 exit=1，输出含 role-ux/SKILL.md + 「枚举表回潮」
  git restore "$F"
  [ "$SHA" = "$(shasum -a 256 "$F" | cut -d' ' -f1)" ] && echo RESTORED-BYTE-IDENTICAL
  pnpm audit:rule-inventory; echo "exit=$?"     # 期望回 exit=0
  ```
- [ ] **3.10 Commit**（后台跑；staged 的闸脚本会让 hook 里 rule-inventory / rule-load-map / rule-number-collision 三闸都真跑一遍——这正是「改闸的人也要过闸」）：

  ```bash
  git commit -F <msg文件> -- scripts/lib/rule-ids.mjs scripts/audit-rule-inventory.mjs tests/audit-rule-inventory.test.ts
  git reset -- scripts/lib/rule-ids.mjs scripts/audit-rule-inventory.mjs tests/audit-rule-inventory.test.ts
  ```
  message: `feat(rule-inventory): S7 —— 已指针化文档禁规则 ID 枚举回潮（行级+表格级，denominator-free；INFRA-F58 扩展）`

## Task 4: 挂载核验 + 全链绿

**产出**：`.husky/pre-commit` rule-inventory 触发面覆盖 4 份新受管文件；四个调用面全验。

- [ ] **4.1 扩触发 regex + AM→AMD**。`.husky/pre-commit` 的 rule-inventory 块（`echo "▶ pre-commit: rule-inventory` 起）：注释尾部追加两行说明，条件行整行替换为：

  ```sh
  # 2026-08-12 扩（S7）：CONVENTIONS-OVERVIEW 与三份 SKILL.md 已指针化，出现规则 ID 枚举形态即红；
  # 触发面同步扩到这 4 份。diff-filter 带 D：删掉受管文件正是 S7 fail closed 该说话的时刻。
  if git diff --cached --name-only --diff-filter=AMD | grep -qE '(docs/internal/design-process\.md|docs/internal/domain-tvu\.md|docs/internal/code-conventions\.md|docs/internal/mockup-conventions\.md|docs/internal/figma-technical-reference\.md|docs/internal/CONVENTIONS-OVERVIEW\.md|docs/session-handoff\.md|docs/multi-session-collaboration-rules\.md|docs/PROJECT_MAP\.md|docs/meta-rules\.md|docs/working-principles\.md|docs/GETTING_STARTED\.md|AGENTS\.md|CLAUDE\.md|README\.md|skills/(tvu-design-mockup|tvu-design-code|role-ux)/SKILL\.md|scripts/audit-rule-inventory\.mjs)'; then
  ```
- [ ] **4.2 regex 正负探针 + 语法核**：

  ```bash
  PAT='(docs/internal/design-process\.md|docs/internal/domain-tvu\.md|docs/internal/code-conventions\.md|docs/internal/mockup-conventions\.md|docs/internal/figma-technical-reference\.md|docs/internal/CONVENTIONS-OVERVIEW\.md|docs/session-handoff\.md|docs/multi-session-collaboration-rules\.md|docs/PROJECT_MAP\.md|docs/meta-rules\.md|docs/working-principles\.md|docs/GETTING_STARTED\.md|AGENTS\.md|CLAUDE\.md|README\.md|skills/(tvu-design-mockup|tvu-design-code|role-ux)/SKILL\.md|scripts/audit-rule-inventory\.mjs)'
  printf '%s\n' docs/internal/CONVENTIONS-OVERVIEW.md | grep -qE "$PAT" && echo POS1-OK
  printf '%s\n' skills/role-ux/SKILL.md               | grep -qE "$PAT" && echo POS2-OK
  printf '%s\n' skills/design-walkthrough/SKILL.md    | grep -qE "$PAT" || echo NEG-OK-未注册skill不触发
  bash -n .husky/pre-commit && echo SYNTAX-OK
  ```
- [ ] **4.3 全链绿（四个调用面）**：

  ```bash
  pnpm audit:rule-inventory                             # CLI 面
  pnpm lint-skills                                      # SKILL 链接完整
  pnpm audit:stale-anchors                              # 锚点零孤儿（OVERVIEW heading 已改，验无孤儿）
  npx vitest run tests/audit-rule-inventory.test.ts     # vitest import 面
  grep -rln "rule-ids" tests/ | xargs npx vitest run    # rule-ids 消费面零回归
  ```
  （L4 面 = 4.4 的 commit 本身：staged `.husky/pre-commit` 不在触发 regex 里，但 hook 全文照跑；L5 面 = prepublishOnly 已含 `audit:rule-inventory`，不另跑全量。）
- [ ] **4.4 Commit**（后台跑）：

  ```bash
  git commit -F <msg文件> -- .husky/pre-commit
  git reset -- .husky/pre-commit
  ```
  message: `chore(hooks): rule-inventory 触发面扩到 CONVENTIONS-OVERVIEW + 3 份 SKILL.md，diff-filter AM→AMD（S7 fail closed 需要 D）`

## Task 5: 收尾登记

- [ ] **5.1 backlog 一句**。`docs/internal/backlog.md` §INFRA-F58 entry 内、「新增机械闸（本次落地）= `pnpm audit:rule-inventory`」那条 bullet 之后追加一条子行（不新增 entry、不动 Active 计数）：

  ```markdown
    - **2026-08-12 扩展（S7）**：08-03 的 S4 行级谓词扫不到两类无闸副本 —— `CONVENTIONS-OVERVIEW.md` §3/§6 的 ID/US 枚举表（自标 2026-05-18，实测对活源大面积 stale）与 `skills/*/SKILL.md` 枚举句。已指针化清零 + 上 S7（受管指针化文档出现 ID 枚举形态即红，行级+表格级、denominator-free，注册面 4 份），pre-commit 触发 regex 同步扩且 AM→AMD。⚠️ S7 注册面与 hook regex 都是手工清单，新指针化文档两处都要加。计划：[`plans/2026-08-12-rule-inventory-mirror-gate-extension.md`](../superpowers/plans/2026-08-12-rule-inventory-mirror-gate-extension.md)
  ```
- [ ] **5.2 changeset：不写**。依据（撰 plan 日实测）：闸初版 commit `85a4bf4f` 同为 docs+scripts+hook 改动、无 .changeset；`.changeset/` 现存条目全是库消费面变更。本计划不触库 API/token/组件 → 跟随惯例不写。
- [ ] **5.3 Commit**（后台跑；本计划文件与 backlog 指针同一 commit 落仓，plan-lifecycle S2 可达性一步到位）：

  ```bash
  git commit -F <msg文件> -- docs/internal/backlog.md docs/superpowers/plans/2026-08-12-rule-inventory-mirror-gate-extension.md
  git reset -- docs/internal/backlog.md docs/superpowers/plans/2026-08-12-rule-inventory-mirror-gate-extension.md
  ```
  message: `docs(backlog): INFRA-F58 登记 2026-08-12 S7 扩展 + 收计划文件`
- [ ] **5.4 push 与验证**：`git push`（本项目 commit 默认含 push），落地以 `git ls-remote` 验 ref 已更新（瞬时 remote-rejected 不作数）。
- [ ] **5.5 session wrap-up 提醒（非本计划任务，执行 session 的协议义务）**：更新 `docs/STATUS.md` Last updated 到当天（本计划做完不留 open 项，无需新增条目）+ work-log 追加。

## 执行期订正（执行时如有偏离在此追加 —— 沿 2026-08-03 前身 plan 惯例）

**执行日 2026-09-03（第九十二轮）。撰写日 2026-08-12 → 执行日相隔 22 天。**

1. **前提复核（触发器 S 四行块）结论：全部成立，且 stale 面比撰写时更大。** 撰写日「活源 67 个 rule heading / OVERVIEW §3.1 只列 13 条」→ 执行日闸自印 **`mockup-conventions.md`：活源 76 个 rule heading · inventory 76 项**（`code-conventions.md` 21/21），OVERVIEW §3.1 仍是 13 条内容行 ⇒ 差集 54 → **63**。§6 表仍停在 US-6，活源 `mockup-conventions.md:168` 已有 US-7。⛔ 这两个数别再抄进任何文档，跑闸现取。
2. **行号订正**：`.husky/pre-commit` 的 rule-inventory 触发块在 **`:314`（echo）/ `:319`（条件行）**，plan §现状实测写的 `:257` 已漂。不影响执行 —— plan 自己规定「行号仅作导航，全部用唯一字符串锚点定位」。
3. **Task 1.9 抓到一处 plan 未列的存量枚举**（plan §现状实测只点了 §3.1/§3.2/§3.3/§6，漏了 §8）：`§8 Convention philosophy` 第 3 条原文 `(M35 affordance, M-COLOR audit log, M-INTEGRITY overlap check)` = **3 个裸 id**，恰好压线触发 S7 inline。按 §判据真源「逃逸方向分析 ②」改为 `§` 引用形态（`§M35 / §M-COLOR / §M-INTEGRITY`），保留信息量、不降级为泛指。三个 id 已亲验都是活源真 heading（`mockup-conventions.md:302 / :1199 / :2112`），不是死指针。
   ⚠️ **这是 STATUS 顶条 ① 那条教训的又一个实例**：plan 撰写时列的「哪些行会红」是**推断**，过期无人报警且偏乐观 —— 判据以闸自己的输出为准，plan 的清单只是导航。
4. **Task 2 有两处 `old_string` 已对不上活源**（22 天里被别的改动动过），按硬规则 #10「锚点找不到 → 停下 re-Read 再定位，不准凭本计划引文硬改」处理：
   - **2.8 code SKILL 回流段**：plan 的 old 尾部是「才续 R17+ 新编号」，活源已改成「才续新编号（**先 `pnpm rule:next R` 要号**，号从 `origin/master` 算）」—— 后者更好且正是 plan 想要的方向。⇒ **只替换括号里的枚举部分，保留活源已有的改进**，不按 plan 的 new 整行覆盖（否则会把 `rule:next R` 那句改回去）。
   - **2.9 role-ux 两行**：活源两行各自尾部**多了 plan 没有的实质内容** —— `**M33 注释层双语 vs 产品 UI 层单语（本 skill "双语文案" 约束的边界就到这里）**` 与 `**R16 = M33 的 code 侧镜像**`。plan 的 new 会把它整块删掉，而那是**本 skill 自己的 scope 边界声明**，不是规则清单镜像。⇒ 改为**两行指针 + 第三行把边界声明按 `§M33` / `§R16` 形态保留**。⛔ 别照 plan 直接覆盖。
5. **Task 2.11 自查又抓到一处 plan 未列的存量**：`skills/tvu-design-mockup/SKILL.md:92` 一行 **6 个裸 id**（`I1 / I2 / I3 / I4 / M23.6 / M23.7`）。两个来源性质不同、分别处理：① `Integrity audit: I1=… / I2=… / I3=… / I4=…` 是**闸机器输出的逐字示例**，而该行本意就是「原文照贴」⇒ 列举 key 本就冗余，改成「`Integrity audit:` 那几行**原文照贴**」，⛔ 没有用 `..` 区间那个已知边界绕过；② `M23.6 / M23.7` 是 prescriptive 规则引用 ⇒ 按逃逸方向 ② 改 `§` 形态。
6. **「四份全 0 命中」配了真实数据的阳性对照，不是空过**：同一份判据实现（scratchpad 一次性探针，自带 10 条已知向量自测 + 其中 6 条阴性对照，自测不全过即拒绝输出扫描结果）对 **`b5a5bc57` 版本的 OVERVIEW = 17 命中**（含 13 行 / 15 行 / 6 行 / 4 行四张表 + 13 处 inline），对改后四份 = 0 命中。⇒ 读数从 17 变到 0，不是「逐字不变的 0」。
7. **Task 3.3 在 plan 的测试之外多加了一条 `it`**（共 16 条新增，plan 列 15 条）：`I 系列计入 token —— 2026-09-03 执行日实测的漏网形态`。理由 = 订正 5 抓到的那条真实存量恰好靠 `I\d+` 命中，而 plan 的用例里 `I` 系列**一条都没有**（只有 M / R / US）。⇒ 把实际咬到的形态钉进回归面，⛔ 不是为凑数。
8. **闸类验收三问补了 plan 没做的第三次注入 —— CLI 级 fail-closed**。plan 的三问第二问写「受管文件读不到 → S7 红，**有单测钉**」，那只证到 `evaluate()` 这一层；`readOrNull → missing:true` 那段接线**没有任何用例覆盖**（正是 memory `gate-regression-face-whole-script-fixture` 说的「抽纯函数单测覆盖不到接线」）。⇒ 执行日补跑：把 `skills/role-ux/SKILL.md` 整个移走 → `node scripts/audit-rule-inventory.mjs` **exit=1**、点名「受管指针化文档读不到 —— 注册表 stale，fail closed」，**且覆盖面自印从 `S7 扫描面 : 4/4` 变成 `3/4`**（读数随注入而变 = 这个数不是装饰）。移回后 sha256 逐字节一致、exit 回 0。
9. **三次注入的 sha256 复原凭据**（都用 `git restore` / `mv` 回移，注入前先 `git diff --quiet` 确认无未提交改动 —— 撞 memory `fault-probe-name-must-be-absent-from-repo` 那条「别用 checkout 抹掉未提交改动」）：`CONVENTIONS-OVERVIEW.md` = `bf52abe79ac07dea7ea7b525acfad5d24291c2e56644ef9d131b0749496222a0`（注入前后同值）· `skills/role-ux/SKILL.md` = `d5bec7ab461b104fbee7d81543954c7456363f997bdd709bfe76fa4a55e55f0b`（两次注入前后共三次同值）。探针 `M900/M901/M902` 执行日重核：四份受管文件 0 命中、全仓无同名 rule heading。
10. 🔴 **Task 3 的 commit 第一次被 pre-commit 拦下 —— plan 对闸的预判第 4 次错，这次错在「有一份测试文件 plan 完全不知道它存在」**。`tests/audit-rule-inventory-cli.test.ts`（[[INFRA-F138]]，2026-08-27 加，晚于本 plan 15 天）是本闸的**整脚本 fixture-root harness**：它在临时目录里搭一棵假仓库树跑真脚本。那棵树里没有 `POINTERIZED_DOCS` 那 4 份 ⇒ S7 每个 fixture 都多报 4 条 fail-closed ⇒ A/B/C/D/E 五组的违例计数集体偏移，**14/16 条红**。
    - **⛔ 没有走「让 S7 对缺文件 skip」那条路** —— 那会把本闸刚建的 fail-closed 判据当场挖空，正是 memory `plan-fixture-is-not-the-criterion-source` 说的「照 fixture 改实现就开逃逸口」。**改 fixture，不改判据。**
    - 落地：① `fixture()` 的文件表补那 4 份**干净版**（概念散文 + 指针，零 id）② A1 的 `contains` 加 `4/4 份已指针化文档`，并注明**这个数不是 fixture 独有读数**（注册面是硬编码常量，真仓库也印 4/4），区分真假仓库仍靠上面四个分母 ③ 顺带把 S7 补进整脚本回归面：**C9** 受管文件读不到 ⇒ fail closed 且分子 4/4→3/4（证明分子是存在性探测不是常量）· **C10** inline 形态且 must-not-hit `[S4]`（证明 S4 对这行确实是瞎的）· **C11** table 形态且 must-not-hit「单行枚举」（证明两形态不互相顶替）。
    - ⇒ **Task 3 的 commit pathspec 比 plan 多一份** `tests/audit-rule-inventory-cli.test.ts`。
    - 读数：CLI harness 16 → **19 passed**；四份 rule-inventory 相关测试合计 **127 passed**；`pnpm audit:gate-regression-ratchet` = `BASELINE 40 · 现取 40 · delta +0` PASS（S7 是既有闸内新增判据、不是新闸，棘轮不该动 —— 实测确实没动）。
