# Tier 1-A — Translation Schema 化 + audit:translation-completeness Spec

> **作者**：plan owner (Claude Code)
> **日期**：2026-05-14
> **状态**：proposal — STOP，等用户审。
> **对齐**：PROJECT_GOAL 能力 5（溯源 schema 化）；tracker §轨道 C Tier 1-A；STATUS.md v0.3.0 主线。
> **估时**：~1.5 周（4 个 sprint，按 baseline-before-plan memory，sprint 1 实跑后再 calibrate）

---

## 0. 关键发现 vs tracker 描述差异

tracker §轨道 C Tier 1-A 写：

> `translation/*` JSON + JSON Schema + audit:translation-completeness L5 gate（5 个 SoT 统一）

实际 baseline（plan owner 6 文件全 read 后）：

| 文件 | 体积 | 现状 | 是否适合直接 JSON 化 |
|---|---:|---|---|
| `axis-implementation-map.md` | 19.7KB | 已有明确 schema（章节 3 字段定义）+ 16 instance | ✅ **是** — 最简单 |
| `prop-aliases.md` | 14.6KB | 11 table + 3 段叙述（单值 axis 原则 / 历史 / 处理规则） | ⚠️ **拆** — table 转 JSON，叙述留 md |
| `divergences.md` | 15.6KB | 9 个大 section，**主要是叙述性 "为什么"** + status enum | ⚠️ **拆** — decisions matrix 抽 JSON，rationale 留 md |
| `icon-aliases.ts` | 3.6KB | typed TS（3 const + ICON_DEFAULT_COLOR_BINDING） | ❌ **不动** — TS 比 JSON 更 typed |
| `token-aliases.ts` | 8.1KB | typed TS（双向 mapping ~60 entries） | ❌ **不动** — 同上 |
| `figma-extract-rules.draft.md` | 7.1KB | Draft，未启用 | 🟡 **决策点**（见 §6） |

**关键 insight**：

1. tracker 写"5 SoT 统一"，实际是 6 个文件 + 1 draft；真正"无 schema"的只有 3 个 md
2. **强行全 md → JSON 会丢失 narrative rationale**（如 divergences.md "Figma SVG export pipeline limitation" 段是 designer 实证调研结论，转 JSON 会失上下文）
3. **正确路径是 "split"**：把 JSON-able 结构数据抽出来给 audit 消费，narrative 留 md 给人类读

---

## 1. 5 维 SoT 目标形态（拆分后）

```
src/design-system/translation/
├─ axis-implementation-map.json          ← 新建（替代 .md）
├─ prop-aliases.json                     ← 新建（11 table 抽取）
├─ prop-aliases.md                       ← 瘦身保留（仅 narrative）
├─ divergences-decisions.json            ← 新建（status enum entries）
├─ divergences.md                        ← 保留（narrative + rationale）
├─ icon-aliases.ts                       ← 不动
├─ token-aliases.ts                      ← 不动
├─ schemas/                              ← 新目录
│  ├─ axis-implementation-map.schema.json
│  ├─ prop-aliases.schema.json
│  └─ divergences-decisions.schema.json
└─ figma-extract-rules.draft.md          ← 见 §6 决策点
```

**真源数**：3 JSON + 2 md + 2 TS = **7 个文件**（更准确反映现状）。

---

## 2. 各 JSON Schema 草案

### 2.1 `axis-implementation-map.json`

直接照搬 `axis-implementation-map.md` 章节 3 的字段定义：

```json
{
  "$schema": "./schemas/axis-implementation-map.schema.json",
  "version": 1,
  "instances": [
    {
      "id": "button-color-palette",
      "figmaComponent": "Button",
      "figmaComponentSetIds": ["1545:51854", "1545:51964", "1545:57246", ...],
      "figmaAxis": "color",
      "figmaValue": "*",
      "codeImplementationLayer": "vue-computed-palette",
      "codeFile": ["src/components/Button/Button.vue"],
      "codeAnchor": "palette computed: 262 / buttonVars computed: 365 / ...",
      "verifyHint": "grep -n \"const palette\\|const buttonVars\" src/components/Button/Button.vue",
      "notes": "palette 按 props (color, status, theme, style) 输入算出 ..."
    }
  ]
}
```

