# 两套设计系统合并 + 设计流程同步 + Sync A 审计 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. **注意:凡 DesignSync 远端读写(list_files/get_file/finalize_plan/write_files)只能在主 session 亲做,不可外包子代理(子代理无此工具)。**

**Goal:** 让 owner 的两个 claude.ai/design 项目(Sync A 手作策展、Sync B CLI 生成)都成为完整、同源的设计系统——组件卡与基础层卡都由本地 repo 真源生成/导出,并把设计流程文档同步进 Sync B。

**Architecture:** 本地 repo 真源 → 生成器/导出脚本 → 两个远端项目都从它导。基础卡从 `playground/docs/pages/designAssetData.ts`(色+图标)+ `atomicAssets.ts`(字+圆角+投影)+ `src/tokens/variables.css` 程序化渲染成独立 `@dsCard`。组件卡建立在 repo 标准 `.ds-sync` 运行时(`_ds_bundle.js` 暴露 `window.TvuDS.<Name>` + 自注册 `<tvu-*>` WC)之上,以策展 demo 形式渲染,再按各项目路径布局适配。设计流程文档 emit 到 Sync B。

**Tech Stack:** Node ESM 脚本(`.mjs`)、现有 `scripts/export-claude-design-bundle.mjs` + gitignored `.ds-sync` driver、DesignSync 工具(仅主 session)做上传、Vitest 做生成器单测。

---

> ## ✅ 执行状态 (2026-07-24 — Phase 2–5 全部 shipped，Claude 直接执行，非 Codex)
>
> Owner 裁决把生成器实现交 Claude 直接做(跳 Codex，因产出质量等同+验收全机械)；subagent 并行实现 + 主线机械亲验 + fresh subagent 对抗复核补独立性。
> - **Phase 2/3/4 生成器**：`scripts/gen-foundation-cards.mjs` + `lib/foundation-card-template.mjs`(6 基础卡)、`scripts/gen-sync-a-component-cards.mjs` + `lib/component-card-template.mjs` + `data/component-card-demos.mjs`(35 组件卡，canonical props，`window.TvuDS`+`React.createElement`)、`scripts/emit-reference-to-sync-b.mjs`(4 reference 文档 emit→ds-bundle)。测试 `tests/gen-*.test.mts` + `emit-*.test.mts` = **98 pass**(repo 默认 vitest config，路径 `tests/*.test.mts`)。`package.json` 加 `gen:foundation-cards`/`gen:sync-a-cards`/`emit:reference-to-sync-b`。对抗复核 3 项修复已应用(单引号 subtitle / 函数 replacer / `<`→`<`)。
> - **Sync A 运行时裁决(owner)**：clean-slate 到 **root 单一标准运行时**(覆盖旧手作 `_ds_bundle.js`/`window.TVUNetworksDesignSystem_bfca04`)；旧 6 卡侧车 + Icon 卡 + 旧 9 preview 删；`styles.css` 补最小 `.btn` 段保邮件 CTA；**弃 5 个旧 JS 演示**(TopBar Demo / Command Center v2 / 3 dc-runtime templates)；保留纯 token 的邮件族 + local-mgmt + share/versions/reference/uploads。
> - **Phase 5 上传(主 session DesignSync)**：Sync A(`bfca04be`) 50 写 + 33 删；Sync B(`406a39da`) 11 写(4 reference + 7 foundation preview，纯加层无删)。各 get_file 亲验字节保真(Button.html canonical / 05-domain-tvu.md 全文)。Sync B CRITICAL 自检全过(define=37/3.5MB/verdict ok/L3 present)。
> - **⏭ Follow-up(owner 排期)**：把 Sync A 弃掉的 5 个旧演示(2 dashboard mockup + 3 dc-runtime template)重写到标准 `window.TvuDS` 运行时(需重建 dc-runtime 工具链，独立工程，超 merge 范围)。
>
> 下方 Phase 0–5 的逐 step checkbox 保留作实现细节参考；完成叙述以本 banner + STATUS + work-log 为准。



## Global Constraints

