# Codex Prompt: T1a — Audit-only：component-token-fidelity 全库报告

> v2 plan 第一刀。**audit only**：写一个新审计脚本 + 跑一次产报告。**不修任何代码**。**不 commit**。
>
> 你的下一步 (T1b) 才动组件本体。审计与修复必须分两轮。

---

## 上下文（必读，按顺序）

1. **`docs/internal/long-term-plan-v2-2026-04-29.md`** — 真源路线图。你正在执行的 T1a 定义在 76–93 行；工作纪律在 22 行（no-commit until milestone）。读完整份。
2. **`docs/internal/component-theme-implementation-audit-2026-04-29.md`** — 11 个 theme-axis 组件的 Class A/B/C 分类，是 T1a 的最直接前置。Progress 已被人工标为 "伪主题"（dark/light figma fills 完全相同但代码自创视觉差异）；Slider / Rating 是 figma-data 缺；Tooltip 命名映射未登记。本任务要把这种人工审计**机器化**到全库。
3. **`docs/internal/_prompts/phase-6.3.5-canonical-reverse-audit.prompt.md`** — 上一份 audit-only prompt 的样板格式，你可以参考结构（**但纪律不同：phase-6.3.5 让 Codex 自己 commit；T1a 不能 commit**）。
4. **`figma-data/normalized/canonical-components.json`** — 组件 axes 真源（`properties` 字段下每 axis 的可选值）。
5. **`figma-data/normalized/components-tokenized/*.json`** — 每组件每 variant 的 `fills[*].hex` 和 `fills[*].token.cssVar`。这是 figma 侧的对比锚点。
6. **`figma-data/normalized/variables.json`** — CSS variable → hex 解析表。
7. **`src/styles/variables.css`** — CSS variable 在代码侧的真源（验证 cssVar 实际存在）。
8. **`figma-sync/audit-component-token-linkage.mjs`** — 已有的 hex→token 反查脚本。**参考其 helper（`buildVariableLookup` / `resolveVariableId` / `toLowerHex` / `normalizeVariableId`）**，但不要直接复用其打分逻辑——本任务方向相反（是 code↔figma 双向 fidelity，不是 hex 候选反查）。
9. **`src/components/<X>/<X>.vue` 和 `src/canonical/<X>.vue`** — 组件本体，CSS resolved value 来源。

---

## 任务范围

**全库审计**——不仅限于 audit 报告里的 11 个 theme-axis 组件。

入选条件：组件在 `figma-data/normalized/components-tokenized/` 有 normalized JSON **且** 在 `figma-data/normalized/canonical-components.json` 有登记。

入选后，对**每个 variant**做 fidelity 比对。

**审计维度**（v2 plan 第 84–88 行）：
- theme axis（dark / light，或 darkTheme=on/off）
- status axis（default / error / success / warning / disable / hover / click ...）
- size axis（XL / L / M / S / XS）
- 颜色 token：fill / track / text / icon
- dimensions：normalized JSON 里 `h` / `w` / `r` / `gap` / `pH` / `pV` 等有明确值的几何字段

---

## 任务 1：写脚本 `figma-sync/audit-component-token-fidelity.mjs`

### 输入
- `figma-data/normalized/canonical-components.json`
- `figma-data/normalized/components-tokenized/*.json`（遍历所有文件）
- `figma-data/normalized/variables.json`
- `src/styles/variables.css`
- `src/components/**/*.vue` 和 `src/canonical/*.vue`

### 算法（核心步骤）

对每个 normalized component JSON 文件：

1. **解析组件身份**：从 `figmaName` + `componentSetId` 反查 `canonical-components.json` 的对应 canonical 组件名（`Progress` / `Notification` / `Tooltip` ...）。如果找不到，标 "未登记 component"，记到异常项。