**Schema 字段** (axis-implementation-map.schema.json)：

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["version", "instances"],
  "properties": {
    "version": { "const": 1 },
    "instances": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["id", "figmaComponent", "figmaComponentSetIds", "figmaAxis", "figmaValue", "codeImplementationLayer", "codeFile", "codeAnchor"],
        "properties": {
          "id": { "type": "string", "pattern": "^[a-z0-9-]+$" },
          "figmaComponent": { "type": "string" },
          "figmaComponentSetIds": { "type": "array", "items": { "type": "string", "pattern": "^[0-9]+:[0-9]+$" } },
          "figmaAxis": { "type": "string" },
          "figmaValue": { "type": "string" },
          "codeImplementationLayer": {
            "enum": ["css-class", "vue-prop", "vue-computed-palette", "css-custom-property-chain", "cross-component-topology"]
          },
          "codeFile": {
            "oneOf": [
              { "type": "string" },
              { "type": "array", "items": { "type": "string" } }
            ]
          },
          "codeAnchor": { "type": "string" },
          "verifyHint": { "type": "string" },
          "notes": { "type": "string" }
        },
        "additionalProperties": false
      }
    }
  }
}
```

### 2.2 `prop-aliases.json`

提取 prop-aliases.md 11 个 table 的统一 schema（所有 table 共享相同字段）：

```json
{
  "$schema": "./schemas/prop-aliases.schema.json",
  "version": 1,
  "entries": [
    {
      "scope": "component-prop",
      "component": "PromptMessage",
      "codeName": "closable",
      "figmaName": "showCloseIcon",
      "status": "approved-alias",
      "notes": "Vue-ecosystem naming wins. ..."
    },
    {
      "scope": "vue-ecosystem-addition",
      "components": ["Input", "InputNumber", "CheckBox", "Radio", "Switch", "Select"],
      "codeName": "modelValue",
      "figmaName": null,
      "status": "vue-ecosystem",
      "notes": "Vue v-model 标准契约"
    },
    {
      "scope": "global-axis-alias",
      "axisFromFigma": "dark theme",
      "axisToCanonical": "theme",
      "status": "approved-alias",
      "notes": "figma 原始命名（含空格）"
    },
    {
      "scope": "boolean-property",
      "component": "CheckBox",
      "codeName": "disabled",
      "figmaName": "enable",
      "status": "inverted-alias",
      "notes": "canonical disabled = !figma.enable"
    }
  ]
}
```

**Status enum**（Sprint 2 实证 12 值，✅ 表示 spec 草案预测，🆕 表示 sprint 2 实跑新发现）:
- ✅ `approved-alias` / `exact-match` / `vue-ecosystem` / `runtime-addition` / `axis-alias` / `inverted-alias` / `match` / `named-slot` / `approved-value-alias` / `n-a`
- 🆕 `approved`（T11 composition-component 用，区别于 `approved-alias`）
- 🆕 `bridge-tier3-dep`（T13 composition-button-blocked 用，对应 md `⏳ BRIDGE-Tier3 dep`）

**Scope enum**:
- `component-prop` / `vue-ecosystem-addition` / `component-naming` / `value-alias` / `global-axis-alias` / `axis-value-alias` / `boolean-property` / `slot-property` / `sub-component-composition`

### 2.3 `divergences-decisions.json`

只抽 divergences.md 中**有明确 status 字段**的 entry，叙述段留 md：

```json
{
  "$schema": "./schemas/divergences-decisions.schema.json",
  "version": 1,
  "decisions": [
    {
      "id": "notification-success-deletion",
      "category": "resolved",
      "component": "Notification",
      "subject": "success status",
      "figmaSide": "no success status in published component set",
      "codeSide": "historical runtime-only status",
      "status": "✅ resolved",
      "resolvedAt": "2026-04-28",
      "resolutionRef": "Phase 6.3",
      "verifyHint": "grep -rE \"['\\\"]success['\\\"]\" src/components/Notification/ → 0 hits"
    },
    {
      "id": "formitem-label-dual-form",
      "category": "dual-form-mapping",
      "component": "FormItem",
      "subject": "label prop ↔ Label SLOT",
      "status": "approved-dual-form-mapping",
      "phase": "Phase 6.6"
    },
    {
      "id": "badge-api-split",
      "category": "api-split",
      "component": "Badge",
      "subject": "type → color + shape",
      "status": "approved-api-split",
      "phase": "Phase 6.7"
    },
    {
      "id": "input-dark-theme-axis",
      "category": "accepted-divergence",
      "components": ["Input", "CheckBox", "Radio", "Switch", "Select", "Tooltip"],
      "subject": "Figma dark theme property",
      "reason": "设计态主题预览开关；运行时主题由全局 ThemeProvider / CSS 变量处理",
      "status": "accepted-documented-divergence"
    }
  ]
}
```

**Category enum**:
- `resolved` / `dual-form-mapping` / `api-split` / `migration-plan` / `accepted-divergence` / `figma-pipeline-limitation` / `code-side-dual-source`

---

## 3. audit:translation-completeness 设计

L5 strict gate（10th in pipeline）。**5 维检查**：

### 维度 1：figma-data → translation 覆盖率（forward）

每个 `figma-data/normalized/figma-components.json` 列出的 component / axis / property：
- 在 `axis-implementation-map.json` 是否有 instance？
- 在 `prop-aliases.json` 是否有 entry？
- → 缺失 = ⚠️ `unmapped-figma-source`

每个 `figma-data/normalized/figma-tokens.json` 列出的 token name：
- 在 `token-aliases.ts FIGMA_TOKEN_TO_CODE_TOKEN` 是否有 key？
- → 缺失 = ⚠️ `unmapped-figma-token`

### 维度 2：translation → code 反向一致（backward）

- `prop-aliases.json` 每条 `component-prop` entry 的 `codeName`：在对应 `src/canonical/<Component>.vue` defineProps 是否存在？
- `axis-implementation-map.json` 每条 instance 的 `verifyHint`：跑通 → ✅；fail → ⚠️ `stale-anchor`
- `icon-aliases.ts ICON_ALIAS_TO_FIGMA_NAME` 每个 value：在 `figma-data/normalized/figma-icons.json` 是否真存在？
- `token-aliases.ts FIGMA_TOKEN_TO_CODE_TOKEN` 每个 key：在 `figma-data/normalized/figma-tokens.json` 是否真存在？

### 维度 3：JSON Schema 验证

3 个 JSON 文件按 schemas/*.schema.json 验证。任何 schema 违反 → fail。

### 维度 4：divergences resolved 实证

`divergences-decisions.json` 标 `"category": "resolved"` 的 entry：跑 `verifyHint`（grep 表达式）实证 code 中已 resolved。

例：Notification.success → `grep -rE "['\"]success['\"]" src/components/Notification/` 必须 0 hits。

### 维度 5：双向一致性（token-aliases.ts）

已有 invariant：`FIGMA_TOKEN_TO_CODE_TOKEN` 与 `CODE_TOKEN_TO_FIGMA_TOKEN` 互为逆映射。audit 验证：

```js
for (const [figmaName, codeToken] of Object.entries(FIGMA_TOKEN_TO_CODE_TOKEN)) {
  if (CODE_TOKEN_TO_FIGMA_TOKEN[codeToken] !== figmaName) → fail
}
```

### Allowlist 机制

新建 `figma-sync/audit-allowlist/translation-completeness.json`（与既有 `dimension-tokens.json` 同目录），已知 unmapped 项白名单（如 Figma 库内部 layout container `Account Name` / `Time Zone` 不映射）。

格式：按 finding 类型分组的对象（不是 flat string[]，因 audit-translation-completeness 有 ~5 类 finding 需区分）：

```json
{
  "unmapped-figma-source": ["Account Name", "Time Zone", "..."],
  "unmapped-figma-token": [],
  "stale-anchor": [],
  "stale-code-anchor": [],
  "regression": []
}
```

实证：`figma-sync/audit-allowlist/dimension-tokens.json` 当前是 `[]` 空数组（flat string[]，audit-no-hardcoded-design-tokens 用 Set<rel:line:value>）。本 audit 因 finding 类型多采用对象式。

### 升 strict gate 时机

✅ 直接 strict（10th gate）— 避免 v0.1.0/v0.1.1 silent fail 复现。warn-only 一周再升的 incremental 路径 ROI 不高（本 audit 不影响 publish artifact，仅 fail CI）。

### AJV import 注意（Sprint 1 实证补）

AJV 8.x 默认 `draft-07`，本项目 schema 用 `https://json-schema.org/draft/2020-12/schema`。**必须**用专门 2020 export：