- **DesignSync 仅主 session**:`list_files`/`get_file`/`finalize_plan`/`write_files`/`delete_files` 只能主线亲做,绝不外包子代理(见 memory `designsync-tool-main-session-only`)。子代理只能干纯本地活。
- **对外上传纪律**:每次 upload 前 `finalize_plan` 锁定精确 file-set + `localDir`,**贴给 owner 确认后**再 `write_files`;写完逐文件 `get_file` 亲验。**面板 `updatedAt`/卡片对非视觉写入不刷新,别拿 UI 当同步凭据**(memory `designsync-nonvisual-writes-no-ui-bump`)。
- **Sync B 空-bundle CRITICAL 三项**(每次同步必过):`[ignored-bare-import]`=0、`_ds_bundle.js` 多 MB、`customElements.define`≥1。
- **`.ds-sync` 重建后**先跑 `node .design-sync/reapply-driver-patches.mjs`(现只剩 L3 patch)再上传。
- **Sync B 上传集** = `.design-sync/NOTES.md:116-120` 定义的 185 文件(`_vendor/*`+`fonts/*`+`components/general/**`=140+`_preview/*.js`=35+root),**不传** build 内部 dotfiles / app 生成的 `_ds_manifest.json` / remote-only 的 `templates/**` / `_adherence.oxlintrc.json`。
- **组件 prop 名以 `src/canonical/*.vue` 真源为准**,绝不照抄现有 Sync A 卡(已确认 drift,见 Phase 1)。已验证:Button/Badge 用 `fill`(非 `variant`/`tag`);UserMenu 用 `actions[]`+`languages[]`(非 `items[]`/`defaultOpen`);Icon 无 `spritePath`、`size` 为自由 number;MenuList item 无 `type` 字段。
- **push 双 remote**(Gitea `origin` + `github`),**以 `git ls-remote` 为准**(GitHub 常 phantom reject)。
- **项目 id**:Sync A=`bfca04be-9783-4eb5-b754-37b04a6c35ef`;Sync B=`406a39da-1681-4edf-ae26-826cf3c33c9c`;**Eduardo 的 `019dfb04…` 绝不碰**。
- **DTCG/composition 是 build 产物**:改动前先 `pnpm build`(缺 `dist/tokens/tokens.json`、`dist/composition/composition.json` 会 warn)。

## 已定方向(owner 拍板,勿再问)

1. 基础卡 → **从本地源程序化生成**(不复用远端 9 张手作卡)。
2. 组件集 → 两套**对齐 35**;Sync A 补缺的 **30 个**;**Icon 并入基础层 icons 卡**,不再单列组件卡。

## 关键机制事实(recon + 亲验,勿重查)

- 两条管道当前**互不相通**:`scripts/export-claude-design-bundle.mjs`(→Sync A,当前**不 emit 任何组件卡/基础卡**)与 `.ds-sync` driver(→`ds-bundle/`→Sync B)。三件事都要**新增生成逻辑**。
- Sync A 现有 6 卡是**远端手作**、靠手作 `_ds_bundle.js` 暴露的 `window.TVUNetworksDesignSystem_bfca04` 渲染;**repo 无此 bundle 的构建脚本**(grep 确认)。→ 新卡改用 repo 标准运行时(`window.TvuDS`),现有 6 卡一并迁移。
- Sync B 卡 = 三级 `components/general/<Name>/`,外链共享运行时(`_vendor/react.js`+`_ds_bundle.js`+`_preview/<Name>.js`);Sync A 卡 = 两级 `components/<Name>/`。运行时文件在项目 root,卡头相对路径按层深调整。
- reference 真源分散在 `docs/internal/` 与 `docs/`,由 export 脚本重命名 01–14;`04-design-process.md` ← `docs/internal/design-process.md`。Sync B 当前**无 reference 文档层**。
- 35 组件(Sync B 已同步,= `.design-sync/config.json` `componentSrcMap`):Badge, Breadcrumb, BreadcrumbItem, Button, Chart, CheckBox, DropDownListSelect, FormItem, Input, InputBoxFilled, InputNumber, Logo, MenuList, Message, Notification, Pagination, PillCounter, PillStatus, PopupBox, Progress, Radio, Rating, SelectBoxFilled, SelectBoxLine, Slider, StepItem, Steps, Switch, Tab, TabItem, TabList, Table, Tooltip, TopBar, UserMenu。Sync A 缺的 30 个 = 上表去掉重叠的 Badge/Button/MenuList/TopBar/UserMenu(Icon 归基础层,不计组件)。

---

## Phase 0 — Sync A 6 组件卡审计(✅ 已完成,结论存档)

