# Figma `_`/`.` 命名约定 pipeline 化 + 52 components 一次性 rename — 2026-05-27

> Date: 2026-05-27（INFRA-F35 audit followup）
> Status: 4-Phase ✅ shipped (commits `5380f2fa` + `318b50ce`)
> Trigger: 用户问 "56 个 components 重新出现在 Figma published，这 56 个是什么？除了前面同步的，我后面应该没有再更新啊"

---

## 一句话

发现 sync pipeline 每次 sync 反复拉回 56 个 "Research / UX Inventory drafts" 又反复标删的 Sisyphus 循环；根因是 **Figma 端 publish 范围 ≠ 我们 pipeline 的 publish 范围**。修法是**机制化 Figma 官方 `_`/`.` 前缀约定**到 pipeline (Phase 1 治本) + 一次性 use_figma rename 52 components (Phase 2 现状清零) + manifest 补 3 漏 icon (Phase 3.a) + audit alert 防未来再犯 (Phase 4)。用户 explicit 约束 "**后续不再编辑 Figma 组件库**"，本次 Phase 2 为最后一次自动 Figma write。

---

## 事故回路 (Sisyphus loop)

```
Figma library (published)
├─ 49 Research Button drafts ("Research on \"Color\"" page)
├─ 3 UX Inventory ("UX Inventory - Micro Level" page)
└─ 4 misc icons in production "— — Icons" page but missing from published manifest

  ↓ pnpm sync:extract (无 _/. filter)

raw/components/*.json (4773 个, 含 56 noise)

  ↓ pnpm cleanup:unpublished --apply (按 pageName 过滤)

raw 净化 (4717, 删 56)

  ↓ 下次 pnpm sync:figma-library --with-extract

回到起点 (重拉 56)
```

每次 sync 16-49s 的循环耗时，diff report "+56 new components" 误报，noise level 99.9%。

---

## 根因三层

| 层 | 根因 |
|---|---|
| **L1 — 工具** | extract.mjs 不感知 Figma 命名约定 (`_`/`.` 前缀 = 不 publish)；cleanup 用 pageName 过滤是 fragile heuristic (页面改名/移动就失效) |
| **L2 — 设计师 workflow** | 历史 v2 探索 Research / UX Inventory components 没加 `_` 前缀 → 默认 published 状态被吃进 sync pipeline |
| **L3 — Doc / 团队约定** | Figma 官方 `_` 约定虽存在但**没在仓库 SoT 登记**，AI / 新成员都不知道这个 escape hatch |

---

## 4-Phase 治理方案

### Phase 1 — Pipeline 内化 Figma 命名约定（治本，commit `5380f2fa`）

| 改动 | 文件 |
|---|---|
| extract.mjs 起手 filter `figmaName.startsWith('_'\|'.')` → 不写 raw/ | `figma-sync/extract.mjs` |
| cleanup-unpublished classify defense-in-depth：旧 raw 残留前缀也归 "intentional skip" | `figma-sync/cleanup-unpublished.mjs` |
| AGENTS.md §Figma 命名约定 段（4 用途场景 + 双层 honor 机制 + 反模式）| `AGENTS.md` |
| mockup-conventions.md M-LIBRARY-HYGIENE rule | `docs/internal/mockup-conventions.md` |

**收益**：未来任何 `_Draft/...` / `_archived/...` / `_internal/...` automatic skip。0 维护。

### Phase 2 — 一次性 Figma rename 52 (use_figma，commit `318b50ce`)

用户 explicit 授权（"删除那些无用的脏数据，后续就不要再编辑 Figma 的组件库了"），通过 `mcp__claude_ai_Figma__use_figma` 并行 2 个 page rename：

- **49 on "Research on Color"** (page id `851:44542`) — 12 Button set roots × 2 nodeId series (1459/1497) + 36 variant children "Property 1=enable/hover/press" + 1 icon/Edit/Add 1
- **3 on "UX Inventory - Micro Level"** (page id `0:1`) — After selecting R wi / auto recover / Home

操作：`node.name = '_' + node.name`，全 52 件 0 skipped 0 errors。Figma file 视觉位置 / 子结构 / instance 引用全保留（rename instance 自动跟随 main component）。

**Important**: 这是 last 自动 Figma library write，未来的 cleanup 都走 designer 手动 + audit alert。

### Phase 3 — 状态清理

- **3.a Published icon manifest 补 3 漏**：`design-system-candidates.json` 是空的（stale），跑 `generate:candidates` 重生 (643 iconAssets including the 3) → `sync:icons` regenerate manifest (642→643)。3 icons 现入 publish manifest 不再被 cleanup 标"图标不在 published manifest"
- **3.b Icon Guides 16*16**：user 已 Figma 端 delete，pending publish → 一次 publish 后下次 sync 自动消失
- **3.c 36 dup candidates** (1 exact `Drop down List/Item` + 35 fuzzy 如 icon/input/NDI vs icon/indicator/NDI rename leftover)：留 auto-generated [`cleanup-dup-candidates.md`](../cleanup-dup-candidates.md) 让 designer 在 Figma 端慢慢 restrict-publish 旧版本

### Phase 4 — Self-healing audit（commit `318b50ce`）

