# Retrospect — Vitest fixture drift（2026-05-13）

> **触发**：F20 验收后跑 `pnpm test` 发现 10 个 vitest 失败；stash 验证全部 pre-existing，不是 F20 引入。
> **结论**：3 个本 session commit（CANONICAL-002/006/008）在 page/component 重构时漂走了 test fixture，commit 时未跑 vitest gate → 直接进 master。
> **修复**：commit [5b39e886](https://github.com/NancyZeng0210/TVU-Design-System/commit/5b39e886)（3 文件 +15/-28）；master 现 `105 passed | 1 skipped`。

---

## 1 — 发生了什么

| commit | 改动 | 漂掉的 fixture |
|---|---|---|
| `596acb05` feat: Rating figma → token-driven (L3) | Rating.vue 删 `theme` prop | tests/Canonical.test.ts Rating: 仍传 `theme: 'light'` + 断言 `data-figma-theme="light"` |
| `d4465778` refactor(Notification): drop success | Notification class 命名 `notif--${status.replace(/ /g, '-')}` | tests/Canonical.test.ts Notification: 断言 `notif--warning`（应为 `notif--secondary-warning`） |
| `ce495da5` fix(CANONICAL-006): Pagination figma alignment | Pagination Classic 删 `.pg-extra` 改 `.pg-jumper` | tests/Canonical.test.ts Pagination: 断言 `.pg-extra` 存在 |
| TopBar 重构 | slot 改名 `logo/left/search/menu/right-content` | tests/Canonical.test.ts TopBar: 用了不存在的 `center/right` slot |
| 多 page 迁移到 `figma-data/normalized/docs-figma-members/*` 数据模块 | variant count 从双 theme 合并 → 单 theme 真实计数（如 Notification 14 → 7） | tests/RemainingCanonicalPages.test.ts: 5 处 variant count / source 名漂移 |
| 多 page 改用 `@/src/canonical/` 别名 import | import 路径风格变化 | tests/FigmaFirstContract.test.ts: regex 仅匹配相对路径 |

10 项都是 fixture drift，**0 项真 bug**。

## 2 — 根因

**Commit 时未跑 vitest gate**。检查 commit chain：
- `596acb05`、`d4465778`、`ce495da5`、`0341d233`（CANONICAL-002）、`8d3ff281`、`903cc324`、`082d0cda` 都没在 commit 前跑 `pnpm test`
- `5a9d4ab7`（F20）也没跑（但 F20 自身不引入 vitest 失败，只是没发现 pre-existing）
- 累计 ~5 个 commit 后，10 failures 静默堆积到 master

为什么没跑？
- 工作流没把 `pnpm test` 当 commit pre-flight 强制 step
- F20 验收清单 1.4 才第一次显式列 `pnpm test` —— 之前几个 commit 跳过 vitest 视为"加速 commit"
- audit gates（`audit:design-system` 等）有自动跑，**但 vitest 没有 hook**

## 3 — 教训 / pattern

### P1：fixture drift 是 silent failure pattern

测试断言 `wrapper.text()).toContain('variant count=14')` 这种**写死 runtime-rendered 文本**的测试，对应数据漂移时不会编译错也不会运行错，**只在跑测时报错**。Commit 时不跑 → 直接进 master。

→ **缓解**：F21 pre-commit hook 加 `pnpm test` 🔴 block（本次 backlog 已升级）

### P2：source-grep test 测的是错的层

`tests/FigmaFirstContract.test.ts` 的 `requiredSourceMarkers` 字段用 regex 在 `.vue` 源码里找 `source=Badge` / `variant count=20`——但页面早就重构成从 `docs-figma-members/*` 数据模块导入再 runtime 渲染，源码里压根没这些字面字符串。

→ **决策**：删 `requiredSourceMarkers` 检查（rendered-text 检查应该走 mount-based `RemainingCanonicalPages.test.ts`，而不是 source grep）

### P3：用 `git stash -u` 做 baseline 分诊很有用

不确定 vitest 失败是不是当前改动引入时，`git stash -u && pnpm test ; git stash pop` 是 5 秒就能给出确凿答案的诊断动作。本次确认 10 failures 全 pre-existing，省下了大量误诊路径。

→ **存档为 debugging 标准动作**：怀疑某改动引入回归 → stash + 跑测 + pop 比对

### P4：scope discipline — fixture 修复不该顺手扩

10 failures 修完后我考虑顺手删 `figmaFirstRegistry.ts` 的 dead field `requiredSourceMarkers`（已没消费者）——但 CLAUDE.md 说"Don't add features, refactor, or introduce abstractions beyond what the task requires"。

最终决定保留 dead field、commit 仅 3 个 test 文件（+15/-28），把 dead field 清理留给后续 lint hygiene 顺手。**最小 diff = 最低回归风险**。

## 4 — Action items

| Item | 状态 |
|---|---|
| Update INFRA-F21 backlog 加 `pnpm test` 🔴 block gate | ✅ done |
| Create INFRA-F29 backlog (normalize.mjs `normalizedAt` follow-up to F26) | ✅ done |
| Master 恢复绿（commit 5b39e886） | ✅ done |
| F26 prompt 写定（commit fa03b1ee） | ✅ done |

## 5 — 跨 session 收获

- **commit chain 走得越快越要警惕**：F19 close / F20 prompt / CANONICAL-002/008 close 一连串 commit 在同一天连发，相互掩护视觉成本（"前一个 commit 都没跑 test，这个也别管了"）。F21 落地前临时纪律：**每个 commit 前 `pnpm test`**，session-end wrap-up 再确认一遍。
- **release health 优先 > dirty 噪音**：vitest 不绿是 release 路径直接 break（changesets → CI → publish），F26 dirty 噪音只是开发体验。推荐路径排序 vitest 修 → F26，事后回看正确。
