# 设计规格：L5 执法下沉——把「样式正确」烧进产物，导向真组件

- **日期**：2026-07-23
- **状态**：设计已批准，待写实现计划
- **性质**：DS 规则消费执法的增量下沉（非新增规则）
- **前序**：`docs/superpowers/specs/2026-07-22-ds-rule-consumption-design.md`（L1–L4 已 ship）

## 命名 reconcile（先澄清，防撞车）

前序 spec 里的「L5」= **并行实测校准**（owner 侧零提示设计、反推真实消费通道）。**那条 L5 已经跑完**——产物是两份 claude.ai/design 导出 mockup，实测反馈「仍不遵守新规则」。本 spec 就是那次校准的**产出**：一个执法增量。

为避免和前序 L5（校准轴）数字撞车，本 spec 内部**不再新造 L1–L5 编号**，而是明确定位为：

- **补全 L1**——把前序 L1 从「只 `body{font}`」扩成完整「元素/属性级 base 层」（下称 **Tier 1**）；
- **新增 Tier 2**——让复合组件导向真 `<tvu-*>` 实例化（而非手搓）。

owner 口语里说的「L5 执法下沉」= 本 spec 的 Tier 1 + Tier 2 整体。

## 问题陈述

前序 L1–L4 落地后，owner 用 claude.ai/design 基于整套 DS（**已喂入完整 sync bundle**）做零提示设计，实测**仍不遵守**：字体观感不对、状态不全、**手搓非 canonical**。

## 根因（本轮实测新取证，推翻若干旧假设）

对两份导出 HTML（`TVUNetworksDesignSystemTVU Device Monitor.html`、`TVUUXDesignSystem_Device Monitor.html`）跑定点 grep + 核 DS 组件实现，得到**真实消费拓扑**：

| 消费通道 | 实测 | 结论 |
|---|---|---|
| **token 层**（variables.css / `_ds_bundle.css`） | `var(--)` 592/280 次、完整 `:root` token 块内联在场 | ✅ **唯一可靠生效通道**——agent 愿意读 token、用 token |
| **组件层**（`_ds_bundle.js` / web components） | `<tvu-*>`=0、`customElements.define`=0、`_ds_bundle`=0 | ❌ **拿得到组件却完全不用**——bundle 已喂入，agent 选择手搓 |
| **L1 body 排版** | L1 标记注释在场，但生效 `body{}` 是 claude.ai **bundler 加载壳自己的** `font-family:-apple-system`（同特异度后者盖前者） | ⚠️ 被 bundler chrome 压掉，且 agent 也在自己容器另设字体 |
| **L3 per-component 通道** | 因 0 组件实例化，`prompt.md` head 的 CORE_POINTER **永不触发** | ❌ 前提落空即失效 |

**关键行为证据**：agent 手写了 31 个 `.tvu-menu-item--active` 之类的 CSS 类并**自己现写 CSS 实现**——这些类**DS 里根本不存在**（真实内部类名是无前缀 BEM `menu-list__item.is-active`，且封装在 shadow DOM 内）。即 agent 是「照着 DS 的样子**重命名 + 重造**」，不是复用 DS 资产。

**更正前序两处误判**（避免打错靶）：
1. 「正文掉 12px」——本轮 grep 里的 12px 绝大多数是 **bundler 壳 + 缩略图 SVG + 布局几何**（`bottom:12px`/`padding:6px 12px`），**非字号**。字号问题需以 owner 观感为准，不以此计数为据。
2. 「ship 一份 `.tvu-*` 样式表就能接住手搓」——**证伪**：那些 `.tvu-*` 是 agent 自由发明的，你 ship 的类名它不一定命中；且组件类**编码了内部 DOM 结构**，agent 手写的 div 树对不上，样式照样不生效（详见「硬约束」）。

### WC 封装事实（决定 Tier 2 形态）

- 组件用 Vue 3 `defineCustomElement`，**默认 Shadow DOM**（35 个里仅 5 个 light DOM）。组件样式编进 WC JS 字符串、注入 shadow root——**外部 light-DOM class 无法复用**。
- Token 靠 CSS 自定义属性**跨 shadow 边界继承**，所以 agent 一旦实例化 `<tvu-*>`，样式 + token 都正常。问题纯粹是 agent **不肯实例化**。

## 硬约束（本设计的地基）

**light-DOM 组件类编码了 DOM 结构，手搓 agent 复现不了。**

- **元素级样式**（button/input/table/字体/颜色）：只认单标签/属性，**零结构依赖** → agent 写语义 HTML 即正确。**可 correct-by-construction。**
- **复合组件**（menu/dropdown/tabs/modal）：样式生效前提是 agent 手写出**与组件一致的内部 DOM 嵌套**；agent 恰恰自由发明结构 → 伪造 `.tvu-*` 组件 CSS = **看着覆盖、实则空**，还多养一层 drift。复合组件唯一真正 correct 的路是**实例化真 WC**（WC 就是来封装这套结构的）。

## 设计原则

延续前序：**执法按可靠性分层，越靠上越不依赖「agent 读没读」。** 本轮再加一条：**只在 agent 已被实证的稳定行为上做文章**——① 用 token（已验证）② 写语义 HTML。不跟 agent 的习惯对着干。

## 设计

