範圍:契約層(Wave 0) = DB schema 定稿 + jedi-oscal-v2 套件對外介面 + API spec 骨架。 這是所有平行開發的「前提合約」;本層定稿後,套件(Wave 1)/ BE(Wave 2)/ FE(Wave 3)才能真正平行開工。 上游需求基準:requirement-analysis.md(D1-D7 + Q1-Q4 決策已鎖定)。 撰寫日:2026-06-14
| 交付物 | 檔案 | 狀態 |
|---|---|---|
| v2 schema base(42 表,FR-037 產出) | ../FR-037-.../oscal-catalog-and-roots-schema-v2.sql |
既有,沿用 |
| v2 schema delta(FR-038 補表) | oscal-v2-deltas.sql |
本層產出 |
| 套件對外介面 + API skeleton | 本文件 §3 / §4 | 本層產出 |
建置方式(D1:drop oscal 重建):dev 先
DROP SCHEMA oscal CASCADE→ 跑 base → 跑 delta。最終發版時 base+delta merge 成單一 build script。
框架層(母版) 資源庫層(公版) 專案層(可編輯) 稽核凍結
───────────── ───────────── ───────────── ─────────
frameworks [clone②] project_audit_rounds(輪次)
└ framework_versions ─┐ catalog 副本 ├ living SSP ──[snapshot③]──▶ 凍結 SSP 快照
└ catalogs ────────┼──▶ profile 副本 ──[clone②]─▶├ AP(per engagement) (round.ssp_id)
(control/AO樹) │ SSP 範本 ├ AR ─ ar_results[](每輪一筆)
[resolve①] └ POA&M(FK 指回 AR,不複製)
四個「複製並固定」邊界(PM 教材 §09):① profile resolve → 資源庫 catalog 副本;② 資源庫三件組 → 專案三件組;③ living SSP → 凍結快照(每輪);④ 框架改版 = 新 framework_version。 鐵則:副本存「內容」或指向「永不原地改的版本」,不可只存 id 即時 join 回會變動母表。
catalog 樹(catalogs/groups/controls/parts(AO)/params)、profiles(+imports)、component_definitions(+子樹)、system_security_plans(+12 子表)、assessment_plans(+reviewed_controls/local_objectives/activities/tasks)、assessment_results(+ar_results)、poams(+poam_items)、assessment-common(observations/risks/findings)、共用層(metadata/roles/parties/resources)。欄位 1:1 對齊 OSCAL v1.2.2,NOT NULL ⟺ required。
oscal-v2-deltas.sql)| Delta | 內容 | 對應決策 |
|---|---|---|
| D-1 | oscal.frameworks + oscal.framework_versions 兩新表;catalogs.framework_version_id FK |
base 缺口 G1 / FR-038 §23-26 |
| D-2 | compliance.project_audit_rounds(7 態 + parent_round_id + assessment_plan_id + ssp_id + ar_result_id) |
Q4 / D2 |
| D-3 | oscal.ap_assessment_subjects(抽查名單關聯化,從 ap_tasks.subjects JSONB 抽出) |
D3 |
| D-4 | oscal.assessment_finding_risks(finding↔︎risk 多對多) |
Q2 |
| D-4b | oscal.assessment_remediations(response/整改計畫)+ oscal.poam_milestones(task/里程碑,每里程碑各別 assignee)— OSCAL risk → remediations[](response) → tasks[] 三層忠實落地 |
PM2 |
| D-5 | oscal.catalog_controls.source_identifier(跨框架溯源佔位) |
D6 |
compliance.project_audit_rounds的status/round_type/parent_round_id⟺close-out已加 CHECK 約束(Q4 鎖定的值域 DB 層強制)。catalogs.framework_version_id的 clone 語意已定:副本沿用指向作 provenance、控制項存內容不 join 回母版。
base 既有的
oscal.poams(OSCAL 文件 root)與舊compliance.poams(扁平產品表)並存語意:本期 POA&M 以 OSCALpoams/poam_items+assessment_risks(remediations/milestones) 為主,舊扁平表的去留在 BE sub-project(Wave 2)決定。
base 每個 root 表都有 status varchar(20)(draft/published/deprecated),註解標「待決策」。本設計保留:framework/catalog/profile/ssp 都需要 draft↔︎published 區分(資源庫只能選 published 版本,FR-038 §42)。
讀 base schema 發現一個必須交代的細節:oscal.assessment_plans.import_ssp_id 是單一 NOT NULL,且 PM 教材 §01 明寫「AP 對著的是那份定版快照,不是還在改的 SSP」。這把 D2 的「1 project 1 AP/AR」精修成「1 engagement 1 AP/AR」:
Engagement = 一個 initial(或 surveillance)輪 + 其下所有 close-out 覆核輪。
| 輪次 | round_type | parent_round_id | AP | AR | ar_results |
|---|---|---|---|---|---|
| #1 首評 | initial | NULL | 新建 AP-1(import-ssp→#1 快照) | 新建 AR-1(import-ap→AP-1) | 追加 result #1 |
| #2 覆核 | close-out | 1 | 沿用 AP-1 | 沿用 AR-1 | 追加 result #2(自帶 narrowed reviewed-controls) |
| #3 再覆核 | close-out | 2 | 沿用 AP-1 | 沿用 AR-1 | 追加 result #3 |
| #4 年度 | surveillance | NULL | 新建 AP-2(import-ssp→#4 新快照) | 新建 AR-2 | 追加 result #1 |
為什麼這樣切:
round.ssp_id,該 result 對著新快照判定;AP 的 import-ssp 維持 engagement 起點快照(系統識別 anchor),point-in-time 差異記在 result 層。project_audit_rounds.assessment_plan_id + ar_result_id 就是把這個 engagement 模型 wire 起來的兩個 FK。
跨 round 結案連動:覆核輪 result 定版確認母輪 not_met 的 AO 過了 → 沿
parent_round_id關閉母輪 POA&M → 母輪轉closed。
~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal-v2/,import 名稱 jedi_oscal_v2(dist jedi-oscal-v2)。domain/(entity/repository interface/service)、infra/(model/mapper/repository impl/adapter)、app/(app service/dto)、ports/(parser provider/factory)、common/(enum/code)。簽章為「契約意圖」,細節 dto 在 Wave 1 各 model sub-project 定稿。所有 method 走
@transaction(由主專案 app service 開 scope,套件 method 為被呼叫端)。
| 模組 | Service | 核心 method(契約) |
|---|---|---|
| Framework | FrameworkService |
add/get/list/update_framework、add_framework_version、list_versions(framework_uid)、publish_version(version_uid) |
| Catalog | CatalogService |
import_catalog_from_pdf(stream, framework_code, …)、get/list_catalog、get_control_tree(catalog_uid)、list_aos(catalog_control_id) |
| Profile | ProfileService |
add_profile(imports, …)、resolve_profile(profile_uid) → resolved control set(邊界①)、get/list_profile |
| Snapshot/Clone | OscalSnapshotService |
clone_resource_library(framework_version_uid) → (catalog,profile,ssp) 副本(邊界②)、snapshot_ssp(ssp_id) → frozen ssp_id(邊界③)、deep_clone_ssp(src_ssp_id) |
| SSP | SspService |
add_empty_ssp、get/update_ssp、子物件 CRUD(system-characteristics / parties / components / inventory / implemented-requirements / by-components / SoA props) |
| AP | AssessmentPlanService |
create_ap(import_ssp_id, …)、generate_draft_from_ssp(ssp_snapshot_id)(AP 草稿自動生成)、set_reviewed_controls、set_assessment_subjects、set_tasks |
| AR | AssessmentResultService |
add_ar(import_ap_id)、add_result(ar_id, snapshot_ref) → ar_result_id、upsert_finding(ar_result_id, ao, state)(AO 全量矩陣)、list_findings(ar_result_id) |
| Risk | AssessmentRiskService |
add_risk(ar_result_id, …)、link_findings(risk_id, finding_ids[])(Q2 多對多)、list_risks(ar_result_id) |
| POA&M | PoamService |
generate_from_findings(ar_result_id)(不複製,FK 指回)、upsert_remediation(risk_id, …)(response/整改計畫;CMMC 可自動建預設一筆)、add_milestone(remediation_id, assignee_user_id, …)(→ oscal.poam_milestones,D-4b)、list_remediations(risk_id)、list_milestones(remediation_id)、list_poam_items |
| Import/Export | OscalIoService |
export_oscal(doc_type, uid, fmt)(JSON/XML/YAML)、import_ssp_docx/excel(落地 v2 schema) |
ports/oscal_parser_factory.get_oscal_parser_adapter(code) → CMMC / ISO / NIST adapter,pdf_parser / excel_parser / convert_to_oscal_catalog_entity。本期把舊 adapter 搬移並對齊新 catalog/group/control/part(AO) 結構(AO 落 catalog_control_parts、method 存 props)。
compliance.project_audit_rounds 狀態機、權限檢查(manager/auditor)、workflow/job 引擎綁定(Q1:綁 SSP 控制項)、跨 schema 業務 mapping → 主專案。URL 慣例(CLAUDE.md):單筆單數、清單/批次複數、kebab-case。envelope
{code,data}。
| 區塊 | 代表路由 | 動作 | sub-project |
|---|---|---|---|
| 合規框架 | POST /oscal/frameworks、/framework-versions/import、/{uid}/publish |
框架/版本/匯入/發布 | BE-B1 |
| 合規資源庫 | POST /resource-libraries(選 framework_version → clone)、匯入 SSP docx/excel |
三件組建立 | BE-B1 |
| 專案 | POST /projects/start(clone 資源庫三件組;AP/AR 延後) |
專案成立 | BE-B2 |
| 稽核輪次 | POST /projects/{uid}/audit-rounds、/{round}/launch-audit(snapshot)、/{round}/launch-reverify |
輪次 + 狀態轉換 | BE-B3 |
| SSP | 既有 ssp_* 路由對齊 v2 schema | SSP 維護 | BE-B3 |
| AP | POST /audit-rounds/{round}/ap(草稿生成)、PUT …/reviewed-controls、…/subjects、…/tasks |
稽核計畫(全新) | BE-B4 / FE-C1 |
| AR | GET …/findings(AO 全量矩陣)、PUT …/finding/{uid}、POST …/risks、PUT …/risk/{uid}/findings(組風險) |
判定 + 風險總結 | BE-B5 / FE-C2 |
| POA&M | POST …/poam/generate、PUT …/poam-item/{uid}、POST …/poam-item/{uid}/milestones、POST …/close-round |
改善 + 結案 | BE-B5 / FE-C3 |
GRC_<HTTP><序號>)新增族群(序號待 Wave 2 開工前 grep common/code/grc_error_code.py 最大值續編,避免衝突):
GRC_ROUND_NOT_FOUND(404x)、GRC_ROUND_INVALID_STATUS_TRANSITION(412x)、GRC_ROUND_SSP_NOT_FROZEN(412x)、GRC_REVERIFY_PARENT_NOT_PENDING(412x)GRC_AP_REVIEWED_CONTROLS_REQUIRED(412x)GRC_AR_FINDING_TARGET_REQUIRED(412x)、GRC_AR_RESULT_NOT_LATEST(412x)GRC_RISK_NO_FINDING_LINKED(412x)status CMMC 允許集:open / investigating / remediating / closed。ISO 的 deviation-* / risk_accepted(base COMMENT 列出的)本期不 wire(D5 follow-up),避免 FE 驅動不到。ap_assessment_subjects.subject_type:component / inventory-item / location / party / user。user 是產品擴充(對 SSP system-user),OSCAL 匯出時映射為 party/component 參照。compliance.poams 扁平表 vs OSCAL poams 的最終取捨(Wave 2)D2 表述更正:requirement-analysis §3.3 早期寫「1 project → 1 AP / 1 AR」,已被本文件 §3 精修為「1 AP/AR per engagement」(surveillance 起新 AP/AR)取代。delta 的
project_audit_rounds.assessment_plan_id是 nullable FK、無 project-unique 約束,正確支援 per-engagement 模型。Wave 2 以本文件 §3 為準。