# FR-037 OSCAL 全表族補強表（欄位對齊 + 必填補齊）

| 項目 | 內容 |
|------|------|
| 目標 | 全部 OSCAL 相關表的欄位名對齊規範名（additive）+ 必填欄位補齊 |
| 原則 | **additive**：保留既有 FK / 欄位，新增 OSCAL 同名欄位；href / import 只存 UUID |
| 命名 | OSCAL 連字號 JSON key → snake_case（`import-profile` → `import_profile`）|
| 來源 | 官方 sample（usnistgov/oscal-content）+ 5 份 schema + jedi-oscal ORM 全表 + `compliance.poams` |
| 涵蓋 | base/共用 12 表 · catalog 5 · profile 2 · ssp 10 · ap 5 · ar 5 · poam 1 |

## 動作圖例

| 記號 | 意義 | 成本 |
|---|---|---|
| ✅ | 已同名對齊（語意一致，免動）| 0 |
| 〰️ | 別名——欄名不同但語意對，**只需在 mapper / 文件標 alias，不動 DB** | 文件 |
| ➕ | 新增 OSCAL 同名欄（additive，既有欄保留）| 小 migration |
| 🔧 | 改約束（nullable→NOT NULL / 詞彙值域 / 反向過嚴放寬）| migration + 資料轉換 |
| 🆕 | 整段未建模，需新增表 / 結構 | 大，綁必填補齊 |

> ⚠️ ORM 加欄與 DB migration 必須同時上（SQLAlchemy 查詢帶所有 mapped column）。每個 ➕/🔧 = model 改 + migration + 套 3 環境。

---

## PART 1 — 命名對齊（additive 欄位，最便宜，「用增加的就好」核心）

純新增 OSCAL 同名欄、保留既有欄，零資料風險。匯出時用新欄、內部 join 用舊欄。

### 1.1 import 跨文件參照（href 只存 UUID）

| 表 | ➕ 新增欄 | 存什麼 | 既有保留 | 回填來源 |
|---|---|---|---|---|
| `oscal.ssps` | `import_profile` UUID | 該 SSP 套用 Profile 的 `uid` | `profile_id` FK | `profiles.uid WHERE id=profile_id` |
| `oscal.assessment_plans` | `import_ssp` UUID | 對應 SSP 的 `uid` | `ssp_id`(stringified int) | `ssps.uid WHERE id=CAST(ssp_id AS int)` |
| `oscal.assessment_results` | `import_ap` UUID | 對應 AP 的 `uid` | `assessment_plan_id` FK | `assessment_plans.uid WHERE id=assessment_plan_id` |
| `compliance.poams` | `import_ssp` UUID | 來源 SSP 的 `uid` | （目前只有 `assessment_plan_id`）| 由 AP→SSP 推 |

### 1.2 OSCAL 同名小欄補強（additive）

| 表 | ➕ 新增同名欄 |
|---|---|
| `oscal.oscal_props` | `uuid`, `group`, `remarks` |
| `oscal.oscal_links` | `resource_fragment`, `text` |
| `oscal.oscal_roles` | `short_name`, `remarks` |
| `oscal.oscal_responsible_parties` | `remarks` |
| `oscal.catalog_groups` | `group_id`(OSCAL token id，如 A.5)、`group_class` |
| `oscal.catalog_controls` | `control_class` |
| `oscal.catalog_control_parts` | `part_id`(token)、`ns`、`part_class`、`title` |
| `oscal.catalog_control_parameters` | `param_class`、`depends_on`、`usage`、`remarks` |
| `oscal.ssp_system_characteristics` | `system_name_short`、`date_authorized`、`network_architecture_description`、`data_flow_description` |
| `oscal.ssp_control_implementations` | `description`(control-implementation block-level，**OSCAL REQ**)、`control_id`(同名於 control-id) |
| `oscal.ssp_control_implementation_objectives` | `statement_id`(同名於 statement-id) |
| `oscal.assessment_result_datas` | `description`(**OSCAL result.description REQ**) |
| `compliance.poams` | `title`(**REQ**)、`description`(**REQ**) |

### 1.3 別名文件化（〰️，不動 DB，只在 mapper / 文件標 alias）

