---
title: "CRA 第二期 CRA 層 — 設計文件 (FR-135)"
brand: "Guidant AI · **FR-135** CRA 合規框架"
eyebrow: "FR-135 · 第二期 design-cra · 2026-10-12（D1～D17 已裁）"
h1: "CRA 專案填得齊產品資料、風險評估與證據來源"
subtitle: "P2-B 表單設定、P2-C 產品版本與風險評估、P2-D CRA 內容（建庫帶預設、評估目標調整、證據來源、文件範本）"
lede: "第〇步拿自家材料走完 CRA 31 條，露出 CRA 本身要補的內容：受評標的沒地方填用途、分級、支援期；沒有製造商與通報聯絡人；沒有風險評估；7 條一條包多件事；有些證據客戶在系統別頁填完就有、卻要他再上傳。本文件把已裁的 D 項展開到「照做就能交」：資源庫第四件「表單設定」決定 CRA 專案多出哪些欄位與頁籤、製造商與通報聯絡人用 OSCAL 的 Party 與角色、風險評估開新表並對齊 OSCAL 風險物件、顧問從畫面新增 CRA 資源庫時由建庫程式依框架帶入預設內容、評估目標（AO）全面調整為 4 群 34 條 49 個、AO 帶「證據來源」屬性直接連到系統內的頁面。CMMC 專案畫面一點都不變。"
chips: [
  {text: "D1～D17 已裁（本文件管 14 項）", kind: ok},
  {text: "13 張卡：F1～F4 V1 R1-a R1-b C1 C1b C2 C2b C3 E1", kind: accent},
  {text: "跨 3 repo：BE／FE／jedi-python-package", kind: accent},
  {text: "表單設定為空＝CMMC 畫面不變", kind: ok},
  {text: "陷阱：預設內容只在建庫當下寫入，舊資源庫不補", kind: crit},
  {text: "陷阱：規劃頁 2,600 行級，新邏輯一律新開元件", kind: crit}
]
footer: "FR-135 · CRA 第二期 CRA 層設計文件 · 2026-10-12 · 討論稿：[phase2-discussion.md](phase2-discussion.md)（現況盤點、選項推演）· 平台層：[design-platform.md](design-platform.md) · 第〇步缺口：[step-zero-gap-list.md](step-zero-gap-list.md) · AO 內容：[cra-assessment-objectives.md](cra-assessment-objectives.md) · 沿革見 LOG 與 git log"
---

> 狀態：**設計定案（D1～D17 已裁，本文件展開 D1 D2 D3 D5 D6 D7 D9 D10 D11 D13 D14 D15 D16 D17）**｜日期：2026-10-12｜平台層（規劃頁條文、無 AO 開工、覆核輪、收證據範本）見 [`design-platform.md`](design-platform.md)

> 🔴 **這份是設計決策，不是現況。** 內文行號是 2026-10-12 的位置，會隨程式漂移；上線後的現況看 SPEC 頁。

## 變更紀錄 {#changelog nav="變更紀錄"}

| 日期 | 變更 | 對應 |
|---|---|---|
| 2026-10-11 | v1：P2-B／C／D 設計定案 | 母卡 CM-2675 |
| 2026-10-12 | v2：對齊已落地的實作與決策者裁示——建庫帶預設內容機制、程序書與證據分兩套、風險評估「依據標準」、AO 全面調整、AO 證據來源機制；十三張卡附實際 Case No | CM-2705 |

---

## 需求背景與端到端流程 {#background nav="背景"}

CRA（歐盟網路韌性法，Regulation (EU) 2024/2847）要求製造商回答「你為什麼這樣做、依據是什麼」，不只是逐條打勾。一個 CRA 專案要能留下：**圈範圍**（用途、誤用、環境、角色、八題適用判定、產品分級）、**Annex II 使用者資訊**（製造商、漏洞通報窗口、揭露政策網址、產品識別、支援期結束日）、**風險評估**（威脅、依據標準、資產、衝擊、可能性、既有控制、措施、對應控制項）、**產品版本與支援期**、**評估目標拆分**（一條控制項底下要查的幾件具體事情）、**證據來源**（系統內已有的資料直接當證據，客戶自有的檔才上傳）。

### 端到端：資源庫四件 → 建專案 → 專案 SSP → 規劃頁

顧問視角：從畫面新增資源庫、框架選 CRA，建庫程式依框架代號 `CRA_2024` 自動帶入表單設定、Annex II 預設說明、製造商與通報聯絡人兩個空殼、五份文件範本。用這個資源庫建專案，規劃頁就多出 CRA 專用欄位與「風險評估」頁籤，參與單位頁已有兩個空位，程序書池已有範本並掛好控制項。CMMC 資源庫沒有這些預設內容，專案完全不受影響。

```{.mermaid cap="圖 1 — 資源庫如何流到 CRA 專案（虛線＝建庫時依框架帶入）"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  subgraph RL["新增 CRA 資源庫（module_frames）"]
    P["① 控制項選集<br/>catalog＋profile<br/>（4 群 34 條 49 AO）"]
    S["② SSP 範本<br/>Party 空殼／A2.x 預設說明"]
    F["③ 稽核流程範本"]
    FS["④ 表單設定<br/>form_settings"]
    POOL["程序書池<br/>文件範本 5 份"]
  end
  DEF["framework_library_defaults/<br/>cra_2024.py（建庫時依框架 code 查表）"]
  subgraph PJ["CRA 專案"]
    SC["受評標的<br/>SC.props ext:cra-*"]
    PARTY["參與單位<br/>製造商／通報聯絡人"]
    RISK["風險評估表<br/>ssp_risks"]
    PLAN["規劃頁<br/>依 sections 開頁籤"]
    AOP["AO 面板<br/>證據來源或上傳"]
  end
  IS["資訊系統盤點<br/>＋版本子表"]
  DEF -.-> S
  DEF -.-> FS
  DEF -.-> POOL
  P -->|建專案複製| PLAN
  S -->|深拷貝| SC
  S -->|深拷貝 metadata| PARTY
  POOL -->|整池複製＋對應| PLAN
  F --> PLAN
  FS -->|專案讀資源庫設定| PLAN
  FS -->|宣告欄位| SC
  PLAN -->|頁籤開關| RISK
  IS -->|版本與支援期| SC
  P -->|AO 屬性 evidence-source| AOP
  RISK --> AOP
  SC --> AOP
  PARTY --> AOP
  IS --> AOP
```

本文件只管 CRA 層；平台層兩項（規劃頁看條文、無 AO 開工）見 [`design-platform.md`](design-platform.md)，兩份可平行施工。

---

## 分工概述（30 秒版） {#overview nav="概述"}

整案一句話：**CRA 專案多出一套 CRA 專用的欄位、角色與風險評估頁，AO 調整為 49 個並可直接連到系統內的證據來源；CMMC 專案一個像素都不變。**