2. **遍历 `variants[]`**：对每条 variant
   - 解析 `variant.name`（形如 `theme=dark, status=default, size=M`）成原始 axis tuple
   - **应用全局 axis 别名标准化**——脚本里硬编码以下**项目级真源约定**（用户 2026-04-29 明确）：

     ```
     darkTheme=on  → theme=dark
     darkTheme=off → theme=light
     ```

     这是**全局规则**，适用于所有组件。理由：项目站点 ThemeProvider 切换时，所有 `darkTheme=on/off` 组件应跟随站点 theme 渲染对应主题——`darkTheme` 跟 `theme` 是同一个轴的两套命名。

     **后续所有比对（步骤 3、4）都使用标准化后的 axis tuple**（即 `theme=dark/light`），不使用原始 `darkTheme=on/off`。

     ⚠️ **这是临时硬编码**——T1b 第一件事必须把这条全局别名登记到 `src/design-system/translation/prop-aliases.md`，之后脚本应从该文件读，不再硬编码。在脚本里给这段加注释：`// TODO(T1b): 移到 prop-aliases.md 后从文件读`。

   - 收集 figma 侧 "关键值"集合：
     - 容器 `fills[]`（取每个 fill 的 `hex` 和 `token.cssVar`）
     - 容器 `strokes[]`
     - `text.fills[]`（如有 text 子结构）
     - 子层 fills（如 normalized JSON 里有嵌套结构）
     - 几何字段：`h` / `w` / `r` / `gap` / `pH` / `pV` / `pR` / `pB`
   - 对每个 fill 字段标 `kind`（`fill` / `text` / `stroke` / `track` / `icon`）和 `axisKey`（如 `theme=dark&status=default&size=M`）

3. **定位代码侧 selector**：
   - 在 `src/components/<canonical>/<canonical>.vue` 和 `src/canonical/<canonical>.vue` 里，找出对应 axis tuple 的 CSS 选择器或 computed class
   - 模式示例：`<comp>--<theme>` / `<comp>--<status>` / `<comp>--<size>` 的组合，或多 class 拼接（参考 audit 报告里 Progress 的实现 `progress--${theme}` / `progress--light`）
   - 提取该 selector 在 CSS 块里的属性值（`background` / `color` / `border-color` / `width` / `height` / `border-radius` ...）
   - **axis 别名查询**（针对 darkTheme/theme **以外** 的其它命名差）：按以下顺序查别名真源：
     1. `src/design-system/translation/prop-aliases.md`
     2. `src/design-system/translation/divergences.md`
     - 如果两份文件都没登记此映射 → finding 里标 `⚠️ axis-name-unregistered`，evidence 写明"figma axis=<X>，code prop=<Y>，两份文件均未登记"。
     - 报告"推荐处置"段自动建议：T1b 登记到 `prop-aliases.md`。
     - 注：`darkTheme=on/off ↔ theme=dark/light` 已在步骤 2 全局标准化，**不会**走到这里。

4. **fidelity 判定**——对每个 figma 关键值，找代码侧对应属性，按下表分类：

| 情况 | 标记 | 含义 |
|---|---|---|
| code 用 `var(--X)` 且 figma `token.cssVar === '--X'` | ✅ token-match | token 一致 |
| code 用 `var(--Y)` 且 figma `token.cssVar === '--X'` | ⚠️ token-mismatch | 用了不同 token |
| code 用 hex literal 且 hex == figma hex | ⚠️ literal-but-equal | 视觉对但绕过 token |
| code 用 hex literal 且 hex != figma hex | ⚠️ visual-drift | 真实视觉漂移 |
| figma `token.cssVar` 存在但代码没在该 axis 实现该属性 | ⚠️ axis-branch-missing | 组件本体没实现该轴 |
| figma axis 名（如 `darkTheme`）vs code prop 名（如 `theme`）不一致，且 prop-aliases.md / divergences.md 均未登记 | ⚠️ axis-name-unregistered | 命名映射债务（Tooltip 案例） |
| figma 该 variant 缺 fills 数据（如 Slider/Rating） | ❌ figma-data-missing | pipeline 缺数据 |
| figma `token.cssVar` 指向的 cssVar 在 `variables.css` 不存在 | ❌ token-broken | token 链路断 |
| figma 两个 axis 值的 fills 完全相同（伪差异） | ⚠️ figma-no-real-axis-diff | 类似 Progress 案例 |

**关键：⚠️ figma-no-real-axis-diff 检测**——对同组件在 axis 上多个值的 fills 集做比较（dark vs light、各 status 之间），若 fills 完全相同但代码侧实现了视觉差异 → 标记为代码自创视觉（Progress 案例）。

5. **聚合输出**：每组件每 variant 一条结果，每条结果含若干 fidelity findings（每个 fill / dimension 一个 finding）。

### 输出
- 报告：`docs/internal/component-token-fidelity-report.md`（人读）
- 中间 JSON：`figma-data/normalized/component-token-fidelity.audit.json`（机读，给 T1b 修复脚本消费）

