狀態 2026-06-15。本檔盤點「V1(舊 jedi_oscal,已出貨的 SSP Update Diff / FR-011.2)」與 「V2(jedi_oscal_v2,FR-038 遷移後現況)」在 SSP 匯入 上的差距。結論:V2 目前只接了 「首次(空殼)建立匯入」的骨架,V1 真正值錢的「差異更新 / 專案落點 / 人員比對 / 手動指派」 整層還沒搬(V1 程式碼躺在 repo 但被 disable)。缺口夠大,建議獨立一個 session 處理。
FR-038 把 BE 從舊 jedi_oscal(V1) 打掉重練成 jedi_oscal_v2(V2)。Wave 2A「地基翻轉」時, 所有依賴 V1 的 SSP 匯入業務碼被乾淨 disable(route 進 config/di_modules.py:EXCLUDE_MODULES、 service chain 仍 import V1 entity → 與 V2 撞同一 SQLAlchemy MetaData,不可 wire)。Wave 2B 的 import-ssp 子弧只重建了首次建立匯入(P1 套件寫入核心 + P2 Excel + P3 Docx),P4(update-diff) 刻意 defer。本檔即盤點 P4 + 其餘未搬能力 = V1 真正的完整匯入體驗。
V1 是一套「差異驅動匯入」系統(FR-011.2「SSP Update Diff」,2026-05-07 出貨):
| 維度 | V1 能力 |
|---|---|
| 格式 | docx 和 excel(各自獨立 route + app service) |
| 建立 + 更新 | 兩者皆有。重複匯入時跑 side-by-side 差異比對(7 類子物件算 unchanged/changed/added/gone)+ smart default action + 逐項決定(per-control + per-AO 雙層)再寫 |
| 差異比對來源 | parsed(匯入檔解析)vs current(既有 SSP/MF 快照),per-field 比對 |
| **落點(2 種) | 合規資源庫範本 SSP(source_type='module_frame')和** 專案 SSP(source_type='ssp') |
| 人員自動比對 | person/organization 三層遞進(exact → normalized → fuzzy + 已選標籤),配到系統 user / org_unit,回填 matched_user_id / matched_org_unit_id |
| 未匹配段落手動指派 | docx 解析提出 unmatched_paragraphs,confirm 時可手動指派到控制項 / AO |
| 寫入子物件(8 類) | system-characteristics、parties、components、leveraged-authorizations、inventory-items、control-implementation、by-component、control-objectives(AO statements) |
| 多框架 | adapter registry(CMMC / ISO …),DocxParserCore + CmmcSspAdapter 可擴充 |
| 檔案 | 角色 |
|---|---|
app/oscal/service/ssp_docx_diff_service.py |
差異計算 + 決策註解核心(7 類 diff_status、smart default、annotate_parse_result) |
domain/oscal/service/party_reconciliation_service.py + domain/oscal/service/reconciliation/{base,person_reconciler,organization_reconciler}.py |
人員/組織三層比對 |
domain/oscal/strategy/ssp_write_strategy.py |
專案 SSP 落點寫入(source_type='ssp') |
domain/oscal/strategy/module_frame_write_strategy.py |
資源庫範本 SSP 落點寫入(source_type='module_frame') |
domain/oscal/strategy/i_ssp_docx_write_strategy.py |
寫入 strategy 介面 |
domain/oscal/parser/ssp_intermediate.py |
ParsedParty / ParsedComponent / ParsedLeveragedAuthorization … 中介資料模型 |
domain/oscal/adapter/cmmc_ssp_adapter.py |
CMMC docx adapter |
api/oscal/routes/ssp/ssp_docx_import_route.py / ssp_scoped_excel_import_route.py |
V1 route(部分已被 V2 P2/P3 取代,scoped excel 仍 dark) |
| 維度 | V2 現況 |
|---|---|
| 格式 | docx + excel 皆可 |
| 建立 / 更新 | 只有建立(create-only)。套件 OscalIoService.import_ssp 僅支援 mode='create',且目標 SSP 必須空殼(有 system-characteristics / system-implementation / control-implementation 任一 → 丟 ValueError → 412)。無 diff、無更新 |
| 落點 | 只有合規資源庫。excel _VALID_SOURCE_TYPES={"framework_version","module_frame"}、docx ={"module_frame"};專案 SSP('ssp')不收 |
| 人員比對 | ⚠️ 待 verify(create 路徑是否保留任何 reconciliation;docx adapter 可能有部分 enrich) |
| 手動指派 | ❌ 無 |
| 逐項決策 | ❌ confirm 只把 parsed 結果直寫(create),無 per-item accept/reject |
| 寫入子物件 | import_ssp 核心 4 類:metadata roles/parties、system-characteristics、system-implementation/components、control-implementation/IR(SoA props)。statements / by-components / leveraged / inventory 不在核心(另有 FR-032 v2-bundle 路徑) |
| 檔案 | 角色 |
|---|---|
jedi-oscal-v2 .../app/service/io/oscal_io_service.py:247-320 |
import_ssp(create-only guard 在 289-298、mode 守門 278) |
app/oscal/service/ssp_excel_import_app_service.py |
Excel 匯入 confirm(Model A/B,create) |
app/oscal/service/ssp_docx_import_app_service.py |
Docx 匯入 confirm(_VALID_SOURCE_TYPES) |
app/oscal/service/import_adapter/{excel_to_oscal_ssp,docx_to_oscal_ssp}.py |
parsed → OSCAL ssp dict adapter |
| # | 能力 | V1 | V2 現況 | 缺口性質 |
|---|---|---|---|---|
| 1 | 既有 SSP 重複匯入 + diff 更新 | ✅ side-by-side + 逐項決定 | ❌ create-only,非空 412 | 最大。需套件 import_ssp merge/diff mode + 主專案 diff service 重接 |
| 2 | 差異預覽後端(7 類 diff_status / smart default) | ✅ ssp_docx_diff_service |
❌ disabled | 主專案 port(current 端改讀 v2 snapshot) |
| 3 | 專案 SSP 落點(source_type='ssp') |
✅ ssp_write_strategy |
❌ 不收 | 需 create+update 都支援專案 SSP(且專案 SSP 恆非空 → 倚賴 #1) |
| 4 | 人員/組織三層自動比對 | ✅ reconciliation/ | ⚠️ 待 verify,多半缺 | 主專案 port(reconciler 改吃 v2 user/org + party) |
| 5 | 未匹配段落手動指派 | ✅ manual_assignment | ❌ | 主專案 port |
| 6 | per-control + per-AO 細粒度決策 | ✅ 雙層 | ⚠️ 待 verify,多半缺 | 跟 #2 一起 |
| 7 | 寫入子物件涵蓋 | 8 類 | 核心 4 類 | statements/by-components/LA/inventory 補齊(部分有 FR-032 路徑) |
A. 套件層(jedi_oscal_v2)— 即 import-ssp 設計的 P4:
OscalIoService.import_ssp 加 mode='update'(或 diff/overwrite):對非空 SSP 不再丟錯,改逐子物件 upsert。build_ssp_snapshot(ssp_id)(current 端中介快照,給 diff 比對用)。B. 主專案層(把 V1 disabled 那套重接 V2):
ssp_docx_diff_service:current 端從讀 V1 entity 改讀 V2 snapshot;移植 7 類 diff 標注 + smart default。reconciliation/:person/org reconciler 改吃 v2 party + 現行 user/org_unit domain service。ssp_write_strategy(專案落點)/ module_frame_write_strategy(資源庫落點):改走 v2 SspService 子物件 CRUD(不再 import V1 entity)。ssp_scoped_excel_import_route(專案 excel)+ docx 的 'ssp' source_type;confirm 改走 diff/decision 流程。規模參考:接近 FR-011.2「SSP Update Diff」當初的工作量(跨套件 + 主專案 diff/reconcile/write strategy + FE 決策 UI)。因此建議獨立一個 session(或子弧)處理,不要塞進其他收尾。
import_ssp 現況再讀一次(mode/guard 是否已被別棒動過)。git log 確認沒被刪;它們 import V1,不可直接 wire 進 boot,只能 port)。/ssp/<uid>/control-implementations/import)— 已 v2 化、是 upsert(對非空 SSP 能用),跟本匯入是不同功能。/oscal/export/...)+ SSP docx/pdf 匯出(/ssp/<uid>/export)— 已 v2 化(Wave2 收尾)。