# Codex Prompt: T4-Spike — Badge Vertical Slice (Figma URL → Code Bridge)

> **Spike 目标**：用最小工作量证明"AI 拿 Figma URL → 1:1 生成 Vue 代码"端到端跑通。
>
> 不在乎完整覆盖；只在 1 个组件 (Badge) 上跑通流程，验证 Code Connect + AI manifest 形态。
>
> **估时 2-3h。不要 commit。**

---

## 背景（必读）

主 session 已审过 v2 plan：T1c (extract pipeline 升级) 完成 Round 1 plan-only，但用户元层面提示"层数越来越深、离项目目标越来越远"。决策暂停 T1c，先做 T4-spike 验证 vertical slice 是否真的能落地——如果 Badge 端到端跑通，我们获得"AI 拿 Figma URL → 1:1 还原"的端到端 demo，可以重估 T1c 必要性。

详见 [docs/internal/long-term-plan-v2-2026-04-29.md](../long-term-plan-v2-2026-04-29.md) "当前进度（2026-04-29 下午 — 拐点）" 段。

---

## 必读前置

1. **`AGENTS.md`** — 协作规则（含 T4 段的 Code Connect 模板示例）
2. **`docs/internal/long-term-plan-v2-2026-04-29.md`** — T4 路线图 + 当前拐点说明
3. **`src/components/Badge/Badge.vue`** — Vue 组件本体，spike 不动它
4. **`figma-data/raw/components/badge__4821_1665.json`** — Badge COMPONENT_SET 原始 extract（30 variants）
5. **`docs/internal/component-token-fidelity-report.md`** Badge 段 — 验证 audit 已认可 Badge

---

## 为什么选 Badge

- 1 个 Figma COMPONENT_SET (`4821:1665`) ↔ 1 个 Badge.vue（**无 Figma↔Code 拓扑错位**）
- 3 axes：`color × tag × type`，30 variants（可控）
- audit 段干净：几乎全 token-match / token-match-via-indirection，**无 figma-data-missing**
- **不依赖 T1c**（extract pipeline 升级未做完也不影响）

Button 不选：在 Figma 里拆成 9 个 set（`Button/dark L|M|S|XS` + `Button/light L|M|S|XS` + `Button/url link`），3200+ variants，spike 不合适。

---

## 任务（5 步，按顺序）

### 任务 1：装 @figma/code-connect

```bash
pnpm add -D @figma/code-connect
```

只装包，**不**跑 `figma connect publish`，**不** init 项目。装完只确认 `package.json` 多一条 devDependency + `pnpm-lock.yaml` 更新。

### 任务 2：写 `src/components/Badge/Badge.figma.ts`

按 Code Connect Vue API 形态：

```ts
import figma from '@figma/code-connect'
import Badge from './Badge.vue'

// FIGMA_FILE_KEY 从 .env 读，spike 不写死；URL 模板留 placeholder 让用户回填
figma.connect(
  Badge,
  'https://www.figma.com/design/<FIGMA_FILE_KEY>/?node-id=4821-1665',
  {
    props: {
      color: figma.enum('Color', { Black: 'black', Blue: 'blue', Green: 'green', Orange: 'orange', Red: 'red' }),
      tag: figma.enum('Tag', { Filled: 'filled', Line: 'line' }),
      type: figma.enum('Type', { Circle: 'circle', Rectangle: 'rectangle' }),
    },
    example: ({ color, tag, type }) =>
      `<Badge color="${color}" tag="${tag}" type="${type}">99+</Badge>`,
  },
)
```

**关键约束**：
- Color / Tag / Type 三个 figma key（左侧）必须跟 figma 实际 axis 名一致——扫 `badge__4821_1665.json` 里 `variants[*].name` 形如 `"Type=Circle, Color=Black, Tag=Filled"` 确认大小写
- code 端 prop 值（右侧）必须查 [Badge.vue](../../src/components/Badge/Badge.vue) 看实际接受的字符串（如果是 `'circle' | 'rectangle'`，对应 figma `Type=Circle/Rectangle` 的 lowercase）
- 不脑补 prop 值——查不到就标 `// TODO: confirm` 让 plan owner 补

### 任务 3：写 `figma-data/normalized/ai-manifest.spike.json`

最小 schema，只含 Badge 一条：

```json
{
  "schemaVersion": "0.1-spike",
  "components": [
    {
      "name": "Badge",
      "figmaNodeId": "4821:1665",
      "figmaUrl": "https://www.figma.com/design/<FIGMA_FILE_KEY>/?node-id=4821-1665",
      "vueImport": "import { Badge } from '@tvu/design-system'",
      "filePath": "src/components/Badge/Badge.vue",
      "props": [
        { "name": "color", "type": "enum", "values": ["black", "blue", "green", "orange", "red"], "figmaAxis": "Color" },
        { "name": "tag", "type": "enum", "values": ["filled", "line"], "figmaAxis": "Tag" },
        { "name": "type", "type": "enum", "values": ["circle", "rectangle"], "figmaAxis": "Type" }
      ],
      "examples": [
        {
          "props": { "color": "green", "tag": "filled", "type": "circle" },
          "code": "<Badge color=\"green\" tag=\"filled\" type=\"circle\">99+</Badge>"
        },
        {
          "props": { "color": "red", "tag": "line", "type": "rectangle" },
          "code": "<Badge color=\"red\" tag=\"line\" type=\"rectangle\">NEW</Badge>"
        }
      ]
    }
  ]
}
```

**关键约束**：
- props/values 必须从 [Badge.vue](../../src/components/Badge/Badge.vue) 实际定义抽，不脑补
- examples 至少 2 条覆盖 type=circle / rectangle 两个分支
- schemaVersion 留 `0.1-spike` 让后续 schema 升级有空间

