# L5 执法下沉（Tier 1 元素级 base + Tier 2 导向真组件）Implementation Plan

> **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:** 把「样式正确」烧进两条 sync 产物——用元素/属性级 base 样式表让 agent 写语义 HTML 即 token 化（Tier 1，真 correct-by-construction），并把复合组件导向真 `<tvu-*>` 实例化（Tier 2，诚实尽力层），补全前序 L1 的字体单点执法。

**Architecture:** Tier 1 把元素级 base 规则追加进 **committed 的 `src/tokens/variables.css` 尾部**——这是两条 sync（Sync A 导出脚本 `@import`+copy；Sync B 驱动经 `cfg.cssEntry` 拷进 `_ds_bundle.css`）**都已消费的唯一单文件真源**，零驱动改动即随两条链 ship，并**吸收掉前序 L1 的 Sync-B driver patch**（减一处 META-DSYNC-01 债）。Tier 2 **不造伪 `.tvu-*` CSS**（复合组件类编码内部 DOM 结构，手搓复现不了）；改为把可粘贴 `<tvu-*>` drop-in 片段 + 复合组件优先规则 + 诚实残余依赖标注写进 committed 的 `§0`（conventions.md / CLAUDE_DESIGN_RULES.md），并在最强通道 prompt.md head 的 CORE_POINTER 追一句复合组件实例化指针。

**Tech Stack:** CSS 自定义属性（token 层）、Node ESM 脚本（Sync A 导出脚本 + `.ds-sync` 驱动 lib）、Markdown 规则文档、DesignSync 工具（对外上传）。

**真源 spec：** [docs/superpowers/specs/2026-07-23-ds-rule-consumption-L5-enforcement-design.md](../specs/2026-07-23-ds-rule-consumption-L5-enforcement-design.md)
**前序 plan（L1–L4 已 ship）：** [docs/superpowers/plans/2026-07-22-ds-rule-consumption.md](2026-07-22-ds-rule-consumption.md)

## Global Constraints

- **注入点 = `src/tokens/variables.css`（owner 已 sign-off，2026-07-23）**：base 规则追加到文件**最后一个 token 块 `}` 之后的顶层**，绝不进 `:root` / `[data-theme="light"]` 块内（进块内会被 `renderTokens` 当 token 行处理）。已核实 `figma-sync/generate-tokens.mjs:87` 对 `scope===null` 的行 `return line` 逐字保留 → `audit:token-contract` 幂等往返通过。
- **base 层过界护栏（硬纪律）**：只设**安全属性**——字体 / 行高 / `color:inherit` 或中性文本色（`--text-body` 等）/ 表单控件的 border·padding·radius·background（全走 `var(--*)`；radius 用 `--r-*` token）。**不设 layout**（width/margin/flex/position/gap）；**不用 `!important`**；**低特异度单元素/单属性选择器**——author 任一规则即可盖过，base 只兜「agent 没管的默认」。`1px` 边框按 G2 惯例豁免（唯一允许的裸 px）。
- **CRITICAL（Sync B 每次同步必过）**：空-bundle 三项全绿——build 日志无 `[ignored-bare-import]`、`_ds_bundle.js` 多 MB、`grep -c customElements.define ds-bundle/_ds_bundle.js` ≥1。NOTES 记录曾两次 ship 成空 bundle。
- **`.ds-sync/` 重建后**：先跑 `node .design-sync/reapply-driver-patches.mjs`（本计划后它只剩 L3 一项）再上传。
- **一次性同步两个项目**（Sync A `bfca04be-9783-4eb5-b754-37b04a6c35ef` → localDir `/Users/nancy/Documents/AICoding/claude-design-bundle`；Sync B `406a39da-1681-4edf-ae26-826cf3c33c9c` → `./ds-bundle`）。对外上传前把 `finalize_plan` 的 file-set 贴 owner 确认，再 `write_files`。每步 `get_file` 亲验远端实况。
- **不造第二真源**：base 层是元素兜底、复合片段是 §0 单处，DRY。移除因 Tier 1 而冗余的旧副本（Sync A `ROOT_STYLES` body 块、Sync B css.mjs baseTypography patch）。
- **全部真源改动并入一个 commit**，push master **双 remote**（Gitea `origin` + `github`）；GitHub 常报 phantom `remote rejected`，以 `git ls-remote` 实际 sha 为准。
- **gate**：`pnpm audit:token-contract` + `pnpm audit:no-hardcoded-design-tokens`（若存在）+ husky `vue-tsc + vitest` 全绿。`variables.css` 是 CSS 改动 → pre-commit 可能触发 `VISUAL_COMMIT_APPROVED` 视觉闸；命中则确认后带 `VISUAL_COMMIT_APPROVED=1` 提交（改动是 base 规则、非视觉 mockup，属误报）。

