# artifact-routing 制品自声明（REWORK-01）— 设计 spec

- **日期**：2026-07-17
- **状态**：设计定稿（owner 已批准 3 决策点：①自声明方向 ②P1 文件型先行 ③落轻量 spec）→ P1 实现 + planted-drift
- **tracked**：backlog INFRA-F68 · TRIG/REWORK 组 · REWORK-01
- **纪律**：改**阻塞型共享 pre-commit gate**（`.husky/pre-commit:258 → node scripts/audit-artifact-routing.mjs || exit 1`），误报会卡住全员提交 → 沿用 DUP-03 范式：先设计 + planted-drift 严测，禁赶工。

---

## 1. 问题与根因（已 live 亲验）

[`scripts/audit-artifact-routing.mjs`](../../../scripts/audit-artifact-routing.mjs) 的 `ARTIFACTS` 数组**硬编码只有 3 条**（PRD / UX 交付卡结构 / 交付物同步），每条断言"def+route+wake 三件齐全"。但项目里可唤醒制品 ≈48 个（skill 13 + prompt 3 + 触发器 ~17 + M-rule ~15）。

**根因**：制品清单硬编码 = 违反 meta-rules 规则 1（真源单一）——制品是否存在/是否可唤醒有了**第二处真源**（脚本数组），与制品本体分叉。后果 = 新增任何可唤醒制品都得手动往数组里加，漏加则该制品的路由**静默不受检**。这就是 INFRA-F68 记的"越自查越频繁"机制根因（越加规则越要手动维护校验清单）。

---

## 2. 目标

制品是否可唤醒、唤醒词是什么，**只存在于制品自己文件里**（自声明）。gate 只做两件事：**扫声明 → 验路由**。加制品时 gate **零改动**。

---

## 3. 方案：制品自声明（self-declaration）

每个文件型制品在**自己的文件**里声明：

```yaml
wakeable: true
wakeWords: ["@TVU mockup"]
```

gate 扫描全部文件型制品（`skills/*/SKILL.md` frontmatter + `docs/internal/_prompts/*.prompt.md` 头部），对每个 **声明 `wakeable: true`** 的制品，断言其每个 `wakeWord` 都出现在中央路由注册表 [`docs/WAKE-WORDS.md`](../../../docs/WAKE-WORDS.md)。这就是防漂的单一跨文件保证：**你声明自己能被某短语唤醒，该短语必须在注册表里、路由层才surface得到**（否则 = 死唤醒词）。

### 3.1 误报防控（三条，保守优先）

| 场景 | gate 行为 | 理由 |
|---|---|---|
| 制品无 `wakeable` 声明 | **skip（不校验）** | 保守默认：没声明就不管，绝不误报阻塞 |
| 制品声明 `wakeable: false` | **skip** | 豁免权在制品本身（内部编排/gate/全局角色可显式退出） |
| 声明存在但解析失败（格式坏 / wakeWords 缺失） | **fail-open：warn 不阻塞** | 解析器脆弱性不该变成全员 commit 障碍；报警提示人工看 |

### 3.2 分期（owner 批准 P1 文件型先行）

- **P1（本次）= 文件型制品**（skill 13 + prompt 3 = 16）。skill 有 YAML frontmatter、prompt 有头部，天然有声明位。
- **P2（后续，单独测）= 规则型制品**（触发器嵌在 `meta-rules.md`、M-rule 嵌在 `mockup-conventions.md`）。无独立文件声明位、需另设解析方案、误报面更大 → 单列。
- **原 3 条 doc 型硬编码保留不动**（它们是 doc-section/规则型，归 P2 自声明化范畴）。P1 = 在其上**新增**动态文件型扫描层，两层都跑、任一 fail 则 exit 1。

### 3.3 P1 现树声明清单（让"现树 PASS"成立）

只标注**在本 repo WAKE-WORDS.md 注册、且确为用户唤醒**的 7 个 skill；其余按"豁免权在制品"不声明（跳过）：

| skill | 声明 wakeWord（已核实在 WAKE-WORDS.md） |
|---|---|
| tvu-design-mockup | `@TVU mockup` |
| tvu-design-code | `@TVU code` |
| tvu-design-pipeline | `@TVU 设计全流程` |
| design-walkthrough | `design walkthrough` |
| design-qa-loop | `design done` |
| setup-tvu-consumer | `初始化 TVU 消费产品` |
| consumer-product-conventions | `收尾` |

**不声明（跳过）的**：role-ux / role-translator（全局角色，唤醒词归 `~/.claude/prompt_roles.md`，非本 repo 注册表）· design-discovery / persona-simulation / upstream-gate（内部编排/gate，由 orchestrator 触发非用户直呼）· shared-vocab-rules（frontmatter 明写"不单独触发"）· 3 个 prompt（file-path 调用非唤醒词）。

---

## 4. enforcement 层级（触发器 K 强制声明）

- **层级 = L4（pre-commit gate）**，与被替换的旧脚本同级、不降级。
- 为何不是 L1/L3：制品-注册表一致性**客观可测**（字符串包含判定），必须机器强制。
- 为何不升 L5（CI）：pre-commit 已覆盖 owner 直推 + 贡献者 PR 两路径（gate 平权原则，PROJECT_GOAL）；升 CI 是正交增强，非本项必需。

---

## 5. planted-drift 严测协议（实现后必跑，DUP-03 范式）

1. **现树 PASS**：`node scripts/audit-artifact-routing.mjs` exit 0（3 doc + 7 声明 skill 全绿）。
2. **植入无路由假制品必 exit 1**：临时建 `skills/__planted-fake__/SKILL.md` 声明 `wakeable: true` + `wakeWords: ["@TVU 不存在的唤醒词XYZ"]`（不在 WAKE-WORDS.md）→ gate 必 exit 1 且指名该制品。
3. **植入豁免必 PASS**：把假制品改 `wakeable: false`（或删声明）→ gate 必 exit 0（跳过）。
4. **复原**：删假制品目录 → gate 回 exit 0，工作树干净。

全程截真实 stdout + exit code 作证据，不凭"应该能过"。

---

## 6. 范围边界

- **本 spec 不含 P2**（规则型自声明）——待 P1 稳定 + owner 排期。
- **不改 WAKE-WORDS.md 内容**——P1 只读它做校验；若发现真死唤醒词，单独报 owner 不顺手改（避免范围膨胀，[[feedback_root-cause-scope-creep]]）。
- **不动 `.husky/pre-commit` 挂载行**——脚本路径不变，挂载无需改。