| 卡 | Case No | 做什麼 | 規模 | 決策者檢查法 |
|---|---|---|---|---|
| **F1** 表單設定落點 | CM-2690 | `module_frames` 加 `form_settings` 欄、程式驗結構 | S | DEV 查 CRA 資源庫 `form_settings` 有值、CMMC 資源庫是 NULL |
| **F2** 專案取設定端點 | CM-2693 | 兩支 GET 端點：資源庫取、專案 SSP 取 | S | Swagger 打 `GET /ssp/<CRA 專案 SSP uid>/form-settings` 回欄位清單；打 CMMC 專案回 `null` |
| **F3** 前端動態欄位與頁籤開關 | CM-2699 | 受評標的頁依設定渲染擴充欄位；BE 收送 `ext_fields`／`ext_links` | M | CRA 專案受評標的頁多四段、填完重整還在；CMMC 專案畫面不變 |
| **F4** 建庫帶 CRA 預設 | CM-2700 | `create_resource_library` 依框架 code 帶表單設定 22 欄、A2.1～A2.9 預設說明、兩個 Party 空殼 | S～M | 畫面新增 CRA 資源庫：表單設定、預設說明、兩個空角色都在；新增 CMMC 資源庫什麼都沒多 |
| **V1** 資訊系統版本子表 | CM-2694 | jedi-asset 新子表＋CRUD＋盤點頁子表格 | M | 盤點任一筆底下新增 v1.0／v1.1，各有上市日與支援期結束日 |
| **R1-a** 風險評估本體 | CM-2701 | 新表 `oscal.ssp_risks`、DDD 四層、端點、手填表格 | M | CRA 專案新增兩筆風險、勾對應控制項、重整還在 |
| **R1-b** 頁籤開關與依據標準 | CM-2702 | 頁籤依表單設定顯示；「依據標準」下拉讀 `system_menus` | S | CMMC 專案看不到頁籤；風險編輯的依據標準有 10 項下拉 |
| **C1** AO 拆分範本 | CM-2691 | 首版 AO 拆分文件與套件 seed 004 | S | 打開 md 看拆點與依據 |
| **C1b** AO 全面調整 | CM-2696 | 新群組、拆條、改句、建議證據三段式掛各 AO、套件 seed 005 | M～L | 框架 4 群 34 條 49 AO；每個 AO 有自己的建議證據 |
| **C2** 收舊套掛檔＋刪舊 clone | CM-2695 | 控制項層舊上傳路徑刪除、詳情改讀程序書池；刪舊 clone 端點 | M | `POST /module-frame/clone` 回 404；控制項詳情 `reference_documents` 回池資料 |
| **C2b** 撤回程序書 locator | CM-2697 | 拔掉加錯層的 `locator` 欄與前後端帶欄 | S～M | `oscal.ssp_reference_document_mappings` 無 `locator` 欄 |
| **C3** 文件範本骨架 | CM-2703 | 五份 docx 骨架，建庫時上傳進程序書池並預掛 | S | 新 CRA 資源庫程序書頁有五份、15 條控制項共 16 筆掛載 |
| **E1** AO 證據來源機制 | CM-2704 | AO 屬性 `evidence-source`、解析端點、規劃頁與判定頁依屬性切顯示 | M | 風險評估那條 AO 面板顯示「證據來源：風險評估頁，已登錄 N 筆」並可跳轉，不顯示上傳區 |

依賴：F1 → F2 → F3 → R1-b；R1-a 與 F3 合流後 R1-b；F4 依賴 F1，畫面等 F3；C3 依賴 F4（共用建庫機制）；C1 → C1b → E1；C2 → C2b；V1 隨時可派。

## 決策定案 {#decisions nav="決策"}

### 決策者已裁

| 項 | 裁示 | 被排除的方案與原因 |
|---|---|---|
| **D1** 複製資源庫 | 本期範圍外；專案直接讀資源庫的表單設定 | 新做複製服務：要連 catalog、profile、SSP 範本、流程範本、程序書池一起處理，L 級工程，第二期用不到 |
| **D2** 表單設定落點 | `compliance.module_frames` 加 JSONB 欄 `form_settings`，結構檢查在應用層 | 新表 `module_frame_form_settings`：多 repo、mapper、RLS 政策，最小版用不到每欄一列的約束 |
| **D3** 專案取設定 | 主專案新端點，不改 jedi-compliance-audit DTO | 改套件 DTO 加欄：要為一個欄位發套件版本、碰其他 consumer |
| **D5** 公版資源庫 | 公版機制裁撤：不隨安裝出貨 CRA 資源庫、不用 seed CLI；驗收由決策者從畫面新建 CRA 資源庫與專案 | 出貨公版並提供 `--rebuild`：資源庫是顧問的事，出貨公版要連帶處理既有專案引用 |
| **D6** AO 拆分 | 不等顧問；依 ENISA 底稿、第〇步拆點、法條原文自產，標「AI 拆分待顧問確認」，經套件 seed 進框架；範圍由矛盾掃描報告與決策者裁示擴大為全面調整（§6.7） | 等顧問先拆：卡住第二期驗收 |
| **D7** 兩套程序書掛法 | 收掉控制項層舊上傳路徑，詳情 API 改讀程序書池；程序書不加 `locator`（程序書是公司 SOP，掛控制項證明有規定，不指章節） | 在程序書對應加 `locator`：加錯層，「證據指到檔內某節」屬證據那條 |
| **D9** 風險評估存哪 | 新表，掛專案 SSP，欄位對齊 OSCAL 風險物件；與改善計畫（POA&M）分開 | 借 AR 殼（主體錯、要先造評估計畫）；借 POA&M（與「改善計畫＝結案才有」的心智模型衝突）；SC.props JSON 陣列（多人覆寫、無外鍵、統計要拆 JSON） |
| **D10** 文件範本 | 顧問主筆；runner 只做格式，本期放骨架佔位；骨架放 repo，建庫時走 F4 機制上傳並預掛 | AI 起稿直接出貨：對外法律文件需有人負責內容 |
| **D11** 舊 clone 端點 | 刪，FE 常數一起刪 | 保留：無人呼叫，讓人誤以為已有「複製資源庫」 |
| **D13** 製造商與通報窗口 | 製造商與漏洞通報聯絡人是 SSP metadata 的 Party，以角色掛上；揭露政策網址、安全通告頁、聲明取得位置是 SSP link；支援期結束日、產品識別等單值走 SC.props `ext:` 前綴 | 全放 SC.props：通報模組要讀「人」與聯絡方式，props 存不了結構；放資訊系統主檔：改 jedi-asset 主表且所有框架都看得到 |
| **D14** 自家 SBOM 成證據 | 本期範圍外 | — |
| **D15** 證據來源機制 | AO 多「證據來源」屬性，指向系統內已有的資料；規劃頁與判定頁依屬性切顯示（§6.11） | 全部要客戶匯出再上傳：同一件事兩份真相；為每個來源各做獨立頁：重複造輪子 |
| **D16** 下拉選項進字典表 | 畫面下拉、單選、分類標籤的選項一律存 `public.system_menus`，由主線 `phase=seed` migration 寫入，FE 走既有選單讀取 | 選項寫進 FE 元件或框架預設內容：管理員想加一項要改程式 |
| **D17** 風險評估不做威脅清單與精靈 | 系統不出威脅清單、不自動帶入；顧問填威脅時選「依據標準」（下拉）並自填條號 | 內建威脅庫：系統不是有公信力的單位；全自由文字：沒有人會填 |

### 實作層決定（本文件定案）

| 編號 | 決定 | 理由 |
|---|---|---|
| **C-P1** | `form_settings` JSON 結構固定三鍵：`version`（整數，現為 1）、`sections`（物件，布林開關）、`groups`（陣列，每組 `key`／`title`／`fields[]`，選填 `collapsed`／`help` 組頂一句說明／`gate`）；欄位物件 `key`／`label`／`type`／`required`／`options`／`help`／`reason`。`type` 限 `text`／`textarea`／`url`／`date`／`select`／`boolean` | `textarea`、`url` 是 `text` 的顯示變體，型別檢查仍歸四型；`reason: true` 讓布林題自帶「理由」文字欄，八題適用判定不必拆 16 個欄位 |
| **C-P2** | 欄位值存受評標的 `SC.props`，鍵一律 `ext:cra-*`；理由欄鍵為 `<key>-reason` | 避開既有 `target-type`／`owner-uid`；`ext:` 前綴讓 OSCAL 匯出時一眼看出是產品擴充 |
| **C-P3** | 新端點只讀：`GET /module-frame/<uid>/form-settings`、`GET /ssp/<uid>/form-settings`；SSP 版只服務專案 SSP（經 `ctx.project.module_frame_id` 取資源庫），資源庫範本編輯頁用資源庫 uid 那支 | 規劃頁手上只有專案 SSP uid；資源庫範本編輯頁手上只有資源庫 uid |
| **C-P4** | Party 角色 id：`manufacturer`、`vulnerability-contact`，登記進 `system_menus` 群組 `ssp_party_role`；現行 DEV 沒有 `oscal.oscal_roles` 表，不寫該表 | 沿用既有 9 個角色的 kebab-case 命名；FE 下拉自動多兩項 |
| **C-P5** | 角色掛法沿用產品既有做法：party 本體的 `props.responsible-role`，**不寫** `metadata.responsible_parties` | 建專案時 party uuid 會重產（jedi-oscal-v2 `metadata_clone_service.py`），`metadata.responsible_parties` 照原樣複製會指到舊 uuid |
| **C-P6** | link 存受評標的 `SC.links`（OSCAL `[{href, rel, text}]`），`rel` 用 `ext:cra-disclosure-policy`／`ext:cra-advisories`／`ext:cra-doc-location` | `SC.links` 已有欄且建專案深拷貝自然帶過；`oscal.metadata.links` DEV 0 筆使用、無服務寫入入口 |
| **C-P7** | 風險表名 `oscal.ssp_risks`；租戶隔離比照 `compliance.source_scans`（`tenant_id`＋`org_unit_id`＋RLS FOR ALL，ENABLE＋FORCE） | `oscal.*` 既有表沒有租戶欄、RLS 擋不到；新表一開始就補上 |
| **C-P9** | AO 的 `part_id` 為 `<control_id>_obj.<N>`，N 從 3 起算（1＝`_smt`、2＝`_gdn` 已佔）；`sort_order` 與 N 相同；`props` 不帶 `workflow-template-uid` | 對齊 CMMC 的 `_obj.N` 與 sort_order 一致慣例；流程範本 uid 由建庫時 `_init_ao_workflows` 補，seed 寫死會指到不存在的範本 |
| **C-P10** | 建庫預設內容放 `app/oscal/service/framework_library_defaults/<框架 code>.py`（純資料），由 `__init__.py` 的 `REGISTRY` 以框架 code 查表；查無＝什麼都不寫 | 一框架一檔，新增框架只加一檔一列；CMMC 沒有檔，行為不變 |
| **C-P11** | AO 證據來源存 AO part 的 `props`，名 `evidence-source`；狀態不隨控制項樹回傳，另打端點取 | 控制項樹已很重；先例是 `workflow-template-uid` 同樣存 AO props |

