# Tenant scope 資料模型（B-2 + FR-042 + SMTP/LDAP 平台層）部署 / Migration Runbook

> 對應 commit BE `d9a2ffe8` + jedi-system-config `afe2dcd`。母案 Notion `38d346da-4cd0-812f-aba6-debcb1e1f632`。
> dev（188/guidant_ai_dev）已全套並驗。**stg / poc / prod 依本檔各跑一次。**

## 0. 前置 / 通則
- 帳號 **`cmmgr`**（系統管理員繞 RLS；`cm_app` 受 RLS 寫系統層會 silent fail）。密碼查 `.env` `DB_SECRET`。
- 每支：`psql -h <host> -p 25432 -U cmmgr -d <db> --single-transaction -v ON_ERROR_STOP=1 -f scripts/sql/<檔>`
- 每支檔尾已含 `INSERT public.schema_migrations`，重跑安全（`ON CONFLICT DO NOTHING` + 條件式冪等）。
- root tenant / root org_unit 各環境皆 id=1（雷門 2026-06-29 確認）。

| Env | host | db |
|-----|------|-----|
| stg | 192.168.50.188 | guidant_ai_stg |
| poc | 192.168.50.189 | guidant_ai_poc |
| prod | （查部署文件）| （查部署文件）|

## 1. SQL migration（**依此順序**，scripts/sql/）
依賴：①→② admin 先入 root tenant，roles 遷移的成員檢查才過；⑤→⑥ SMTP 先 null→root 再刪 per-tenant。其餘大致獨立。

| # | 檔案 | 作用 |
|---|------|------|
| 1 | `2026-06-29-b2-admin-root-tenant.sql` | admin users.tenant_id NULL→1 + user_tenants + user_org_units |
| 2 | `2026-06-29-b2-admin-root-tenant-roles.sql` | admin role/user_role global→tenant(1)（修空選單）|
| 3 | `2026-06-28-fr042-upload-files-add-storage-scope.sql` | upload_files 加 storage_scope（system/customer）|
| 4 | `2026-06-29-fr042-tenant-scope-module-frames-flow-templates.sql` | module_frames 補 scope+RLS；flow_templates builtin→ROOT(1)+is_builtin policy |
| 5 | `2026-06-29-system-configs-null-to-root.sql` | 5 筆 null system_configs → ROOT(1) 平台預設 |
| 6 | `2026-06-29-smtp-platform-only-drop-per-tenant.sql` | 刪所有非 ROOT 的 per-tenant SMTP（平台層共用）|
| 7 | `2026-06-29-cleanup-orphan-projects-and-test-root-tenant.sql` | 刪孤兒 projects + 測試 root tenant 134/135 |
| 8 | `2026-06-29-platform-only-smtp-ldap-log-capabilities.sql` | SMTP/LDAP/操作日誌 capability 改 admin-only（移除租戶角色）|
| 9 | `2026-06-29-platform-capability-flag-and-guard.sql` | capabilities 加 is_platform + 標記 4 組；role_capabilities trigger 擋非 root 角色持平台 cap；清非 root 角色 issue cap + per-tenant 回饋整合設定 |

> ⚠️ #7（刪測試 root 134/135 + 孤兒 projects）**各環境的孤兒/測試租戶不同**——套前先各環境自查（`SELECT id,name,path FROM tenants WHERE parent_id IS NULL;`、`SELECT count(*) FROM compliance.projects WHERE tenant_id IS NULL;`）。條件式（tenant_id IS NULL / id IN (134,135)）多環境通用，但**確認該環境的 134/135 確實是測試垃圾再跑**。
> ⚠️ #6/#8 以 `tenant_id<>1` / capability id 收斂；各環境若 capability/role id 不同需先核對（dev 上 smtp=74-77 / ldap=78-81 / log=29）。

## 2. 程式碼 / 套件
- BE commit `d9a2ffe8`（+ 後續 commit）部署。
- **jedi-system-config / jedi-auth / jedi-file-upload**：dev 走 path-dep（`pyproject.toml` 未 commit）。stg/poc/prod 需**發版**：各套件 bump version → push Nexus → 主專案 pyproject 改回 pin 新版 → `poetry update <pkg>`。（feature 驗收後、雷門明示才發版。）
  - jedi-system-config：STORAGE_CONFIG tenant-scope 確定性讀寫。
  - jedi-auth：capabilities.is_platform + capability/route 矩陣過濾 + tenant menu parent_id。
  - jedi-file-upload：FR-042 storage_scope。
- ⚠️ stg/poc/prod 在套件發版前，BE 仍 import 舊版套件 → migration 套了但程式行為要等套件發版才一致。建議「套件發版 + migration」同批上。

## 3. 套用後
- **重啟 BE**（吃套件 + 程式變更，無 hot reload）。
- **admin / 租戶管理員重新登入**（重解析 tenant context + capability，選單才更新）。

## 4. 驗證（每環境套完跑）
```sql
-- admin 入 root + 無殘留 null
SELECT id,login_name,tenant_id FROM users WHERE login_name='admin';            -- tenant_id=1
SELECT count(*) FROM users WHERE tenant_id IS NULL AND is_super_admin=true;     -- 0
SELECT scope,tenant_id FROM user_roles ur JOIN users u ON u.id=ur.user_id WHERE u.login_name='admin'; -- tenant/1
-- system_configs / SMTP
SELECT count(*) FROM system_configs WHERE tenant_id IS NULL;                    -- 0
SELECT id,tenant_id FROM system_configs WHERE "group"='SMTP';                   -- 只剩 tenant 1
-- root 收斂
SELECT id,name,path FROM tenants WHERE parent_id IS NULL;                       -- 只剩 root 1
-- module_frames / flow_templates
SELECT scope,count(*) FROM compliance.module_frames GROUP BY scope;
SELECT tenant_id,is_builtin,count(*) FROM compliance.flow_templates GROUP BY 1,2; -- builtin→tenant 1, 0 null
```
手測：admin 上傳框架進 minio、儲存設定確定性、子租戶寄信走平台 SMTP、租戶系統設定只剩通知設定。

## 5. schema_migrations 已登記檔名（核對用）
2026-06-28-fr042-upload-files-add-storage-scope / 2026-06-29-b2-admin-root-tenant / -roles /
-fr042-tenant-scope-module-frames-flow-templates / -system-configs-null-to-root /
-smtp-platform-only-drop-per-tenant / -cleanup-orphan-projects-and-test-root-tenant /
-platform-only-smtp-ldap-log-capabilities
