# T2 样板 Badge 计划（plan-only，2026-04-30）

> 本文件是 plan owner 产出。覆盖 T2 样板"先 Badge 跑通整链"的**语义层 + 约束 + 判定标准**。
> **不覆盖**：generator TS 类型 / FigmaMembersGrid Vue API / audit 脚本实现——这些是 executor 提议，plan owner 复审。
> 范围对齐 v2 plan T2 样板段（Badge / Tooltip / Select），本计划只锁 Badge；Tooltip / Select 在 Badge 跑通后按"扩展实例"复用本 schema。

---

## TL;DR

- **目标**：把 BadgePage.vue 的 Figma Coverage / 三段 axis 对照段从手写 axes 改成消费 generator 输出，且能跑 4 个 page-level audit 验证不漂移
- **样板边界**：只做 Badge 一个组件 + generator 增量支持单 COMPONENT_SET 类型；Tooltip（多 component）/ Select（带 theme axis）的复杂度本轮不进入
- **拆轮**：Round 1 = plan owner 写本文件 + executor 提议 TS/API/算法 → plan owner 复审定稿；Round 2 = executor 按定稿实现脚本/组件/改 BadgePage + 跑 4 audit
- **No commit**：所有改动 dirty 工作区累积，等 M1 一刀 commit（沿用 v2 工作纪律 #1）

---

## 背景：现有 BadgePage.vue 的漂移信号

- `badgeAxes`、`badgeColors`、`badgeColorMembers`、`figmaNodeIds`、"variant count=20" 字面量全手写
- `badgeColors` 顺序（Green/Blue/Red/Orange/Black）≠ `badgeColorMembers` 顺序（Black/Blue/Green/Orange/Red）→ **漂移已发生**
- 没有任何机制保证下次 figma 改 axis 值时这一页同步——这正是 generator + audit 要消除的风险

Badge figma 真源：`figma-data/normalized/components-tokenized/badge__4821_1665.json`，COMPONENT_SET，3 axes（Type / Color / Tag），20 variants，**无 theme axis**。

---

## §1. Generator 输出 — 语义层（plan owner 任务）

> 本节锁**下游消费需求**：FigmaMembersGrid + 3 个 page audit（α/β/γ）必须能从这份输出读到什么。
> **不锁** TS 类型 / 字段命名 / 嵌套结构——executor Round 1 提议，plan owner 复审。

### 1.1 输出位置（已锁）

`figma-data/normalized/docs-figma-members/<X>.ts`

- `.ts` 不是 `.json`：因为下游是 Vue/TS 消费，TS 能附类型给 grid + audit；同时仍需是机器生成产物（每次 generator 跑会覆写）
- 文件名小写 + kebab/snake——具体命名 executor 提议（看仓库现有 normalized 输出风格保持一致）
- generator 脚本位置：`figma-sync/generate-docs-figma-members.mjs`（与既有 `figma-sync/normalize-*.mjs` 同 layer）

### 1.2 必填语义（下游消费驱动）

下游消费者列出来作为**必填驱动**，每条必填语义旁注哪个消费者需要。所有字段 executor Round 1 自由提议命名 + 嵌套，但下面这些信息**必须能读到**：

| # | 必填语义 | 说明 | 哪个下游需要 |
|---|---|---|---|
| S1 | **Component identity** | 组件名（与 canonical 文件名一致，如 `Badge`） | grid（导入解析）/ β audit（错误报告里的组件名） |
| S2 | **Figma 来源溯源** | figmaNodeId（如 `4821:1665`）+ figmaPage 名（如 `— — Notifications & Pop box`）+ figmaFileKey | grid 顶部展示"source=Badge · nodeId=...·variant count=N" / γ audit（"from-figma" 锚点真源） |
| S3 | **Axes 枚举** | 每个 axis 的名字（如 `Type`/`Color`/`Tag`）+ 该 axis 全部 figma 出现过的值（如 `['Circle','Rectangle']`）+ 顺序 stable | β audit（页面用过的 prop 值必须在此枚举里）/ grid（chip 列表渲染用） |
| S4 | **Variant tuple 集合** | 每个 figma variant 的完整 prop 值组合（如 `{type:'Circle',color:'Green',tag:'Filled'}`）+ 该 variant 的 figma node id | grid（每个 cell 一条 variant）/ α audit（如有 theme axis，filter 入口） |
| S5 | **Theme 标识（条件必填）** | 如果组件有 theme axis（如 Tooltip/Select），每个 variant 必须能读到 `theme: 'dark'\|'light'`；Badge 无 theme axis，本字段可缺省/空 | α audit（site theme 与 variant theme 匹配判定） |
| S6 | **Generator 元数据** | `generatedAt` 时间戳 + `generatorVersion`（如 `t2-sample-v0.1`）+ 来源文件路径（如 `figma-data/normalized/components-tokenized/badge__4821_1665.json`） | 排查漂移时溯源 |