### 不要做
- ❌ 不要尝试用 headless 浏览器 / runtime DOM 来解析 CSS——直接静态读 `.vue` 文件的 `<style>` 块即可
- ❌ 不要假装能比对所有 figma 字段——对 normalized JSON 里没有 `token.cssVar` 也没有 `hex` 的 fill，标 `figma-data-missing` 跳过
- ❌ 不要在脚本里写"已知豁免"硬编码（如"Progress dark/light 同色是已知问题"）——让算法发现，让报告呈现
- ❌ 不要复用 `audit-component-token-linkage.mjs` 的打分逻辑（方向不同）——可以 import 它的 helper

---

## 任务 2：跑脚本生成报告

```bash
node figma-sync/audit-component-token-fidelity.mjs
```

报告 schema（严格按此结构写，让主 Session 能机读）：

```markdown
# Component-Token Fidelity Report

跑时间：YYYY-MM-DD HH:MM:SS
脚本：figma-sync/audit-component-token-fidelity.mjs
输入：figma-data/normalized/components-tokenized/*.json (N files)

## Summary

| 指标 | 数量 |
|---|---:|
| 入选组件总数 | N |
| Variant 总数 | N |
| Finding 总数 | N |
| ✅ token-match | N |
| ⚠️ token-mismatch | N |
| ⚠️ literal-but-equal | N |
| ⚠️ visual-drift | N |
| ⚠️ axis-branch-missing | N |
| ⚠️ figma-no-real-axis-diff | N |
| ❌ figma-data-missing | N |
| ❌ token-broken | N |

## 按组件 × variant 明细

### Component: Progress (4915:7287)

axes: theme [dark|light] × status [default|success|error|warning] × size [M|S]

| variant | finding kind | figma | code | 标记 |
|---|---|---|---|---|
| theme=dark, status=default, size=M | track-fill | `#cccccc` (--color-grey-5) | `var(--bg-layer3)` | ⚠️ token-mismatch |
| theme=dark, status=default, size=M | text-fill | `#cccccc` (--color-grey-5) | `var(--text-tips)` | ⚠️ token-mismatch |
| theme=light, status=default, size=M | (axis-diff check) | dark==light fills | code 实现了 `progress--light` | ⚠️ figma-no-real-axis-diff |
| ... | ... | ... | ... | ... |

### Component: Notification (...)
...

（每组件一段）

## 异常项

- 组件 X：normalized JSON 缺 fills（Slider/Rating 已知）
- 组件 Y：在 canonical-components.json 找不到对应登记
- 组件 Z：vue 文件 selector 解析失败
- ...

## 推荐处置（仅建议，不修复）

**T1b 头号任务（先于其它）**：把硬编码在脚本步骤 2 的全局别名 `darkTheme=on/off ↔ theme=dark/light` 登记到 `src/design-system/translation/prop-aliases.md`，并改脚本从该文件读，删除硬编码 + 删除 `// TODO(T1b)` 注释。这是项目级真源约定，不是单组件特例，覆盖所有 Class B 5 组件 + Tooltip。

| 组件 | 建议归入 T1b 哪类 | 理由（指向上面具体行） |
|---|---|---|
| (全局) | **T1b 头号**：登记 darkTheme↔theme 全局别名 | 见上方说明 |
| Progress | T1b：CSS 改成消费 figma fills | 大量 token-mismatch + figma-no-real-axis-diff（伪主题） |
| Slider | T1b：补 figma-data pipeline 提取 | 大量 figma-data-missing |
| Rating | T1b：补 figma-data pipeline 提取 | 大量 figma-data-missing |
| ... | ... | ... |
```

中间 JSON schema（`figma-data/normalized/component-token-fidelity.audit.json`）：

```json
{
  "auditedAt": "ISO 时间戳",
  "scriptVersion": "1.0",
  "summary": { "✅ token-match": N, "⚠️ token-mismatch": N, ... },
  "components": [
    {
      "componentName": "Progress",
      "componentSetId": "4915:7287",
      "axes": { "theme": ["dark","light"], "status": [...], "size": [...] },
      "variants": [
        {
          "variantName": "theme=dark, status=default, size=M",
          "axisTuple": { "theme": "dark", "status": "default", "size": "M" },
          "findings": [
            {
              "kind": "track-fill",
              "figma": { "hex": "#cccccc", "cssVar": "--color-grey-5" },
              "code": { "selector": ".progress__track", "value": "var(--bg-layer3)", "filePath": "src/components/Progress/Progress.vue", "line": 71 },
              "verdict": "⚠️ token-mismatch"
            }
          ]
        }
      ]
    }
  ],
  "anomalies": [...]
}
```

---

## 任务 3：自验

```bash
# 脚本可重跑（幂等）
node figma-sync/audit-component-token-fidelity.mjs
node figma-sync/audit-component-token-fidelity.mjs