---

### Task 1: Tier 1 — 元素级 base 层追加进 `src/tokens/variables.css`

**Files:**
- Modify: `src/tokens/variables.css`（在文件末尾、最后一个 `[data-theme="light"]{…}` 块的收尾 `}` 之后追加 base 层）

**Interfaces:**
- Consumes: 现有 token —— `--text-style-body` / `--text-style-headline-1` / `--text-style-headline-2` / `--text-style-pop-title`、`--font-size-tips`、`--text-body` / `--text-2` / `--text-placeholder`、`--line-border`、`--sp-xs` / `--sp-s`、`--input-filled-bg`、`--bg-layer2`、`--brand`（均已在 `:root` + light 双主题定义，跨 shadow 继承）。
- Produces: 追加在 `variables.css` 尾部的顶层元素/属性规则块（→ Sync A `tokens/variables.css` + Sync B `_ds_bundle.css` 自动带上）。

- [ ] **Step 1: 读文件尾部确认插入锚点**

Run: `tail -n 3 src/tokens/variables.css`
Expected: 末行是 `}`（收尾 light 主题块 `[data-theme="light"]{…}`）。base 层追加到这个 `}` **之后**。

- [ ] **Step 2: 追加 base 层（append 到文件末尾）**

在 `src/tokens/variables.css` 末尾（最后的 `}` 后空一行）追加：

```css

/* ==========================================================================
 * Element base layer — spec 2026-07-23-ds-rule-consumption-L5-enforcement (Tier 1)
 * 语义 HTML 的 correct-by-construction 兜底（非 <tvu-*> WC；WC 自带 shadow 样式）。
 * 顶层规则、token-only、低特异度单元素/属性选择器、仅安全属性
 * （font / line-height / color / 表单控件 border·padding·radius·background）。
 * 无 layout、无 !important —— author 规则恒胜。radius 走 --r-* token。
 * 随 variables.css 走两条 sync；吸收前序 L1 的 body 排版（不再靠 driver patch）。
 * ⚠️ 必须在最后一个 token 块 } 之后（顶层），否则 renderTokens 会当 token 行处理。
 * ========================================================================== */

/* 正文：默认 14px + DS 字族（对齐前序 L1；只设 font，色由 Surface/组件按主题定） */
body {
  font: var(--text-style-body);
  font-family: var(--font-family-base);
}

/* 标题层级 */
h1 { font: var(--text-style-headline-1); color: var(--text-body); }
h2 { font: var(--text-style-headline-2); color: var(--text-body); }
h3 { font: var(--text-style-pop-title); color: var(--text-body); }

/* 文本元素 */
label { font: var(--text-style-body); color: var(--text-body); }
a { color: var(--brand); }
code,
kbd { font-family: monospace; font-size: var(--font-size-tips); }

/* 表单控件（safe box props only；radius 走 --r-s token） */
button {
  font: var(--text-style-body);
  color: inherit;
  border-radius: var(--r-s);
}
input,
textarea,
select {
  font: var(--text-style-body);
  color: var(--text-body);
  background: var(--input-filled-bg);
  border: 1px solid var(--line-border);
  border-radius: var(--r-s);
  padding: var(--sp-xs) var(--sp-s);
}
input::placeholder,
textarea::placeholder { color: var(--text-placeholder); }

/* 表格 */
table {
  font: var(--text-style-body);
  color: var(--text-body);
  border-collapse: collapse;
}
th {
  font-weight: 600;
  color: var(--text-body);
  text-align: left;
  padding: var(--sp-xs) var(--sp-s);
}
td {
  padding: var(--sp-xs) var(--sp-s);
  border-top: 1px solid var(--line-border);
}

/* ARIA 复合角色（元素级兜底；结构仍应走真 <tvu-*>，见 §0 Tier 2） */
[role="menuitem"] { font: var(--text-style-body); color: var(--text-body); }
[role="tab"] { font: var(--text-style-body); color: var(--text-body); }
[role="dialog"] { font: var(--text-style-body); color: var(--text-body); background: var(--bg-layer2); border-radius: var(--r-l); }
```

- [ ] **Step 3: 验证 token-contract gate（承重——确认追加不破坏幂等往返）**

Run: `pnpm audit:token-contract`
Expected: PASS（`renderTokens(current)===current` 成立；base 层在 `scope===null` 段逐字保留）。若 FAIL，说明追加位置进了 token 块——移到最后 `}` 之后。

- [ ] **Step 4: 验证无硬编码 + 无遗漏 var**