---

## 接入點 {#inventory nav="接入點"}

| 項目 | 落點 | 做法 |
|---|---|---|
| 資源庫表 | `compliance.module_frames`；ORM `infra/module_frame/models/module_frame.py`、mapper `module_frame_mapper.py`、entity `module_frame_entity.py` | `form_settings` JSONB 欄，三處帶欄 |
| 建庫入口 | `app/oscal/service/resource_library_app_service.py` 的 `create_resource_library` | 建庫尾端兩行呼叫 `apply_framework_defaults`，同一交易 |
| 建庫預設內容 | `app/oscal/service/framework_library_defaults/`（`__init__.py` 查表套用、`cra_2024.py` 純資料） | 表單設定、A2.x 預設說明、Party 空殼、文件範本清單 |
| 受評標的 props | `module_frame_system_characteristic_service.py`（資源庫端）、`ssp_system_characteristic_app_service.py`（專案端，讀 `require_participant`、寫 `require_manager`） | 擴充鍵走同一合併工具，收送 `ext_fields`／`ext_links` |
| Party | 資源庫 `module_frame_party_service.py`、專案 `ssp_party_app_service.py`；角色存 `props.responsible-role`；FE `ModuleFramePartiesPanel.vue` 讀 `system_menus` `ssp_party_role` | 兩個角色進字典表；建庫預建兩筆空殼 |
| 風險 | 新表 `oscal.ssp_risks`；稽核結果層 `oscal.assessment_risks` 不動 | 四層＋頁籤 |
| 下拉字典 | `public.system_menus`（`group`／`key`／`value`／`sort`／`enable`／`public`） | 角色、風險依據標準都在這 |
| 資訊系統 | `compliance.information_systems`（jedi-asset，有 tenant_id／org_unit_id、RLS 已開） | 版本子表＋套件 migration 006＋子表格元件 |
| 程序書池 | `oscal.ssp_reference_document_mappings` 與資源庫程序書池服務 `module_frame_reference_document_service.py` | 建庫時上傳範本並預掛；詳情 API 讀池資料 |
| AO | `oscal.catalog_control_parts` name=`assessment-objective`，底下的 `guidance` 子段落放該 AO 的建議證據；建庫補流程範本 `_init_ao_workflows` | 套件 seed 005；讀法 `app/oscal/service/control_part_prose.py` 的 `ao_guidance_map` |

體積：`ProjectPlanningView.vue`、`ssp_control_implementation_service.py`、`RoundAuditReviewView.vue` 已超標，本期在這幾檔只准刪與少數幾行（一行 `v-if`、字典補欄），新邏輯一律新開檔；`InformationSystemManage.vue` 797 行貼線，子表格必須新開元件。

## 詳細設計 {#design nav="設計"}

### 6.1 表單設定 {#design-form}

**資料庫**：migration `scripts/sql/2026-10-11-fr135-module-frame-form-settings.sql`

```sql
ALTER TABLE compliance.module_frames ADD COLUMN IF NOT EXISTS form_settings JSONB;
COMMENT ON COLUMN compliance.module_frames.form_settings IS '表單設定：專案受評標的多顯示哪些欄位、哪些 SSP 子頁籤；結構由應用層驗';
```

NULL＝照固定畫面（CMMC 不受影響）。不新建表，既有 GRANT 與 RLS 照舊。`manifest.tsv` 登記 `active *`，出貨基線待重產。

**三層帶欄**：ORM `form_settings: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)`；mapper 兩向帶；entity 建構子加參數。

**結構驗證**：`app/module_frame/service/form_settings_schema.py` 的 `validate_form_settings(dict) -> dict`，檢查三鍵、`type` 白名單、`key` 必為 `ext:` 開頭且不重複、`select` 必有 `options`。不合格拋 `BadRequestError(ModuleFrameErrorCode.MODULE_FRAME_FORM_SETTINGS_INVALID)`（`MODULE_FRAME_400013`）。唯一寫入者是建庫預設（§6.9），寫入前驗一次；讀取端不再驗。

**組的兩個選填鍵**：`collapsed`（布林，前端預設折疊該組）與 `gate`（`{when_any_true, when_any_false, message_key}`，兩個陣列只能列本組的布林欄位；任一題命中即判「不在範圍」，前端收起其餘組並顯示 `message_key` 的提示，使用者仍可手動展開）。CRA 的「適用判定」排第一組並帶 `gate`，其餘四組 `collapsed: true`；沒有這兩鍵的設定照舊全部展開。`sections.nist_fields: false`（CRA 預設）讓共用基本欄位藏起 FIPS 199 與授權邊界兩欄與頂部 NIST 導向說明；有表單設定時共用欄位也包成同樣的可折疊組「系統基本資料（各框架共用）」，預設展開。

**端點**（`api/module_frame/routes/form_settings_route.py`，app service `app/module_frame/service/form_settings_app_service.py`）：

| 端點 | 守門 | 回傳 |
|---|---|---|
| `GET /module-frame/<uid>/form-settings` | `verify_module_frame_exists_by_uid(uid)`（讀） | `{"data": <form_settings 或 null>}` |
| `GET /ssp/<ssp_uid>/form-settings` | `SspPermissionChecker.require_read_access(ssp_uid)` | 專案 SSP：`ctx.project.module_frame_id` → 資源庫設定 |

兩支都只讀。

**前端**（FE repo）：

- `src/composables/useFormSettings.js`：依 `apiBase` 打兩支端點之一、快取。
- `src/components/grc/ssp/SspExtFieldsSection.vue`：吃 `groups`，各型別對應 PrimeVue 元件（`InputText`／`Textarea`／`Calendar`／`Dropdown`／`InputSwitch`），`reason: true` 的欄位下方多一個理由框；送出走既有 system-characteristic PUT，多帶 `ext_fields: {<key>: <value>}`。
- `SspBasicSection.vue` 底部一行掛 `<SspExtFieldsSection v-if="settings?.groups?.length">`。
- `SspTabPanel.vue`：「風險評估」子頁籤 `v-if="settings?.sections?.risk_assessment"`，其他子頁籤不動。
- BE 受評標的服務 `_apply_payload` 多收 `ext_fields`：只接受 `ext:` 開頭的鍵，逐鍵 `_set_prop`；`_to_dict` 回傳 `ext_fields`（把 props 中 `ext:` 開頭者攤平）。資源庫與專案共用此對應，兩頁同時生效。

**CMMC 不變的條件**：`form_settings` 為 NULL 時，端點回 `null`、前端兩個 `v-if` 都不成立，DOM 與 CMMC 現況相同。