| 我們欄位 | OSCAL key | 表 |
|---|---|---|
| `prop_class` | `class` | oscal_props |
| `role_id` | `id` | oscal_roles |
| `uid` | `uuid` | 所有有 uid 的表（party/location/catalog/profile/ssp/…）|
| `party_type` | `type` | oscal_parties |
| `component_type` | `type` | ssp_components |
| `name` | `system-name` | ssp_system_characteristics |
| `system_identifier` | `system-id.id` | ssp_system_characteristics |
| `started_at` / `completed_at` | `start` / `end` | assessment_result_datas |
| `reference_url` | `href` | assessment_result_evidences |

> 別名是「避 Python keyword / PK 衝突」的既有合理設計（`class`→`prop_class`、`id`→`role_id`、`uuid`→`uid`），**不建議改名**（會 breaking），只需序列化時 map 回 OSCAL 名。

---

## PART 2 — 約束 / 詞彙修正（🔧）

| 表.欄位 | 現況 | OSCAL | 動作 |
|---|---|---|---|
| `catalog_control_parts.prose` | NOT NULL | part.prose **選填** | 🔧 放寬 nullable（反向過嚴，擋匯入）|
| `ssp_components.status` / `.description` | nullable | component 必填 | 🔧 → NOT NULL |
| `ssp_leveraged_authorizations.party_uuid` / `.date_authorized` | nullable | 必填 | 🔧 → NOT NULL |
| `ssp_system_characteristics.status` | active/archived | state: operational/under-development/under-major-modification/disposition/other | 🔧 詞彙改值域 |
| `ssp_system_characteristics.description` | nullable | 必填 | 🔧 → NOT NULL |
| `ssp_system_characteristics.scope_description` | nullable | authorization-boundary.description 必填 | 🔧 → NOT NULL（並〰️別名 authorization_boundary_description）|
| `assessment_result_datas.title` / `.started_at` | nullable | result.title/start 必填 | 🔧 → NOT NULL |
| `assessment_result_findings.title` | nullable | finding.title 必填 | 🔧 → NOT NULL |
| `assessment_result_evidences.description` | nullable | relevant-evidence.description 必填 | 🔧 → NOT NULL |
| `assessment_plan_tasks.task_type` | manual/automated/hybrid | task.type: milestone/action | 🔧 OSCAL type 被占用 → 另立 `task_type_oscal` 或詞彙修正；manual/hybrid 移 prop |
| `assessment_result_controls.verdict` | pass/fail/partial/na | finding-target status: satisfied/not-satisfied | 🔧 詞彙 mapping（partial/na 在二值下有損，補 implementation-status/remarks）|

---

## PART 3 — 必填補齊需新建表 / 結構（🆕）

依層與規模排序。這些是「整段沒表、required 欄位無處存」。

### SSP（FR-036 同期動表最划算）

| 🆕 新結構 | 必填欄位 | 對應 OSCAL |
|---|---|---|
| `ssp_system_information_types`（+ C/I/A impact 子欄/表）| title / description / categorization.system / impact.base | system-information.information-types（FIPS 199）|
| security-impact-level（3 欄或子表）| security-objective-confidentiality / -integrity / -availability | security-impact-level |
| `ssp_system_users`（+ authorized-privileges）| user.uuid；privilege.title / functions-performed | system-implementation.users |
| `ssp_by_components`（+ export/provided/responsibility/inherited/satisfied）| component-uuid / uuid / description / implementation-status.state | statement.by-components |
| `ssp_set_parameters` | param-id / values | control-impl / impl-req / by-component |
| `ssp_diagrams` | diagram.uuid | authorization-boundary / network / data-flow |
| `ssp_component_responsible_roles`（link）| role-id | component.responsible-roles |

### AR（規模大，建議獨立 phase）

| 🆕 新結構 | 必填欄位 | 對應 OSCAL |
|---|---|---|
| finding-target（欄位群或表）| type / target-id / status.state / implementation-status.state | finding.target（finding 核心）|
| `assessment_result_observations` 表族 | uuid / description / methods / collected（+ origins/subjects/evidence）| result.observations |
| `assessment_result_risks` 表族 | uuid / title / description / statement / status（+ characterization/facets/response/risk-log/threat-id）| result.risks |
| assessment-log / attestations | entry.uuid/start；attestation.parts | result（選填）|

### 共用層（影響所有模型 round-trip）

