---
title: "FR-155 舊庫升級排練：專案角色建立與參與者回填"
---

# 舊庫升級排練：專案角色建立與參與者回填

拿一份還沒有 FR-155 異動的真實舊庫（POC），走客戶升級的正式路徑（`scripts/init/migrate.sh`）升到現版，逐列比對升級前後的參與者角色、流程站主責與專案實例流程。這份紀錄回答：**舊客戶升級後，每個參與者是不是對到同一個角色？流程範本的主責有沒有換對？進行中的專案流程有沒有被動到？**

## 結論

- **每個租戶都建好四個範例專案角色**：2 個租戶各有負責人／審核者／稽核員／瀏覽者，`kind=project`、`is_template=true`，每個租戶恰好一個預設負責人（負責人）。能力點與 `b1-project-capabilities.md` 共同設定表逐條相同（負責人 15、審核者 3、稽核員 4、瀏覽者 1）。
- **參與者回填零遺漏、零錯配**：9 列參與者全部回填，舊字與角色名一一對應（manager→負責人 3、reviewer→審核者 2、auditor→稽核員 2、viewer→瀏覽者 2），回填的角色全屬同租戶。原 `role` 欄逐列不變。
- **流程站主責全數換成能力點**：`stage_objects` 6 列、`flow_templates` 3 筆（含同一次升級中 FR-140 新插入、帶舊字的「內外稽核流程」）都換成能力點，不殘留舊四字。
- **專案實例流程不動**：`workflow_templates` 原有 245 筆的 `main_role` 值逐筆與升級前相同（8 筆 manager、8 筆 auditor 原樣保留）。
- **第三輪沒有碰專案角色**：能力點宣告 upsert 與角色回補跑完後，專案角色的 pattern 仍是 46 條，與 migration 寫入的一樣；系統角色沒有被補授任何 `proj.*`。
- **正式路徑本身**：主線 83 支、套件 24 支依 manifest 行序一次套完、無錯；重跑一次待套 0，再單獨重跑兩支 FR-155 migration，角色、pattern、回填、站主責全部不變（冪等）。

## 驗收打折（照實標）

| 項目 | 打折在哪 | 影響 |
|---|---|---|
| 資料量 | POC 只有 2 個租戶、3 個專案、9 列專案層參與者；群組／控制項／控制群組／流程四張參與者表是 0 列 | 那四張表的回填在真實舊資料上**沒有被測到**，只證明「空表時不出錯」；回填語法五張表共用同一段，正確性靠 `project_participants` 間接佐證 |
| 客戶自訂角色 | 舊庫沒有 `roles.kind` 欄位，因此不可能有客戶自建或改名過的專案角色 | migration A「已有同名就跳過」「已有預設負責人就不搶旗標」兩條分支在這份庫上沒走到 |
| 同名角色 | POC 的系統角色只有 Administrator、System Manager 等，沒有叫「稽核員」的系統角色 | 「同租戶系統角色與專案角色同名」的情境沒有實際出現；出貨預設的系統角色裡有「稽核員」，新裝客戶會遇到 |
| 畫面 | 沒有起 BE、沒有登入看參與者頁 | 參與者頁顯示的仍是舊 `role` 欄（本棒不改讀法），故預期不變；畫面手測留給決策者 |
| 套件版本 | 套件 SQL 從本機 `.venv` 的 site-packages 攤平（Nexus 版本），不是 path 模式的套件源碼 | 本棒對 jedi-iam 只改程式、沒有新增套件 migration，排練結果不受影響 |

## 怎麼做的

**舊庫**：POC（189 安裝版 stack）唯讀 `pg_dump -Fc`，45MB，md5 `96b79d64393ba565917d92035332ca59`。來源狀態：已套 311 支 migration、最後一支套於 2026-10-09、BE image 1.26.0。restore 到本機 `localhost:5432` 的暫存庫 `guidant_ai_fr155_rehearsal`（`pg_restore --no-owner`），零錯誤。連線帳號 cmmgr，密碼請查 `.env`。