Run: `pnpm audit:no-hardcoded-design-tokens 2>/dev/null || echo "(no such script — skip)"`；再 `grep -nE "#[0-9a-fA-F]{3,6}" src/tokens/variables.css | grep -A0 "Element base layer" ` 手核 base 段无 hex 字面量（`1px`/`monospace`/`600`/`left`/`inherit` 允许）。
Expected: gate PASS（或脚本不存在跳过）；base 段无 `#hex`、无裸 `px`（除 `1px` 边框）。

- [ ] **Step 5: 验证 husky 门（vue-tsc + vitest 不受 CSS 影响，快确认）**

Run: `pnpm build 2>&1 | tail -5`（确认 CSS 改动不破坏构建；token 出口 `dist/tokens/tokens.json` 仍生成）
Expected: build 成功。

---

### Task 2: Tier 1 — Sync A 去重：`ROOT_STYLES` 移除冗余 body 块

**Files:**
- Modify: `scripts/export-claude-design-bundle.mjs`（`ROOT_STYLES` 常量，约 line 93–107）

**Interfaces:**
- Consumes: Task 1 的 base 层（body 排版现住 `tokens/variables.css`，Sync A 经 `@import "./tokens/variables.css"` + `TOKENS` copy 已带上）。
- Produces: 精简的 `ROOT_STYLES`（只 `@import` token，不再自带 body 块，消除双源）。

- [ ] **Step 1: 确认现状**

Run: `grep -n -A15 "const ROOT_STYLES" scripts/export-claude-design-bundle.mjs`
Expected: 含 `@import "./tokens/variables.css";` + 一段 `body { font: var(--text-style-body); font-family: var(--font-family-base); }`。

- [ ] **Step 2: 把 `ROOT_STYLES` 的 body 块替换为指针注释**

把 `const ROOT_STYLES = \`…\`` 整体改为（删掉 `body{}` 块，base 排版改由 token 文件承载）：

```js
const ROOT_STYLES = `/* TVU Design System — Claude Design 编译入口
 * token 真源 = tokens/variables.css（整份，未按规格拆分/改名）。
 * dark 默认（:root），light = [data-theme="light"]。
 * 元素级 base 层（body 排版 / 表单 / 表格 / 标题）住 tokens/variables.css 尾部
 * （spec 2026-07-23 Tier 1，correct-by-construction），随本 @import 一并生效。
 */
@import "./tokens/variables.css";
`
```

- [ ] **Step 3: 重导出并验证 base 排版仍在（现经 token 文件生效）**

Run: `pnpm export:claude-design-bundle && grep -A3 "^body" /Users/nancy/Documents/AICoding/claude-design-bundle/tokens/variables.css && grep -c "Element base layer" /Users/nancy/Documents/AICoding/claude-design-bundle/tokens/variables.css`
Expected: 导出成功；`tokens/variables.css` 含 `body { font: var(--text-style-body)… }` + `Element base layer` 标记 `1`。`styles.css` 只剩 `@import`（`grep -c "^body" .../styles.css` = 0，body 现在 token 文件里）。

---

### Task 3: Tier 1 — 吸收 Sync-B L1 driver patch（css.mjs baseTypography 下线）

**Files:**
- Modify: `.ds-sync/lib/css.mjs`（**易失/gitignored 驱动**——移除 `writeStylesCss` 里的 `baseTypography` 追加，恢复朴素写；仅影响当次同步）
- Modify: `.design-sync/reapply-driver-patches.mjs`（**committed**——移除 L1（`patchCss` + L1 target），只留 L3）
- Modify: `.design-sync/NOTES.md`（**committed**——删 L1 patch 段，记「L1 已升为 variables.css committed lever」）

**Interfaces:**
- Consumes: Task 1（body 规则现由 `_ds_bundle.css`（= tokens.css = variables.css）经 styles.css `@import` ship）。
- Produces: reapply 脚本只剩 L3 一项；NOTES 记录 L1 债已抵偿。

- [ ] **Step 1: 移除 live css.mjs 的 baseTypography（当次同步用）**

`.ds-sync/lib/css.mjs` 现约 152–159 的 `if (styleImports.length) { const baseTypography = …; writeFileSync(join(out,'styles.css'), styleImports.join('\n') + '\n' + baseTypography); … }` 改回朴素写：

```js
  if (styleImports.length) {
    writeFileSync(join(out, 'styles.css'), styleImports.join('\n'));
    console.error(`  styles.css: ${styleImports.length} @import(s)${hasBundleCss ? ' (incl. _ds_bundle.css — component styles ship to designs via this closure)' : ''}`);
    return;
  }
```
（body 排版现随 `_ds_bundle.css` 内的 base 层 ship，无需在 styles.css 尾再追加。）

- [ ] **Step 2: 从 reapply-driver-patches.mjs 移除 L1**