```js
const Ajv2020 = require('ajv/dist/2020.js').default;  // 不是 require('ajv')
const ajv = new Ajv2020({ strict: true });
```

Sprint 2/3 schema + Sprint 4 audit 实现统一用此 import。

---

## 4. 分 sprint 拆解（4 个 executor prompt）

按风险升序 + 依赖关系。每 sprint 1 prompt，fire 后 STOP 等 plan owner 验。

### Sprint 1 — axis-implementation-map JSON 化（~1 day，最简单）

**为什么先做这个**：
- 已 schema-ready（md 章节 3 已给明确字段）
- 16 实例已结构化
- 只 1 个文件，blast radius 小
- baseline-before-plan：跑完后 calibrate sprint 2-4 估时

**Deliverables**：
1. 新 `src/design-system/translation/axis-implementation-map.json` — 16 实例转 JSON
2. 新 `src/design-system/translation/schemas/axis-implementation-map.schema.json`
3. 归档 `axis-implementation-map.md` → `docs/internal/_archive/translation-md-pre-schema/` 保历史
4. grep 所有现有 audit 脚本对 `axis-implementation-map.md` 的引用 → 改读 JSON
5. Verify：现有 `pnpm audit:*` 全跑通

**Executor prompt**：`docs/internal/_prompts/tier1a-sprint1-axis-impl-json.prompt.md`

