# P0 系统缺口修复计划 — 由 3-way UX 受控实验驱动（2026-07-24）

> 起因：owner 让验证"用这两个设计系统设计产品时,AI 知不知道何时用哪个组件/字体/图标/spacing",并加测 UX 流程/旅程。
> 做法：3 个 subagent 冷跑同一「直播源管理」任务,各只喂对应环境(当前仓库 / Sync A bundle / Sync B bundle)实际发出的引导材料,我逐份 Playwright 渲染 + DOM 普查 + 源码核 + 扩展 rubric 打分。
> owner 决策(2026-07-24):**先修系统缺口**(本计划),之后再扩 rubric 批量重测验证。

## 实验效度边界（写给下次执行者,别过度解读）

- **n=1**:排名(repo≈73 / SyncA≈88 / SyncB≈93)**不可信**,重跑可能反转。
- **可信的是跨独立运行复现的系统缺口**(下方 P0 全部满足"3/3 或 2/3 独立撞到")+ 能力存在性(三者都产出了连贯多状态 UX)。
- 混淆变量:Sync A/B 是"只喂 bundle 材料"的模拟(非真 claude.ai 运行时);三者同一底模(测的是材料质量不是模型);仅一个任务;我既出题又打分。→ 批量重测须修正:多任务 × 每环境 n≥5 × 盲评/多评审 × rubric 从项目自有 UX 合同派生。
- 证据留档:`scratch/ux-test/`(render-{repo,syncA,syncB}.png + 各 *.report.md + shots/)。

## 核心洞察（决定修法方向）

**仓库版有 `docs/CLAUDE_DESIGN_RULES.md`(白纸黑字写"确认用 Notification 不用 PopupBox")却仍用了 PopupBox。** → 缺的不是规则(prose),是**执行保障**。所以按 ROI:让组件真够用(P0-1)+ 加机器闸(P0-2)> 再写十页规则。

---

## P0-1 · Table 补交互单元格能力（DS 代码,动 npm 包+bundle）

**证据(3/3 独立撞到)**:三个 builder 都发现 `src/components/Table/Table.vue` 单元格是 `<span>{{ row[col.key] }}</span>` 纯文本、`TableColumn` 类型只有 `key/title/align/width/showLeftIcon/rightIconName/sortOrder`,**无 `render`/cell-slot**;React 侧传组件被字符串化。运营台核心 UX(行内状态 pill + 行内操作按钮,domain M3/M7/M9)无法用它表达 → 三者都被迫放弃 Table 手搓 flex/语义 `<table>`。

**⚠️ 开工前必做:owner 拍 API 分叉**(组件公开 API = owner 决策)。

> **推荐已于 2026-07-24 修正(原 "B 主" 有硬伤,已翻案)**:证据——`Table.vue` 现仅 `#empty` **静态** slot 且注释写明 "INFRA-F54 CE-safe slot-presence check — only forward the #empty slot";react-pilot **无 scoped-slot 通道**(具名 slot 只投影成 prop 或静态 `<div slot=>`,无逐行回传数据机制)。故 B(作用域 cell slot)**在 CE/react-pilot 路径不可行**,而那正是 3/3 builder 撞墙的路径。

- **✅ C(数据描述符)= 唯一方案**(owner 2026-07-24 拍定 C only):`columns[].cell:{kind:'pill'|'button'|'text'|'link'|'icon'|'actions', …}`,当 property 传(与现有 `columns`/`data` 同通道),Table 在 shadow root 内映射成真 PillStatus/Button。跨 Vue-npm + CE 双出口都可用。代价:Table 要 import 域组件 → 用内部小 registry 收敛。
- **B(作用域 cell slot)= 砍掉**(owner:已无纯 Vue-SFC 消费者;C 已覆盖两条路径;真有需要再加,可逆)。
- **A(函数 `render`)= 出局**:VNode 绑 Vue 运行时,React/CE 拿不到;react-pilot 无 render 通道。
- 前置:核 **Figma 真源**(Table 有无状态/操作列变体;没有则 rich cell = code-first,需 owner ack divergence,见 FIGMA_AS_SOURCE_OF_TRUTH)+ 不撞 **APID-01「Table 状态视觉仅 presentational」**裁决。
- **开工首步先 live 验**:实测 C 的数据描述符经 react-pilot 传入后,Table shadow root 内真渲染出 PillStatus/Button(pill 数!=0),再动实现。