**CRA 資源庫的表單設定內容**（`cra_2024.py` 的 `FORM_SETTINGS`；`label` 為繁中、`help` 簡述法源；5 群 22 欄）：

```json
{
  "version": 1,
  "sections": {"risk_assessment": true},
  "groups": [
    { "key": "scope", "title": "產品用途與環境", "fields": [
      {"key": "ext:cra-intended-purpose", "label": "預期用途", "type": "textarea", "required": true, "help": "Art. 3(12)；技術文件第 1 項"},
      {"key": "ext:cra-foreseeable-misuse", "label": "可合理預見的誤用", "type": "textarea", "required": true, "help": "Art. 13(3) 風險評估要涵蓋"},
      {"key": "ext:cra-operational-environment", "label": "預期使用環境", "type": "textarea", "required": true, "help": "Annex II 第 4 項"},
      {"key": "ext:cra-operator-role", "label": "客戶在 CRA 的角色", "type": "select", "required": true, "options": ["manufacturer", "importer", "distributor", "authorised-representative", "open-source-steward"], "help": "Art. 3(13)～(18)"}
    ]},
    { "key": "applicability", "title": "適用判定", "fields": [
      {"key": "ext:cra-q-eu-market", "label": "會在歐盟市場上市或提供", "type": "boolean", "reason": true, "help": "Art. 2(1)"},
      {"key": "ext:cra-q-digital-elements", "label": "產品含數位元素（軟體或含軟體的硬體）", "type": "boolean", "reason": true, "help": "Art. 3(1)"},
      {"key": "ext:cra-q-data-connection", "label": "預期用途包含與裝置或網路的資料連線", "type": "boolean", "reason": true, "help": "Art. 2(1)"},
      {"key": "ext:cra-q-pure-saas", "label": "是純 SaaS（無製造商設計的遠端處理以外的產品部分）", "type": "boolean", "reason": true, "help": "前言 (12)；純 SaaS 不在範圍"},
      {"key": "ext:cra-q-spare-part", "label": "是同規格替換用的備品", "type": "boolean", "reason": true, "help": "Art. 2(6)"},
      {"key": "ext:cra-q-open-source-non-commercial", "label": "是非商業活動提供的開源軟體", "type": "boolean", "reason": true, "help": "前言 (18)～(19)、Art. 24"},
      {"key": "ext:cra-q-sector-exempt", "label": "已受專法規範（醫療器材、汽車、民航、船舶等）", "type": "boolean", "reason": true, "help": "Art. 2(2)～(4)"},
      {"key": "ext:cra-q-defence-security", "label": "專為國安或國防目的開發", "type": "boolean", "reason": true, "help": "Art. 2(7)～(8)"}
    ]},
    { "key": "classification", "title": "產品分級與支援期", "fields": [
      {"key": "ext:cra-product-class", "label": "產品分級", "type": "select", "required": true, "options": ["default", "important-class-i", "important-class-ii", "critical"], "help": "Art. 7～8、Annex III／IV；細節見 (EU) 2025/2392"},
      {"key": "ext:cra-product-class-reason", "label": "分級理由（對到 Annex III／IV 哪一類）", "type": "textarea"},
      {"key": "ext:cra-support-end-date", "label": "支援期結束日", "type": "date", "required": true, "help": "Art. 13(8)、Annex II 第 7 項"},
      {"key": "ext:cra-support-end-reason", "label": "支援期長度的理由", "type": "textarea", "required": true, "help": "Art. 13(8)：至少 5 年，短於預期使用期需說明"}
    ]},
    { "key": "annex2", "title": "產品識別與公開資訊", "help": "這些資訊會印進 Annex II 使用者資訊文件；用途、支援期、製造商、風險分別在其他組與頁籤填。", "fields": [
      {"key": "ext:cra-product-name", "label": "產品名稱與型號", "type": "text", "required": true, "help": "Annex II 第 3 項"},
      {"key": "ext:cra-product-identifier", "label": "產品識別（類型、批號、序號或版本）", "type": "text", "required": true, "help": "Annex II 第 3 項"},
      {"key": "ext:cra-sbom-access", "label": "SBOM 取得方式（若對使用者提供）", "type": "textarea", "help": "Annex II 第 9 項（選擇性）"}
    ]},
    { "key": "links", "title": "公開資訊網址", "fields": [
      {"key": "ext:cra-disclosure-policy", "label": "漏洞揭露政策網址", "type": "url", "help": "Annex II 第 2 項"},
      {"key": "ext:cra-advisories", "label": "安全通告頁網址", "type": "url", "help": "CRA.II.4／II.8 引用"},
      {"key": "ext:cra-doc-location", "label": "EU 符合性聲明取得位置", "type": "url", "help": "Annex II 第 6 項"}
    ]}
  ]
}
```

欄位數：scope 4、applicability 8、classification 4、annex2 3、links 3，共 22 個；8 題適用判定各帶理由欄，3 個網址欄存 links、其餘 19 欄加 8 個理由欄，props 最多 27 鍵。八題的「理由」對應 Art. 2 範圍判斷，是技術文件要寫的「為什麼適用／不適用」。欄位 `key` 是 SC.props 的鍵，有專案填過值就是資料契約，不可改名。

### 6.2 Annex II 單值與預設說明文字 {#design-annex2}

Annex II 九項按 D13 分三類：

| Annex II 項 | 控制項 | 落點 | 鍵或角色 |
|---|---|---|---|
| 1 製造商名稱、地址、聯絡方式 | CRA.A2.1 | Party（§6.3） | 角色 `manufacturer` |
| 2 漏洞通報單一聯絡點、揭露政策位置 | CRA.A2.2 | Party＋link | 角色 `vulnerability-contact`；`rel=ext:cra-disclosure-policy` |
| 3 產品名稱、類型、識別 | CRA.A2.3 | SC.props | `ext:cra-product-name`、`ext:cra-product-identifier` |
| 4 預期用途、安全環境、功能 | CRA.A2.4 | SC.props | `ext:cra-intended-purpose`、`ext:cra-operational-environment` |
| 5 已知或可預見的風險情境 | CRA.A2.5 | 風險評估表（§6.4） | — |
| 6 EU 符合性聲明取得位置 | CRA.A2.6 | link | `rel=ext:cra-doc-location` |
| 7 技術支援方式、支援期結束日 | CRA.A2.7 | SC.props | `ext:cra-support-end-date`、`ext:cra-support-end-reason` |
| 8 安全安裝、更新、停用等說明 (a)～(f) | CRA.A2.8 | 拆 AO（§6.7）＋證據 | — |
| 9 SBOM 取得方式 | CRA.A2.9 | SC.props | `ext:cra-sbom-access` |

安全通告頁網址另以 `rel=ext:cra-advisories` 存，供 CRA.II.4／II.8 引用。

**link 怎麼編輯**：受評標的服務 `_apply_payload` 多收 `ext_links: [{rel, href, text}]`，只接受 `ext:cra-` 開頭的 `rel`，同 `rel` 覆寫、其他 link 保留；`_to_dict` 回傳 `ext_links`。表單設定的 `links` 群組宣告三種 `rel` 的標籤，`SspExtFieldsSection` 以文字框渲染網址，型別 `url`。**欄位 `key` 要 `ext:cra-` 開頭才存得進 links。**

**預設說明文字**：A2.1～A2.9 九條的 `implementation-description` 寫進資源庫範本 SSP 的實作需求 props（`module_frame_control_default_service.py` 的 `_P_DESC`），建專案隨範本 SSP 複製。文字放 `cra_2024.py` 的 `A2_DEFAULT_DESCRIPTIONS`，英文、每條 2～4 句，說明「這段資訊放在使用者資訊的哪一章、對應受評標的哪個欄位」，標 `[AI draft – consultant to confirm]`。只寫入該資源庫 profile 內的控制項。

### 6.3 製造商與通報聯絡人 {#design-party}

**角色登記**（migration `scripts/sql/2026-10-12-fr135-cra-party-roles.sql`，`manifest.tsv` 登記 `seed *`）：

```sql
INSERT INTO system_menus ("group", key, value, sort, enable, public) VALUES
('ssp_party_role', 'manufacturer',          'manufacturer',          100, 1, 1),
('ssp_party_role', 'vulnerability-contact', 'vulnerability-contact', 110, 1, 1)
ON CONFLICT DO NOTHING;
```

