# Phase A0 Design — 既有 `system_security_plan_system_implementations` 表擴充

> **Phase**：A0（基礎建設）
> **級別**：重型
> **狀態**：design draft，等 raymond review
> **brainstorm 來源**：[requirement-understanding.md §2](requirement-understanding.md)（10 個問題已 resolved）
> **依賴**：無（Phase 2 的最前置）
> **後續依賴本 phase**：A1, A2, A3, A4, A5（Track A 全部）+ B2, B5（Track B 內容組裝與 OSCAL 匯出）

---

## 1. 設計目標

擴充既有 `oscal.system_security_plan_system_implementations` 表，使其能同時承載：

1. **「合規資源庫」與「專案 SSP 版本」兩種範圍**（既有只服 SSP）
2. **與 tenant 層的 `public.devices` / `compliance.information_systems` 鉤稽**（既有只存純文字 name）
3. **OSCAL leveraged-authorization 的特有欄位**（既有無 party_uuid / date_authorized）
4. **OSCAL component 的常用 optional 欄位**（title / purpose / status）

完成後，Phase 2 後續所有匯入流程（A1~A5）寫入此表、所有匯出流程（B2 / B5）讀取此表，無需新建任何 OSCAL mirror 表。

---

## 2. 既有現況盤點

### 2.1 Schema

```
oscal.system_security_plan_system_implementations
├── id (PK)
├── uid (UUID, unique, default uuid4)
├── system_security_plan_id (FK → oscal.system_security_plans.id, NOT NULL, ON DELETE CASCADE)
├── name (varchar(255), NOT NULL)
├── description (text)
├── implementation_type (varchar(50), NOT NULL)
├── responsible_party (varchar(100))
└── audit: created_at / updated_at / created_user / updated_user
```

### 2.2 既有 enum

`SystemImplementationType`（jedi-oscal `common/enum/code_enum.py`）：

```python
class SystemImplementationType(StrEnum):
    SYSTEM = "system"
    SUBSYSTEM = "subsystem"
    SERVICE = "service"
    COMPONENT = "component"
    HARDWARE = "hardware"
    SOFTWARE = "software"
```

### 2.3 既有資料

DB 內 140 筆，全部 `implementation_type='hardware'`（= devices 鏡像，由 `project_device_mapping_service` 寫入）。

### 2.4 既有 caller

| 模組 | 行為 |
|------|-----|
| `app/associations/service/project_device_mapping_service.py` | 寫 `hardware` 紀錄 |
| `app/oscal/service/ssp_versioning_service.py` | SSP 版本複製時複製 system_implementations |

### 2.5 既有 jedi-oscal stack

| 層 | 檔案 |
|----|-----|
| ORM | `jedi_oscal/infra/model/ssp/ssp_system_implementation.py` |
| Entity | `jedi_oscal/domain/entity/ssp/ssp_system_implementation_*.py` |
| Repo interface | `jedi_oscal/domain/repository/ssp/system_implementation_repo.py` |
| Repo impl | `jedi_oscal/infra/repository/ssp/ssp_system_implementation_repo_impl.py` |
| Mapper | `jedi_oscal/infra/mapper/ssp/system_implementation_mapper.py` |
| OSCAL YAML mapper | `jedi_oscal/infra/mapper/ssp/ssp_yaml_mapper.py` |
| DTO | `jedi_oscal/app/dto/ssp/ssp_system_implementation_dto.py` |
| Enum | `jedi_oscal/common/enum/code_enum.py` → `SystemImplementationType` |

---

## 3. Schema 變更

### 3.1 ADD COLUMN（共 9 個新欄位）

| 欄位 | 型別 | nullable | 用途 |
|------|-----|---------|-----|
| `scope_type` | `varchar(20)` | NOT NULL（既有資料 migration 為 `'ssp'`）| `'ssp'` 或 `'module_frame'` |
| `scope_id` | `integer` | NOT NULL（既有資料 migration 為 `system_security_plan_id`）| 對應 scope 表 id（soft FK，無 DB constraint）|
| `device_id` | `integer` | nullable | soft FK → `public.devices.id`（鉤稽既有 device 時填）|
| `information_system_id` | `integer` | nullable | soft FK → `compliance.information_systems.id`（鉤稽既有 system 時填）|
| `title` | `varchar(255)` | nullable | OSCAL `component.title` 人類可讀標題（與 name 區分）|
| `purpose` | `text` | nullable | OSCAL `component.purpose` 用途說明 |
| `status` | `varchar(50)` | nullable | OSCAL `component.status`（under-development / operational / disposition / other）|
| `party_uuid` | `varchar(36)` | nullable | OSCAL `leveraged-authorization.party-uuid`（型別對齊既有 `oscal_responsible_parties.party_uuid`）|
| `date_authorized` | `date` | nullable | OSCAL `leveraged-authorization.date-authorized` |

