# C1 — Implementation Plan

> 對應 design：[design-C1.md](design-C1.md)
> 級別：中型
> 預估工時：BE 0.5 天 + FE 0.5 天

---

## Pre-flight 驗證（開工前必跑）

```bash
# 1. 確認 system_menus 表存在且 schema 是 (group, key, value, sort, enable, public)
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c "\d system_menus"

# 2. 確認 system_menus 是否已有 UNIQUE(group, key) constraint
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c \
  "SELECT conname FROM pg_constraint WHERE conrelid = 'system_menus'::regclass AND contype = 'u';"

# 3. 確認 oscal.oscal_roles 表 schema (C3-D5 同步 seed 需要)
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c "\d oscal.oscal_roles"

# 4. 確認 oscal.oscal_roles 是否已有 UNIQUE(role_id) constraint
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c \
  "SELECT conname FROM pg_constraint WHERE conrelid = 'oscal.oscal_roles'::regclass AND contype = 'u';"

# 5. 確認 oscal.oscal_roles 現有資料（避免衝突）
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c \
  "SELECT role_id, title FROM oscal.oscal_roles ORDER BY role_id;"

# 6. 確認 GET /system/menu/<group> endpoint 路徑 + 回傳格式
grep -n "SystemGroupMenuRoute" api/system_menu/__init__.py
# 預期：/system/menu/<string:group>

# 7. 確認 jedi-system-menu 在 BE pyproject.toml 已 pin
grep "jedi-system-menu" pyproject.toml
```

**Verification 結果填回 design-C1.md 對應段落（特別是兩個 UNIQUE 是否已存在）。**

---

## 任務清單

### T1 — SQL Migration: seed 9 個角色到兩個表（BE）

**檔案**：
- 新建 `scripts/sql/2026-MM-DD-seed-oscal-ssp-roles.sql`
- 內容對齊 design-C1.md §3.2（含 §3.2.A system_menus + §3.2.B oscal_roles）

**行為**：
- Part A: 9 個 INSERT 進 system_menus（UI dropdown 用），`ON CONFLICT DO NOTHING` idempotent
- Part B: 9 個 INSERT 進 oscal.oscal_roles（OSCAL 文件用），同樣 idempotent
- 跑完輸出兩個 `SELECT COUNT(*)` 確認各 9 筆

**前置處理**：
- 若 pre-flight 發現 `system_menus` 無 UNIQUE(group, key)：
  - 先跑 `SELECT "group", key, COUNT(*) FROM system_menus GROUP BY "group", key HAVING COUNT(*) > 1;` 確認無重複
  - 加 UNIQUE：`ALTER TABLE system_menus ADD CONSTRAINT uq_system_menus_group_key UNIQUE ("group", key);`
  - 同 migration script 內處理
- 若 pre-flight 發現 `oscal.oscal_roles` 無 UNIQUE(role_id)：
  - 先 audit 有無重複 `SELECT role_id, COUNT(*) FROM oscal.oscal_roles GROUP BY role_id HAVING COUNT(*) > 1;`
  - 加 UNIQUE：`ALTER TABLE oscal.oscal_roles ADD CONSTRAINT uq_oscal_roles_role_id UNIQUE (role_id);`

**驗證**：
```sql
SELECT * FROM system_menus WHERE "group" = 'ssp_party_role' ORDER BY sort;
-- 應看到 9 筆，sort 為 10, 20, ..., 90

SELECT role_id, title FROM oscal.oscal_roles WHERE role_id IN (
  'responsible-organization','system-owner','system-security-officer',
  'authorizing-official','information-owner','information-provider',
  'information-receiver','prepared-by','prepared-for'
) ORDER BY role_id;
-- 應看到 9 筆
```

**Commit message**：`chore(sql): C1 seed OSCAL SSP 9 個角色到 system_menus + oscal_roles`

---

### T2 — Smoke test: API 可取得（BE）

**驗證**：
```bash
# 取 JWT token 後
curl -H "Authorization: Bearer <token>" \
  http://localhost:8000/api/1.0/system/menu/ssp_party_role | jq '.data.menus | length'
# 預期：9
```

**不需新增 BE 程式碼**（沿用 jedi-system-menu 既有 endpoint）。

---

### T3 — FE i18n 檔案（FE）

**檔案**：
- 新建 `src/config/locales/i18n/zh-tw/oscal-role.json`
- 新建 `src/config/locales/i18n/en/oscal-role.json`
- 內容對齊 design-C1.md §5

**接入 i18n loader**：
- 確認 `src/config/locales/i18n/zh-tw/index.js`（或對應 loader）有自動引入新檔
- 若需手動註冊，加上 import

**驗證**：
```js
// FE console
$t('lang.oscal_role.ssp_party_role.system-owner.label')
// 預期：'系統所有者'
```

**Commit message**：`feat(i18n): C1 新增 oscal-role i18n 9 個 OSCAL SSP 角色`

---

### T4 — Changelog（BE + FE）

**BE 檔案**：`docs/changelog/YYYY-MM-DD-feat-oscal-ssp-roles-master-data.md`

```markdown
---
type: feat
breaking: false
modules: [system_menu]
---

## 需求說明
C1：OSCAL SSP 角色 master data 化 — 9 個標準 OSCAL role 種子進 system_menus，
供 C2 SSP party endpoint dropdown 使用。

## 變更範圍
### BE
- `scripts/sql/2026-MM-DD-seed-oscal-ssp-roles.sql`：9 個 INSERT + (optional) UNIQUE constraint

## 部署備註
- SQL migration 須在 dev DB 跑一次
- 不影響既有功能
```

**FE 檔案**：同步建立 changelog（FE repo 對應位置）

---

## 測試規格

| 測試 | 工具 | 內容 |
|------|------|------|
| BE smoke | curl | `GET /system/menu/ssp_party_role` 回 9 筆 |
| FE i18n | console | `$t(...)` 解析正確 |
| Idempotent | psql | seed SQL 重跑兩次不會重複 |

不需新 unit test（純資料 seed）。

---

## 完成標準（DoD）

- [ ] SQL migration 跑過、9 筆資料存在
- [ ] `GET /system/menu/ssp_party_role` 在 dev 環境回正確
- [ ] FE zh-tw / en 兩個 `oscal-role.json` 建立
- [ ] FE i18n key 在 console 可解析
- [ ] BE + FE 各一份 changelog
- [ ] README tracker (C1 行) 狀態 `pending` → `shipped` + commit hash

---

## 風險 / Rollback

| 風險 | 緩解 |
|------|------|
| seed 跑失敗 | ON CONFLICT DO NOTHING idempotent，可重跑 |
| 既有 system_menus 同 group 已有同 key | UNIQUE check 阻擋 / ON CONFLICT 跳過 |
| 不小心 seed 到 prod | migration SQL 不會自動跑，須手動執行 |

**Rollback**：
```sql
DELETE FROM system_menus WHERE "group" = 'ssp_party_role';
```