只寫 `system_menus`；DEV 沒有 `oscal.oscal_roles` 表。冪等，資料類不進出貨基線。FE `ModuleFramePartiesPanel.vue` 從 `system_menus` 讀，**下拉自動多兩項**，所有框架都看得到這兩個選項——這是可接受的副作用（選項存在不影響 CMMC 既有資料）。i18n 標籤走 `lang.oscal_role.ssp_party_role.<key>.label`，`roleLabel()` 查不到時退回顯示 key，不會壞。

**角色怎麼掛**：沿用產品既有做法（C-P5）——party 本體 `props.responsible-role`。同一角色允許多人（例如通報聯絡人有主要與備援），由現有參與單位頁增刪。

**CRA 資源庫預建空殼**：建庫預設（§6.9）呼叫既有 `ModuleFramePartyService.add_party` 兩次：

| party_type | name | role | 說明 |
|---|---|---|---|
| organization | `Manufacturer (to be filled)` | `manufacturer` | 客戶在專案參與單位頁改名、填地址、email |
| organization | `Vulnerability contact (to be filled)` | `vulnerability-contact` | 填通報信箱或網頁表單網址 |

名稱帶 `(to be filled)` 讓顧問一眼看出還沒填。建專案時 `clone_metadata_with_party_map` 整批複製 party，專案端由 `ssp_party_app_service.py` 讀寫，FE 參與單位頁不用改。

**第三期接點**：通報模組讀專案 SSP metadata 下 `props.responsible-role in ('manufacturer','vulnerability-contact')` 的 party，取 `name`／`email_addresses`／`props.address`。

### 6.4 風險評估新表 {#design-risk}

**表**：`oscal.ssp_risks`（migration `scripts/sql/2026-10-12-fr135-ssp-risks.sql`）

| 欄 | 型別 | 對齊 `assessment_risks` | 說明 |
|---|---|---|---|
| `id` | BIGSERIAL PK | 同 | |
| `uid` | UUID NOT NULL UNIQUE | — | 對外識別 |
| `tenant_id` | BIGINT NOT NULL | — | RLS 依此隔離（C-P7） |
| `org_unit_id` | BIGINT | — | 租戶表必含 |
| `ssp_id` | BIGINT NOT NULL FK → `oscal.system_security_plans.id` ON DELETE CASCADE | 取代 `ar_result_id`／`poam_id` | 專案 SSP |
| `sort_order` | INT NOT NULL DEFAULT 0 | 同 | |
| `uuid` | UUID NOT NULL | 同 | OSCAL risk uuid，匯出用 |
| `title` | TEXT NOT NULL | 同 | 威脅（一句話） |
| `description` | TEXT | 同（原為必填） | 威脅情境說明 |
| `statement` | TEXT | 同（原為必填） | 受影響資產 |
| `status` | VARCHAR(40) NOT NULL DEFAULT 'open' | 同 | `open`／`investigating`／`remediating`／`deviation-approved`／`closed`（OSCAL risk-status，有 CHECK） |
| `threat_ids` | JSONB | 同 | **依據標準**：`[{key, system, id, href}]`，畫面開放讀寫（見下） |
| `characterizations` | JSONB | 同 | 衝擊與可能性：`[{facets:[{name:"impact",value:"high"},{name:"likelihood",value:"moderate"}]}]` |
| `mitigating_factors` | JSONB | 同 | 既有控制：`[{description}]` |
| `remediations` | JSONB | 同 | 處理措施：`[{lifecycle:"planned", title, description}]` |
| `related_control_ids` | JSONB | 新 | 對應控制項 `["CRA.I.2.d", …]` |
| `component_uuids` | JSONB | 新 | **元件連結預留**：SSP 元件 uuid 陣列；畫面不出現 |
| `props`／`links` | JSONB | 同 | OSCAL 擴充 |
| `remarks` | TEXT | — | 備註 |
| `created_user`／`updated_user` | VARCHAR(255) | 同 | 審計 |
| `created_at`／`updated_at` | TIMESTAMPTZ NOT NULL DEFAULT now() | 同 | |

不帶 `origins`／`deadline`／`risk_log`／`related_observations`（屬稽核發現，風險評估期沒有）。`description`／`statement` 可空，讓顧問先記一句威脅再補。衝擊與可能性用 OSCAL `characterizations.facets` 存，值域 `low`／`moderate`／`high`，風險等級在畫面算（衝擊與可能性取較高者），不存欄。

migration 登記 `active *`（出貨基線待重產）：`ENABLE`＋`FORCE ROW LEVEL SECURITY`、`POLICY ... FOR ALL`（超管放行或 `app_tenant_allowed_for_session(tenant_id)`）、`GRANT ... TO cm_app` 含 sequence、索引 `(ssp_id)`、`(tenant_id)`。

**依據標準**：風險編輯對話框的「依據標準（選填）」每列三格：標準名（下拉）、條號（自由文字）、網址（選填）。

- 標準名選項讀 `public.system_menus` 群組 `risk_reference_standard`（D16；migration `scripts/sql/2026-10-12-fr135-risk-reference-standards.sql`，`seed *`，十列：`etsi-en-303-645`、`iec-62443-4-2`、`iec-62443-4-1`、`en-18031`、`iso-iec-27005`、`capec`、`attack`、`owasp-iot-top10`、`enisa-threat-landscape`、`other`）。管理員可在系統選單維護頁增刪；顯示名優先走 i18n `lang.risk_reference_standard.<key>`。
- 存進 `threat_ids` 的 `key` 是 `system_menus.key`（程式用）、`system` 是顯示名；選 `other` 時標準名變文字框自填。
- 條號 placeholder 與網址樣板（CAPEC、ATT&CK 填條號後自動帶網址）是只含附加屬性、不含選項本身的 FE 小對照；其餘用 `value` 當提示。
- 後端 app service 去前後空白、丟掉「標準名和條號都空」的列、網址沒填就不存 `href` 鍵；網址只有 `http://`、`https://` 開頭才渲染成連結。
- **第一版不做威脅清單與精靈，依據由顧問自標**（D17）。

**DDD 四層**（主專案，不進套件——SSP 層風險是產品概念）：

| 層 | 檔 |
|---|---|
| domain | `domain/oscal/entity/ssp_risk_entity.py`、`domain/oscal/repository/i_ssp_risk_repo.py`（介面）、`domain/oscal/service/ssp_risk_domain_service.py` |
| infra | `infra/oscal/model/ssp_risk.py`、`infra/oscal/mapper/ssp_risk_mapper.py`、`infra/oscal/repository/ssp_risk_repo_impl.py`（繼承 `BaseRepositoryImpl`） |
| app | `app/oscal/service/ssp_risk_app_service.py`：讀 `require_participant`、寫 `require_manager`；`tenant_id`／`org_unit_id` 取自專案；審計欄暱稱以 `enrich_audit_nickname_objs` 補 `*_user_name` |
| api | `api/oscal/routes/ssp/ssp_risk_route.py`、`api/oscal/serializers/ssp/ssp_risk.py`；DI 進 `di_containers/oscal/` |

端點：`GET/POST /ssp/<ssp_uid>/risks`、`PUT/DELETE /ssp/<ssp_uid>/risk/<risk_uid>`。`related_control_ids` 寫入時驗證每個 id 在該專案的控制項範圍內（查法與控制項導航樹同一個 profile 解析），不在拋 `GRC_400140`；刪改時同時比對風險屬於該 SSP，找不到拋 `GRC_404051`（`common/code/flow_control_error_code.py`），同步 FE i18n。

**前端**：`src/components/grc/ssp/SspRiskAssessmentSection.vue`（DataTable＋編輯對話框；衝擊、可能性兩個下拉，風險等級顯示徽章、依據標準多列、對應控制項多選），掛在 `SspTabPanel.vue` 子頁籤，`v-if="settings?.sections?.risk_assessment"`。

**與 POA&M 不互動**：不讀、不寫 `assessment_risks`；稽核結果頁不顯示。GRC 期再決定是否合併（§後續）。

### 6.5 資訊系統版本子表 {#design-version}

