# 2026-06-01 — affordance SoT 分片重构 + 并行同工作树纠缠

> commit `f1e63e3b`（拆分 + loader + 迁移）/ `47fe013a`（wrap-up）。
> 触发 retrospect 的原因：两条可复用工程经验（loader-shard 模式 / 并行 session 纠缠识别），不是单纯完成一个 backlog entry。

---

## 任务

`docs/internal/affordance-categories.json`（affordance/synonym 层 SoT，14798 行 / 644 icon 条目）太大。按 Figma 图标文件夹（`path` 顶层段，28 个）拆分。

---

## 经验 1 — Loader-Shard 模式（可复用于任何过大 JSON SoT）

把单文件 SoT 拆成目录时，**不要让分片变成"第二份真源"**。做法：

1. **目录 = 单一逻辑 SoT**：`_index.json`（非 icon 段：`_meta` + `affordance_categories` taxonomy）+ 每文件夹一个 `<Folder>.json`（该文件夹的条目数组）。
2. **一个共享 loader** [`figma-sync/lib/affordance-sot.mjs`](../affordance-categories/) 是唯一知道磁盘形状的地方：
   - `loadAffordanceSot()` 把目录**重组回旧的合并形状** `{ _meta, affordance_categories, icons }` → 所有读 `sot.icons` / `sot._meta` 的消费方**零语义改动**。
   - `writeAffordanceSot()` 是逆操作（按文件夹 re-shard），**幂等**：`write(load())` 产出 byte-identical（merged 顺序 = 分片按文件名排序 concat，写时按文件夹分组保留组内序）。
3. **拆分键选 `path` 顶层而非 `registry_name`**：`path` 644/644 全有值，`registry_name` 有 null → 后者会产生孤儿桶；且 `path` 1:1 镜像 Figma 文件夹，符合项目 Figma↔Code 镜像命题。

**先验消费方再动手**：grep 出 3 个硬消费方（audit-figma-vs-sot 阻塞门 / extract-icon-worklist / phase-c-merge），确认 affordance-search agent **不读**此文件（它扫 Figma 库 / svg dir）。两个 read 消费方按 component_key 建 map → **顺序无关**，所以分片顺序不影响行为；phase-c 是已耗尽的一次性脚本（断言 35 条，现 644 条会 throw）。

**验证门**：`audit:figma-vs-sot` 全绿（644/644，A=B=C=D=F=G=0）+ round-trip byte-identical + 188 vitest + pre-commit gate。

---

## 经验 2 — 并行 session 共享同一工作树的纠缠（核心教训）

### 识别：Read 快照会在 session 内变陈旧

起手 Read 到 audit 脚本是 **317 行（只有 A/B/C/D）**；动手前复核时磁盘已是 **409 行（含 INFRA-F38 F/G null-key/key-mismatch）**。三个独立信号实证另一 session 正在写 `figma-sync/`：

| 信号 | 证据 |
|---|---|
| 行数 | 317 → 409 |
| git-status 集扩张 | session 起手只有 2 个 dirty，复核时多出 7 个（含 3 个 figma-sync/*.mjs） |
| mtime | audit 脚本 mtime 比当前时刻早几分钟 |

→ 命中 [[user_parallel-sessions]]「dirty/untracked 文件先假设别 session 在动」。按纪律**先停下报告 + 等用户拍板**，没有基于陈旧 Read 直接改。

### 关键认知纠正：并行 session 共享同一工作树

一度误以为「留给那个 session」有意义 —— 错。同机同目录的并行 Claude session **共享同一个工作树 / 同一个 git repo**，只有一份工作树。所以"提交对那个 session 无害"才是对的：F/G 是 INFRA-F38 同批活，提交后对所有 session 都是已落地。

### 文件级无法剥离时的提交决策

`audit-figma-vs-sot.mjs` 同时含：(a) 我的 loader 迁移（**必须**进拆分 commit，否则删了单文件 audit 还读单文件 → 坏树）；(b) 另一 session 未提交的 F/G 逻辑。二者在**同一文件**，文件粒度 `git add` 无法分离，hunk 级手术 + 报告重生成成本过高且 ROI 低。

**决策**：精确 `git add` 拆分相关文件（含被迫同行的 audit 文件），**不**带上其余纯 INFRA-F38 文件（sync-figma-library / generate-component-candidates / AGENTS / backlog —— 这些后被该 session 自己 commit 成 `36f4ebe6`）；commit body 写清 F/G provenance。结果：自洽、audit 全绿、可工作的树。

---

## 反模式记录

- **基于 session 起手的 Read 快照直接改 dirty 文件** → 可能 clobber 并行 session 的在途工作。复核 mtime/行数/git-status 是廉价保险。
- **为了"提交干净"对同文件做 hunk 级手术剥离他人在途改动** → 当生成物（drift report）依赖该文件的完整逻辑时会引入不一致；ROI 低时不如整体提交 + provenance 注明。

---

## 落地产物

- 新结构 + loader：`docs/internal/affordance-categories/` + `figma-sync/lib/affordance-sot.mjs`
- 活文档校正：`src/design-system/translation/affordance-layer.md` + `figma-sync/README.md`
- tracker 轨道 C 已完成 +1 行；STATUS.md Last updated → 2026-06-01
- 无新增 memory（教训已被既有 [[user_parallel-sessions]] 覆盖；loader-shard 模式记在此复盘 + affordance-layer.md）
