A4 Phase Final SUMMARY — devices / info_systems / leveraged / 控制項 / AO 鉤稽 + SSP 寫入完整化

Phase:A4(Track A:Excel 匯入第四階段) 狀態:BE shipped ✅(Session A→F 共 6 session 全完成) Ship 日期:2026-05-20 Task arc 跨度:2026-05-20 同日 6 session(A 設計 / B plan / C reconciler 骨架 / D reconciler 細節 / E 寫入 pipeline / F 補測試 + 收尾) 依賴本 phase:A5(預覽 UI + Confirm)讀本 phase Step 7 回填的 matched_*_id / match_method


1. 為什麼做 A4(與 A4 行為差異)

A3 ship 後 Excel/docx flow 的 parties 已走完整 reconcile(exact / normalized / fuzzy 三階段),但 SSP Excel confirm_import其餘 5 個 entity(devices / information_systems / leveraged / catalog controls / assessment objectives)跟 SSP shell(SystemSecurityPlan + SystemCharacteristic + SspSystemImplementation main)仍是 stub log 計數,沒實際落地。

A4 把這段補完:

  • Reconciliation 層 — 5 個新 reconciler 建在 A3 BaseReconciliationService 上,加 USER_SELECTED stage 處理樣板的 matched_* 預配對 column,並 retroactive 補 A3 PersonReconciler / OrganizationReconciler
  • Write 層 — SSP shell 建立 / 既有 SSP 解析 + 5 WriteStrategy(含 Device / InfoSystem / Leveraged 共用的 AbstractSspImplementationItemWriteStrategy base class)
  • App service _write_all_data 重整為 8-step 完整 pipeline(party reconcile → SSP shell → typed dataclass → orchestrator reconcile → party write → 5 entity write → parsed_result 回填 → 統計 return)
  • A2 §15.3 整併 — superset flow 的 include_controls 從 A4 ControlReconciler 結果直接反查 catalog,A4 ship 後 subset profile 立刻生效
  • A3 retroactive_dict_to_partiesmatched_user_label / matched_org_unit_label 傳遞,A3 漏掉的樣板 lookup col USER_SELECTED stage 一次補完
