# FR-093 套件 migration 與能力點種子 — LOG（append-only）

> **只追加、不改寫既有 block。** 每棒一個 block：commits／決策／教訓／推翻了什麼。

---

## 第 4 棒 — .1d CM-1678 首次真實驗收（2026-09-13）

**卡**：CM-1756（驗收棒，不改檔）
**commits**：無（純驗收，未改任何檔；本檔為新增）
**前置**：.1a CM-1753、.1b CM-1754、.1c CM-1755 均已 Done

### ① 002 檔核對結果

`.venv/lib/python3.11/site-packages/jedi_asset/migrations/002-asset-rls-grants.sql`
enum RENAME VALUE 段落在 120-146 行，之後（147-159 行）只有 GRANT 語句，**沒有任何
INSERT／UPDATE 用到新值**。核無問題，不需拆 003。

### ② 升級路徑（DEV）—— 🔴 卡在 migrate.sh 的基線 marker 守門，未能實套

DEV（`localhost:5432` / `guidant_ai_dev`）套前 `\dT+` 兩型別**已全小寫**
（`operational, under-development, disposition, under-major-modification, other` /
`low, moderate, high`）——CM-1678 手套過。`schema_migrations` 內 `packages/%` 為
23 筆，確認 `jedi_asset/002` 尚未登記（符合 .1c 的刻意排除）。

跑 `MIGRATE_SQL_DIR=$(pwd)/scripts/sql MIGRATE_DB_NAME=guidant_ai_dev MIGRATE_DB_USER=cmmgr
MIGRATE_APPLY=0 bash scripts/init/migrate.sh`：

```
[migrate] 目標：cmmgr@localhost:5432／資料庫 guidant_ai_dev
[migrate] 模式：dry-run（不寫入）
[migrate] 已套 189 支（主線＋套件合計）
[migrate][error] 資料庫缺少 __init_baseline_* 標記——這個庫不是由 scripts/init/ 建立的。
  升級只能套在 installer 建出來的資料庫上；請確認 MIGRATE_DB_NAME 是否正確。
```

**根因**：DEV 是主線 `align_migrations.sh` 管理的舊制庫，`schema_migrations` 裡的
哨兵是 `__baseline_pre_2026-05-28__`，不是 `migrate.sh` 要求的 `__init_baseline_v*__`
（那是 installer 建庫才會有的 marker，見 `99-stamp.sql`）。`migrate.sh` 的這道檢查
（`scripts/init/migrate.sh:92-95`）是刻意設計成「只認 installer 建的庫」，本意是擋
「拿錯庫套」，但也連帶把「舊制 DEV 想借用 migrate.sh 的第二輪套件差集邏輯」這條路堵死。

**而主線 `align_migrations.sh`（DEV 現行套用工具）完全沒有套件 migration 的第二輪邏輯**
（`grep -n "packages\|PKG" scripts/align_migrations.sh` 零命中）——它只認
`scripts/sql/manifest.tsv`，不知道 `scripts/sql/packages/manifest.tsv` 的存在。

**結論：目前沒有一條路徑能讓 DEV 這種舊制庫套用 `packages/jedi_asset/002`**：
- `migrate.sh` 有邏輯但拒絕非 installer 建的庫
- `align_migrations.sh` 認 DEV 但沒有套件差集邏輯

這是 design 沒講透的缺口（design.md §5.1⑦(a) 只討論了「新裝 vs 升級」的 enum 終態
不一致，沒討論「舊制既有庫（DEV/STG/POC/客戶既有安裝）根本套不到套件 migration」
這個更前面的問題）。**已回寫本卡，未自行修改任何腳本。**

### ③ 新裝路徑（本機臨時庫 `guidant_ai_fresh_cm1756`）—— 驗證了設計已預見的不一致

用 `scripts/init/init.sh` 對本機 cmmgr（本身即 superuser）建一顆全新庫：

```
INIT_DB_HOST=localhost INIT_DB_PORT=5432 INIT_DB_NAME=guidant_ai_fresh_cm1756 \
INIT_SUPERUSER=cmmgr INIT_SUPERUSER_PASSWORD=<.env DB_PASSWORD> \
INIT_CMMGR_PASSWORD=<同上> INIT_CMAPP_PASSWORD=<同上> \
bash scripts/init/init.sh
```

結果：`✅ init 完成：資料庫 guidant_ai_fresh_cm1756 已就緒`（167 表／106 policy／
缺權限表 0／缺 USAGE sequence 0）。

init 後立即查兩型別（**未跑 migrate**）：

```
system_status_enum: OPERATIONAL, UNDER_DEVELOPMENT, DISPOSITION, under-major-modification, other
security_sensitivity_level_enum: LOW, MODERATE, HIGH
```

