# Prompt — Phase A1 Generator Auto-Discovery for Tier 1 ~19 components

> **角色**：executor
> **范围**：把 [`figma-sync/generate-docs-figma-members.mjs`](../../../figma-sync/generate-docs-figma-members.mjs) 从硬编码 `COMPONENTS = { Badge, Tooltip }` 改为**自动派生**——扫 components-tokenized/ + 读 canonical/* + 读 prop-aliases.md（A0 已表格化）→ 一刀输出 ~19 个 Tier 1 `<x>.ts`。
>
> ⚠️ **不要 commit / 不要 git add**——dirty 工作区累积到 Phase A1 通过后 commit。
> ⚠️ 完成后 **STOP**，按底部"完成报告"格式回报。
> ⚠️ **不扩范围**：只动 `figma-sync/generate-docs-figma-members.mjs`（重写）+ 输出 `figma-data/normalized/docs-figma-members/*.ts`。**不改** types.ts schema、不动 audit 脚本、不动 prop-aliases.md、不动 canonical、不改任何 page。
> ⚠️ 如发现裁定与代码事实冲突的 **blocker**（不只是命名分歧），先指出再决定是否执行。

---

## §0 — Plan owner 已定裁定（最终决议）

### Tier 1 / 2 / 3 分类规则

扫 `figma-data/normalized/components-tokenized/*.json` 取 `type === 'COMPONENT_SET'`（共 50 个），然后**按下面 3 条规则分类**：

#### Tier 3 排除（generator throw）

`figmaName` **以下列任一前缀开头** → 标 Tier 3：

- `Button/`（如 `Button/dark L`、`Button/light XS`、`Button/url link`）
- `Dark/`（如 `Dark/filled btn`、`Dark/outlined btn`、`Dark/text btn`）
- `Light/`（如 `Light/filled btn`、`Light/outlined btn`、`Light/text btn`）

预期 Tier 3 = 21 sets（9 Button + 12 Dark/Light btn 多版本）

#### Tier 2 排除（generator throw）

`figmaName` **以下列任一前缀开头** → 标 Tier 2：

- `Drop down List/`
- `input box/`
- `select box/`

预期 Tier 2 = 8 sets（4 Drop down + 2 input + 2 select）

⚠️ **不要**用"figmaName 含 `/`"作为 Tier 2 判定——否则会误伤 `Step/Item` / `Tab/Item` / `Breadcrumb/Item`（这 3 个是 Tier 1）。**必须**用上面具体前缀白名单。

#### Tier 1 包含

剩下 50 - 21 (Tier 3) - 8 (Tier 2) = 21 个候选。

但其中 2 个**没有 canonical 文件**：
- `Chart` → `src/canonical/Chart.vue` 不存在
- `Popup Box` → `src/canonical/Popup.vue` / `PopupBox.vue` 都不存在

→ 这 2 个 `warn` + 跳过（**不 throw**，不 hard fail），最终生成 `~19 个 Tier 1 .ts`。

### Component name 派生（figmaName → canonical name）

按下面顺序匹配：

1. **优先**查 [`prop-aliases.md`](../../../src/design-system/translation/prop-aliases.md) "组件级命名映射" 表 → 取 `Code Component` 列
2. **fallback**：
   - `figmaName` 中 `/` 前后两段拼接（如 `Step/Item` → `StepItem`、`Tab/Item` → `TabItem`、`Breadcrumb/Item` → `BreadcrumbItem`）
   - lowercase / 含空格 figmaName → PascalCase（如 `Top bar` → `TopBar`、`switch` → `Switch`、`Tooltips` → `Tooltips`——第二步 alias 表 `Tooltips → Tooltip` 应已优先匹配；若 alias 表查不到 fallback 走通用 PascalCase）
   - 已经 PascalCase 的保留（`Badge` / `Notification` / `InputNumber` / `Pagination` / `Progress` / `Rating` / `Slider` / `Table`）
3. **必须验证**：`src/canonical/<derivedName>.vue` 存在（fs.existsSync）
   - 不存在：warn 跳过（如 Chart / Popup Box）
   - 不在白名单 alias 表 + fallback 也找不到 canonical：warn 跳过 + 在 STOP 报告里列出 figmaName 让 plan owner 后续登记真源

### Output filename 派生

`<lowercase canonical name>.ts`（如 `badge.ts` / `tooltip.ts` / `topbar.ts` / `formitem.ts` / `stepitem.ts` / `breadcrumbitem.ts` / `tablist.ts` / `tabitem.ts`）。

### propByAxis 派生（figma axis → canonical prop name）

每个 Tier 1 组件按下面顺序：

1. 解析 `<canonical>.vue` 的 `defineProps<{ ... }>()` 取 prop 名集合（用 @vue/compiler-sfc 已经在 audit 脚本里，generator 也用）
2. 解析 figma JSON variant.name 取 axis 名集合（如 `Type=Circle, Color=Green` → axes = `['Type', 'Color']`）
3. 对每个 figma axis 名，按下面顺序映射到 canonical prop name：
   - **第一步**：查 prop-aliases.md "项目级 GLOBAL axis alias / 表 A" → 如命中，axis 转 derived name（如 `dark theme` → `theme`，但 derived 仅供后续 theme 派生用）；同时**保留 figma 原 axis 名作为 prop 名**——这是反 alias 应用，参考 commit 8870419 Tooltip 处理（`darkTheme: 'on'|'off'` 而非 `theme: 'dark'|'light'`）
   - **第二步**：fallback PascalCase / camelCase 转换（`Type` → `type` / `Color` → `color` / `dark theme` → `darkTheme` / `Tag` → `tag` / `Status` → `status` / `Size` → `size` 等）
4. **验证**：每个派生 prop name 必须存在于 `<canonical>.vue` 实际 props 集合
   - 不命中：warn 该组件 + 在 STOP 报告列出 figmaName / 派生 propName / canonical actual props，**不 throw**（让 plan owner 复审决定登记 alias 真源还是改 canonical）
   - 该组件本次跳过生成

### Theme axis 派生 (variant.theme 字段)

对每个 figma axis：

1. 查 prop-aliases.md "项目级 GLOBAL axis alias / 表 A" → 如该 axis 命中（→ `theme`）→ 该组件**有 theme axis**
2. 该组件每个 variant 按 prop-aliases.md "表 B" 派生 `variant.theme`：
   - 表 B `figma value` 列匹配 → `derived value` 列填入 `variant.theme`
3. 没有 theme axis 的组件：variant **不含** `theme` 字段（如 Badge）

### CLI 行为

- `node figma-sync/generate-docs-figma-members.mjs`（无参）→ 生成所有 Tier 1（默认行为，本 phase 主目标）
- `node figma-sync/generate-docs-figma-members.mjs <ComponentName>`（单组件）→ 生成单个 Tier 1，**向后兼容** Badge / Tooltip 之前的调用方式
  - `<ComponentName>` 在 Tier 2 / Tier 3 → throw with 引导 backlog ID 错误消息
- `node figma-sync/generate-docs-figma-members.mjs <Tier3Name>`（如 `Button`） → throw `"Tier 3 component '<X>' not supported in Phase A1; tracked as BRIDGE-Tier3 (mega component_set aggregation pending design)"` 后退出 1
- `node figma-sync/generate-docs-figma-members.mjs <Tier2Name>`（如 `DropDownList`） → throw `"Tier 2 component '<X>' not supported in Phase A1; tracked as BRIDGE-Tier2 (multi-set per canonical aggregation pending design)"` 后退出 1

### prop-aliases.md 真源**只读**

- 不要给 prop-aliases.md 加 entry——任何 missing alias 都在 STOP 报告里列出让 plan owner 决定如何登记
- 反模式 #1 合规：generator 是规则**消费者**，不是**编辑者**

### 不动

- `figma-data/normalized/docs-figma-members/types.ts`（schema 已锁，commit f6e9a77）
- `figma-sync/audit-page-t2-sample.mjs`（Phase A2 范围）
- `playground/docs/pages/*Page.vue`（Phase C 范围）
- `src/canonical/*`（A 系列不改 canonical）
- `src/design-system/translation/prop-aliases.md`（plan owner 后续增量登记）

### Badge / Tooltip baseline regression

跑完后 `figma-data/normalized/docs-figma-members/badge.ts` 和 `tooltip.ts` 必须**与 commit 8870419 时的内容除 `generatedAt` 时间戳外字面相同**。

如有任一差异（除 generatedAt） → STOP 立即报告，不要"修到一致"。

---

## §1 — 必读输入

按顺序读：

1. [`AGENTS.md`](../../../AGENTS.md) — 硬规则 + 必读链路（注意 #6 backlog.md）
2. [`docs/meta-rules.md`](../../meta-rules.md) — 反模式 #1 (硬编码项目级规则) + 触发器 G
3. [`docs/internal/backlog.md`](../../backlog.md) — 看是否新增 backlog 影响本任务
4. [`figma-sync/generate-docs-figma-members.mjs`](../../../figma-sync/generate-docs-figma-members.mjs) — 现有硬编码版本（commit 8870419），重写起点
5. [`figma-sync/audit-page-t2-sample.mjs`](../../../figma-sync/audit-page-t2-sample.mjs) — 静态扫 SFC 的 @vue/compiler-sfc 用法参考（auditBeta 等函数）
6. [`figma-data/normalized/docs-figma-members/types.ts`](../../../figma-data/normalized/docs-figma-members/types.ts) — schema（不改，必须输出对齐）
7. [`figma-data/normalized/docs-figma-members/badge.ts`](../../../figma-data/normalized/docs-figma-members/badge.ts) — Badge baseline regression 对照
8. [`figma-data/normalized/docs-figma-members/tooltip.ts`](../../../figma-data/normalized/docs-figma-members/tooltip.ts) — Tooltip baseline regression 对照
9. [`src/design-system/translation/prop-aliases.md`](../../../src/design-system/translation/prop-aliases.md) — A0 已表格化的 alias 真源
10. [`src/canonical/`](../../../src/canonical/) — 28 个 .vue（按 §0 派生 propByAxis 时读）

依赖检查：`@vue/compiler-sfc` 项目已装（audit 脚本在用）。

---

## §2 — 任务清单

### 任务 2.1 — 设计 + 实现 generator 重写

**算法骨架**（伪代码，executor 实现具体）：

```
async function discoverTier1():
  files = scan('figma-data/normalized/components-tokenized/*.json')
  for each file:
    if data.type !== 'COMPONENT_SET': skip
    figmaName = data.figmaName
    
    if figmaName.startsWith(['Button/', 'Dark/', 'Light/']):
      tier = 'Tier 3' (skip + 计入 stats)
      continue
    if figmaName.startsWith(['Drop down List/', 'input box/', 'select box/']):
      tier = 'Tier 2' (skip + 计入 stats)
      continue
    
    canonicalName = lookupComponentNameAlias(figmaName) ?? fallbackPascalCase(figmaName)
    canonicalPath = `src/canonical/${canonicalName}.vue`
    if not exists(canonicalPath):
      warn "no canonical for figmaName='${figmaName}', tried '${canonicalName}'"
      continue
    
    canonicalProps = parseDefinePropsNames(canonicalPath)
    figmaAxes = parseAxesFromFirstVariantName(data.variants[0].name)
    propByAxis = {}
    for each figmaAxis in figmaAxes:
      propName = camelCaseFromFigmaAxis(figmaAxis)
      if propName not in canonicalProps:
        warn "axis '${figmaAxis}' → propName '${propName}' not in canonical ${canonicalName} props"
        skip-this-component
        break
      propByAxis[figmaAxis] = propName
    
    themeAxis = detectThemeAxis(figmaAxes, propByAxis, propAliasesGlobalAliasTable)
    themeValueMap = themeAxis ? readGlobalValueAliases(propAliasesGlobalAliasTable) : null
    
    yield { figmaName, canonicalName, sourceFile, propByAxis, themeAxis, themeValueMap }

async function generate(componentName?: string):
  if componentName:
    discovered = discoverSingle(componentName)  // throw if Tier 2/3
  else:
    discovered = await discoverTier1()  // 全部
  
  for each entry in discovered:
    parseVariantsAndAxes(entry.sourceFile, entry.propByAxis, entry.themeAxis, entry.themeValueMap)
    writeOutput(entry.canonicalName, ...)  // 输出 <name>.ts

main()
```

⚠️ 上面是骨架——具体函数命名、文件结构（拆 module 还是单文件）、prop-aliases.md 解析（markdown table parser 实现）、@vue/compiler-sfc 用法**全部 executor 提议**。

### 任务 2.2 — 跑 generator 全量

```bash
# Run discovery + 全量生成
node figma-sync/generate-docs-figma-members.mjs

# 控制台必须打印：
#   Discovered: Tier 1 = N · Tier 2 skipped = M · Tier 3 skipped = K
#   Generated: <list of N filenames>
#   Warnings: <missing canonical / unmappable axes>
```

### 任务 2.3 — Badge / Tooltip baseline regression

```bash
# 与 commit 8870419 的 badge.ts / tooltip.ts diff（仅 generatedAt 应变）
git diff figma-data/normalized/docs-figma-members/badge.ts
git diff figma-data/normalized/docs-figma-members/tooltip.ts
```

任一文件出现非 generatedAt 行的 diff → STOP 报告，不修。

### 任务 2.4 — 单组件 CLI 向后兼容

```bash
node figma-sync/generate-docs-figma-members.mjs Badge       # 应正常生成 badge.ts
node figma-sync/generate-docs-figma-members.mjs Button      # 应 throw + 引导 BRIDGE-Tier3
node figma-sync/generate-docs-figma-members.mjs DropDownList # 应 throw + 引导 BRIDGE-Tier2 (假设这是有效 alias，否则 throw "no such component")
node figma-sync/generate-docs-figma-members.mjs Chart       # 应 warn + 跳过 (no canonical)
```

### 任务 2.5 — typecheck

```bash
pnpm exec vue-tsc --noEmit
```

预期 0 错误。新生成的 `<x>.ts` 必须 satisfy `DocsFigmaMembers<XFigmaProps>` schema。

---

## §3 — 验收清单

- [ ] generator 重写后**仍**支持单组件 CLI（向后兼容 Badge/Tooltip）
- [ ] 无参调用生成 ~19 个 Tier 1 .ts 文件（含已有 badge.ts / tooltip.ts）
- [ ] Tier 2 / Tier 3 显式 throw 引导 backlog ID
- [ ] Chart / Popup Box 等无 canonical 的组件 warn 跳过（不 throw）
- [ ] missing alias 组件 warn + STOP 报告列出（让 plan owner 后续登记）
- [ ] **badge.ts / tooltip.ts 与 commit 8870419 字面一致除 generatedAt**（regression check）
- [ ] generator 不写新真源 .md（不动 prop-aliases.md / 不动 backlog.md）
- [ ] types.ts / audit script / canonical / page **没动**
- [ ] `pnpm exec vue-tsc --noEmit` 0 错误（含新增 ~17 个 .ts）
- [ ] 没 commit / 没 git add

---

## §4 — 完成报告（按下方格式回报）

```
## Phase A1 Generator Auto-Discovery 完成报告

### 改动文件
- figma-sync/generate-docs-figma-members.mjs (重写, +XXX/-YYY 行)
- figma-data/normalized/docs-figma-members/<X>.ts × N (新增 N-2 个; +badge.ts/tooltip.ts 重生)

### Discovery 统计
- 总 COMPONENT_SET: 50
- Tier 1 generated: <N>（应 ~19）
- Tier 2 skipped (with names): <list>
- Tier 3 skipped (with names): <list>
- Warning - no canonical: <list>（如 Chart / Popup Box）
- Warning - missing alias / unmappable axis: <list>（plan owner 待登记）

### Tier 1 生成清单
[列出 N 个 <component> → <output_filename> 映射]

### Baseline regression
- badge.ts: <byte-identical 除 generatedAt / 出现非时间戳 diff + 描述>
- tooltip.ts: <byte-identical 除 generatedAt / 出现非时间戳 diff + 描述>

### CLI 向后兼容验证
- Badge 单组件: <OK / 错误描述>
- Button (Tier 3): <throw with 引导 / 行为>
- Chart (no canonical): <warn 跳过 / 行为>

### typecheck
- vue-tsc: 0 错误

### 设计决策提议（plan owner 复审）
[列出 executor 提议的关键决策点：
  - 文件结构（拆 module / 单文件）
  - prop-aliases.md markdown table parser 实现细节
  - PascalCase fallback 算法
  - 等等]

### 验收 self-check
- [ ] CLI 向后兼容
- [ ] Tier 1 ~19 输出
- [ ] Tier 2/3 throw
- [ ] no-canonical warn
- [ ] regression-pass on Badge/Tooltip
- [ ] 不动真源 .md / types.ts / audit / canonical / page
- [ ] vue-tsc 0 错
- [ ] 没 commit
- [ ] 没扩范围

### 未解决项 / blocker
[如 prop-aliases.md schema 解析模糊 / Tier 1 候选某组件 axis 派生失败 / 其它]

STOP — 等 plan owner 复审 + 决定 Phase A2 (audit auto-discovery + ε audit) 启动。
```

---

## §5 — 严守约束总览

- ⚠️ **不要 commit / 不要 git add**
- ⚠️ §0 裁定**最终**（Tier 分类规则 / 派生规则 / regression baseline / 不动列表）
- ⚠️ generator 是真源**消费者**，不是**编辑者**——不动 prop-aliases.md / backlog.md / 任何 .md
- ⚠️ Tier 2 / 3 throw（不 silent skip）；no-canonical warn（不 throw）
- ⚠️ 派生规则 fail 时 warn 跳过单组件，**不 throw 中断整个 batch**
- ⚠️ Badge/Tooltip baseline regression 必须 pass（除 generatedAt）
- ⚠️ 完成 STOP，按 §4 格式回报