Caller / 區塊 A2 / A3 ship 後 A4 Session F ship 後
_write_all_data superset flow 寫 MF + parties;devices/info_systems/leveraged/controls/AO 只 stub log 計數 寫 MF + parties + SSP shell + control_implementations + control_impl_objectives + 3 種 ssp_system_implementation_items
_write_all_data update flow 寫既有 MF 的 parties;其他 stub 同上 + _resolve_existing_ssp_shell 找既有 SSP(缺 SSP raise GRC_412026
import_summary response 欄位 parties_written + 5 個 *_pending 計數 parties_written + controls_written / aos_written / devices_written / info_systems_written / leveraged_written + ssp_id / sys_impl_main_id
5 entity reconciler 不存在 Device / InformationSystem / Leveraged / CatalogControl / AssessmentObjective 完整三階段(USER_SELECTED / EXACT / NORMALIZED;Leveraged + fuzzy;無 fuzzy on Device/InfoSys/Control/AO 嚴格識別符)
A3 parties USER_SELECTED stage 漏處理(樣板 matched_user_label / matched_org_unit_label 沒生效) 補齊 — 同流程 A3 reconciler 也支援
UNMATCHED 行為 未實作 Control / AO: log warning + skip(沒 FK 寫不進去);Device / InfoSystem / Leveraged: 仍寫入純文字 + FK=null(保留 user data 給 A5 UI 後補配對)

詳細 reconciler 演算法 / pipeline 步驟見 design-A4.md §5 / §6.1。


2. Commit 清單

compliance-manager-be(branch feature/ssp-import-export-phase2

Commit 內容 Session
1de5f51 docs(ssp-import-export-phase2): A4 design.md A
57bcb2e docs(ssp-import-export-phase2): A4 implementation-plan.md B
57a9567 feat(oscal): A4 T1 stage 0 hook + dataclass + helpers C
72ef229 feat(oscal): A4 T2 reconciler + orchestrator skeleton + DI C
7c2b860 feat(oscal): A4 T3 reconciler full algorithm D
8936aab feat(oscal): A4 T4 A3 retroactive USER_SELECTED stage D
9d7c102 feat(oscal): A4 T5 A2 §15.3 superset include_controls 反查 D
40c4a12 feat(oscal): A4 T6 SSP shell + GRC_412026 error code E
579eb76 feat(oscal): A4 T7 5 WriteStrategy + base class E
78118eb feat(oscal): A4 T8 _write_all_data 8-step pipeline + DI 完整化 E
81073ef docs(ssp-import-export-phase2): A4 Session E 收尾 — design §11.1 + changelog + tracker + handoff E→F E
600f50a test(oscal): A4 T9 unit + integration tests 補齊 boundary case F
00731d0 docs(ssp-import-export-phase2): A4 Session F → T11 handoff prompt(縮 scope 後) F
(本 commit) docs(ssp-import-export-phase2): A4 Session F / T11 收尾 — SUMMARY + tracker + T9 changelog F

A4 BE 共 10 個實作 commit(feat × 8 + test × 1 + docs E 收尾 × 1)+ 4 個 docs handoff/SUMMARY commit。

jedi-* 套件

不動(design §10 鎖定範圍)。jedi-oscal 維持 path-dep(feature 整體完工才一次 bump version + 推 Nexus)。

compliance-manager-test

T10 cucumber 拍板跳過 — 留 follow-up F-A4-cucumber(理由見 §5)。


3. 改動範圍

新建檔案

Domain layer — reconciliation/(5 個 + 1 個 helper):

domain/oscal/service/reconciliation/
├── catalog_control_reconciler.py        # 二階段 (USER_SELECTED + EXACT/NORMALIZED on control_id)
├── assessment_objective_reconciler.py   # 走父 control 配對,reconcile_with_control_map
├── device_reconciler.py                 # 三階段 (USER_SELECTED label / EXACT IP+name / fallback)
├── information_system_reconciler.py     # 三階段 + system_owner label 反查
├── leveraged_reconciler.py              # 三階段 (USER_SELECTED party / EXACT name / FUZZY_NAME_PREFIX)
└── ssp_entity_orchestrator.py           # facade — 5 reconciler dispatch by entity type

Domain layer — write_strategy/(5 strategy + 1 base):

domain/oscal/service/write_strategy/
├── base.py                                       # AbstractSspImplementationItemWriteStrategy
├── control_write_strategy.py                     # 寫 control_implementations,返 ctrl_impl_id_map
├── ao_write_strategy.py                          # 寫 control_impl_objectives,需 ctrl_impl_id_map 注入
├── device_write_strategy.py                      # kind='hardware'(繼承 base)
├── information_system_write_strategy.py          # kind='system'(繼承 base)
└── leveraged_write_strategy.py                   # kind='leveraged-authorization'(繼承 base)

Tests(17 個 A4 test 檔,~165 test):

tests/test_a4_*.py
├── test_a4_parsed_entity_fixtures.py             # 共用 fixture
├── test_a4_reconciliation_helpers.py             # stage 0 dispatch / by-key 對齊
├── test_a4_reconciliation_orchestrator.py        # facade dispatch
├── test_a4_reconciliation_device.py              # 三階段 + boundary
├── test_a4_reconciliation_information_system.py  # 三階段 + system_owner
├── test_a4_reconciliation_leveraged.py           # fuzzy + suffix strip
├── test_a4_reconciliation_catalog_control.py     # 二階段
├── test_a4_reconciliation_assessment_objective.py # reconcile_with_control_map
├── test_a4_a3_retroactive.py                     # A3 retroactive PersonReconciler / OrganizationReconciler
├── test_a4_a2_integration.py                     # A2 §15.3 superset include_controls 反查
├── test_a4_ssp_shell.py                          # _create_ssp_shell / _resolve_existing_ssp_shell
├── test_a4_write_strategy_base.py                # base template method
├── test_a4_write_strategy_control.py             # 寫 control_implementations
├── test_a4_write_strategy_ao.py                  # 寫 control_impl_objectives
├── test_a4_write_strategy_device.py              # 繼承 base
├── test_a4_write_strategy_information_system.py  # 繼承 base
├── test_a4_write_strategy_leveraged.py           # 繼承 base
├── test_a4_write_all_data_pipeline.py            # 8-step pipeline + _dict_to_* + by-key 對齊
├── test_a4_boundary_cases.py                     # T9 boundary(reconciler 多 candidate / abbr 解析 / Control+AO empty list)
└── test_a4_reconcile_to_write_integration.py     # T9 真實 reconciler+strategy 整合

改動既有檔案

檔案 改動範圍
common/code/grc_error_code.py 新增 GRC_EXCEL_UPDATE_FLOW_NO_SSP = ("Excel update flow 失敗:對應 MF 尚未建立 SSP", "GRC_412026")
app/oscal/service/ssp_excel_import_app_service.py __init__ 從 8 個 dep → 19 個(A2 8 + A3 1 + A4 phase 10 + profile_domain extra 1);_write_all_data 重整為 8-step pipeline;新增 _create_ssp_shell / _resolve_existing_ssp_shell / _resolve_catalog_id / _update_parsed_result_with_reconcile / 5 個 _dict_to_* helper / 1 個 module-level _iter_paired by-key 對齊 helper
di_containers/oscal/oscal_containers.py 加 5 reconciler Factory + 1 orchestrator Factory + 5 WriteStrategy Factory;ssp_excel_import_app_service Factory 19 個 wiring
domain/oscal/service/reconciliation/base.py 加 stage 0 dispatch hook(檢查 matched_*_id / matched_*_label 直接出 USER_SELECTED)
domain/oscal/service/reconciliation/person_reconciler.py + organization_reconciler.py A3 retroactive — 接 stage 0 hook,配 _dict_to_partiesmatched_user_label / matched_org_unit_label
domain/oscal/parser/ssp_intermediate.py ParsedPartymatched_user_label / matched_org_unit_label(optional);新增 5 個 Parsed* dataclass(ParsedDevice / ParsedInformationSystem / ParsedLeveraged / ParsedCatalogControl / ParsedAssessmentObjective)+ MatchMethod 擴張 (USER_SELECTED)
app/oscal/service/ssp_excel_parser_service.py _dict_to_parsed_parties 補 label 傳遞

4. 規範文件齊全度

項目 路徑 狀態
Design.md docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A4.md ✅ Session A ship (1de5f51;含 §11.1.1-16 共 16 條偏差紀錄)
Implementation plan docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-A4.md ✅ Session B ship (57bcb2e;3466 行 / 11 task / 4 session)
Changelog(Session E 主 ship) docs/changelog/2026-05-20-feat-ssp-excel-import-phase2-a4-write-pipeline.md ✅ commit 40c4a12 + 579eb76 + 78118eb;含 7 條 Session E 偏差摘要
Changelog(T9 boundary tests) docs/changelog/2026-05-20-tweak-a4-boundary-and-integration-tests.md ✅ commit 600f50a;type=tweak
Tracker docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md ✅ A4 row → BE shipped + 10 commits + 2 changelog
對話歷史 dump docs/conversation-history/2026-05-20/ssp-import-export-phase2-A4/ pending — 等 user 提供 conversation export 後再 dump(不二次加工 / 原樣寫入)
Session handoff prompt docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-20-a4-*.md (b-to-c / c-to-d / d-to-e / e-to-f / f-t11-only) ✅ 5 個 handoff prompt 完整保留

design-A4.md §11.1 已含 Session C / D / E 共 16 條偏差紀錄。Session F (T9 + T11) 沒新偏差 — T9 純補 test 行為一致,T11 純文件,§11.1 不再追加。


5. 已知 follow-up

# 項目 處理時機
F-A4-cucumber T10 cucumber regression(5 scenarios)— 拍板跳過,留 follow-up env 配齊那天一次性補完
F-A5 A5 phase 預覽 UI + Confirm 寫入(用本 phase Step 7 回填的 matched_*_id / match_method 讀),fuzzy 拍板路徑 A5 phase
F-conv-history 對話歷史 dump 到 docs/conversation-history/2026-05-20/ssp-import-export-phase2-A4/(拆 part 6 個 sub-topic:A 設計 / B plan / C reconciler / D reconciler-details / E write-pipeline / F test) user 提供 export 後
F-A4-refactor A4 內部 refactor(如 reconciler 是否再合併 / orchestrator 是否再抽象)— 暫無觸發點,看下游 phase 是否需要 視 A5/B2 / 跨 entity 需求
F-jedi-oscal-publish feature 整體 ship 後 user 拍板 → bump jedi-oscal version + 推 Nexus + 主專案 pin 新版本 Phase 2 task arc 收口時

F-A4-cucumber 撰寫前置條件(env 配齊那天執行)

跨 repo work — 在 ~/Projects/Billows/Audit-Manager/compliance-manager-test/ 開 branch。

前置條件

  1. GitLab CI pipeline 配齊(A1/A2/A3 cucumber 都同期 partial ship / pending — 同期 4 phase 都等 env 配齊)
  2. A4 fixture seed helper 配齊:device 'Web-01' / info_system 'ERP' / Party 'AWS GovCloud' / catalog 'AC-1' 等

範圍:新建 features/regression/module-frame/06-ssp-excel-import-entity-match.feature 含 5 scenarios:

  • control_id 對到 catalog 內 control (exact)
  • device matched_device 下拉選定 (user_selected)
  • info_system name 對到 tenant entity (exact)
  • leveraged provider fuzzy 對到 Party (fuzzy_name_prefix)
  • AO objective_id 父 control 配到後自身配到 (exact)

step + page object 沿 A3 cucumber pattern(feature/a3-ssp-party-match-regression ship 在 commit 27e52fe)。

partial ship pattern:env 配齊那天可一次性補完 A1 / A2 / A3 / A4 共 4 phase cucumber。

跳 T10 理由(2026-05-20 拍板)

  • GitLab CI pipeline 配齊時程 user 不清楚 → 寫 cucumber 也只是 dead code
  • A1 / A2 / A3 cucumber 都同步 partial ship / pending — 同期 4 phase 都等 env 配齊
  • 留 follow-up F-A4-cucumber:env 配齊那天一次性補 4 phase 比較有效率

6. 部署 Handover

6.1 套件狀態

  • jedi-oscal:維持 path-dep(pyproject.toml dev-path 改動不 commit;feature 完成才一次 bump version + 推 Nexus)
  • jedi-auth / jedi-common:A4 不動 — 維持 Nexus pin
  • 主專案 pyproject.toml:A4 期間 dev-path 改動 working tree 仍未 commit(正常 — Phase 2 整體完工才一起還原 + commit)

6.2 BE 重啟需求

A4 改動了 app service / DI Container / domain layerapp/oscal/service/ssp_excel_import_app_service.py + di_containers/oscal/oscal_containers.py + 11 個新 domain class)— BE 沒 hot reload,下次 manual smoke 測 A4 confirm flow 前必先重啟

lsof -ti:8000 | xargs kill -9
nohup poetry run python main_app.py > /dev/null 2>&1 &

6.3 DB / Migration

  • A4 無 schema 異動(依賴 A0 / A0.1 既有結構 + A0.1 ship 的 ssp_system_implementations main + ssp_system_implementation_items
  • oscal_parties / oscal_responsible_parties schema 不動
  • system_security_plans / system_characteristics / control_implementations / control_impl_objectives schema 不動

6.4 後續 phase 開工建議

A4 完工後:

  • A5 phase(預覽 UI + Confirm 寫入)— 讀 A4 Step 7 回填的 matched_*_id / match_method / match_confidence;fuzzy 拍板攔截路徑
  • Track B(SSP 匯出 B1-B6)— 可獨立起手,與 A5 解耦

下一個決策點:

  • (a) 走 A5 phase(接續 Track A 完成 Excel 匯入全程)
  • (b) 跳 Track B(SSP 匯出 B1-B6;可平行 / 換 thread)
  • (c) 暫停評估其他 priorities

7. A4 task arc 規範遵守清單


8. A4 phase 收口確認

A4 phase 完整 ship — 6 session(A→F)on schedule,T10 cucumber 拍板跳過留 follow-up F-A4-cucumber

進入 A5 phase / Track B 條件已就緒:

  • 5 個 reconciler + 5 個 WriteStrategy + SSP shell + 8-step pipeline + DI 19 dep wiring + ~165 個 a4 test 全綠
  • A5 phase UI 需要的 matched_*_id / match_method / match_confidence 全部由 Step 7 回填到 parsed_result
  • import_summary response 含 ssp_id / sys_impl_main_id / 5 個 *_written 計數,FE 可直接讀

A4 累計交付:

  • 10 個 BE 實作 commit(feat × 8 + test × 1 + docs E 收尾 × 1)
  • ~165 個 a4 test 全綠(含 Session E 58 + T9 補 12)
  • 0 regression(a3+a4+a2 ssp_excel 282 全綠)
  • 16 條 design §11.1 偏差條目 完整保留決策軌跡
  • 2 個 changelog(feat write-pipeline + tweak boundary tests)

附錄 — Session 切分回顧

Session 主題 Commit Plan task
A 設計(brainstorm + design-A4.md) 1de5f51 design
B 計畫(implementation-plan-A4.md + T0 verify) 57bcb2e plan + T0
C reconciler 骨架(base hook + 5 dataclass + 5 reconciler skeleton + orchestrator + DI) 57a9567 + 72ef229 T1 + T2
D reconciler 細節 + A3 retroactive + A2 §15.3 整併 7c2b860 + 8936aab + 9d7c102 T3 + T4 + T5
E SSP shell + 5 WriteStrategy + 8-step pipeline + DI 完整化 + 收尾 docs 40c4a12 + 579eb76 + 78118eb + 81073ef T6 + T7 + T8
F T9 補測試 + T11 收尾(T10 跳) 600f50a + (本 commit) T9 + T11 (T10 follow-up)

實際耗時 6 session 跟 design §12 預估的 6 session 完全對齊。