**大寫混雜**（`02-schema.sql` 建的原始定義，符合預期——02-schema.sql 尚未跟著
CM-1678 的 enum 修正走）。`schema_migrations` 內 `packages/%` 已是 **24 筆**（含
`jedi_asset/002`），因為 `99-stamp.sql` 在 .1c 時已把 24 支套件檔全部登記為
「converged into `__init_baseline_v1.19.0__`」——**新裝庫從一出生就被標記「002 已收斂
進基線」，但基線 schema（02-schema.sql）實際上還是舊的大小寫混雜值。**

跟著跑 `migrate.sh` dry-run 驗證這個「假裝已套」的狀態確實會讓 migrate 判定零待套：

```
[migrate] 已套 177 支（主線＋套件合計）
主線待套 0 支（無，主線已與本版對齊）
套件待套 0 支（無，套件已與本版對齊）
✅ 無待套項目，資料庫結構已是最新
```

**驗證了 design.md §5.1⑦(a) 的預警是真的**：新裝路徑的 enum 終態＝大寫混雜（永遠
不會被 002 修正，因為 99-stamp 已把它標記為「基線已收斂」，migrate 差集看不到它）；
升級路徑（真正走過 002 的庫）的 enum 終態＝小寫。**兩條路徑的終態不一致**，且新裝
路徑目前**沒有任何機制**會讓它變成小寫（`02-schema.sql` 本身沒改、99-stamp 又把
002 標記成已收斂）。

臨時庫已 `DROP DATABASE guidant_ai_fresh_cm1756` 清除。

### 待首腦裁決的兩個問題

**Q1（②的前提缺口，新發現，design 未討論）**：DEV／STG／POC／既有客戶庫是舊制
`align_migrations.sh` 管的庫，沒有 `__init_baseline_v*__` marker，`migrate.sh` 拒絕
它們。**這些庫要怎麼真的套上 `packages/jedi_asset/002`？** 選項可能是：
  (A) 放寬 `migrate.sh` 的 marker 檢查，改認舊制 `__baseline_pre_*` 也算合法目標；
  (B) 幫舊制庫補一個 `__init_baseline_v*__` marker（等於承認它已收斂到某版基線）；
  (C) DEV 另外直接用 `psql --single-transaction` 手套 002（繞過 migrate.sh，僅限
      內部環境，正式客戶升級仍需 migrate.sh 能動）；
  (D) 其他。

**Q2（③卡上已預見的選項，design §5.1⑦(a) 沒講透）**：新裝路徑的 002 天生被
99-stamp 標記為已收斂、不會真的套，enum 終態停在大寫混雜，與升級路徑（小寫）不
一致。選項：
  (A) `gen_stamp_sql.sh` 補登清單排除 `jedi_asset/002`（讓新裝也走一次真套用，
      需求是它必須是冪等且不依賴其他已收斂的表結構——已核對過，002 只有
      FK／NOT NULL／enum RENAME／GRANT，都可對剛建好的 02-schema.sql 冪等執行）；
  (B) 修 `02-schema.sql` 基線把兩個 enum 定義直接改小寫並重生（動基線，違反
      D3「不手改 02/04/05，只能靠 regenerate」精神，且 02-schema.sql 是從基線庫
      dump 出來的，要先把基線庫本身的 enum 也改小寫）；
  (C) 接受不一致，記錄為已知缺口。

runner 判斷：**Q1 是更急迫的問題**——它代表現有機制目前完全無法讓任何真實既有庫
（含未來客戶的既有安裝）吃到 002 這類套件 migration，不只是「enum 終態不一致」的
美觀問題，而是「升級鏈本身對舊制庫失靈」。Q2 則是 Q1 解掉後才會實際發生的次要不一致。

### 手測結果彙整（供首腦核對）

| 項目 | DEV（升級路徑） | 臨時庫（新裝路徑，未跑 migrate） |
|---|---|---|
| `system_status_enum` | 小寫（CM-1678 已手套過） | 大寫混雜（02-schema.sql 原始值） |
| `security_sensitivity_level_enum` | 小寫 | 大寫混雜 |
| `packages/%` 筆數 | 23（不含 002） | 24（含 002，但只是登記非實套） |
| `migrate.sh` dry-run | **拒絕**（缺 `__init_baseline_v*__`） | 主線 0／套件 0（誤判已對齊） |

### 教訓

**① 「.1c 已補登 23／獨留 002 給 .1d 驗收」的假設，卡在更前面一關——DEV 根本不是
migrate.sh 認得的目標庫**。design 只想到「002 該不該被新裝路徑收斂」，沒想到
「migrate.sh 本身的守門條件排除了 DEV 這種舊制庫」。**開卡前對「這條路走不走得通」
的假設要親手跑一次 dry-run 驗證，不能只看 manifest 差集算出來的數字。**

**② 新裝路徑的「packages/% = 24」不能只看筆數，要對照 enum 實際值**——筆數對的
上不代表真的套了，99-stamp 的「收斂進基線」登記本身就是一種「假裝已套」，卡上明確
提醒了這點（「這裡要想清楚」），驗證時確實要交叉看兩個指標。

