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
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 把這段補完:
BaseReconciliationService 上,加 USER_SELECTED stage 處理樣板的 matched_* 預配對 column,並 retroactive 補 A3 PersonReconciler / OrganizationReconcilerAbstractSspImplementationItemWriteStrategy base class)include_controls 從 A4 ControlReconciler 結果直接反查 catalog,A4 ship 後 subset profile 立刻生效_dict_to_parties 補 matched_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。
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。
不動(design §10 鎖定範圍)。jedi-oscal 維持 path-dep(feature 整體完工才一次 bump version + 推 Nexus)。
T10 cucumber 拍板跳過 — 留 follow-up F-A4-cucumber(理由見 §5)。
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_parties 傳 matched_user_label / matched_org_unit_label |
domain/oscal/parser/ssp_intermediate.py |
ParsedParty 加 matched_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 傳遞 |
| 項目 | 路徑 | 狀態 |
|---|---|---|
| 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 不再追加。
| # | 項目 | 處理時機 |
|---|---|---|
| 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 收口時 |
跨 repo work — 在 ~/Projects/Billows/Audit-Manager/compliance-manager-test/ 開 branch。
前置條件:
範圍:新建 features/regression/module-frame/06-ssp-excel-import-entity-match.feature 含 5 scenarios:
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。
pyproject.toml dev-path 改動不 commit;feature 完成才一次 bump version + 推 Nexus)pyproject.toml:A4 期間 dev-path 改動 working tree 仍未 commit(正常 — Phase 2 整體完工才一起還原 + commit)A4 改動了 app service / DI Container / domain layer(app/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 &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 不動A4 完工後:
matched_*_id / match_method / match_confidence;fuzzy 拍板攔截路徑下一個決策點:
✅ A4 phase 完整 ship — 6 session(A→F)on schedule,T10 cucumber 拍板跳過留 follow-up F-A4-cucumber
進入 A5 phase / Track B 條件已就緒:
import_summary response 含 ssp_id / sys_impl_main_id / 5 個 *_written 計數,FE 可直接讀A4 累計交付:
| 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 完全對齊。