**套件 migration** `jedi-asset/jedi_asset/migrations/006-information-system-versions.sql`：

| 欄 | 型別 | 說明 |
|---|---|---|
| `id` | SERIAL PK | |
| `uid` | VARCHAR(50) NOT NULL UNIQUE | 比照母表 |
| `information_system_id` | INTEGER NOT NULL FK → `compliance.information_systems.id` ON DELETE CASCADE | |
| `version` | VARCHAR(100) NOT NULL | 版號，同母表唯一 `(information_system_id, version)` |
| `release_date` | DATE | 上市日 |
| `support_end_date` | DATE | 支援期結束日 |
| `support_end_reason` | TEXT | 支援期長度理由 |
| `remarks` | TEXT | |
| `tenant_id` | INTEGER NOT NULL | 比照母表 |
| `org_unit_id` | INTEGER | |
| `created_user`／`updated_user` | VARCHAR(50) | |
| `created_at`／`updated_at` | TIMESTAMPTZ NOT NULL DEFAULT now() | |

RLS 四支政策照母表寫法（select／insert／update／delete 各一）、GRANT `cm_app` 含 sequence。build 期自動同步到主專案 `scripts/sql/packages/jedi_asset/` 與 `packages/manifest.tsv`。

**套件四層**：`infra/models/information_system_version.py`、mapper、repo；`domain/entity`＋`domain/repository`；`app/service/information_system_version_service.py`（新檔，不塞進母服務）；`api/routes/information_system_version_route.py`：

| 端點 | 守門 |
|---|---|
| `GET/POST /information-system/<uid>/versions` | 讀／寫各套母表的 `information_system_read_capability`／`information_system_update_capability` |
| `PUT/DELETE /information-system/<uid>/version/<version_uid>` | `update_capability` |

`FROZEN_URLS`（`routing.py`）同步加兩條；能力點沿用母表，不新增。jedi-asset 版號 1.2.4，發版等決策者明示。

**前端**：`src/views/information-system/InformationSystemVersionTable.vue`，`InformationSystemManage.vue` 的編輯對話框底部一行掛上（該檔 797 行，只准加一行）。

**SSP 端不動**：受評標的以 `inventory_items.props` 的 `ref-type`／`ref-id` 軟連到資訊系統。「專案選定某個版本」本期不做；受評標的的 `ext:cra-product-identifier` 由顧問手填版本字串。

### 6.6 程序書與證據是兩套 {#design-docs-vs-evidence}

::: {.callout .crit}
**🔴 概念鐵則：程序書 ≠ 證據，兩套路、兩種畫面，不可混**

**程序書**＝公司的 SOP／流程文件（CMMC、ISO 都有），掛在**控制項**上，證明「公司有這個規定」，不指檔內章節。**證據**＝證明達到控制項要求的附件，掛在準備期**工作**（job evidence）底下。
:::

程序書的對應表 `oscal.ssp_reference_document_mappings` 只有「哪份檔掛哪個控制項／AO」，沒有出處欄。第〇步缺口「證據指不到檔內某一節」屬證據那條，是否在證據上加出處另案討論，不在本文件範圍。

**舊路已收掉**：控制項層舊上傳與刪除（`add_reference_documents`、`delete_reference_document`）、AO 層舊讀寫與其 route 已刪，FE 對應呼叫一併刪除；控制項詳情 API 的 `reference_documents` 讀程序書池對應。`ssp_control_implementation_service.py` 超標，這塊只刪不加。

### 6.7 AO 全面調整 {#design-ao}

**結果**：CRA 框架為 **4 群組、34 條控制項、49 個 AO**，每個 AO 可獨立判定「有／沒有」，句型比照 CMMC（句首 `[a]`、`[b]` 字母配合分類器解析）。依據是 [`cra-ao-contradiction-scan-2026-10-12.md`](cra-ao-contradiction-scan-2026-10-12.md) 的矛盾掃描與決策者裁示。

| 動作 | 內容 |
|---|---|
| 新群組 Articles 13 and 14 | 三條：`CRA.ART.13.RA` 風險評估（3 AO）、`CRA.ART.13.SP` 支援期（3 AO）、`CRA.ART.14.RP` 通報（3 AO）。三件製造商義務的法源在 Article 13／14，不在 Annex I |
| 搬移 | `CRA.I.1` 底下三個屬 Article 13 的 AO 搬到 `CRA.ART.13.RA`；搬移是改掛（保留原列 id、換控制項與 `part_id`），不是刪掉重建 |
| 拆 6 條 | `CRA.I.2.c`、`CRA.I.2.f`、`CRA.I.2.m`、`CRA.II.1`、`CRA.II.4`、`CRA.II.8`；`CRA.II.6` 不拆 |
| 改句 | 既有 AO 依矛盾掃描改寫，消除互相重疊與一句多件事 |
| 建議證據 | 每個 AO 底下掛一個 `guidance` 子段落，內容為三段式：`Deliverable`（交什麼檔或紀錄）、`Must show`（審閱者要在裡面看到什麼）、`Evidence`（`upload`｜`system — <頁面>`｜`template — <範本>`）；不帶 `[obj.N]` 與 `[beyond ENISA]` 之類內部標記 |

**內容單一來源**：`scripts/cra_ao_content.py`，產生器 `scripts/gen_cra_ao_restructure.py` 一次產出 `cra-assessment-objectives.md`、`cra-suggested-evidence.md` 的建議證據段與套件 seed 005。AO 句與證據依 `docs/reference/CRA/` 法條原文自寫，不抄 ENISA 或 EN 40000 條文；`Basis`（依據）欄只進 md，不進 seed。md 頁首標「AI 依 ENISA 子要求拆分，待顧問確認」。

**套件 seed 005** `jedi-oscal-v2/jedi_oscal_v2/migrations/005-cra-2024-ao-restructure.sql`（套件版號 2.5.4，發版推 Nexus 等決策者明示）：

- 冪等：每列 `INSERT ... WHERE NOT EXISTS (part_id)`，重跑計數不變；`catalog_id` 以 CRA catalog 固定 uuid 定位。
- `name='assessment-objective'`、`parent_id=NULL`（CMMC 也是平鋪）、`part_id='<control_id>_obj.<N>'`、N 從 3 起（C-P9）。
- AO 的 guidance 子段落 `parent_id` 指向該 AO。

**讀法陷阱**：AO 底下的 guidance `sort_order=1` 會排在控制項層 `_gdn` 前面。控制項層的 `statement`／`guidance` 只取 `parent_id` 為空的列，AO 層建議證據走 `control_part_prose.py` 的 `ao_guidance_map`；`framework_version_edit_service` 的 `_guidance_prose`／`_upsert_part` 同樣加此過濾，否則瀏覽對話框與編輯會被子段落干擾。

**生效範圍**：只對之後新建的資源庫與專案（建庫整份複製 catalog）。

### 6.8 文件範本 {#design-docs}

本期只做骨架佔位（D10）：五份 docx，每份有封面、法源、版本紀錄表、目錄，每節一句「此節要寫什麼、依據哪條」與紅字 `[Consultant to complete]` 佔位，內容由顧問日後替換。

| 檔 | 掛的控制項 |
|---|---|
| `CRA-CVD-Policy-Template.docx`（協調漏洞揭露政策） | II.5、II.6、A2.2 |
| `CRA-Security-Advisory-Template.docx`（安全通告） | II.4、II.8 |
| `CRA-Support-Period-Statement-Template.docx`（支援期聲明） | A2.7、I.2.c |
| `CRA-User-Information-Template.docx`（Annex II 使用者資訊） | A2.1、A2.3、A2.4、A2.5、A2.8 |
| `CRA-Update-and-Decommission-Procedure-Template.docx`（更新與停用程序） | I.2.m、II.2、II.7、A2.8 |

合計 **15 條控制項、16 筆掛載**（A2.8 同時掛兩份）。

- 產生腳本 `scripts/generate_cra_doc_templates.py`（python-docx 手寫，可重跑覆蓋），產物放 `app/oscal/templates/cra/`——打包腳本只帶清單內的資料夾，此路徑已在清單內。
- 建庫預設（§6.9）把檔上傳到**系統儲存**（與框架 PDF 同一條路），再掛到資源庫程序書池的對應控制項；同一份範本隨建專案被各租戶共用，放建庫者租戶其他租戶會下載不到。不在資源庫 profile 內的控制項不掛。
- 建專案時程序書池與掛載整批複製（`project_start_app_service.py` 的 `_clone_reference_documents`）。

