# 迁移历史债务待办

> 状态：⏳ 待处理（独立工程项，不阻塞当前 PR）
> 发现于：RBAC 重构（PR #138）阶段 8 生成总迁移文件时
> 优先级：中（不影响生产/UAT，但影响 CI 自动化、新人 setup、灾难恢复）

## 问题摘要

`prisma migrate deploy` 在空数据库上 fresh replay 整个迁移历史会**卡在多个串联断点**。第一个已知断点：
```
20260320000731_cleanup_deprecated_models →
  ERROR: column "assignment_status" of relation "kpi_assignment" does not exist
```
修这个之后下一个又卡 `kpi_assignment.name` 不存在。**断点链可能 5~10 个**。

## 根因

项目本地启动用的是 `prisma db push`（CLAUDE.md 写明），不是 `migrate dev`。
- `db push` 直接同步 schema 到 DB，**不写迁移文件**
- 后续 `migrate dev --create-only` 时，Prisma 看 DB 当前 vs schema.prisma 写"差异部分"，**假定 db push 时改的部分已存在**
- 但迁移历史里从来没建过它们 → fresh replay 时踩在不存在的地基上

业界叫 **migration drift / schema drift / out-of-band schema changes**，是 Prisma + `db push` 工作流的已知通病。

## 影响清单

**会卡住**：
- 新人按文档跑 `prisma migrate deploy` 在空 DB → ❌
- CI 每次重建测试 DB 用 migrate → ❌（如果有这种 workflow）
- 灾难恢复"按迁移历史重建" → ❌
- 想用 `prisma migrate dev` 自动生成新迁移 → ❌（它内部用 fresh replay 验证）

**不卡**：
- 现有 UAT/staging/production（增量长出来的，已应用历史迁移）
- 在已有 dev DB 上 `migrate deploy` 应用新迁移
- `prisma db push`（直接 schema 同步，不重放）

## 推荐修复方案：Squash 基线

实测发现"插补丁迁移"路线 ROI 急剧下降（每修一个断点出下一个）。**Squash 更可靠**。

### 步骤

1. **生成基线 SQL**：
   ```bash
   cd backend
   npx prisma migrate diff \
     --from-empty \
     --to-schema-datamodel prisma/schema \
     --script > /tmp/baseline.sql
   ```
2. **归档现有迁移**：把 `prisma/migrations/<existing>/` 全部移到 `prisma/migrations/_archive/<existing>/`
   - Prisma 不会扫子目录里的 migration.sql（如果用统一命名约定可以让 archive 显式跳过）
   - 或者 `_archive/` 目录前加下划线确保不被识别
3. **创建新基线迁移目录**：
   ```
   prisma/migrations/00000000000000_baseline_squash/migration.sql
   ```
   内容是 step 1 生成的 SQL
4. **每个现有环境执行**（dev / UAT / staging / prod）：
   ```sql
   BEGIN;
   DELETE FROM _prisma_migrations;
   INSERT INTO _prisma_migrations
     (id, checksum, finished_at, migration_name, started_at, applied_steps_count)
   VALUES (
     gen_random_uuid(),
     '<SHA256 of new baseline SQL>',
     NOW(),
     '00000000000000_baseline_squash',
     NOW(),
     1
   );
   COMMIT;
   ```
5. **CI 强制守护**：加 GitHub Actions / Gitea workflow，每个 PR 都跑 fresh replay 测试，任何漂移当场拒绝合并

### 工作量

- 生成 + 归档 + commit：~30 分钟
- 协调每个环境执行 SQL：0.5~1 工程日（多人协调 + 验证）
- CI 加 workflow：~30 分钟

### 风险

- 数据 0 风险（只动 `_prisma_migrations` 表）
- 但部署窗口要协调好，避免新迁移在 squash 期间被推上来
- 建议在低峰期 + 短时间窗口完成

## 临时缓解（短期 workaround）

在 `scripts/dev/setup-worktree.sh` 和新人入职文档里写明：

> 新环境 / CI 不要用 `prisma migrate deploy`（fresh replay 历史断点）。
> 用 `prisma db push --schema prisma/schema --skip-generate` 直接同步 schema。
> 同步完后用 `prisma migrate resolve --applied <each>` 把历史迁移标为"已应用"。
> 长期方案见本文件。

## 相关链接

- Prisma 官方 squash 教程：https://www.prisma.io/docs/concepts/components/prisma-migrate/squash-migrations
- 引发本债的 PR：#138（IAM 重构）
- 本债被发现时的对话沉淀：阶段 8 生成总迁移文件，绕过 fresh replay 用 `db push develop schema → 单独 apply 新迁移 → diff 验证`

## 不变量

修复完成前，**禁止**：
- 在 CI 流水线引入"每 PR fresh replay 测试"步骤（会一直 fail）
- 让"按迁移历史从头重建 DB"成为部署/灾难恢复的依赖路径