**验收**:选定方案后 → 改 `Table.vue` + `TableColumn` 类型 + 单测(断言 pill/button 真渲染进单元格,数!=0)→ `pnpm build` 重建 bundle → 若 Table 卡受影响则重生成上传两个 Sync + 重编译验 → CHANGELOG(minor)。

## P0-2 · Mockup 一致性 linter（流程/机制,补 conventions 自认缺的"机器闸")

**证据**:`.design-sync/conventions.md` 自己写"this guidance works by being READ … not a machine-enforced guarantee";仓库版正是"读了规则仍违反"。补一道**对生成产物的确定性机检**(非 prose)。

**首批规则(都来自本次实测到的真实违例)**:
1. **确认弹窗用 PopupBox** → flag(确认/告警应 Notification `form="dialog"`;PopupBox 仅大型自定义模态)。启发式:含"确认/取消/删除/停止/confirm/cancel"按钮对的 popup-box。
2. **未约束尺寸的内联 `<svg>`**(无 width/height 且无 CSS 尺寸)→ flag(repo 版图标撑爆根因)。
3. **有 MenuList 却手搓 `<ul>`/`<nav>` 导航** → flag。
4. **硬编码 `#hex` / 裸 `px`(排除 token 定义文件)** → flag。
5. (可选)**Notification `form="alert"` 传了 `confirmText`**(该用 `okText`)→ flag。

**落地**:`scripts/audit-mockup-conformance.mjs` + 测试 fixtures(正例/反例各一)+ 挂 pre-commit(见 memory `feedback_audit-pre-commit-gate`:新 audit 必同时挂 hook)。claude.ai 侧无 git → 该闸主要护 repo 内产物 + 作为"可移植规则"文档化(支柱②设计网 缺口 A)。

## P1（P0 完再做）— **owner 2026-07-24 逐项拍定 API（直接执行，不再问）**

> 起手已对 4 项 live 源亲验 + Figma 真源 live 核（library `YbsPRUVmNdsbN40NNwh1Gn`）。执行顺序 = Notification（零码）→ MenuList → Tab（含 build:wc live CE 核）→ Icon（audit 级联风险最大，放最后）。全走 TDD。纪律：`.vue` 改动 commit 用 `VISUAL_COMMIT_APPROVED=1`；push 以 `git ls-remote` 为准；Eduardo `019dfb04` 绝不碰。

1. **Icon → 出公开 Icon 组件 + 默认 size**（owner 拍）：现有内部 `src/components/Icon/Icon.vue`（已含 `size` 默认 16 + `:deep(svg){100%}` 强制）**提升为 canonical 公开出口**（新建 `src/canonical/Icon.vue` 包一层 + `data-figma-component="Icon"`；`index.ts` 从 canonical 导入；`components.config.ts` `canonicalPath`→canonical）。同时堵两缺口：① bundle 无公开 Icon → 加进 `component-card-demos.mjs`（bundle 卡片）；② repo 内联超大 = raw-SVG 消费路径 → 靠"用公开 Icon 组件（默认 size）"取代内联 + docs 指引。注意 audit 级联（component-affordances / render-verification / figma-vs-canonical）。
2. **Notification → alert 用 `okText`；dialog/pop confirm 用 `confirmText`+`cancelText`**（owner 拍）。⚠️ **已亲验组件本体无需改**：`Notification.vue` L44 `showsOk` + L129-139 alert 只渲染 OK 只读 `okLabel`；`confirmText` 仅走 cancelConfirm 分支（L107-128）。→ P1-3 = **docs（NotificationPage）+ CLAUDE_DESIGN_RULES 裁决 + divergence 登记 + 确认与 P0-2 linter `alert-confirmText` 规则对齐**，不动 .vue（owner:别无中生有改）。
3. **Tab → 补 `change` 事件跨 CE**（owner 拍）。已核：base+canonical Tab/TabList 现只 `update:modelValue`（v-model），`components.config.ts` `events:[]`。补一等 `change` DOM CustomEvent（base Tab/TabList emit `change` + TabItem 经注入触发；config `events` 加 `change`→重生成 react `onChange` 绑定使其监听 `change`）。Figma 真源 = Tab List + Tab/Item component_set（state∈{Normal,Active}，2026-06-09）。Tab 是真交互，补 change 合理，纯 additive 无 breaking。build:wc + Playwright 真机核 CE 边界。
4. **MenuList → per-item `badge`/`icon` 字段**（owner 拍）= **code-first additive**。已核：`MenuListItem` 已有 `icon?`（无需动）；新增 `badge?`（复用 canonical Badge：color/fill/type；红点/count 语义 canonical 无 → 先用 red Circle + 数字作内容，dot-mode 标 follow-up）。MenuList 无独立 Figma 源，已在 divergence `logo-menulist-usermenu-code-first-2026-07-02` → 扩这条即可。纯 additive 无 breaking；改 base MenuList.vue + config items tsType + 重生成 react 类型。