### Tier 1 · 元素/属性级 base 样式表（承重层，真 correct-by-construction）

用 token 给语义元素兜底，把前序 L1 从「只 `body{font}`」补全成完整元素层。

**覆盖元素（初始清单，plan 阶段可增删）**：
`button` · `input`/`textarea`/`select` · `table`/`th`/`td` · `h1`–`h3` · `a` · `label` · `code`/`kbd` · `[role="menuitem"]`/`[role="tab"]`/`[role="dialog"]`。

**过界护栏（关键纪律，沿用 L1「只设 font 不设 color」的克制）**：
- 只设**安全属性**：字体 / 行高 / `color:inherit` 或中性文本色 / 表单控件的 border·padding·radius（全用 token）。
- **低特异度、单元素选择器**——agent 一旦自己写规则即可盖过；base 只兜「agent 没管的默认」，绝不锁死设计意图。
- **不碰 layout**（不设 width/margin/flex/position），避免和 agent 布局打架。

**交付与耐久**：
- 落成 **committed 源文件**（倾向 `src/tokens/base.css`，或并入现有 token 产出链）。
- 目标：随 **`_ds_bundle.css` 一起 ship**——实测 `_ds_bundle.css` 内容 == `variables.css`。若 base 规则能进这条**已 committed、两条 sync 都自动带**的链路，则 Tier 1 是**真 committed lever**，不像现 L1 body 规则是易失 driver patch。
- **附带收益**：若成立，Tier 1 顺带把 L1 的 Sync-B driver patch（`.ds-sync/lib/css.mjs`）**吸收掉**，减一处 META-DSYNC-01 债、少一处重建后要重打的补丁。
- ⚠️ **plan 阶段待核实假设**：`_ds_bundle.css` 的确切生成源（是否直接由某 committed CSS 产出、能否把 base 规则注入该源而不动易失驱动）。若证伪，退化为「Tier 1 走和 L1 同样的 driver-patch + reapply 脚本」路径，并在 NOTES.md 增补对应补丁 SoT。

### Tier 2 · 让真 `<tvu-*>` 成为复合组件最省力路径（诚实尽力层）

**明确不做**伪 `.tvu-*` 组件 CSS（见硬约束）。做三件：

1. **保证 import 即全量自动注册**——核实 `src/web-components/register.ts` + 空-bundle keepalive 已让「引入 bundle」自动 `define` 全部元素，消费方一行 import 即可用 `<tvu-*>`。
2. **每个复合组件给一段可粘贴 `<tvu-*>` drop-in 片段**，放进现有**最强通道**：L3 的 `prompt.md` head（已有 CORE_POINTER，追实例化示例）+ README §0。
3. **诚实标注残余依赖**：此层靠通道被读到，**非保证生效**——是复合组件的物理下限。spec/交付说明须写清，避免 owner 复测时把「复合组件仍被手搓」误判为 Tier 2 失败。

## 验证（testing）

- **Tier 1**：构造一份「纯手写语义 HTML、零 class」的 mockup，加载 bundle 后 grep + 目测——`button`/`table`/正文字体是否自动 token 化。
- **两条 sync 都验**：Tier 1 规则在 `_ds_bundle.css` / `styles.css` 在场；每次过**空-bundle CRITICAL 三项**（`[ignored-bare-import]`=0、`_ds_bundle.js` 多 MB、`customElements.define`≥1）。
- **`.ds-sync` 重建后**：先跑 `reapply-driver-patches.mjs`（若 Tier 1 退化为 driver-patch 路径则含新补丁）再上传。
- **回归**：owner 复测零提示设计，看「手搓样式不对」是否被 Tier 1 按住、复合组件手搓是否收敛。

## 非目标（YAGNI）

- **不做**伪 `.tvu-*` 组件样式表。
- **不管**「状态不全（空/加载/错误/边界）」——归前序 L2/L4 清单，不进本 spec。
- **不含**长期根治（给 `/design-sync` 提 `promptHead`/`baseCss` config 钩子）——仍是 backlog **META-DSYNC-01**。
- 不重写驱动架构、不给 claude.ai/design 产物做机检遵守闸（够不到其运行时）。

## 涉及文件（初判，plan 阶段定稿）

| 层 | 文件 |
|---|---|
| Tier 1 源 | `src/tokens/base.css`（新，或并入 token 链）；`scripts/export-claude-design-bundle.mjs` 的 `ROOT_STYLES`（Sync A） |
| Tier 1 耐久 | 待核实 `_ds_bundle.css` 生成源；若退化 → `.ds-sync/lib/css.mjs` + `.design-sync/reapply-driver-patches.mjs` + `.design-sync/NOTES.md` |
| Tier 2 | `src/web-components/register.ts`（核实自动注册）；`.ds-sync/lib/emit.mjs`（prompt.md 追 drop-in 片段）；`.design-sync/conventions.md`（README §0 追指针） |
| 验证 | 新增手写语义 HTML 冒烟样例 |

## backlog 关联

- **META-DSYNC-01**（P2）：Tier 1 若走 committed-lever 路径可**部分抵偿**该债（吸收 L1 的 Sync-B driver patch）；Tier 2 的 prompt.md 注入仍是易失驱动改动，若走该路径需并入 reapply 脚本 + NOTES SoT。长期根治不变。
