# TVU 设计系统 — 场景化强制（Scenario-Aware Enforcement）设计 spec

> 状态：**草案 — 待 owner 审定**（2026-06-10）。本文是 deep-optimization 的产物：把「4 规则类强制」对齐到「产品设计场景」，定缺口 + 增量落地路径。
> 真源关系：**指引层已存在**（`code-conventions.md` R6/R7 · `design-process.md` M21/M21.1/M21.2）。本 spec **不重写指引**，只补**自动化强制 + 接线**，并保证自动化**尊重既有场景模式**。

---

## 0. 背景（为什么写这份）

owner 实证痛点：「让 AI 做个很简单的产品任务，它手搓图标 + 硬编码背景 / 文字色，没按设计系统组件 / 规则来。」

追因（2026-06-10 实跑核实）：**不是缺指引**——`code-conventions.md` 已有完整的 R0-R7 规则 + 4 场景矩阵；`design-process.md` 已有 M21 系列。**真问题是**：
1. 自动化 audit / lint **没覆盖**指引已要求的两类（手搓 native 控件、硬编码间距）；
2. 现有 audit / eslint-plugin **没接线**进消费产品（scaffold 模板 + setup 技能都不自动挂 lint / pre-commit）→ 违例 silent 通过；
3. 自动化若上严格全量模式，会**违背 R6/场景2**（"不借机修 pre-design-system 合规债"）→ 必须场景感知。

---

## 1. 4 规则类（强制目标）

| # | 规则类 | 含义 |
|---|---|---|
| ① | COLOR-TOKEN | 色值用 DS token / CSS var，不硬编码 hex/rgb |
| ② | DS-ICON | 图标用 DS Icon / dist，不内联自画 SVG / unicode arrow / emoji |
| ③ | SPACING-TOKEN | 间距 / radius / size 用 token，不硬编码 px |
| ④ | DS-COMPONENT | 用 DS 组件（Button/Input/Select/...），不手搓 native `<button>/<input>/<select>` |

## 2. 场景映射（owner 三场景 ↔ 已有 R7 四场景）

owner 描述的场景在 `code-conventions.md §R7` 已编码，**一一对应**：

| owner 场景 | R7 场景 | 强制模式 | 真源规则 |
|---|---|---|---|
| 新产品全量 TVU | **场景 3** 全新产品 TVU 构建 | **STRICT 全量**（4 类全开、阻塞） | R0-R3 全套 / M0/M1/M32 |
| 已有产品功能迭代（与现存保持统一，除非重设计） | **场景 2** 已有产品增量（+ **场景 1** 非 TVU 视觉还原） | **INCREMENTAL / 非阻塞**（只看新模块；旧的不反复报；**不借机修 pre-DS 债**） | R6 决策树 / M21 / "保持统一" |
| 重构达标 | **场景 4** TVU 规范校正 | **MIGRATION 报告**（全量列违例为 worklist，用户显式发起） | 独立 refactor 任务 |

> **关键纪律（owner 2026-06-10 确认）**：场景 2 的"合规"= **与现存产品一致**，不是强行套 TVU。所以场景 2 **不能**跑 strict-TVU 当阻塞闸——否则 legacy 全量误报、且会诱导违反 R6 的 silent compliance fix。**自动化必须按场景切模式**，这是本 spec 的核心约束。

## 3. 强制覆盖矩阵（2026-06-10 实跑核实）

| 规则类 | Code: ESLint plugin | Code: audit-product-code | Mockup: audit | 状态 |
|---|---|---|---|---|
| ① COLOR-TOKEN | `no-hardcoded-color` ✓ | R2 ✓ | `audit-mockup-colors` ✓ | **已覆盖** |
| ② DS-ICON | `no-inline-svg`+`icon-from-dist` ✓ | R1 ✓ | `audit-mockup-typography-icon`+`library-binding` ✓ | **已覆盖** |
| ③ SPACING-TOKEN | ✗ | ✗ | ✗ | **GAP（两轨皆缺）** |
| ④ DS-COMPONENT | `no-base-component-import`（仅管 import，**不管手搓 native 控件**）| ✗ | `library-binding/origin`（部分）| **GAP（Code 手搓控件完全无规则）** |

**接线缺口（已核实）**：
- eslint-plugin 已通过 subpath 导出（`./eslint-plugin`）可用，但 `templates/consumer-product/` **无 eslint config / 无 pre-commit / 无 package.json lint 脚本**；`setup-tvu-consumer` 技能也不接线 → **新消费产品默认零强制**。
- `audit-mockup-library-origin.mjs` **无 npm script**，从不运行；无 `audit:mockup-conformance` 总闸（Tier 1-B 未收尾，5 个分散脚本）。