### 1.3 不必填（明确排除，避免 executor 加料）

- ❌ Per-variant 的 fills/strokes/text token 引用（视觉真源）——T2 样板**不做视觉回归**，留给 T5
- ❌ canonical 文件路径——硬规则 #6 已锁 SoT，不需要 generator 重述
- ❌ Code Connect mapping——T4 batch 已 DEFERRED，本轮不要混入

### 1.4 增量支持范围（本轮）

- generator 本轮只支持 **单 COMPONENT_SET + 无 theme axis**（即 Badge 形态）
- 多 component（Tooltip）/ 带 theme axis（Select）的支持留 Tooltip / Select 扩展实例轮
- 但 schema 必须**预留** S5 theme 字段（条件必填），让后续扩展不用改 schema

---

## §2. FigmaMembersGrid Contract — 约束层（plan owner 任务）

> 本节锁**数据流 + 渲染约束 + theme isolation 语义**。
> **不锁** Vue props 形态 / v-for 嵌套 / CSS 类名——executor Round 1 提议。

### 2.1 数据流约束

- **输入唯一**：`<X>.ts` generator 输出（按 §1）；**禁止**接受任何手写 axis 数据 / 手写 variant 数组
- **不接受 raw figma JSON**——避免 grid 自己跑 normalization 逻辑（违反双层导入）
- 组件渲染消费的目标 Vue 组件（如 `Badge`）由 page 注入，grid 不内置组件名 → canonical 路径硬编码

### 2.2 渲染约束

- 每个**符合当前 site theme** 的 variant 渲染 1 个 cell
- 每个 cell 必须呈现：(a) variant 标签（如 `Type=Circle, Color=Green, Tag=Filled`）+ (b) 真实组件实例（按该 variant 的 prop tuple）
- Grid 顶部 meta 行：`source=<Component> · nodeId=<id> · variant count=<N>`
- **不允许**在 grid 渲染范围内混入 Try It / 手写 demo / 静态截图等 runtime fabrication（γ audit 入口）

### 2.3 Theme isolation 语义

- 当 generator 输出**有 S5 theme 字段**（如 Select）：grid 必须按 site theme 过滤——site=dark 只渲染 theme='dark' 的 variants
- 当 generator 输出**无 S5**（如 Badge）：grid 渲染全部 variants，α audit 该组件 verdict = `N/A`
- Site theme 来源由 page 注入（不在 grid 内部探测全局 state）——保持 grid 是纯派生组件

### 2.4 审计锚点约束（γ audit 入口）

- 每个 grid cell 的根 DOM element **必须**带可机械识别的标记，表明"此 cell 来自 generator"（具体属性名 executor 提议，如 `data-figma-source` / `data-from-generator`）
- Page 上**手写 demo** / Try It 区块**禁止**带这个标记
- γ audit 通过 grep DOM 标记 vs 检测到的渲染数量做对照

### 2.5 Vue 组件文件位置

`playground/docs/components/FigmaMembersGrid.vue`（与既有 `DocsExampleBlock.vue` 同 folder）

---

## §3. 4 Page-level Audit — 判定标准 + verdict schema（plan owner 任务整块）

> 本节是判定逻辑设计，与 T1a evidenceLevel schema 同模式。
> 4 个 audit 必须输出**统一 verdict schema**（schema 真源在本节）；executor 按已定 schema 写脚本 + 报告。

### 3.1 统一 verdict schema

```jsonc
{
  "auditId": "alpha-theme-isolation" | "beta-axis-coverage" | "gamma-runtime-fabrication" | "delta-token-purity",
  "page": "BadgePage",
  "component": "Badge",
  "verdict": "pass" | "fail" | "N/A",
  "evidenceLevel": "direct" | "heuristic" | "semantic-inference",
  "evidenceSource": ["dom-attribute" | "static-css-regex" | "generator-output" | "..."],
  "findings": [
    { "severity": "error" | "warn" | "info", "location": "<file:line> 或 <DOM selector>", "message": "..." }
  ],
  "checkedAt": "ISO-8601"
}
```

evidenceLevel 沿用 meta-rules.md §5 定义，复用 T1a 经验。

### 3.2 α — Theme Isolation