### 3.2 既有欄位變更

| 欄位 | 變更 | 理由 |
|------|-----|-----|
| `system_security_plan_id` | NOT NULL → **nullable**，CASCADE FK 保留 | `scope_type='module_frame'` 時此欄為 null |
| `responsible_party` | **不動，標 deprecated**（docstring） | 不破壞既有 140 筆資料的寫入；新功能改用 `oscal_responsible_parties` (polymorphic with `context_type='system_implementation'`)。後續優化資料遷移列入「最後統整優化清單」 |

### 3.3 enum 擴充

`SystemImplementationType` 加 **1 個值**：

```python
LEVERAGED_AUTHORIZATION = "leveraged-authorization"
```

> **不加 `inventory-item`**：既有 `hardware` 已 cover device 鏡像用途，加 `inventory-item` 會兩個 enum 值意義重疊。OSCAL 匯出時由 mapper 把 `hardware` 翻譯成 OSCAL `inventory-item` 結構，不動 DB enum。

### 3.4 新增 Index

| Index | 欄位 | 用途 |
|-------|-----|-----|
| `ix_ssp_sys_impl_scope` | (scope_type, scope_id) | 依 scope 查詢 |
| `ix_ssp_sys_impl_device_id` | device_id | 依 device 反查 |
| `ix_ssp_sys_impl_info_system_id` | information_system_id | 依 information_system 反查 |

### 3.5 Table COMMENT

```sql
COMMENT ON TABLE oscal.system_security_plan_system_implementations IS
'OSCAL system-implementation 多型鏡像表（v4 擴充：scope_type 區分 ssp / module_frame）。
 implementation_type 區分 hardware (device 鏡像) / component (information_system 鏡像) /
 leveraged-authorization / system / subsystem / service / software。';
```

### 3.6 GRANT 權限

```sql
GRANT SELECT, INSERT, UPDATE, DELETE ON oscal.system_security_plan_system_implementations TO cm_app;
-- sequence 既有，不需要重複 GRANT
```

---

## 4. 既有資料 Migration

### 4.1 既有 140 筆 hardware 資料

```sql
UPDATE oscal.system_security_plan_system_implementations
   SET scope_type = 'ssp',
       scope_id = system_security_plan_id
 WHERE scope_type IS NULL;  -- ADD COLUMN 後既有 row 此欄為 null

-- 驗證
SELECT COUNT(*) FROM oscal.system_security_plan_system_implementations
 WHERE scope_type = 'ssp' AND scope_id IS NOT NULL;
-- 預期 140
```

### 4.2 不動的部分

- `implementation_type` 維持 `hardware`（不轉 `inventory-item`）
- `responsible_party` 既有純文字保留
- 既有 `system_security_plan_id` 既有資料維持指向原 SSP（CASCADE FK 保留）

---

## 5. jedi-oscal 程式碼擴充

> dev 期間走 **poetry path dependency**（主專案 `pyproject.toml` 改 path 指本地 source），Phase 2 完成後一次性 bump 版本推 Nexus。

### 5.1 ORM model 擴充

`jedi_oscal/infra/model/ssp/ssp_system_implementation.py`：

加 9 個 `Mapped[]` column 對應 §3.1；`system_security_plan_id` 改 `nullable=True`；補 3 個 Index。

### 5.2 Entity / Query Entity 擴充

`jedi_oscal/domain/entity/ssp/`：

- `SspSystemImplementationEntity` 加 9 個 attribute
- `SspSystemImplementationQueryEntity` 加 scope_type / scope_id / device_id / information_system_id 篩選欄位

### 5.3 Repository 擴充

`jedi_oscal/infra/repository/ssp/ssp_system_implementation_repo_impl.py`：

- 既有 CRUD 維持
- 補 query method：`list_by_scope(scope_type, scope_id)` / `find_by_device_id(device_id)` / `find_by_information_system_id(info_system_id)`

### 5.4 Mapper 擴充

`jedi_oscal/infra/mapper/ssp/system_implementation_mapper.py`：

entity ↔ model 對應 9 個新欄位的雙向 mapping

### 5.5 OSCAL YAML Mapper 擴充

`jedi_oscal/infra/mapper/ssp/ssp_yaml_mapper.py`：