### Sprint 2 — prop-aliases.md split（~2-3 days）

**Deliverables**：
1. 新 `prop-aliases.json` — 抽 11 table 全部 entries
2. 新 `schemas/prop-aliases.schema.json`
3. 瘦身 `prop-aliases.md` — 仅保留：
   - `## Figma axis 处理规则` 段（单值 axis 跳过原则 + 实施位置 + 实证案例）
   - 各表头 + "详见 prop-aliases.json" 链接
   - 历史段（T1a / T1b / T2 sample / Phase A0 cleanup）
4. grep 所有现有 audit 脚本引用 → 改读 JSON
5. Verify：现有 audit 跑通；prop-aliases.json schema 验证通过

**Executor prompt**：`docs/internal/_prompts/tier1a-sprint2-prop-aliases-json.prompt.md`

### Sprint 3 — divergences.md split（~2-3 days）

**Deliverables**：
1. 新 `divergences-decisions.json` — 抽所有 status entry
2. 新 `schemas/divergences-decisions.schema.json`
3. 保留 `divergences.md`（narrative + rationale 段不动）+ 各 status entry 后加 "决议详情见 divergences-decisions.json#<id>" 链接
4. Verify：schema 验证通过

**Executor prompt**：`docs/internal/_prompts/tier1a-sprint3-divergences-json.prompt.md`

### Sprint 4 — audit:translation-completeness 实现（~2-3 days）

**Deliverables**：
1. 新 `figma-sync/audit-translation-completeness.mjs` 实现 5 维
2. 新 `figma-data/audit-allowlist/translation-completeness.json` 白名单种子
3. 新 `package.json` `"audit:translation-completeness"` script
4. `package.json` `prepublishOnly` 加入 10th strict gate
5. 升级 `RELEASING.md` gate 表 + `STATUS.md` "Release 流程" 段
6. 复盘到 `docs/internal/retrospection/2026-05-XX-tier1a-translation-schema.md`
7. tracker.md + STATUS.md wrap-up sync

**Executor prompt**：`docs/internal/_prompts/tier1a-sprint4-audit-impl.prompt.md`

---

## 5. 风险 + Mitigation

| 风险 | 概率 | 影响 | Mitigation |
|---|---:|---|---|
| Sprint 1 实跑发现 16 instance 字段有不规范的（如 codeFile 多种格式）| Medium | 估时 +1 day | schema 加 `oneOf` 容忍，必要 normalize 一轮 |
| Sprint 2 prop-aliases 11 table 字段不完全 uniform（如 GLOBAL axis alias table 字段集不一样）| High | 估时 +2 day | 用 `scope` 区分 sub-shape，允许字段 union |
| Sprint 3 divergences 9 段，部分叙述里有"隐藏" status（如 "Code 端双源 Badge" 实质是 dual-source decision 但格式不一）| Medium | 估时 +1 day | 抽时 plan owner 先列清单给用户拍板每段归类 |
| Sprint 4 audit 维度 1（forward 覆盖率）发现大量 unmapped → allowlist 爆炸 | High | 估时 +3 day | warn 一轮先拿 baseline，再决定 allowlist 范围或补 translation |
| translation/ 文件被多 session 并行修改 | Low | 冲突 | 每 sprint 前 git status 确认 0 dirty |

**估时鲁棒区间**：4-10 day 单线；按 sprint 1 实跑数据 calibrate。

---

## 6. Open Decisions（用户拍板项）