本 Phase 已在起草期用主线 DesignSync `get_file` 6 卡 + 对照 `src/canonical/*.vue`、`.design-sync/conventions.md`、`docs/internal/component-affordances.md` 完成并 spot-check 亲验。结论:

| 组件 | 结论 | 问题 | canonical 出处 |
|---|---|---|---|
| Badge | 有问题 | prop `tag`→应 `fill`;把会话运行态(Live/Preview/Inactive)揽进 Badge(越界) | `src/canonical/Badge.vue:5,10,45`;`conventions.md:23,32`;`component-affordances.md:575,600-608` |
| Button | 有问题 | prop `variant` 杜撰→应 `fill` | `ButtonBridge.vue:11,22`;`conventions.md:23`(“there is no variant prop”) |
| Icon | 有问题 | `Icon.spritePath` 杜撰(组件 v-html 内联 SVG、不走 sprite);`size` 应为自由 number 非四值枚举 | `src/components/Icon/Icon.vue:3,8-14,18,45,70` |
| MenuList | 轻微 | item 多了杜撰 `type:section\|divider` 字段 | `src/components/MenuList/MenuList.vue:9-20` |
| TopBar | 符合 | (卡里“顶栏下方 strip”说法未溯源,不矛盾) | `conventions.md:36`;`src/canonical/TopBar.vue:5,7-15` |
| UserMenu | 有问题 | actions 列表 prop 写成 `items[]`→应 `actions[]`;`defaultOpen` 杜撰;漏 `languages[]` | `src/canonical/UserMenu.vue:23-32`;`conventions.md:39` |

**含义**:这些 drift 全部通过 Phase 3 用 canonical 真值重新生成/迁移来消除,不在远端手改。**报告 owner 时把此表作为 deliverable ③ 的交付。**

- [ ] **Step 1: 把上表写入 handoff/交付说明,回报 owner deliverable ③ 完成**(纯文本汇报,不改远端)。

---

## Phase 1 — 基线:确认标准运行时可承载策展 demo 卡(spike,零上传)

**Files:**
- Read: `.design-sync/config.json`、`ds-bundle/`(driver 产物,可能需先重建)
- Create: `scratch/spike-tvuds-render/` (本地临时,验证用,不进 repo)

**Interfaces:**
- Produces: 确认 `window.TvuDS.<Name>` 是否可直接 `React.createElement(TvuDS.Button, {fill:'filling', color:'green'}, 'Start')` 渲染 → 决定 Phase 3 组件卡模板走 React 封装还是直接实例化 `<tvu-*>` WC。

- [x] **Step 1: 重建 driver 产物并确认三项健康** ✅ 2026-07-23

```bash
cd /Users/nancy/Documents/AICoding/VS_Code/tvu-design-system
# 按 .design-sync/NOTES.md 的 re-sync runbook 跑 driver(/design-sync skill),产出 ds-bundle/
node .design-sync/reapply-driver-patches.mjs   # 重建后必跑,注入 L3 CORE_POINTER
grep -c 'customElements.define' ds-bundle/_ds_bundle.js   # 期望 ≥35
grep -rl '组合前先过核心规则' ds-bundle/components/general/*/*.prompt.md | wc -l  # 期望 35
```
Expected: `customElements.define` ≥35;35 个 prompt.md 带 CORE_POINTER 行。

> **实测裁决(2026-07-23)**:现有 `ds-bundle/` 已是当日在当前 HEAD 重建的健康产物(无需再 rebuild):`_ds_bundle.js` 3.65 MB、`grep -o customElements.define` = **37**(`grep -c` 返回 1 是 minified 单行行计数假象,NOTES §CRITICAL 口径本就是"≥1 + multi-MB")、`.resync-verdict.json` `ok:true` 35 组件全 verified、CORE_POINTER prompt.md = **35**、`reapply-driver-patches.mjs --check` = L3 present。namespace = `TvuDS`。

- [ ] **Step 2: 本地起一个最小 HTML,验证 `window.TvuDS.Button` 的 React 可用性**

写 `scratch/spike-tvuds-render/index.html`,head 引 `ds-bundle/_vendor/react.js`、`react-dom.js`、`_ds_bundle.js`,body:
```html
<div id="r"></div>
<script>
  const e=React.createElement;
  ReactDOM.createRoot(document.getElementById('r')).render(
    e(window.TvuDS.Button,{fill:'filling',color:'green'},'Start'));
</script>
```
用 `pnpm dlx serve scratch/spike-tvuds-render` 或既有 playground 静态服务打开,截图确认按钮渲染。
Expected: 绿色 filling 按钮渲染出来(证明 `TvuDS.<Name>` 是 React 组件,策展 demo 模板可沿用现有 Sync A 卡的 `React.createElement` 风格)。