### 任务 4：写测试说明 `docs/internal/_spikes/badge-vertical-slice-test.md`

让用户在另一个 AI session（Claude / Cursor / Cline）里手动测**3 个对照实验**：

```markdown
# T4-Spike Badge Vertical Slice 测试说明

## 准备
- 在浏览器打开 Figma 文件，找到 Badge (node-id 4821:1665) 任一 variant，复制 URL
- 准备 3 个独立 AI session（Claude / Cursor / Cline 任选）

## 测试 1：给 AI Code Connect 文件 + Figma URL
**输入**：
- 完整 `Badge.figma.ts` 内容
- `Badge.vue` 内容
- 一个 Figma URL（如 node-id=<某个 variant>）

**指令**：
> "根据这个 Figma URL，给我生成对应的 Badge Vue 代码"

**期望输出**：`<Badge color="green" tag="filled" type="circle">...</Badge>`（具体值跟 URL 选的 variant 一致）

**判 1:1**：手动比对 figma variant axes ↔ AI 生成的 props，全对 = pass

## 测试 2：给 AI manifest + Figma URL
**输入**：
- `ai-manifest.spike.json` 内容
- 一个 Figma URL

**指令**：
> "用这份 manifest，根据 Figma URL 给我生成 Badge Vue 代码"

**期望**：同测试 1

## 测试 3：什么都不给（基线对照）
**输入**：
- 只给 Figma URL + 项目根路径

**指令**：
> "项目里有 Badge.vue，根据这个 Figma URL 生成对应代码"

**期望**：AI 自己摸出来。**这条对照基线** ——
- 如果通过：说明 manifest/.figma.ts 在 Badge 这种简单组件上**多余**，重估 T4a/T4b 必要性
- 如果不通过：说明 manifest/.figma.ts 真的有用，spike 验证项目目标可行

## 判定矩阵

| 测试 | Pass = | Fail = |
|---|---|---|
| 1 | AI 生成代码 1:1 匹配 figma | 不匹配 → .figma.ts 形态有问题 |
| 2 | AI 生成代码 1:1 匹配 figma | 不匹配 → manifest schema 不够 |
| 3 | AI 自己摸到 → 测试 1/2 多余 | AI 失败 → manifest 有真价值 |

**给主 session 的反馈**：3 个测试结果 + 1:1 是否达成 + 哪些场景生成失败。
```

### 任务 5：tsc 类型检查

```bash
pnpm typecheck   # 或 pnpm tsc --noEmit
```

确保 `Badge.figma.ts` 不破坏 typecheck。如果失败，**先尝试**：
- 检查 import 路径
- 检查 `figma.enum(...)` 的类型签名是否支持 `{ Black: 'black', ... }` 这种 mapping

如果 typecheck 还是失败，**STOP** 并报告——不要为了通过 typecheck 改 Badge.vue 或其它组件。

---

## 不要做的事

- ❌ **不 commit / 不 push**
- ❌ 不跑 `figma connect publish`（不动 Figma）
- ❌ 不 init Code Connect 项目（不跑 `figma connect init` / `figma connect create`）
- ❌ 不写其它组件的 .figma.ts
- ❌ 不改 [Badge.vue](../../src/components/Badge/Badge.vue) 本体
- ❌ 不改 [figma-data/raw/](../../figma-data/raw/) 任何文件
- ❌ 不动 normalize 输出
- ❌ 不 install 多余依赖（**只**装 `@figma/code-connect`）

允许的：

- ✅ 读项目内任何文件
- ✅ `pnpm add -D @figma/code-connect`
- ✅ `pnpm typecheck`
- ✅ `node -e` 查 Badge raw JSON / Badge.vue prop 定义
- ✅ 新增 3 个文件：
  - `src/components/Badge/Badge.figma.ts`
  - `figma-data/normalized/ai-manifest.spike.json`
  - `docs/internal/_spikes/badge-vertical-slice-test.md`
- ✅ 更新 `package.json` + `pnpm-lock.yaml`（任务 1 装包导致）

---

## 自验

```bash
ls -la src/components/Badge/Badge.figma.ts
ls -la figma-data/normalized/ai-manifest.spike.json
ls -la docs/internal/_spikes/badge-vertical-slice-test.md

pnpm typecheck   # 应 PASS

# 工作区状态
git status --short | grep '^??' | wc -l    # 应 ≥ 3（3 新文件 + 可能 _spikes 目录）
git status --short | grep -E '^.M' | wc -l  # 应 = 2（package.json + pnpm-lock.yaml）
git log --oneline -1   # HEAD 仍是 f5941f2
```

---

## 完成后 STOP

报告格式：

```
新增文件：
  - src/components/Badge/Badge.figma.ts (~N 行)
  - figma-data/normalized/ai-manifest.spike.json (~N 行)
  - docs/internal/_spikes/badge-vertical-slice-test.md (~N 行)
修改文件：
  - package.json + pnpm-lock.yaml (+@figma/code-connect)

typecheck: PASS / FAIL（如 FAIL，列错误前 5 行）

阻碍 / 困惑（如有）：
- 例：Badge.vue 的 type prop 值没找到 → 用 TODO 标记

工作区状态：N untracked，2 modified，0 commits
```

主 session 复审两件事：
1. .figma.ts 形态正确性（API 用法对不对、props 映射全不全）
2. 测试说明可执行性（用户能不能照着跑 3 个测试）

复审通过后由**用户**实际跑 3 个测试，验证项目目标 1:1 达成度。

**STOP。**