### D1：figma-extract-rules.draft.md 处理路径

| 选项 | 描述 | 推荐 |
|---|---|---|
| A: 留 draft | 不在 v0.3.0 范围 | ✅ 推荐 |
| B: promote → .md | v0.3.0 内启用 extract pipeline child-rules | ❌ 牵连 figma-sync/extract.mjs 大改 |
| C: 删除 | drift risk 但 not maintained | ❌ 失实证 |

### D2：axis-implementation-map.md 删除 vs 归档

| 选项 | 描述 | 推荐 |
|---|---|---|
| A: 归档 `_archive/translation-md-pre-schema/` | 保历史 + git log 完整 | ✅ 推荐 |
| B: 直接 rm | 依赖 git log | ❌ retrieval 不便 |

### D3：JSON 文件位置

| 选项 | 描述 | 推荐 |
|---|---|---|
| A: `translation/*.json` 与 .ts 平级 | 简单 | ✅ 推荐 |
| B: `translation/data/*.json` 单独目录 | 隔离更清晰 | ❌ 过度组织 |

### D4：schemas 位置

| 选项 | 描述 | 推荐 |
|---|---|---|
| A: `translation/schemas/*.schema.json` | 与 data 物理近 | ✅ 推荐 |
| B: 仓库根 `schemas/` | 跨场景共用 | ❌ 当前只有 translation 用 |

### D5：L5 gate 升 strict 时机

| 选项 | 描述 | 推荐 |
|---|---|---|
| A: 直接 strict（10th gate）| 避免 silent fail | ✅ 推荐 |
| B: warn-only 1 周再升 | safer rollout | ❌ ROI 低（audit 不影响 publish） |

### D6：保留 .md 详细 hint 的范围

`prop-aliases.md` 瘦身后保留哪些？
- 强制保留：`## Figma axis 处理规则` 段（单值 axis 原则有 generator 实施位置 + 实证）+ 历史段
- 可选保留：每 table 留表头 + "详见 prop-aliases.json" stub
- 可选保留：md 文件名不变（兼容现有引用）

**推荐 D6**：✅ 全保留（瘦身后 ~3-4KB），生成跟 .json 配对的 doc 体验。

---

## 7. 验收标准

整个 Tier 1-A 完成判定（v0.3.0 ship 前）：

- [ ] 7 个 SoT 文件就位（3 JSON + 2 md + 2 TS）+ 3 schema 文件
- [ ] `pnpm audit:translation-completeness` 跑通（0 fail；allowlist 已 review）
- [ ] `prepublishOnly` 含 10th strict gate
- [ ] `RELEASING.md` + `STATUS.md` + `tracker.md` 同步
- [ ] 复盘文件 ship
- [ ] 现有 9 个 strict gate 仍全 pass（无回归）
- [ ] vitest / vue-tsc 0 错（pre-commit hook v1 仍 green）
- [ ] git diff baseline：`figma-data/` 0 churn（INFRA-F30 idempotence 仍有效）

---

## 8. 完成后效应

**unblock**：
- Phase 6.6（FormItem dual-form）/ 6.7（Badge prop split）/ 6.8（Button migration）— 翻译层增改有 schema 验证
- BRIDGE-005（Figma boolean / instance-swap schema 升级）— prop-aliases.json scope 已含 `boolean-property`，加 `instance-swap-property` 即可
- T4b AI manifest（PROJECT_GOAL 能力 5 schema 化）— audit 是 manifest 生成前置
- Tier 1-B audit:mockup-conformance（v0.5.0）— 复用 5 维 audit 范式
- Tier 1-C ESLint plugin（v0.6.0）— consumer-side 自检读 prop-aliases.json

**对齐 PROJECT_GOAL**：
- 能力 5（每个 Code 组件可追溯到 Figma 来源）schema 化 — 落地
- 能力 1/2 间接受益 — translation 不再是文本，AI 工具按 schema 消费

---

## 9. Plan owner 行动清单（spec approval 后）

按用户拍板 D1-D6 → plan owner 写 Sprint 1 executor prompt → 用户 fire executor → plan owner 验收 → 进 Sprint 2 ... 4。

每 sprint 完成后**必须**：
1. 跑现有 `pnpm audit:*` 全套确认 0 回归
2. 跑 vitest + vue-tsc 确认 0 错
3. commit 含 retrospect 链接（如有学到新 pattern）
4. STATUS.md "Last updated" + tracker "已完成" sync