**三份法定文件匯出**：與上面五份骨架是兩回事——骨架是空白章節給顧問主筆，匯出是**從專案已填資料直接組出內容**的 docx（D4 只出 docx），顧問下載後可再改字。

| 匯出 | `format` 值 | 範本 |
|---|---|---|
| Annex VII 技術文件 | `cra-annex7` | `CRA-Annex-VII-Technical-Documentation-Export.docx` |
| Annex II 使用者資訊 | `cra-annex2` | `CRA-Annex-II-User-Information-Export.docx` |
| EU 符合性聲明（Annex V） | `cra-doc` | `CRA-EU-Declaration-of-Conformity-Export.docx` |

- 端點沿用 `GET /ssp/<uid>/export?format=…`，同一顆 `oscal.ssp.export` 能力點、同樣只限專案參與者；非 CRA 專案（表單設定沒開風險評估段）回 412 `GRC_412059`。
- 產生在 `app/oscal/service/export/cra_legal_docs_export_service.py`，每份一支 context 組裝；內容來源：受評標的 `ext:cra-*` 欄與連結、風險評估（含依據標準，歸併成「採用標準」表）、資訊系統版本（支援期以最新版本為準，沒建版本才讀 `ext:cra-support-end-date`）、參與單位 `manufacturer`／`vulnerability-contact`（名字仍是建庫佔位的不算填了）、控制項實作說明（Annex VII 列全部控制項）。
- 沒填的欄位印 `(not provided)`；符合性聲明的簽署人、地點日期與簽名留底線空白給製造商親簽。
- 範本由同一支 `scripts/generate_cra_doc_templates.py` 產（docxtpl `{{ }}` 佔位，變數名與 service context 鍵對應）；重跑會順手重寫五份骨架的檔案時間戳，只改匯出範本時記得還原那五份。
- 規劃頁「匯出」選單：CRA 專案多這三項，CMMC 等其他框架看不到。

### 6.9 建資源庫時帶框架預設內容 {#design-defaults}

顧問從畫面新增資源庫、框架選 CRA 時，`create_resource_library`（`app/oscal/service/resource_library_app_service.py`）在建庫尾端兩行呼叫 `FrameworkLibraryDefaultsService.apply_framework_defaults(rl_uid, framework_id, user)`，與建庫同一交易，任一步失敗整個建庫回滾。

流程：由 `framework_id` 反查 `oscal.frameworks.code`（CRA 是 `CRA_2024`）→ 以 `REGISTRY` 查 `framework_library_defaults/<code>.py` → 依序寫入：

1. 表單設定（先 `validate_form_settings`，再設到 `module_frames.form_settings`）
2. A2.1～A2.9 預設說明（只寫 profile 內的控制項）
3. 兩個 Party 空殼
4. 五份文件範本上傳並預掛

查無該框架 code＝什麼都不寫，CMMC 行為不變。預設內容寫在資源庫層即可：建專案時 party、SC props、範本 SSP 的控制項預設值、程序書池都會整批複製。

**陷阱**：

- **DI 迴圈**：既有控制項預設值 service 依賴資源庫 service，資源庫 service 再依賴它會成環。預設內容 service 另組一支不帶資源庫 service 的控制項預設值 service，少掉的「控制項必須在 profile 內」守門由預設內容 service 先用 `resolve_profile` 過濾。
- **module_frame repo 的 `update` 會把 entity 所有非空欄位寫回**，entity 的 `enable` 預設 0。自己 new 一個 entity 去 update 會把新庫打回草稿；要讀回完整 entity 再改 `form_settings`。
- **只對新建的資源庫生效**：舊資源庫不補預設內容；驗收一律用新建的庫與專案。

### 6.10 刪舊 clone 端點 {#design-clone}

`clone_module_frame` service、route（註冊於 `api/module_frame/__init__.py`）、schema、FE `src/config/api/api.js` 的 `MODULE_FRAME_CLONE` 已刪（D11）。刪前 grep BE／FE／test 三處確認無呼叫端；刪後端點少一條，route guard inventory 守衛清單同步。

### 6.11 AO 證據來源機制 {#design-evidence-source}

**問題**：CRA 有一批證據是客戶在系統其他頁填完就有的——風險評估頁籤、每條控制項的適用／不適用與理由、受評標的的支援期結束日與產品識別、參與單位的製造商與通報聯絡人、資訊系統版本、FR-134 的 SBOM 與弱點掃描。只有「上傳檔案」一種掛法，客戶得把系統裡的東西匯出再傳回來，同一件事兩份真相。矛盾掃描報告標出 116 點建議證據中 15 點主要來源是系統內、另 10 點混合。

**做法**：AO part 的 `props` 多一個 `evidence-source`：

```json
{"name": "evidence-source", "value": "<type>", "ns": "guidant"}
```

值由 §6.7 三段式第三段 `Evidence: system — <頁面>` 轉出，由框架 seed 以冪等 UPDATE 寫入。六種 `type`：

| type | 讀哪裡 | 狀態句例 |
|---|---|---|
| `risk-assessment` | `oscal.ssp_risks` 筆數與最後更新 | 已登錄 6 筆，最後更新 10/12 |
| `applicability` | 該專案控制項的適用／不適用判定數 | 已判定 28／34 |
| `system-characteristic:<ext-key>` | 受評標的 props 該鍵的值 | 已填：2031-12 |
| `party:<role>` | 有無該角色 party 且名稱不是 `(to be filled)` | 尚未填寫 |
| `information-system-version` | 受評標的所連資訊系統的版本數 | 已登錄 2 個版本 |
| `source-scan` | 該專案最近一次原始碼掃描 | 最近掃描 10/10 |

**BE**：

- `app/oscal/service/ao_evidence_source.py`：一支 `resolve(ssp_id, source)`，六個 type 各一小函式，回統一形狀 `{type, label_key, status: 'filled'|'empty'|'partial', count, summary, link: {route, params}}`；各物件的讀取走既有 service，不另寫查詢，不新增表。
- AO dict 帶 `evidence_source: {type, ref}`（只帶 type 與 ref；`ssp_control_implementation_service.py` 超標，只加幾行，解析放新檔）。
- 端點 `GET /ssp/<ssp_uid>/ao/<part_id>/evidence-source`（參與者可讀）取狀態，lazy 打，避免控制項樹變重。

**FE**：

- 新元件 `src/components/grc/planning/AoEvidenceSourcePanel.vue`：顯示「證據來源：<白話名稱>」、狀態一行、「前往」按鈕（router 到對應頁籤）。
- 規劃頁 AO 面板（`ProjectPlanningView.vue`）：`evidence_source` 有值 → 掛 `AoEvidenceSourcePanel`、**隱藏**關聯程序書與上傳區；無值 → 照舊。一行 `v-if`。
- 稽核判定頁（`RoundAuditReviewView.vue`、`AuditControlRef.vue`）：有值顯示來源狀態與「前往」，稽核員看那頁判定。
- i18n 兩語系：六種 type 的白話名稱與狀態句。

**稽核判定邏輯不改**：稽核員仍手動判定，本機制只改「看證據的入口」。

**收證據任務**：`evidence-source-only` 為 true 的 AO（沒 AO 的控制項看 `catalog_controls.props`）啟動稽核與覆核輪都不產收證據任務，統計卡也就不算它；混合型（有 `evidence-source` 但 only 不是 true）照常產。判斷在 `prep_job_generation_service.py`，與規劃頁藏上傳區同一旗標。

**CMMC 不變**：CMMC 所有 AO 無此屬性，畫面與現況完全相同。

## 拆分 {#breakdown nav="拆分"}

共通鐵則（每張卡都適用，開卡照抄）：只動 DEV；不切 branch、不 push；顯式 `git add`；動到已超標檔只准刪或一行呼叫；migration 照 `sql-migration` skill（日期註解、GRANT、`schema_migrations`、`manifest.tsv`，`active *` 要回報出貨基線待重產）；套件走 path dependency、發版等令；commit 前跑 `test/test_module_boundaries.py` 與 RLS 形狀守衛；做完 commit＋Notion 回寫＋子卡改「修正待驗證」。