| 维度 | 判定 |
|---|---|
| **目的** | 防止 page 在 site=dark 时渲染了 light theme variants（或反之） |
| **输入** | (a) 当前 site theme（page 编译产物或运行时 inject）；(b) generator 输出 §1 S5 theme 字段（每 variant 的 theme） |
| **判定算法** | 1) 读 generator 输出全部 variants；2) 如无 S5 → verdict=`N/A`；3) 有 S5 → 收集 page 实际渲染的 variants 集合，**任一 variant 的 theme ≠ site theme** → fail，否则 pass |
| **evidenceLevel** | `direct`（DOM 标记 + generator 输出字面比对） |
| **Badge 预期 verdict** | `N/A`（Badge 无 theme axis） |
| **Tooltip / Select 预期** | `pass`（grid 应按 site theme 过滤） |

### 3.3 β — Figma Axis Coverage

| 维度 | 判定 |
|---|---|
| **目的** | 防止 page 用了 figma axis 枚举之外的 prop 值（自创 / 漂移）|
| **输入** | (a) generator 输出 §1 S3 axes 枚举；(b) page 上所有 `<Component prop="value">` 的 prop=value 出现集合（包括 Try It 区块和 grid 区块） |
| **判定算法** | 1) 收集 page 上每个 axis 用过的全部 value（静态扫 SFC template + script setup ref/computed）；2) **任一 value ∉ generator axes 枚举** → fail，列出 off-axis values 与 location；否则 pass |
| **evidenceLevel** | `heuristic`（静态扫描可能漏动态拼接的值；脚本内必须在 finding 里标 `evidenceLevel: heuristic` 提示下游） |
| **Badge 预期** | `pass`（改造后所有 prop 值来自 generator） |

### 3.4 γ — Runtime Fabrication

| 维度 | 判定 |
|---|---|
| **目的** | 防止 grid 区块混入手写 demo / Try It / 截图 → 让"figma 一致性"段失真 |
| **输入** | 编译产物 DOM 树（或 SFC template 静态扫） |
| **判定算法** | 1) 找到 grid 区块（如标记为 `<section data-figma-members>`）；2) 区块内**所有渲染组件实例**必须带 §2.4 的 generator 标记；3) 任何**未带标记的渲染** / Try It 控件 / 静态字符串 demo **泄漏**到 grid 区块 → fail，列出位置 |
| **evidenceLevel** | `direct`（DOM 标记字面对比） |
| **Badge 预期** | `pass`（Try It / Tag/Type/Color 三段对照都不在 grid 区块内） |

### 3.5 δ — Token Purity

| 维度 | 判定 |
|---|---|
| **目的** | 防止 hex/rgb literal 出现在 canonical 组件 / page CSS / grid 组件 CSS（硬规则 #4） |
| **输入** | 源文件文本（不读编译产物）：(a) `src/canonical/Badge.vue` (b) `playground/docs/pages/BadgePage.vue` (c) `playground/docs/components/FigmaMembersGrid.vue` |
| **判定算法** | 1) 抽出每个 `.vue` 的 `<style>` 块；2) 排除 CSS 注释（`/* */`）；3) regex 扫 `#[0-9a-fA-F]{3,8}` 和 `rgba?\(...\)`；4) 任一命中 → fail 列 file:line + 命中字符串 |
| **evidenceLevel** | `heuristic`（regex 可能漏 `data-color` 字符串等非 CSS 上下文，脚本须在 finding 里提示） |
| **Badge 预期** | `pass`（Badge.vue 已用 `var(--brand)` 等 token） |

### 3.6 报告输出位置

`docs/internal/t2-sample-audit-report.md`（4 个 audit 合并产出，便于 plan owner 一次复审）

---

## §4. 执行轮拆分 + Executor 任务清单

### Round 1 — Schema 表达层 + 算法实现提议（executor → plan owner 复审）

Executor 在 prompt 里产出（**只产文档草案 + 算法描述，不动 src/ 实代码**）：

1. **`docs/internal/_plans/t2-sample-generator-output-schema.draft.md`**
   按 §1 必填语义，提议：generator 输出 `.ts` 的具体 TS 类型 + 字段命名 + 嵌套结构 + 1 实例（Badge 完整 ts 输出预览）
2. **`docs/internal/_plans/t2-sample-grid-contract.draft.md`**
   按 §2 约束，提议：FigmaMembersGrid Vue props API + 渲染算法描述（伪代码）+ 标记属性具体名（如 `data-figma-source` 或别名）
3. **`docs/internal/_plans/t2-sample-audit-impl.draft.md`**
   按 §3 verdict schema，提议：4 个 audit 的脚本算法描述 + 输入输出契约 + 边界 case 处理（如 grid 区块定位失败、CSS 注释排除算法）

Round 1 末尾 **STOP**，等 plan owner 复审三份 draft → 给定稿指示。

### Round 2 — 实现 + 跑 audit

按 plan owner 复审通过的 draft：