删除 `.design-sync/reapply-driver-patches.mjs` 中：`L1_MARKER` / `L1_FIND` / `L1_REPLACE` 常量、`export function patchCss(...)`、`CSS_FILE` 常量、`run()` 内 `targets` 数组里的 `{ name: 'L1 css.mjs', file: CSS_FILE, patch: patchCss }` 项，以及末尾成功提示里 `grep -A3 '^body' ds-bundle/styles.css` 那行。`targets` 只剩 `{ name: 'L3 emit.mjs', file: EMIT_FILE, patch: patchEmit }`。顶部注释块把 `L1-on-SyncB` 那行删掉。

- [ ] **Step 3: 验证 reapply 脚本自洽**

Run: `node .design-sync/reapply-driver-patches.mjs --check`
Expected: 只报 L3 状态（`✓ L3 emit.mjs: …`），无 L1 行，退出码符合 L3 现状（已在则 0）。

- [ ] **Step 4: NOTES.md 更新**

`.design-sync/NOTES.md` 的「Ephemeral driver patches」段：删除 Patch 1（L1 css.mjs body-font）小节；在其位置记一行：`L1（body 排版）已升级为 committed lever —— 规则住 src/tokens/variables.css 尾部的 Element base layer（spec 2026-07-23 Tier 1），随两条 sync 自动 ship，不再需要 css.mjs driver patch。`。Patch 2（L3 emit.mjs）保留。同时把 §"cssEntry" 附近记一句：`variables.css 尾部现含元素级 base 层（非纯 token）；cp 步骤照旧整份拷贝即可带上。`

- [ ] **Step 5: 验证 NOTES 无残留 L1 引用**

Run: `grep -n "base typography\|L1 css.mjs\|patchCss" .design-sync/NOTES.md .design-sync/reapply-driver-patches.mjs`
Expected: 无匹配（L1 已彻底移除）。

---

### Task 4: Tier 2 — §0 追加复合组件 `<tvu-*>` drop-in 片段 + 优先规则 + 诚实标注（committed 双写）

**Files:**
- Modify: `.design-sync/conventions.md`（Sync B README header——在 `## Which component for what` 之后、`## Verify rendered reality` 之前插入新节）
- Modify: `docs/CLAUDE_DESIGN_RULES.md`（Sync A SKILL 源——在 §0 之后、组件章节处镜像一节精简 CN 版）

**Interfaces:**
- Consumes: register.ts 已核实的真 tag 名 + 各复合组件 attribute/slot API（含两坑：PopupBox 需裸 `visible`；Tab/TabItem provide/inject 不跨 CE 边界，active 显式 `state="Active"`）。
- Produces: `§Composite components` 节（→ Sync B README + Sync A SKILL），prompt.md 的 CORE_POINTER「见 §0」指针即指向此。

- [ ] **Step 1: 在 `.design-sync/conventions.md` 插入 EN 节**

在 `## Which component for what (do not improvise)` 段结束后、`## Verify rendered reality` 前插入：

````markdown
## Composite components — instantiate the real `<tvu-*>`, never hand-build a `div` tree
The bundle auto-registers all 37 `<tvu-*>` custom elements on import (`_ds_bundle.js` self-registers — no provider needed). Composite components (menu / tabs / dialog / dropdown / top bar) encode their internal DOM inside a shadow root, so a hand-built `div`+CSS lookalike will NOT pick up their styles or behavior — the only correct path is to instantiate the real element. Paste-able drop-ins (dark theme is default; tokens cross the shadow boundary automatically):

**Dialog / confirmation → `<tvu-notification form="dialog">`** (all string attrs; buttons are form-intrinsic):
```html
<tvu-notification form="dialog" type="success" theme="dark"
  title="Stream saved" description="Your changes have been published."
  cancel-text="Cancel" confirm-text="Save"></tvu-notification>
```

**Large custom modal → `<tvu-popup-box visible>`** (⚠️ the bare `visible` attribute is REQUIRED or nothing renders; body goes in the default slot):
```html
<tvu-popup-box visible title="Edit source" width="680" confirm-text="Save" cancel-text="Cancel">
  <p>Body content in the default slot.</p>
</tvu-popup-box>
```

**Top bar → `<tvu-top-bar>`** (attrs for the bar; regions via `slot="…"` light-DOM children):
```html
<tvu-top-bar tag="After Login" title="TVU Producer" show-menu show-search-box>
  <tvu-logo slot="logo"></tvu-logo>
  <input slot="search" placeholder="Search" />
  <tvu-menu-list slot="menu" orientation="horizontal" id="nav"></tvu-menu-list>
  <tvu-user-menu slot="right-content"></tvu-user-menu>
</tvu-top-bar>
```