## P2 · 扩展 rubric + 批量重测（验证修复见效）

> **owner 已定：P1 全 ship 后单独 session，别塞进 P1 session。**

- rubric 维度从项目自有真源派生:`design-process.md` 的 **M49 设计质量合同**(Baseline/Delta/Semantic/Feedback Inventory/UX/Simplicity/Content)+ **5-stage×5-lens 用户体验地图** + persona-simulation(F1/F2)+ 补 **WCAG**(对比/键盘焦点/ARIA/点击区)+ **Nielsen 十启发** + 错误预防恢复/可发现性/一致性/响应式。
- 设计:2-3 个任务(列表台 / 表单流 / 数据可视化)× 每环境 n≥5 × 盲评(多评审 agent 独立打分取中位)。
- 目标:测**缺陷率**(PopupBox-当确认率、超大图标率…)并对比 P0 修复前后是否下降。

## 状态

- [x] P0-1 owner 拍 API 分叉 → **C only**(数据描述符;B 砍、A 出局,2026-07-24)
- [x] P0-1 开工首步(全 PASS,2026-07-24):(a) 核 Figma 真源——`search_design_system` 确认 Table=6 成员集(Type×Align,`updatedAt 2026-04-01` 自 APID-01 起未变),无状态/操作/单元格内容变体 → rich cell code-first(owner C-only=ack);(b) APID-01 presentational-only 裁决——C 无冲突(无 emits,click 走原生冒泡+`composedPath()`);(c) live 验——描述符经 CE+react-pilot DOM-property 路径真渲染 3 PillStatus+6 Button+3 link+3 icon 进 shadow root(pill≠0)、嵌套 scoped CSS 自动注入、0 error、composedPath 委派捕获 {action,rowKey}。
- [x] P0-1 实现 **shipped `fc9bd4d4`**(master,双 remote ls-remote 验):`TableCell.vue` 内部 registry + `Table.vue` `cell?` + tbody 分支(text/header 不动=12 render-verif 保留)+ config columns tsType + react bindings + 公开 `TableColumn`/`TableCellDescriptor` 类型 + docs/React demo "Rich cells" 段 + affordances(+md 重生)+ page-recipes cellRendering + CLAUDE_DESIGN_RULES §Table + divergence `table-cell-descriptor-code-first-2026-07-24` + changeset(minor) + 8 单测。验证:vitest 833 pass、vue-tsc 0 err、binding-parity 37/37、export-coverage/slot-guard/affordances audit 全 PASS。**Table 卡未受影响**(`data/component-card-demos.mjs` 未动 → Sync 卡字节等同,无需重传/DesignSync)。
- [x] **P0-2 linter shipped**（2026-07-24）：`scripts/audit-mockup-html-conformance.mjs`（**换名避开已存在的 Figma-侧 gate**）——扫生成 mockup HTML 产物，5 规则：popupbox-for-confirm / unconstrained-svg / handrolled-nav（error 阻断）+ hardcoded-color / alert-confirmText（warn 报告）。CSS-class-aware svg 尺寸解析、`${}` 动态 class 放行、HTML 注释 masking（避免注释内文字/注释掉的标签误报）、`<!-- mockup-lint-disable-next-line -->` 内联抑制、纯中性 stage 背景放行。fixtures 正/反例 + 8 单测（`tests/mockup-html-conformance.test.ts`）+ npm `audit:mockup-html-conformance` + pre-commit hook（staged `_demos/*.html` 或 linter 自身变更→跑；NUL-delimited 处理带空格文件名）。**校准硬线达成**：2 个现有 `_demos/*.html` 均 0 error 0 warn。
- [x] **P1 四项全 shipped（2026-07-24，owner 拍定 API 后一次做完；全走 TDD）**：
  - **P1-2 Notification**（零 .vue 改动）：亲验组件本体已符合 owner 决策（alert=okText / dialog·pop-confirm=confirmText+cancelText，L44 showsOk 分流）→ 仅补 `CLAUDE_DESIGN_RULES` §Notification 生成指引（alert→ok-text，对齐 P0-2 linter `alert-confirmText`）。
  - **P1-4 MenuList**：per-item `badge?`（shorthand string|number→红 Circle 计数 / object→canonical Badge props）additive，复用 canonical Badge；base MenuList.vue + config items tsType + 重生成 react 类型 + 扩 divergence `logo-menulist-usermenu-code-first-2026-07-02` + affordances + 7 单测。（MenuList 不经 src/index.ts npm 出口 → 无 changeset。）
  - **P1-3 Tab**：新增 `change` 事件跨 CE。**实测发现** slotted `<tvu-tab-item>` 的 provide/inject **不跨 CE**（update:modelValue 与 change 都不触发 Tab root）→ 改用 **composed 冒泡 DOM 委派**（TabItem 派发 `tab-item-activate`，Tab/TabList root 原生监听）；build:wc + Playwright 真机核验 3/3 PASS（change + update:modelValue 现都跨 CE）。base×3 + canonical×2 + config 不动(vModel 已给 onChange) + TabsPage docs + `CLAUDE_DESIGN_RULES` L80 更新 + affordances + 6 单测。changeset(minor)。
  - **P1-1 Icon**：`src/components/Icon/Icon.vue`（已含默认 size 16）提升为公开 canonical——新建 `src/canonical/Icon.vue` + index.ts/canonical/index.ts 导出 + config canonicalPath→canonical + canonical-exempt.json 登记（asset 聚合）+ component-affordances 条目 + Sync A **卡片 demo 数据**（component-card-demos.mjs，35→36）+ 更新 divergence `icon-registry-asset-aggregation` + 4 单测。changeset(minor)。
  - **验证**：vitest 857 pass / 0 fail、vue-tsc 0 err、self-audit-phase2 全链 exit 0、export-coverage/demo-slot-boolean/framework-parity/**P0-2 linter(2 demos 0/0)**/doc-sync/stale-anchors/binding-parity 全 PASS。
  - **⚠️ P1-1 唯一剩余子步（remote，需 owner）**：DesignSync **Sync A/B bundle 重传 + manifest 重编译**（Icon 卡片数据已就绪，但远端 bundle 未含）——需重生成卡片 + DesignSync 上传 + 写 `_ds_needs_recompile` 哨兵 + owner 重开 SPA 触发重编译 + 亲验 manifest。此为刚重建过的 bundle 的重操作，且 manifest 重编译必须 owner 重开 SPA → 建议作为独立后续（可并入 P2 session 或单独执行）。