- [x] **Step 2: 本地起最小 HTML 验证 React 可用性** ✅ 2026-07-23 — 用 Playwright(headless chromium)载入 `scratch/spike-tvuds-render/index.html` 亲核渲染实况,PASS:`TvuDS.Button` 是 React 函数组件、`React.createElement(TvuDS.Button,{fill:'filling',color:'green'},'Start')` 渲染零 console error、`<tvu-button>` CE upgrade(shadowRoot 有内容)、wrapper 透传 `el.fill==='filling'`/`el.color==='green'`→`.btn--fill-green` 生效,截图为暗色面板上的绿色 "Start" 按钮。

- [x] **Step 3: 记录裁决** ✅ 2026-07-23 — **裁决 = React 封装可用 → Phase 3 组件卡模板走 `window.TvuDS.<Name>` + `React.createElement`(沿用现有 Sync A 卡风格),WC 实例化 fallback 分支不需要**。裁决 folded 进 **`.design-sync/NOTES.md`**(§Merge-plan runtime spike verdict),**未**写成 `.design-sync/learnings/*.md`:该目录任何 `.md` 都会触发 driver 的 learnings gate(`resync.mjs` — 非空 → 下次 verdict `ok:false`/exit 1),NOTES 才是 gate 期望的"已 fold"耐久 SoT。

- [x] **Step 4: Commit** ✅ 2026-07-23 — `scratch/` 不提交(未 gitignore,仅本地);裁决落 NOTES.md + 本 plan Phase 1 勾选,随同一 commit。

---

## Phase 2 — 基础卡生成器(色/字/圆角/投影/图标)

**Files:**
- Create: `scripts/gen-foundation-cards.mjs`(生成器)
- Create: `scripts/lib/foundation-card-template.mjs`(卡 HTML 模板 + `@dsCard` 头)
- Test: `test/gen-foundation-cards.test.mjs`
- Read(数据源): `playground/docs/pages/designAssetData.ts`(色+图标)、`playground/docs/pages/atomicAssets.ts`(字/圆角/投影)、`src/tokens/variables.css`
- Reference: 远端现有 9 张卡的视觉基线(`preview/colors-brand.html` 等,仅参考布局,不复制内容)、`preview/_card.css`(需一并产出到两项目)

**Interfaces:**
- Produces: `genFoundationCards({outDir})` → 写出独立自包含 `@dsCard` 卡到 `<outDir>/preview/*.html` + `<outDir>/preview/_card.css`,组:`Colors`/`Type`/`Spacing`/`Elevation`/`Brand`/`Icons`。每卡首行 `<!-- @dsCard group="…" name="…" subtitle="…" -->`,用 `getComputedStyle` 从 `styles.css` 的 token 实时取值(与远端 9 卡同机制),布局数据来自 designAssetData/atomicAssets。
- Consumes: Phase 1 无依赖(基础卡不需组件运行时,纯 token/CSS)。

- [ ] **Step 1: 写失败测试 — 色卡含全部 5 组色 token**

```js
// test/gen-foundation-cards.test.mjs
import { test, expect } from 'vitest'
import { buildColorCard } from '../scripts/lib/foundation-card-template.mjs'
import { colorSections } from '../playground/docs/pages/designAssetData.ts'

test('color card lists every token from all 5 sections', () => {
  const html = buildColorCard(colorSections)
  expect(html.startsWith('<!-- @dsCard group="Colors"')).toBe(true)
  for (const section of colorSections)
    for (const tok of section.tokens)
      expect(html).toContain(tok.name)   // 每个 --token 名都出现
})
```

- [ ] **Step 2: 跑测试确认失败** — `pnpm vitest run test/gen-foundation-cards.test.mjs` → FAIL(`buildColorCard` 未定义)。

- [ ] **Step 3: 实现 `buildColorCard`(最小)**:遍历 `colorSections`,每 token 输出一 swatch 行(读 `var(--name)` via 内联 style + `getComputedStyle` 显示值),包 `@dsCard group="Colors"` 头。

- [ ] **Step 4: 跑测试确认通过**。