**Tabs → `<tvu-tab>` + `<tvu-tab-item>`** (⚠️ selection state does NOT cross the custom-element boundary — mark the active item with `state="Active"`, do not rely on `model-value`):
```html
<tvu-tab fill="Line" color="Green">
  <tvu-tab-item value="overview" state="Active">Overview</tvu-tab-item>
  <tvu-tab-item value="sources">Sources</tvu-tab-item>
  <tvu-tab-item value="monitor" disabled>Monitor</tvu-tab-item>
</tvu-tab>
```

**Menu / dropdown are ARRAY-driven** — `<tvu-menu-list>` (`items`) and `<tvu-drop-down-list-select>` (`items`) have NO slot; an HTML attribute cannot carry an array, so set the property in JS (still the real element, never a hand-built `<ul>`):
```html
<tvu-menu-list orientation="vertical" id="m"></tvu-menu-list>
<script>
  document.getElementById('m').items = [
    { label: 'Overview', value: 'overview', active: true },
    { label: 'Sources', value: 'sources', icon: 'action/add' },
  ]
</script>
```

> **Honest limitation (physical floor):** this guidance works by being READ in this channel — it is not a machine-enforced guarantee like the element base layer. If a design still hand-builds a composite, that is a channel-adherence miss, not a base-layer failure. Tag names + full prop enums: each `<Name>.d.ts` / `<Name>.prompt.md`.
````

- [ ] **Step 2: 在 `docs/CLAUDE_DESIGN_RULES.md` 镜像精简 CN 节**

定位 §0 之后的组件章节起点（`grep -n "## 3" docs/CLAUDE_DESIGN_RULES.md` 附近，紧接 §3 组件章节前或内），插入精简 CN 版（同一批片段，语言换 CN，保留两个 ⚠️ 坑 + 诚实标注）：

```markdown
### 复合组件：实例化真 `<tvu-*>`，别手搓 div 树

bundle 引入即自动注册全部 37 个 `<tvu-*>`（`_ds_bundle.js` 自注册，无需 provider）。复合组件（菜单/标签/对话框/下拉/顶栏）把内部 DOM 封在 shadow root 里，手搓 `div`+CSS 仿造**拿不到其样式/行为**——唯一正确路是实例化真元素。可粘贴片段（dark 默认；token 跨 shadow 自动继承）：

- **对话框 → `<tvu-notification form="dialog" type="success" title="…" confirm-text="…" cancel-text="…">`**（纯字符串 attr，按钮 form 内建）。
- **大型自定义弹窗 → `<tvu-popup-box visible title="…" width="680" confirm-text="…">…body…</tvu-popup-box>`**（⚠️ 裸 `visible` 必写否则不渲染；body 走默认 slot）。
- **顶栏 → `<tvu-top-bar tag="After Login" title="…" show-menu show-search-box>`** + `slot="logo|search|menu|right-content"` 光 DOM 子节点。
- **标签 → `<tvu-tab>` 内嵌 `<tvu-tab-item value="…" state="Active">`**（⚠️ 选中态不跨 CE 边界，active 用 `state="Active"` 显式标，别靠 `model-value`）。
- **菜单/下拉是数组驱动**（`<tvu-menu-list>`/`<tvu-drop-down-list-select>` 的 `items`，无 slot）——HTML attr 传不了数组，用 JS `el.items = [...]` 赋值（仍是真元素，别手搓 `<ul>`）。

> **诚实标注（物理下限）**：本层靠「通道被读到」生效，非像 base 层那样机检保证。仍被手搓 = 通道遵守问题，非 base 层失败。tag 名 + 完整 prop 枚举见各 `<Name>.d.ts` / `<Name>.prompt.md`。
```

- [ ] **Step 3: 验证**

Run: `grep -c "Composite components — instantiate the real" .design-sync/conventions.md && grep -c "复合组件：实例化真" docs/CLAUDE_DESIGN_RULES.md && grep -c "tvu-notification form=\"dialog\"" .design-sync/conventions.md`
Expected: 各 `1`（后者 ≥1）。

---

### Task 5: Tier 2 — prompt.md head CORE_POINTER 追复合组件实例化指针（driver patch，三处 SoT 同步）

**Files:**
- Modify: `.ds-sync/lib/emit.mjs`（**易失/gitignored 驱动**——`CORE_POINTER` 常量值，约 line 362；当次同步用）
- Modify: `.design-sync/reapply-driver-patches.mjs`（**committed**——`CORE_POINTER_VALUE` 常量，约 line 52–54，必须与 live 一致，否则重建后注旧值）
- Modify: `.design-sync/NOTES.md`（**committed**——Patch 2 的 CORE_POINTER 值 SoT）