## 4. 增量落地计划（按依赖 + 对齐排，非按耗时）

> 每项标注**场景感知模式**——这是与"草率上严格全量 lint"的关键区别。

| 项 | 内容 | 场景模式要求 | 归属 | 优先级 |
|---|---|---|---|---|
| **G1** ④ native-control 规则 | 新 ESLint 规则 `no-native-element`（手搓 `<button>/<input>/<select>/<textarea>` 而 DS 有等价物时报错）+ 镜像 `audit-product-code` R 规则；接进 `audit:consumer-code` | 场景3 阻塞；场景2 **仅警告 / 改动文件 scope**；场景1 关闭 | Tier 1-C 扩展 | **High**（直击 owner 痛点） |
| **G2** ③ spacing-token 规则 | ESLint `no-hardcoded-spacing`（px 在有 token 时报）+ audit 镜像 | 同上；**先建 px↔token 映射表防误报**（code 里合法 px 多） | Tier 1-C 扩展 | Medium（误报风险，需设计） |
| **G3** 消费接线 | 模板 ship `eslint.config.(js\|flat)` extends recommended + `package.json` lint/audit script + pre-commit；`setup-tvu-consumer` 技能按场景接线（场景3 严格 / 场景2 增量 git-diff scope） | **接线本身按场景**：场景3 全量挂；场景2 只挂 `lint-staged` 改动文件 | Tier 1-C 收尾 | **High**（"silent 通过"根因） |
| **G4** mockup 总闸 | 建 `audit:mockup-conformance` = 5 个 mockup audit 合并 + 接 `library-origin`；接进 sync / 消费 mockup gate | 场景2 mockup 同理非阻塞（M21 优先 local 视觉合同）| Tier 1-B 收尾 | Medium |

**实现顺序**：G1 + G3（直击痛点 + 堵 silent）→ G4（mockup 对称）→ G2（最易误报，最后且需映射设计）。

## 5. 场景路由（指引侧 — 已有，确认即可）

`code-conventions.md §R7` 已要求 AI 起手 disambiguate 场景；`design-process.md` M22 / Stage 0.5 同理。**无需新增**；G3 的技能接线只需在 scaffold / 接入时把场景判断结果**喂给自动化模式开关**（strict / incremental / migration / off）。

## 6. 完成定义（本 spec 自身）

- [x] 场景映射 + 矩阵 + 缺口实跑核实
- [x] owner 审定 G1-G4 优先级 + 场景模式开关机制（owner 2026-06-10 拍板全做；场景开关 = `tvuEnforcementIncremental` report-only / `tvuEnforcementStrict` blocking / `--non-blocking` flag）
- [x] **G1-G4 全部实现（2026-06-10）**：
  - **G1** `no-native-element` ESLint 规则 + CLI R15（commit `ae9b0ba5`）— flag 手搓 `<button>/<input>/<select>/<textarea>`，导向 canonical 组件
  - **G3** 消费接线（commit `f0faa32b`）— `eslint.config.fragment.mjs` + `lint-staged.fragment.json` + `setup-tvu-consumer` Step 2.6 场景感知 + `CONSUMER_AUDIT_SETUP.md` 场景表/pre-commit
  - **G4** `audit:mockup-conformance` 总闸（commit `adc183be`）— 合并 5 个 mockup audit + 接入原 orphan 的 library-origin
  - **G2** `no-hardcoded-spacing` ESLint 规则 + CLI R2S（commit `ed510c6d`）— spacing/radius px↔token 映射，FP 纪律（只 flag 有 token 的 spacing/radius 属性值）

> **场景感知落地（spec §2 核心约束）**：G1/G2/G3 的场景 2 增量 = `tvuEnforcementIncremental` + lint-staged 改动文件 **report-only warnings**（不使用 `--max-warnings=0`）；场景 3 全量 = `tvuEnforcementStrict` / blocking CI；`audit-product-code` 场景 2 使用 `--non-blocking`；G4 mockup 场景 2 同样使用 `--non-blocking`。这些入口都不对 legacy 全量误报，也不把 pre-DS 债强塞进增量任务（守 R6）。

> **本 session 已先行交付的相关项**（不在 G1-G4 内，是 docs 侧 dogfood + gate 修复）：
> - docs PopupBox 页改用真 DS Button + FormItem 组合（消除手搓触发钮，示范场景3 正路）。
> - `audit:canonical-compliance` 白名单识别 root-fix（注解插入不再破白名单）——一个 mandated gate 从 master RED 转绿。