- [ ] **Step 5–N: 同法 TDD 其余卡** — `buildTypeCard`(`atomicAssets.typographyStyles`)、`buildRadiusCard`(`borderRadiusTokens`)、`buildElevationCard`(`effectStyles`)、`buildBrandCard`(logo)、`buildIconsCard`(`actionIcons`/`statusIcons`/`logoIcons` + 尺寸档)。每卡:失败测试(断言关键 token/图标名全覆盖 + `@dsCard` 头组名正确)→ 实现 → 通过。**Icon 基础卡即 Q2 里 Icon 的归宿**,须覆盖 registry 全量分类(从 `designAssetData.ts` 的 `figmaPublishedIconCategories`)。

- [ ] **Step N+1: 组装 `gen-foundation-cards.mjs`**:调各 `build*`,写 `<outDir>/preview/*.html` + 拷 `_card.css`;加 `--out=` 参数;`package.json` 加 `"gen:foundation-cards": "node scripts/gen-foundation-cards.mjs"`。

- [ ] **Step N+2: 本地渲染回归** — 输出到 `scratch/`,浏览器打开每张卡,确认明暗主题下 swatch/字样/阴影正确(参考远端 9 卡视觉)。

- [ ] **Step N+3: Commit** — `git commit -m "feat(ds-merge): foundation-card generator from local token source"`

---

## Phase 3 — 组件卡:迁移现有 6 + 生成缺的 30(Sync A)

**Files:**
- Create: `scripts/gen-sync-a-component-cards.mjs`
- Create: `scripts/lib/component-card-template.mjs`(两级路径卡模板,head 引 root 运行时)
- Create: `scripts/data/component-card-demos.mjs`(每组件的策展 demo 配方:用哪些 props/示例值,**props 名取自 canonical**)
- Test: `test/gen-sync-a-component-cards.test.mjs`
- Read: `src/canonical/*.vue`(prop 真值)、Phase 1 spike 裁决、现有 6 卡的 demo 布局(仅借鉴,prop 名必须改对)

**Interfaces:**
- Consumes: Phase 1 的运行时裁决(`window.TvuDS` vs `<tvu-*>`);Phase 2 无关。
- Produces: `<outDir>/components/<Name>/<Name>.html`(两级、`@dsCard group="Components"`,head 引 `../../_ds_bundle.js` 等 root 运行时),覆盖全 35 组件(6 迁移 + 30 新增,不含 Icon)。同时把 root 运行时(`_ds_bundle.js`、`_vendor/*`、`styles.css`、`_card.css`)纳入 Sync A 上传集。

- [ ] **Step 1: 写失败测试 — 生成的卡只用 canonical 合法 prop 名**

```js
// test/gen-sync-a-component-cards.test.mjs
import { test, expect } from 'vitest'
import { buildComponentCard } from '../scripts/lib/component-card-template.mjs'
import { demos } from '../scripts/data/component-card-demos.mjs'

test('Button card uses fill= not variant=', () => {
  const html = buildComponentCard('Button', demos.Button)
  expect(html).toContain('fill:')          // canonical prop
  expect(html).not.toContain('variant:')   // drift 名禁止
})
test('Badge card uses fill= and does not claim session run-state', () => {
  const html = buildComponentCard('Badge', demos.Badge)
  expect(html).toContain('fill:')
  expect(html.toLowerCase()).not.toMatch(/on-?air|preview 运行|=live/i)  // 运行态归 PillStatus
})
test('UserMenu card uses actions[] and languages[], not items/defaultOpen', () => {
  const html = buildComponentCard('UserMenu', demos.UserMenu)
  expect(html).toContain('actions:')
  expect(html).toContain('languages:')
  expect(html).not.toContain('defaultOpen')
})
```

- [ ] **Step 2: 跑测试确认失败**。

- [ ] **Step 3: 定义 6 个已有组件的 demo 配方(修正版)** in `component-card-demos.mjs`:沿用现有卡的布局意图,但 prop 名/值改成 canonical 真值(Badge `fill`、Button `fill`、MenuList item 去 `type`、UserMenu `actions`/`languages`)。

- [ ] **Step 4: 实现 `buildComponentCard`**:据 Phase 1 裁决产出两级卡(`@dsCard group="Components"`,`whenReady` 等运行时就绪后 `React.createElement(TvuDS[Name], demoProps)` 或实例化 WC)。跑测试通过。