**Interfaces:**
- Consumes: Task 4 的 §0 复合片段（指针里「见 §0」即指向它，DRY——片段不重复进 prompt.md）。
- Produces: 每个 `*.prompt.md` head 的 CORE_POINTER 多一句复合组件实例化指针（→ Sync B 35 个 prompt.md）。

- [ ] **Step 1: 定 CORE_POINTER 新值（在末尾追一句复合指针，其余不动）**

新值（在原句末尾、句号前追加「· 复合组件用真 `<tvu-*>` 实例(片段见 §0)不手搓」）：

```
⚠️ 组合前先过核心规则(详见 README / SKILL §0):token 不硬编码 · 正文 14px(12px=tips) · 交付前枚举状态(空/加载/错误/边界) · 用 canonical 组件不手搓,复合组件(菜单/标签/对话框/下拉/顶栏)实例化真 <tvu-*>(片段见 §0) · 用了组件≠渲染对了(亲核渲染实况)。
```

- [ ] **Step 2: 改 live emit.mjs 的 CORE_POINTER**

`.ds-sync/lib/emit.mjs` 约 line 362 的 `const CORE_POINTER = '…';` 整行替换为上面新值（保持单引号字符串、内部无需转义）。

- [ ] **Step 3: 改 reapply-driver-patches.mjs 的 CORE_POINTER_VALUE（SoT，必须与 live 一致）**

`.design-sync/reapply-driver-patches.mjs` 约 line 52–54 的 `CORE_POINTER_VALUE`（现为多行拼接）替换为与 Step 1 完全一致的文本（可拆多行 `+` 拼接，但拼出的最终字符串须逐字相同）：

```js
const CORE_POINTER_VALUE =
  '⚠️ 组合前先过核心规则(详见 README / SKILL §0):token 不硬编码 · 正文 14px(12px=tips) · ' +
  '交付前枚举状态(空/加载/错误/边界) · 用 canonical 组件不手搓,复合组件(菜单/标签/对话框/下拉/顶栏)' +
  '实例化真 <tvu-*>(片段见 §0) · 用了组件≠渲染对了(亲核渲染实况)。';
```

- [ ] **Step 4: 改 NOTES.md 的 Patch 2 CORE_POINTER 值 SoT**

`.design-sync/NOTES.md` 记录 Patch 2（L3 emit.mjs）CORE_POINTER 值处，同步为 Step 1 的新文本。

- [ ] **Step 5: 验证三处一致 + reapply 幂等**

Run: `node .design-sync/reapply-driver-patches.mjs --check`
Expected: `✓ L3 emit.mjs: already present`（live 已含新值 → marker 命中）。再 `grep -c "复合组件(菜单/标签/对话框/下拉/顶栏)" .ds-sync/lib/emit.mjs .design-sync/reapply-driver-patches.mjs .design-sync/NOTES.md`：三文件各 ≥1，文本一致。

---

### Task 6: Tier 1/2 — committed 冒烟 fixture（语义 HTML + 真 `<tvu-*>`，供渲染核验）

**Files:**
- Create: `docs/superpowers/verification/2026-07-23-L5-base-layer-smoke.html`（纯手写语义 HTML、零 class——验 Tier 1 自动 token 化）
- Create: `docs/superpowers/verification/2026-07-23-L5-composite-smoke.html`（真 `<tvu-*>` 复合片段——验 Tier 2 drop-in 真渲染）

**Interfaces:**
- Consumes: 导出/同步产物的 `styles.css` + `_ds_bundle.js`（相对引用，测试时置于 bundle 目录旁或改相对路径）。
- Produces: 两个 committed 回归 fixture（owner 复测零提示设计前的 baseline 参照）。

- [ ] **Step 1: 写 Tier 1 语义 HTML fixture**

`docs/superpowers/verification/2026-07-23-L5-base-layer-smoke.html`：

```html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>L5 Tier 1 base-layer smoke — 纯语义 HTML 零 class</title>
  <!-- 测试时把本文件放到 bundle 目录旁，或改成指向导出的 styles.css -->
  <link rel="stylesheet" href="./styles.css">
</head>
<body>
  <h1>Device Monitor</h1>
  <h2>Sources</h2>
  <h3>Channel 1</h3>
  <p>正文默认应为 14px + DS 字族（--text-style-body），不应掉到 12px / 非 DS 字体。</p>
  <label>Stream name</label>
  <input placeholder="Enter name">
  <button>Go Live</button>
  <table>
    <thead><tr><th>Name</th><th>Bitrate</th></tr></thead>
    <tbody><tr><td>CH1</td><td>5 Mbps</td></tr></tbody>
  </table>
  <a href="#">View details</a>
</body>
</html>
```

- [ ] **Step 2: 写 Tier 2 复合 `<tvu-*>` fixture**

`docs/superpowers/verification/2026-07-23-L5-composite-smoke.html`：