---

## 首腦復核與決策者裁定（2026-09-13～14）

首腦唯讀複驗兩個發現屬實，並**修正 Q1 的範圍判斷**：runner 原判斷「DEV／STG／POC／
既有客戶都是同一種舊制庫」不對——實查 STG（188 stack）與 POC（189 stack）哨兵都是
`__init_baseline_v1.14.0__`，是 installer 建的庫，`migrate.sh` 對它們是通的。**舊制
庫只有 DEV 與 188 基線庫兩個**，客戶端沒有這個問題。Q1 因此不是「升級鏈對舊制庫
失靈」的系統性問題，只是「DEV 這顆內部庫怎麼套」——答案就是 DEV 一貫的 SOP：
`psql --single-transaction` 手套、補登 `schema_migrations`。**`migrate.sh` 的
marker 守門是對的，不放寬**。

Q2 屬實且需修：新裝庫被 stamp 標「002 已收斂」但 `02-schema.sql` 基線仍大寫，標記
是假的。首腦建議正規解法是「migration 先套基線庫、再重產 02」（對 188 基線庫套
002 並登記 → 重產 `02-schema.sql` → enum 變小寫 → 99-stamp 的「已收斂」才是真的），
不採 (A) 排除 002 讓新裝再套一次（純 init 路徑沒有 migrate 步驟，仍會留大寫）、
不採 (B) 手改 02（違反 D3）。**動基線庫屬出貨基線寫入，決策者裁示先不做**（見下）。

**決策者裁定（2026-09-14）：一律只在本機，不套 188。**
- Q1：DEV（本機 `localhost:5432`）用 `psql --single-transaction -v ON_ERROR_STOP=1`
  手套 jedi-asset 002，補登一列進 `schema_migrations`。`migrate.sh` marker 守門
  不放寬。
- Q2：**188 基線庫不動**。「基線庫套 002＋重產 02-schema.sql」列為出貨前待辦，
  時間由決策者另定；在那之前新裝路徑 enum 仍大寫，是已知缺口，記在 FR-093 README。

### 第 5 棒 — .1d 續做：DEV 真套＋補登＋新裝路徑二次確認（2026-09-14）

**卡**：CM-1756（續做，接續第 4 棒）
**commits**：無邏輯檔案改動（純 DB 操作＋本 LOG 追記）

**Step 1：DEV 真套 002**

套前狀態確認（與第 4 棒一致，無漂移）：enum 皆小寫、`packages/%`＝23（不含 002）。

```
PGPASSWORD=*** psql -h localhost -p 5432 -U cmmgr -d guidant_ai_dev \
  --single-transaction -v ON_ERROR_STOP=1 \
  -f scripts/sql/packages/jedi_asset/002-asset-rls-grants.sql
```
輸出：`DO ×15`／`ALTER TABLE ×3`，無錯誤（enum RENAME 因舊 label 已不存在，判斷式
跳過，符合冪等預期——DEV 早被 CM-1678 手套過，這次是**把「登記」補齊**，不是第一次
真的改變 enum 值）。

補登：
```sql
INSERT INTO public.schema_migrations(filename, note)
VALUES ('packages/jedi_asset/002-asset-rls-grants.sql',
        'applied by CM-1756 (FR-093.1d) manual apply per 決策者裁定 2026-09-14')
ON CONFLICT (filename) DO NOTHING;
```
→ `INSERT 0 1`。套後 `packages/%`＝**24**，含 002 且 note 正確。套後 `\dT+` 仍
小寫（未變，符合預期）。

**Step 2：188 基線庫**——未連線、未操作，符合裁定。

**Step 3：本機臨時庫二次驗證新裝路徑**

用 `scripts/init/init.sh` 建 `guidant_ai_fresh_cm1756_v2`（腳本與檔案自第 4 棒起
未變動），init 成功（167 表／106 policy／缺權限 0）。

```
system_status_enum: OPERATIONAL, UNDER_DEVELOPMENT, DISPOSITION, under-major-modification, other
security_sensitivity_level_enum: LOW, MODERATE, HIGH
packages/% 筆數: 24
```

**大寫混雜，與第 4 棒結果一致**——確認 Q2 缺口仍在（DEV 手套不影響新裝路徑，兩者
互不干擾，符合預期：DEV 是既有庫走升級 SOP，新裝路徑走 `02-schema.sql`，兩條線
在基線庫未套 002 前天然分岔）。臨時庫已 `DROP DATABASE` 清除。

**本卡狀態**：Q1 已完全解決（DEV 已真套＋補登，`packages/%`＝24 且非假登記）。
Q2 是已知缺口，待日後排定時間對 188 基線庫套 002＋重產 `02-schema.sql`（出貨前
待辦，非本卡範圍）。回寫 Notion 卡→改「修正待驗證」。