依 `implementation_type` 分支序列化：
- `hardware` → OSCAL `inventory-items[*]`（含 uuid / description / props（補 device_id soft ref））
- `component` / `software` / `service` 等 → OSCAL `components[*]`（含 type / title / description / purpose / status）
- `leveraged-authorization` → OSCAL `leveraged-authorizations[*]`（含 title / party-uuid / date-authorized）

### 5.6 DTO 擴充

`jedi_oscal/app/dto/ssp/ssp_system_implementation_dto.py`：加 9 個欄位。

### 5.7 Enum 擴充

`jedi_oscal/common/enum/code_enum.py`：`SystemImplementationType` 加 `LEVERAGED_AUTHORIZATION = "leveraged-authorization"`。

---

## 6. 既有 Caller Regression 驗證

A0 不改變既有 caller 的寫入行為（既有 caller 不寫新增的 9 欄，全部 nullable 預設 null）。但要驗證：

### 6.1 `project_device_mapping_service`

- 既有寫入 `hardware` row 流程不變
- 驗證新欄位 nullable 不影響 insert

### 6.2 `ssp_versioning_service`

- 既有 SSP 版本複製流程不變
- 驗證 `system_security_plan_id` 改 nullable 後不影響既有複製邏輯

### 6.3 既有 query

`grep` 全 repo 確認沒有 hard-code 假設 `system_security_plan_id` 為 NOT NULL 的 query。

---

## 7. 測試範圍

| 測試類別 | 範圍 |
|---------|-----|
| **Migration test** | 跑 migration → 驗證既有 140 筆 scope 已補；驗證新欄位為 null；驗證 enum 已加 leveraged-authorization |
| **ORM CRUD** | 新欄位讀寫測試（scope / device_id / information_system_id / title / purpose / status / party_uuid / date_authorized）|
| **Repository query** | `list_by_scope` / `find_by_device_id` / `find_by_information_system_id` 三個新 method |
| **Mapper round-trip** | entity ↔ model 雙向 mapping 完整性 |
| **OSCAL YAML serialize** | implementation_type 三分支（hardware / component / leveraged-authorization）序列化正確 |
| **既有 caller regression** | `project_device_mapping_service` + `ssp_versioning_service` 既有測試跑通 |

---

## 8. 不在 A0 範圍

| 不做 | 理由 / 何時做 |
|------|------------|
| Excel parser / 匯入流程 | A2 |
| 鉤稽（match）演算法 | A3 / A4 |
| Confirm 寫入 service（含 inline 新建 device）| A5 |
| docx generator | B1 / B2 |
| OSCAL JSON / XML serialize（YAML 以外）| B5 — A0 只擴充 yaml_mapper，JSON/XML 另案 |
| `responsible_party` 既有純字串資料遷移到 `oscal_responsible_parties` | 列入「最後統整優化清單」 |
| 既有 `hardware` 是否完全淘汰改 `inventory-item` | 列入「最後統整優化清單」 |
| Frontend 改動 | A5 / B6 |

---

## 9. 開工前置檢查（pre-flight verification）

實作開工前需 verify：

- [ ] `jedi_oscal/infra/model/ssp/ssp_system_implementation.py` 仍存在且結構與 §2.1 相符
- [ ] `jedi_oscal/common/enum/code_enum.py` 內 `SystemImplementationType` 仍有 6 個值
- [ ] `app/associations/service/project_device_mapping_service.py` + `app/oscal/service/ssp_versioning_service.py` 仍是既有 caller
- [ ] DB 140 筆 hardware 數量未變
- [ ] 主專案 `pyproject.toml` 內 `jedi-oscal` 版本 pin 確認，準備改 path dependency

---

## 10. Acceptance Criteria

A0 視為 shipped 的條件：

1. ✅ Migration SQL 在 dev DB 跑通，140 筆既有資料 scope_type='ssp' 補全
2. ✅ jedi-oscal 8 個層（ORM / entity / query_entity / repo interface + impl / mapper / yaml_mapper / DTO / enum）擴充完成
3. ✅ 新欄位 CRUD 單元測試 + scope query 測試通過
4. ✅ Mapper round-trip 測試通過
5. ✅ OSCAL YAML 三分支 serialize 測試通過
6. ✅ 既有 caller (`project_device_mapping_service` + `ssp_versioning_service`) integration 測試通過
7. ✅ 主專案 dev 啟動 + smoke test 通過（驗證 nullable 化既有欄位後 boot 不破）
8. ✅ Changelog 完成（type=tweak，因為是基礎建設不直接讓 user 能做新事情）