```html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>L5 Tier 2 composite smoke — 真 &lt;tvu-*&gt; drop-in</title>
  <link rel="stylesheet" href="./styles.css">
  <script type="module" src="./_ds_bundle.js"></script>
</head>
<body>
  <tvu-notification form="dialog" type="success" theme="dark"
    title="Stream saved" description="Your changes have been published."
    cancel-text="Cancel" confirm-text="Save"></tvu-notification>
  <tvu-tab fill="Line" color="Green">
    <tvu-tab-item value="overview" state="Active">Overview</tvu-tab-item>
    <tvu-tab-item value="sources">Sources</tvu-tab-item>
  </tvu-tab>
  <tvu-menu-list orientation="vertical" id="m"></tvu-menu-list>
  <script>
    customElements.whenDefined('tvu-menu-list').then(() => {
      document.getElementById('m').items = [
        { label: 'Overview', value: 'overview', active: true },
        { label: 'Sources', value: 'sources' },
      ]
    })
  </script>
</body>
</html>
```

- [ ] **Step 3: 验证 fixture 存在 + 结构自洽**

Run: `ls docs/superpowers/verification/2026-07-23-L5-*.html && grep -c "class=" docs/superpowers/verification/2026-07-23-L5-base-layer-smoke.html`
Expected: 两文件在；base-layer fixture `class=` 计数 `0`（纯语义、零 class）。渲染核验（真 token 化 / 复合真渲染）在 Task 8 同步后对 bundle 产物执行。

---

### Task 7: 合并 commit + push 双 remote

**Files:** Task 1–6 全部 committed 改动（`src/tokens/variables.css`、`scripts/export-claude-design-bundle.mjs`、`.design-sync/reapply-driver-patches.mjs`、`.design-sync/NOTES.md`、`.design-sync/conventions.md`、`docs/CLAUDE_DESIGN_RULES.md`、两个 fixture、本 plan）。
> 注：`.ds-sync/lib/*.mjs` 是 gitignored 易失驱动，不进 commit（Task 3 Step 1 / Task 5 Step 2 只为当次同步生效）。

- [ ] **Step 1: 过一遍 diff**

Run: `git status -s && git diff --stat`
Expected: 改动限于上列 committed 文件；无 `.ds-sync/` 项（gitignored）。

- [ ] **Step 2: gate 全绿后一个 commit**

先 `pnpm audit:token-contract` 再 commit：

```bash
git add src/tokens/variables.css scripts/export-claude-design-bundle.mjs .design-sync/reapply-driver-patches.mjs .design-sync/NOTES.md .design-sync/conventions.md docs/CLAUDE_DESIGN_RULES.md docs/superpowers/verification/2026-07-23-L5-base-layer-smoke.html docs/superpowers/verification/2026-07-23-L5-composite-smoke.html docs/superpowers/plans/2026-07-23-ds-rule-consumption-L5-enforcement.md
git commit -m "feat(design-rules): L5 enforcement — element base layer (Tier 1) + composite drop-ins (Tier 2)"
```
（message 尾加 `Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>`。pre-commit husky vue-tsc+vitest 须全绿；若 CSS 触发 `VISUAL_COMMIT_APPROVED` 视觉闸，确认属误报后带 `VISUAL_COMMIT_APPROVED=1` 重跑。）

- [ ] **Step 3: push + ls-remote 核实**

Run: `git push origin master`，再 `git push github master`；然后 `git ls-remote origin -h refs/heads/master` 与 `git ls-remote github -h refs/heads/master` 均 = 新 sha（GitHub phantom reject 不算失败，以 ls-remote 为准）。

---

### Task 8: 一次同步两个项目（attended）+ 渲染亲验

**Files:** DesignSync 上传（Sync A `bfca04be-9783-4eb5-b754-37b04a6c35ef` / Sync B `406a39da-1681-4edf-ae26-826cf3c33c9c`）。

- [ ] **Step 1: 重导出 Sync A bundle**

Run: `pnpm export:claude-design-bundle`（README 带新 commit sha）。记录变更文件（预期 `tokens/variables.css`、`styles.css`、`SKILL.md`）。

- [ ] **Step 2: Sync A finalize_plan 前把 file-set 贴 owner 确认，再 write_files**

file-set（localDir = `/Users/nancy/Documents/AICoding/claude-design-bundle`）：`tokens/variables.css`（base 层）、`styles.css`（精简 ROOT_STYLES）、`SKILL.md`（§0 复合节 CN）。targeted overwrite，不动策展 components/mockups/versions。

- [ ] **Step 3: get_file 核实 Sync A**

`tokens/variables.css` 含 `Element base layer` + `body{font:var(--text-style-body)…}`；`styles.css` 只剩 `@import`（无 body 块）；`SKILL.md` 含「复合组件：实例化真 `<tvu-*>`」节。