| 卡 | Case No | 依賴 | 規模 | 重點 |
|---|---|---|---|---|
| F1 表單設定落點 | CM-2690 | 無 | S | §6.1 資料庫、三層帶欄、`validate_form_settings`；本卡需單元測試（結構守門屬共用邏輯） |
| F2 專案取設定端點 | CM-2693 | F1 | S | §6.1 兩支 GET；SSP 版只服務專案 SSP |
| F3 前端動態欄位與頁籤開關 | CM-2699 | F2 | M | BE `ext_fields`／`ext_links` 收送；FE `useFormSettings.js`、`SspExtFieldsSection.vue`；驗收專查 `target-type`／`owner-uid` 未被清掉 |
| F4 建庫帶 CRA 預設 | CM-2700 | F1 | S～M | §6.9 機制＋`cra_2024.py` 內容＋角色 migration；FE i18n 兩個角色標籤；畫面等 F3 |
| V1 資訊系統版本子表 | CM-2694 | 無 | M | §6.5；套件異動，發版等令 |
| R1-a 風險評估本體 | CM-2701 | 無 | M | §6.4 新表＋四層＋端點＋FE 表格；新租戶表，RLS 形狀守衛要綠，本卡含 repo 層 RLS 測試 |
| R1-b 頁籤開關與依據標準 | CM-2702 | F3、R1-a | S | 頁籤 `v-if`＋依據標準下拉（讀 `system_menus`，seed migration 一支） |
| C1 AO 拆分範本 | CM-2691 | 無 | S | 首版 AO 拆分文件與 seed 004 |
| C1b AO 全面調整 | CM-2696 | C1 | M～L | §6.7；套件 seed 005、BE／FE 讀 AO 自己的建議證據 |
| C2 收舊套掛檔＋刪舊 clone | CM-2695 | 無 | M | §6.6 收舊路、§6.10 刪 clone；套件異動等令 |
| C2b 撤回程序書 locator | CM-2697 | C2 | S～M | 拔掉 `locator` 欄與前後端帶欄；保留 C2 收舊路 |
| C3 文件範本骨架 | CM-2703 | F4 | S | §6.8；產生腳本＋五份 docx＋建庫預掛 |
| E1 AO 證據來源機制 | CM-2704 | C1b | M | §6.11；`resolve` 六個 type 各一 case 的測試，突變拿掉任一分支要紅；FE 新 v-can 掛點要重跑權限矩陣 |

```{.mermaid cap="圖 2 — 卡依賴（虛線＝跨 design-platform.md）"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  F1 --> F2 --> F3 --> R1b["R1-b"]
  R1a["R1-a"] --> R1b
  F1 --> F4
  F3 -.畫面.-> F4
  F4 --> C3
  C1 --> C1b --> E1
  C2 --> C2b
  V1
```

## 端到端驗收 {#acceptance nav="驗收"}

用**非 102 租戶**的帳號，從畫面新增一個 CRA 資源庫、再用它建驗收專案，逐項做（批次跑完再一次回報）：

1. 受評標的頁出現四段 CRA 欄位＋三個網址欄；填滿、存檔、重整都在；DB `SC.props` 只多 `ext:cra-*`，`target-type`／`owner-uid` 仍在。
2. A2.1～A2.9 控制項實作說明已預填英文草稿。
3. 參與單位頁有「製造商」「漏洞通報聯絡人」兩筆空殼，改名填 email 後重整還在。
4. 「風險評估」子頁籤出現；新增兩筆、勾對應控制項、依據標準下拉有 10 項；CMMC 專案同一頁沒有此頁籤。
5. 框架為 4 群 34 條 49 AO；準備期工作按 AO 數產生（平台層 B）。
6. 規劃頁看得到每條控制項的 guidance（平台層 A）與每個 AO 自己的三段式建議證據，沒有 `[obj.N]` 標記。
7. 程序書頁有五份範本，15 條控制項共 16 筆掛載；自行再掛一份成功。
8. 資訊系統盤點頁新增兩個版本。
9. 風險評估那條 AO 的面板顯示證據來源與狀態並可跳轉，不顯示上傳區（E1）。
10. 開一個 CMMC 專案，規劃頁、受評標的、參與單位下拉以外的畫面與改版前截圖一致（下拉多兩個角色屬預期）。

---

## 風險與陷阱 {#risks nav="風險"}

::: {.callout .crit}
**🔴 預設內容只在建庫當下寫入，任何 seed 或預設內容對既有資源庫無效**

AO、guidance、表單設定、預設說明、party 空殼、文件範本全部只對建庫之後的新資源庫生效。驗收一律用新建的資源庫與專案，不拿第〇步測資驗。
:::

| 陷阱 | 後果 | 對策 |
|---|---|---|
| 建專案時 party uuid 重產 | 用 uuid 指 party 的寫法（`metadata.responsible_parties`）在專案端斷鏈 | 只用 party 本體 `props.responsible-role`；第三期通報模組照此讀 |
| 受評標的整包覆寫 | 清掉固定 8 欄或 `target-type`／`owner-uid` | `ext_fields` 逐鍵 `_set_prop`；F3 驗收專查 |
| 照抄 `oscal.assessment_risks` 欄位 | 該表 RLS 未開，新表沒隔離 | `ssp_risks` 必含 `tenant_id`＋`org_unit_id`＋RLS FOR ALL |
| 兩個新角色進全域下拉 | CMMC 專案參與單位也看得到「製造商」 | 無害、不過濾；要只在 CRA 出現需新機制，本期外 |
| docx 範本是資料檔 | Nuitka 不自動帶，客戶端建庫時上傳失敗 | 範本放 `app/oscal/templates/cra/`（打包清單內）；出包後確認 image 內有五份 |
| 建庫預設的 repo `update` 寫回 entity 全部非空欄位 | 新庫被打回草稿（`enable` 預設 0） | 讀回完整 entity 再改欄位 |
| AO 底下的 guidance 排在控制項層 `_gdn` 前面 | 控制項層取到 AO 的證據 | 控制項層只取 `parent_id` 為空的列 |
| `ProjectPlanningView.vue`、`ssp_control_implementation_service.py`、`RoundAuditReviewView.vue` 超標 | 往裡加會被 reviewer 判阻斷 | 只准刪與少數幾行，邏輯進新檔 |

---

## 已知未做與後續 {#future nav="後續"}

| 項目 | 去向 |
|---|---|
| 複製資源庫（連表單設定一起帶） | D1，GRC 期 |
| 自家 SBOM 掃描結果轉控制項證據、定期執行證明 | D14，FR-134 後續或 GRC |
| 證據（job evidence）指到檔內某一節的出處欄 | 另案討論 |
| 專案選定資訊系統的某個版本 | 第三期三份文件需要時再做；本期以 `ext:cra-product-identifier` 手填 |
| 畫面上的表單設定編輯器、條件顯示、驗證規則 | GRC 期 |
| 風險評估與改善計畫（POA&M）合併、組織層風險登錄、依元件建議威脅 | GRC 期；`component_uuids` 已預留 |
| 框架版本瀏覽對話框列出 AO 層建議證據 | 小卡，未開 |
| 文件範本正式內容、Annex II 預設說明、AO 拆法 | 顧問確認後以框架新版本或補灌修正 |
| 「填欄位自動產出」三份法定文件 | 第三期 |

---

## 與其他需求關係 {#relations nav="關係"}

- **design-platform.md（P2-A）**：平台層 A（規劃頁條文與建議證據）、B1～B3（無 AO 開工、覆核輪）。驗收第 5、6 項依賴它；兩份文件改不同檔，平行施工不衝突。
- **第三期通報模組**：讀 §6.3 的 party 與 §6.2 的 `ext:cra-*`、link；版本子表供支援期到期排程。
- **第三期三份文件匯出**：欄位來源是 SC.props、party、風險表、版本子表。
- **FR-134 原始碼掃描**：`source-scan` 證據來源讀其掃描紀錄；SBOM 轉證據本期不接（D14）。
- **CM-2687 框架版本預覽第三態**：獨立卡，不在本文件範圍。