- [ ] **Step 5: 为其余 30 组件写 demo 配方** — 每个从 `src/canonical/<Name>.vue` 读 props 真值,给 2–4 个代表性示例(遵循 `component-affordances.md` 的用途)。**分批 TDD**:每批加断言“该组件卡不含任何 canonical 之外的 prop 名”。

- [ ] **Step 6: 组装 `gen-sync-a-component-cards.mjs`** → 写全 35 卡 + 复制 root 运行时到 `<outDir>`;`package.json` 加 `"gen:sync-a-cards"`。

- [ ] **Step 7: 本地渲染回归** — 输出到 `scratch/`,起静态服务逐卡确认渲染(尤其 6 迁移卡视觉不回退)。

- [ ] **Step 8: Commit** — `git commit -m "feat(ds-merge): Sync A component-card generator on standard runtime (35, canonical props)"`

---

## Phase 4 — 设计流程文档 → Sync B

**Files:**
- Modify: `scripts/export-claude-design-bundle.mjs`(或新增 `scripts/emit-reference-to-sync-b.mjs`)
- Read: `docs/internal/design-process.md`(→`reference/04-design-process.md`)及 REFERENCE_DOCS 表其余 md
- Reference: `.design-sync/NOTES.md`(上传集)

**Interfaces:**
- Produces: `ds-bundle/reference/04-design-process.md`(+ 视需要 01/03/05 等设计流程相关篇),纳入 Sync B 上传集。
- Consumes: 无(独立小任务,可先做)。

- [ ] **Step 1: 写失败测试** — 断言 emit 后 `ds-bundle/reference/04-design-process.md` 存在且非空、内容 == `docs/internal/design-process.md`。
- [ ] **Step 2: 跑测试确认失败**。
- [ ] **Step 3: 实现**:在 driver emit 或新脚本里把 REFERENCE_DOCS 里的设计流程文档拷进 `ds-bundle/reference/`;把 `reference/**` 追加进 Sync B 上传集(更新 `.design-sync/NOTES.md` 的 185→新计数)。
- [ ] **Step 4: 跑测试确认通过**。
- [ ] **Step 5: Commit** — `git commit -m "feat(ds-merge): emit design-process reference docs into Sync B bundle"`

---

## Phase 5 — 上传两项目 + 亲验(主 session 唯一)

**Files:** 无 repo 改动;纯 DesignSync 操作(主 session)。

- [ ] **Step 1: 生成两套 bundle 到本地 outDir**:跑 `gen:foundation-cards`(→两项目)、`gen:sync-a-cards`(→Sync A)、driver + Phase 4(→Sync B)。
- [ ] **Step 2: Sync B CRITICAL 三项自检** — `grep -c 'customElements.define'`、`_ds_bundle.js` 大小、`[ignored-bare-import]`=0;`node .design-sync/reapply-driver-patches.mjs`。
- [ ] **Step 3: `finalize_plan` 各项目一次**,把精确 writes/deletes + `localDir` **贴给 owner 确认**(Sync A: `components/**`+`preview/**`+root 运行时;Sync B: 185+`reference/**`+`preview/**`)。
- [ ] **Step 4: owner 批准后 `write_files`**(每次 ≤256 文件,localPath 读盘;大 bundle 分批同一 planId)。
- [ ] **Step 5: 亲验** — 逐关键路径 `get_file` 比对(别信面板 updatedAt);抽查 Sync A 一张迁移卡 + 一张新卡 + 一张基础卡,Sync B 一张基础卡 + `reference/04-design-process.md`。
- [ ] **Step 6: push 双 remote**,`git ls-remote` 核对 HEAD 一致(忽略 GitHub phantom reject)。

---

## Self-Review 备注

- **Spec 覆盖**:①双向合并 = Phase 2(基础卡→两项目)+ Phase 3(组件卡→Sync A);②设计流程文档→Sync B = Phase 4;③审计 = Phase 0(已完成)。✓
- **口径**:组件补 30(非 29)、Icon 归基础层——已按 owner Q2 裁决。
- **未决/风险**:Phase 1 spike 的运行时裁决是 Phase 3 的前置;若 `window.TvuDS` React 封装不可直接用,Phase 3 模板改走 WC 实例化(已在 Step 3 留分支)。基础卡“自包含 + getComputedStyle 取值”需确保 `styles.css`/`_card.css` 一并进两项目上传集。