| 🆕 新結構 | 必填欄位 | 備註 |
|---|---|---|
| `oscal_resources`（back-matter resource）| resource.uuid；rlink.href；base64.value；citation.text | **整體最大洞**；`oscal_documents` 是原始檔 blob，≠ resource，不可混用 |
| `oscal_hashes` | value / algorithm | 掛 resource.rlinks |
| `oscal_document_ids` | identifier | metadata / resource 的 document-ids[] |
| `oscal_revisions` | version | metadata.revisions[]（現 `revision` 單一純量存不了清單）|

### Profile（OSCAL optional，但功能性大缺口）

| 🆕 新結構 | 對應 OSCAL |
|---|---|
| `profile_imports`（多 catalog / include-all / matching / with-child-controls）| profile.imports（現壓成單一 catalog_id + 扁平 profile_controls）|
| merge / modify(set-parameters / alters) | profile.merge / modify（完全沒建模）|

### Catalog / 共用 巢狀 & props（OSCAL optional）

| 🆕 結構 | 備註 |
|---|---|
| `catalog_controls.parent_control_id` self-FK | sub-control / enhancement（如 AC-2(1)）|
| `catalog_control_parts.parent_part_id` self-FK | 巢狀 part（statement→item）|
| props / links association（catalog/profile 全層）| OSCAL 通用擴展點，目前全層未建模 |
| param constraints / guidelines / select / 多值 values | parameter 豐富結構 |
| party / location 多值子表（email/phone/address/url 陣列）| 現為單值純量 |

### AP（OSCAL optional）

| 🆕 結構 | 對應 OSCAL |
|---|---|
| activity / step（local-definitions）| activity.uuid/description；step.uuid/description |
| assessment-subjects / assessment-assets | subject-uuid/type；platform.uuid |
| task subjects（relationship 目前註解停用）/ timing / dependencies | task 子結構 |

---

## PART 4 — 非 OSCAL 擴充表（標明，不對齊）

這些是產品需要、OSCAL 無對應的表，**保留不動**，只標明避免後人誤以為是 OSCAL 構件：

| 表 | 說明 |
|---|---|
| `catalog_control_assessments` | 內部「控制評估樣板」，產 AP tasks 用；docstring 自承非 OSCAL |
| `assessment_plan_groups` | 鏡射 catalog group；OSCAL AP 無 group |
| `assessment_task_controls` | task↔control 直連；OSCAL 走 task→activity→related-controls |
| `oscal_documents` | 原始 OSCAL 檔 blob；**≠ back-matter resource** |
| ssp 表的 `tenant_id` / `org_unit_id` / `is_active` | RLS / 軟刪除 |
| `ssp_inventory_items` 的 `asset_type` / `ref_type` / `ref_id` | FR-032 hybrid soft-ref |
| `ssp_control_implementation_objectives.reference_documents`(JSONB) | 程序書池 |

---

## PART 5 — 遷移風險（先處理）

`oscal.ssp_inventory_item_components` 仍 FK 到 legacy `oscal.ssp_system_implementation_items`；OSCAL-aligned 正版 `ssp_inventory_implemented_components`（FK 指 `ssp_inventory_items`/`ssp_components`）已存在並並存。動 inventory↔component 前先確認 live 表，legacy 表依 Task 12 DROP。

---

## PART 6 — 建議分批

1. **Batch A｜命名對齊（PART 1）+ 約束修正（PART 2）**：全 additive 欄位 + 約束/詞彙。低風險、效益高（後人零 mapping）。一支 migration。
2. **Batch B｜SSP 必填補齊（PART 3 SSP 段）**：與 FR-036 動 `ssp_*` 同期。information-type / by-component / system-user / set-parameter / diagrams。
3. **Batch C｜AR 必填補齊 + back-matter（PART 3 AR + 共用段）**：規模大，獨立 phase。observation / risk / finding-target / oscal_resources。
4. **Batch D｜Profile / Catalog 巢狀 / props（OSCAL optional）**：視 conformance 目標決定做不做。

> 全程：jedi-oscal 走 path-dep dev、user 明示才發版；migration 用 cmmgr、加日期註解、INSERT schema_migrations、dev→stg→poc 漸進；app 與 migration 同步上版。

---

## 待 user 確認

1. **Batch A 是否先動手**（命名對齊 + 約束修正，全 additive + 低風險，最符合「用增加的就好」）？
2. **Batch B 是否併入 FR-036**（兩者都動 `ssp_*`，FR-036 結構先定、本批欄位補在其上）？
3. **Batch C / D conformance 目標**：要做到完整 OSCAL round-trip（observation/risk/back-matter），還是先擱置？