1. 写 `figma-sync/generate-docs-figma-members.mjs`
2. 跑生成 `figma-data/normalized/docs-figma-members/badge.ts`（具体文件名按 Round 1 定稿）
3. 写 `playground/docs/components/FigmaMembersGrid.vue`
4. 改 `playground/docs/pages/BadgePage.vue`：把手写 axes/variants 改成消费 generator 输出 + 用 grid 渲染 figma 段；Try It / 手写对照不动（验证 γ audit 能区分）
5. 写 4 个 audit 脚本：`figma-sync/audit-page-{alpha,beta,gamma,delta}.mjs`
6. 跑 4 个 audit 输出 `docs/internal/t2-sample-audit-report.md`
7. 跑 `pnpm dev` 视觉验收 BadgePage 渲染正确（Round 2 末尾贴截图给 plan owner）

Round 2 末尾 **STOP** + 不 commit。

### 关键约束（写进 Round 1/Round 2 prompt 顶部）

- ⚠️ **不要 commit / 不要 git add**——所有改动 dirty 累积
- ⚠️ Round 1 **不动 src/ 实代码**，只产 draft .md
- ⚠️ Round 2 不能扩范围到 Tooltip / Select（按 v2 plan，本轮只 Badge）
- ⚠️ 关键参数（如 generator 输出 ts 字段命名 / 标记 attr 名）先列出来给 plan owner 拍板，不自决

---

## §5. 验收标准

### Round 1 验收

- [ ] 三份 draft .md 都覆盖了 §1/§2/§3 全部必填语义/约束/verdict schema 条目
- [ ] 没有遗漏字段或自创 generator 不产出的字段（双层导入合规）
- [ ] 没有引入 §1.3 排除项
- [ ] generator schema 预留了 S5 theme 字段（哪怕 Badge 不用）

### Round 2 验收

- [ ] BadgePage.vue 不再含手写 `badgeAxes` / `badgeColors` / `badgeColorMembers` / `figmaNodeIds` / "variant count=20" 字面量
- [ ] BadgePage 编译 + 视觉无 regression
- [ ] 4 audit 全部 pass（Badge α 应为 N/A，其余 pass）
- [ ] generator 输出 `badge.ts` 与 figma normalized JSON 数据一致（按 §1 必填字段对账）
- [ ] no commit，dirty 工作区累积

---

## §6. Plan Owner / Executor 边界自检（按触发器 G 机械跑）

| 任务关键词 | 角色 | 本计划落点 |
|---|---|---|
| "下游消费需求" / "必填语义" / "判定标准" / "verdict schema" / "约束语义" | **plan owner** | §1.2 / §2.1-2.4 / §3 全部 |
| "TS 类型形态" / "字段命名" / "嵌套结构" / "Vue props API" / "渲染算法实现" / "脚本实现" | **executor**（Round 1 提议草案，plan owner 复审） | §4 Round 1 三份 draft + Round 2 |
| "拆轮策略" / "验收标准" / "范围边界" | **plan owner** | §4 拆轮 + §5 验收 + §1.4 范围 |

✅ 自检通过：本计划只覆盖判定/语义/约束层；表达层全部丢给 Round 1 executor 提议、plan owner 复审定稿。

---

## §7. 反模式自检（meta-rules §3 + 触发器 F 机械跑）

| # | 反模式 | 本计划检查点 | 状态 |
|---|---|---|---|
| 1 | 脚本/工具里硬编码项目级规则 | generator 输出 schema 真源 = 本文件定稿后 + Round 1 draft 定稿；脚本读结果，不藏规则 | ✅ |
| 2 | 打补丁不抽象 | grid 不为 Badge 加特例；§1.4 明确 generator 增量支持但 schema 预留 S5 theme | ✅ |
| 3 | to-do list 思维（没产出契约） | §3 verdict schema + §1 必填语义清单 = 契约 | ✅ |
| 4 | 没问下游怎么消费 | §1.2 每条必填都注了下游消费者；§3 各 audit 输入输出明示 | ✅ |
| 5 | 没问扩展时改哪里 | §1.4 + §6 明示 schema 预留 S5；Tooltip/Select 扩展只在 generator + grid 加 theme filter，不改本 schema | ✅ |

---

## §8. 后续（不在本计划范围）

- Tooltip 扩展实例（多 component 形态 + theme axis）
- Select 扩展实例（多 axis + theme axis）
- T2 批量阶段（剩 19 页改造）—— 按本样板范式批量
- T5 视觉回归 baseline（Playwright 截图）—— Per-variant fills/strokes 视觉锚点本计划已明确不做（§1.3）

---

## 工作区状态

- HEAD: 8529487
- 本文件 dirty（plan-only 文档，plan owner 角色直接产出）
- Round 1 fire 前需 plan owner STOP 等用户拍 OK