**套件 SQL 攤平**：把 `scripts/sql` 整份複製到 scratchpad，以 `scripts/build/flatten_pkg_migrations.sh <scratchpad>` 攤出套件 SQL 98 支、能力點宣告 222 筆。**repo 內的 `scripts/sql/packages/` 沒動**。

**升級**：`migrate.sh` 先 dry-run（主線待套 83、套件 24、第三輪 222），再 `MIGRATE_APPLY=1`，結尾「成功套用 107 支／能力點 upsert 222 筆／root 新增 0 筆／參照角色法命中 1 個租戶角色／租戶側新增 0 筆」。

**比對口徑**：

- 角色層：`public.roles` 取 `kind='project'` 各列與其 `role_capability_patterns`，對 `b1` 共同設定表；另查系統角色是否被補授 `proj.*`、同租戶有沒有同名角色。
- 參與者層：五張參與者表各自 join `projects` 取租戶，按「租戶 × 舊 role × 回填角色名 × 角色是否同租戶」分組計數；升級前後逐列比 `(project_id, user_id, role)`。
- 流程站：`stage_objects.default_main_roles` 逐列；`flow_templates.bpmn_xml` 抽出每個 `main_role` 屬性值逐筆列。
- 專案實例：`workflow_templates.xml` 抽出 `main_role` 值，升級前後逐筆 diff（全表 md5 也有變，但那是 FR-140 改流程線標籤造成的，不是 FR-155）。
- 冪等：升級完快照一次 → 再跑 `migrate.sh` → 再單獨重跑兩支 FR-155 → 再快照，diff 為空。

## 專案角色

| 租戶 | 角色 | 預設負責人 | 能力點數 |
|---|---|---|---|
| 1 Guidant.AI | 負責人／審核者／稽核員／瀏覽者 | 負責人 | 15／3／4／1 |
| 3 Billows | 負責人／審核者／稽核員／瀏覽者 | 負責人 | 15／3／4／1 |

## 參與者回填（`project_participants`，租戶 3）

| 舊 role | 回填角色 | 同租戶 | 列數 |
|---|---|---|---|
| manager | 負責人 | 是 | 3 |
| reviewer | 審核者 | 是 | 2 |
| auditor | 稽核員 | 是 | 2 |
| viewer | 瀏覽者 | 是 | 2 |

其餘四張表 0 列，未回填數 0。

## 流程站主責

| 對象 | 升級前 | 升級後 |
|---|---|---|
| `stage_objects` 1、2、4 | `["manager"]` | `["proj.stage.advance-any"]` |
| `stage_objects` 3、6 | `["auditor"]` | `["proj.audit.judge"]` |
| `stage_objects` 5 | `["reviewer"]` | `["proj.review.approve"]` |
| `flow_templates` 1 完整稽核流程 | manager, auditor, auditor, manager | advance-any, audit.judge, audit.judge, advance-any |
| `flow_templates` 3 自我評估稽核 | manager, auditor, auditor | advance-any, audit.judge, audit.judge |
| `flow_templates` 4 內外稽核流程（FR-140 本次新增） | 帶舊字插入 | 七個站全為能力點 |

## 觀察（不屬本棒範圍，提醒後續卡）

- 系統管理員角色的 pattern 是 `*.*.*`，依萬用字元規則會涵蓋 `proj.*`。專案內的判定要只看專案角色、不吃系統角色的萬用字元，T-3.x 換判法時要守住這點。
- 升級後到 T-3.3 換判法之前，流程站存的是能力點、判定仍比對角色字，新開的專案流程會出現稽核員／審核者推不動站、通知收件人變少；已存在的專案流程（`workflow_templates`）不受影響。

升級後的暫存庫比對完已刪除；沿革見 LOG 與 git log。