- [ ] **Step 4: Sync B 前置 + reapply + 驱动**

`cp src/tokens/variables.css react-pilot/ds-sync/tokens.css`（带上 base 层）→ 确认 `.ds-sync/` 若被重建先 `node .design-sync/reapply-driver-patches.mjs`（现只 L3）→ 确认 `entry.tsx` `__tvu_wc_keepalive` 在 → fetch 远端 `_ds_sync.json` → `.design-sync/.cache/remote-sync.json`（先 Read 再 Write）。驱动：
```
node .ds-sync/resync.mjs --config .design-sync/config.json --node-modules react-pilot/node_modules --out ./ds-bundle --remote .design-sync/.cache/remote-sync.json
```

- [ ] **Step 5: CRITICAL 空-bundle 三项核验（必过）**

`ds-bundle/.resync-verdict.json` `ok:true`；build 日志无 `[ignored-bare-import]`；`grep -c customElements.define ds-bundle/_ds_bundle.js` ≥1 且多 MB。base 层核：`grep -A3 "^body" ds-bundle/_ds_bundle.css`（含 body 排版）+ `grep -c "Element base layer" ds-bundle/_ds_bundle.css` = 1。prompt.md 指针核：`grep -l "复合组件(菜单/标签/对话框/下拉/顶栏)" ds-bundle/components/general/*/*.prompt.md | wc -l` = 35。

- [ ] **Step 6: Sync B finalize_plan（file-set 贴 owner）+ 上传**

预期变更：`README.md`（§0 复合节）、`_ds_bundle.css` + `styles.css`（base 层 ship / styles.css 去 baseTypography）、35 个 `*.prompt.md`（CORE_POINTER 追指针）、`_ds_sync.json`（anchor 刷新）。以驱动 `.sync-diff.json` 实际变更集为准；`_ds_bundle.js` 若 hash 变也带上。

- [ ] **Step 7: get_file 核实 Sync B + 渲染冒烟**

远端 `README.md` 含复合节；`_ds_bundle.css` 含 base 层；任取 2–3 个 `*.prompt.md` 含新 CORE_POINTER 指针；`_ds_sync.json` `bundleSha12` 已刷新。**渲染核验**：把 Task 6 两个 fixture 拷到 `ds-bundle/` 旁，浏览器打开 —— base-layer fixture 的 `button`/`table`/正文自动 token 化（14px + DS 字体、表单有 border）；composite fixture 的 `<tvu-notification>`/`<tvu-tab>`/`<tvu-menu-list>` 真渲染（非空节点，slot/items 填充）。截图或 computed-style 核，不止看 DOM 在场。

---

## Self-Review（对照 spec 核查）

- **Tier 1 承重层** → Task 1（base 层进 variables.css）+ Task 2（Sync A 去重）+ Task 3（吸收 Sync-B patch）。committed-lever 假设已亲验（`package-build.mjs:384`、`config.json:9`、`export-claude-design-bundle.mjs:97`、`css.mjs`、`generate-tokens.mjs:87`）。✅
- **过界护栏**（安全属性/低特异度/无 layout/无 radius token/1px 豁免）→ Global Constraints + Task 1 CSS 块。✅
- **附带收益吸收 L1 driver patch + 减 META-DSYNC-01** → Task 3。✅
- **Tier 2 三件事**：① import 即全量注册 → register.ts eager 已核 + Task 8 Step 5 CRITICAL `customElements.define≥1` 佐证；② 复合 drop-in 片段进最强通道 → Task 4（§0 committed）+ Task 5（prompt.md CORE_POINTER 指针，DRY 指向 §0）；③ 诚实标注残余依赖 → Task 4 两处 fixture 节末的 honest-limitation。✅
- **明确不做伪 `.tvu-*` CSS** → 全程未造；Task 4 显式反对手搓。✅
- **验证**：Tier 1 语义 HTML fixture（Task 6/8）；两条 sync 都验（Task 8 Sync A + Sync B）；空-bundle CRITICAL 三项（Task 8 Step 5）；.ds-sync 重建后先 reapply（Task 8 Step 4）。✅
- **YAGNI**：未碰状态完备性（归前序 L2/L4）、未做 `/design-sync` config 钩子（backlog META-DSYNC-01）。✅
- **类型/命名一致性**：tag 名全部取自 register.ts（`tvu-notification`/`tvu-tab`/`tvu-tab-item`/`tvu-menu-list`/`tvu-top-bar`/`tvu-popup-box`/`tvu-drop-down-list-select`）；token 名全部取自 variables.css grep 实证；CORE_POINTER 新值在 Task 5 三处逐字一致。✅