- [ ] P2 扩 rubric + 批量重测（owner 已定单独 session）

## ⚠️ P0-2 开工发现（2026-07-24，动手前必读——改变设计）

1. **文件名冲突**：计划提议的 `scripts/audit-mockup-conformance.mjs` **已存在**，是 **Figma-API 侧**主 gate（聚合 integrity/colors/typography-icon/library-origin/library-binding 等 5 个扫 Figma 文件的子审计）。P0-2 要的是**扫生成 HTML 产物**的 linter，性质不同 → **必须换名**（建议 `scripts/audit-mockup-html-conformance.mjs`），**绝不覆盖现有 Figma gate**。
2. **假阳性校准（真 mockup 实测）**：`docs/internal/_demos/TVU Command Center v2.html` 有 **2 处硬编码 hex**（`#000`/`#0a0a0a` 全屏 stage letterbox 背景，非 DS 设计面）+ **16 个内联 `<svg>`**（全部经 CSS class 如 `.tb-logo{width:28px}` 约束尺寸）。→ 规则 4（硬编码 hex）与规则 2（未约束 svg）**必须解析 CSS class 尺寸 / 放行 stage 脚手架背景**，否则会把合规 mockup 大面积误报、pre-commit 反而挡住正常提交。设计要点：
   - 规则 2：`<svg>` 需在「无 width/height 属性 **且** 其 class 无 CSS 宽高 **且** 无内联 style 尺寸」三者全缺时才报（要读 `<style>` 块解析 class→尺寸）。
   - 规则 4：需排除 token 定义文件 + 考虑 stage/letterbox 纯黑背景这类非设计面（或提供 `<!-- mockup-lint-disable-line 规则 原因 -->` 内联抑制机制，别硬报）。
   - **目标集**：linter 只扫「mockup 产物」HTML（如 `docs/internal/_demos/**/*.html` + 可配置），不扫 playground/docs（那些 token/示例文件合法含大量 hex）。pre-commit 只对 staged 的 mockup-dir HTML 跑。
   - **验收硬线**：linter 对现有 2 个 `_demos/*.html` 必须 **PASS**（否则日后编辑它们会被 hook 挡）；fixtures 正/反例各一验规则真能抓真能放。