# 报告 / JSON 存在且可解析
ls -la docs/internal/component-token-fidelity-report.md
wc -l docs/internal/component-token-fidelity-report.md
node -e "console.log(Object.keys(JSON.parse(require('fs').readFileSync('figma-data/normalized/component-token-fidelity.audit.json','utf8'))).join(','))"

# 第二次跑后 git diff 应只有时间戳变化
git diff docs/internal/component-token-fidelity-report.md | head -20

# 不要跑 pnpm test / build — 本轮无源码改动
```

**预期 sanity check**（用人工 audit 报告交叉验证）：
- Progress 必须出现 ⚠️ figma-no-real-axis-diff（dark==light fills）
- Slider / Rating 必须出现大量 ❌ figma-data-missing
- Notification / FormItem / Tooltip 应主要是 ✅ token-match
- Class B 5 组件（Input / CheckBox / Radio / Switch / Select）的 figma `darkTheme=on/off` 经过步骤 2 全局别名标准化后应作为 `theme=dark/light` 跟 code 站点 theme 实现比对——**不应**因为命名差异被标 `axis-name-unregistered`。如果出现，说明步骤 2 标准化没生效，是 bug。

如果跑出来跟人工 audit 对不上，**先报告差异原因，不要为了对上而调整算法**——可能是人工 audit 错了，也可能是机器 audit 有 bug，让主 Session 判定。

---

## 任务 4：⚠️ 不要 commit

**v2 plan 工作纪律 1**（[long-term-plan-v2-2026-04-29.md:22](../long-term-plan-v2-2026-04-29.md#L22)）：

> No-commit until milestone：所有 Codex prompt 严禁 `git add` / `git commit`。所有改动累积到 dirty 工作区，最终 M1 一刀 commit。

本轮产物（4 个文件）**全部留在 dirty 工作区**：
- `figma-sync/audit-component-token-fidelity.mjs`（新脚本）
- `docs/internal/component-token-fidelity-report.md`（报告）
- `figma-data/normalized/component-token-fidelity.audit.json`（机读中间产物）
- `docs/internal/_prompts/t1a-audit-component-token-fidelity.prompt.md`（本 prompt 已存在，不要动它）

**禁止**：
- ❌ `git add`
- ❌ `git commit`
- ❌ `git push`
- ❌ `git stash`
- ❌ 任何对 `.git` 的写操作

跑完 `git status` 一次即可，给主 Session 看一眼工作区状态，但不要清理它。

---

## 总禁止清单

- ❌ 不修任何 `src/**/*.vue` / `src/**/*.ts` / `src/**/*.css`
- ❌ 不修 `prop-aliases.md` / `divergences.md` / `runtime-additions.md`（即使审出明显该补登记，只在报告"推荐处置"里写）
- ❌ 不修 `figma-data/raw/*`（始终只读）
- ❌ 不修 `figma-data/normalized/*.json`（除新增 `component-token-fidelity.audit.json` 外）
- ❌ 不修任何已有的 `figma-sync/*.mjs`（只新增 `audit-component-token-fidelity.mjs`）
- ❌ 不要把 T1b 修复 sneak 进本轮（即使诱惑很大）
- ❌ 不要"看起来 OK 就跳过 variant"——所有 variant 必须出现在 finding 集合里
- ❌ 不要假数据 / 不要硬编码已知豁免——让算法跑出来什么就是什么
- ❌ 不 commit / 不 push（见任务 4）

---

## 完成后 STOP

跑完 + 自验通过后，输出给主 Session 这几个数字：

```
入选组件总数：N
Variant 总数：N
✅ token-match：N
⚠️ token-mismatch：N
⚠️ literal-but-equal：N
⚠️ visual-drift：N
⚠️ axis-branch-missing：N
⚠️ figma-no-real-axis-diff：N
❌ figma-data-missing：N
❌ token-broken：N

报告路径：docs/internal/component-token-fidelity-report.md
JSON 路径：figma-data/normalized/component-token-fidelity.audit.json
脚本路径：figma-sync/audit-component-token-fidelity.mjs

工作区状态（git status 摘要）：
  modified: 0
  untracked: 3 (脚本 + 报告 + JSON)
```

主 Session 接到数字后，决定 T1b 怎么拆（按组件 / 按问题类型）。

**STOP。不要继续 T1b。不要 commit。**