新 script `figma-sync/audit-figma-naming-hygiene.mjs` 检测：
- pageName **NOT** 以 `— —` 开头 (非生产页)
- AND figmaName **不**以 `_`/`.` 开头

→ 报警 designer review（输出 `docs/internal/figma-naming-hygiene-report.md`），**read-only，永不自动改 Figma**（用户约束）。

Wired as sync-figma-library Step 12 非阻塞。当前 0 violations（Phase 2 rename 后所有违规已清）。

### 额外 — raw orphan-purge in extract.mjs

Phase 2 rename 后 raw 出现 52 个 stale 文件（filename = 旧名 `light_filled_btn__1459_77724.json`，但 figmaName 已变 `_Light/filled btn`，extract filter 不再 rewrite）。

extract.mjs 末尾加 orphan-purge：
- 走 readdirSync(raw/components/)
- 文件 filename 不在当前 published filename set → 候选
- **Safeguard**：nodeId **不**在 `components` (filtered published set，**不**含 `_` skip) → 安全 purge
  - 走 `components` 而非 `allComponents` 让"rename for `_` 前缀"场景能清旧 file
  - 同时保留"rename 中间态同 nodeId 两 filename 都还发布"的 case

实测：first run 7 purged (太保守 — safeguard 用 allComponents bug)；改 safeguard 后 52 purged ✅。

---

## 教训

### 教训 1 — Figma 官方约定一定要 mirror 到我们 pipeline

`_`/`.` 前缀 = 不 publish 是 Figma 原生 + 跨工具行为，但我们 sync pipeline 没 honor → 制造 Sisyphus loop。**任何"Figma 端原生约定"都应该在我们 pipeline 等价 mirror**（不光是 manifest data，也包括 naming convention / page name 约定 / 发布机制 / restrict-publishing 状态等）。

机制化：审 figma-sync/extract.mjs 看还有什么 Figma 端语义没 honor。

### 教训 2 — User 约束"不再写 Figma" + 仍能自动化 cleanup

矛盾 resolution：Phase 2 是**一次性 last automated write**（用户 explicit 例外授权清现状），Phase 4 audit 是**read-only alert**（永远不自动改）。未来 cleanup 入口由 designer 手动 + audit 报警，AI 不再触碰。这种"一次性 exception + 永久 read-only" 模式是 destructive automation 的合理边界。

### 教训 3 — Safeguard 误防忽略真意图（safeguard inversion）

第一版 raw orphan-purge safeguard 用 `allComponents`（含 `_` skip）→ 把"被 filter 的 nodeId 仍当成 published"→ 该 purge 的 stale 文件被错误保留。

正解：safeguard 用 `components` (filtered set，纯 published) — `_` 重命名场景 nodeId 不在 set → 触发 purge；rename 中间态同 nodeId 两 filename 都还 published → safeguard 兜底保留。

通用 takeaway：**safeguard 集合定义要跟"已发布" 1:1 对齐，不能把"被显式排除的"当成"发布的"**。

---

## Artifacts

### Commits

```
318b50ce  feat(figma-sync): Phase 2-4 — Figma rename 52 + manifest fix + naming-hygiene audit
5380f2fa  feat(figma-sync): honor Figma `_`/`.` prefix convention — pipeline + docs
```

### 新增文件

| 文件 | 类型 |
|---|---|
| `figma-sync/audit-figma-naming-hygiene.mjs` | 新 audit script (Phase 4) |
| `docs/internal/figma-naming-hygiene-report.md` | auto-gen audit 报告 |
| `docs/internal/retrospection/2026-05-27-figma-naming-convention-and-rename.md` | 本复盘 |

### 修改文件

- `figma-sync/extract.mjs` — `_`/`.` filter + raw orphan-purge with safeguard
- `figma-sync/cleanup-unpublished.mjs` — classify defense-in-depth
- `figma-sync/sync-figma-library.mjs` — Step 12 audit wiring + docstring 13-step
- `AGENTS.md` — §Figma 命名约定 段
- `docs/internal/mockup-conventions.md` — M-LIBRARY-HYGIENE rule
- `docs/internal/backlog.md` — INFRA-F35 audit followup trail
- `figma-data/published/icons/manifest.json` — 642 → 643
- `figma-data/published/icons/svg/*.svg` — 643 regenerated
- `package.json` — `pnpm audit:figma-naming-hygiene` script

### Figma 端

- 52 components renamed (49 + 3) with `_` prefix
- 0 instance breakage (rename auto-propagates)

---

## 下一步

不需要自动化追加。剩余 cleanup 是 designer 手动：

1. Icon Guides 16x16 → user 已删，记得 publish
2. 36 dup candidates → designer 看 `cleanup-dup-candidates.md` 抽空在 Figma 端 restrict-publish 旧版本（特别是 NDI / RTMP push pull / RTSP push pull / SRT caller listener / SDI / MXL / ISSP / Kick / Microsoft / Facebook 这 10 对 input/* → indicator/* rename leftover）
3. 任何未来新建调研 / 临时 component → 直接 `_Draft/<name>` / `_Research/<name>` 命名，自动 honor

如再次发现 Figma 端 publish 状态 ≠ 我们 pipeline 预期 → 跑 `pnpm audit:figma-naming-hygiene` 看 report；不要再自动 Figma write。
