Bug D FIXED — 收尾 SUMMARY(2026-05-25)

項目 內容
Branch feature/ssp-oscal-alignment(BE only — FE 無改動)
起源 handoff handoff/2026-05-25-bug-d-leveraged-services-handoff.md
涵蓋 fix docx adapter Table #7 (Category) 也 emit ParsedLeveragedAuthorization;ParsedLeveragedAuthorization 新增 props 欄位;write strategy merge props 進 entity JSONB
Verify evidence mf 369 (f489a145-47da-4506-8675-5cf45a6f218b) ssp 267 oscal.ssp_leveraged_authorizations 4 筆完整(MDR / Microsoft Windows Update / Fortinet / 印表機)+ FE template-edit 「外部利用服務 (4)」tab 4 列完整
接續 Bug A / B / C / D 四 bug 全 close,design §11.17 + §11.22 + §11.23 + §11.24 + §11.25 完整覆蓋 ssp-oscal-alignment Phase 4 fix arc

1. Bug D root cause(單段,無需 follow-up)

症狀:mf template-edit「外部利用服務 (1)」但 docx 預覽「外部利用服務 (4)」— 寫 1/4 (3 筆漏)。

Root causecmmc_ssp_adapter.adapt_to_bundle Table #6 (FedRAMP CSP/CSO) emit ParsedLeveragedAuthorization + ParsedComponent pair;Table #7 (Category 一般外部服務) 卻只 emit ParsedComponent完全沒 emit LA。User docx 4 筆分佈:1 T6 + 3 T7 → 寫 1 LA。Product「外部利用服務」tab 讀 oscal.ssp_leveraged_authorizations(不讀 Component),所以 3 筆漏。

Fix(commit 本期):T7 loop 也 parsed_las.append(ParsedLeveragedAuthorization(title=..., props={"category", "purpose", "protocol", "port_ranges", "security_auth"})),並補上 paired Component 的 leveraged_authorization_ref=titleParsedLeveragedAuthorization 新增 props Optional Dict 欄位裝非 OSCAL 標準的 product-specific fields。LeveragedWriteStrategy.write merge parsed.props 進 entity props JSONB(OSCAL 標準欄位 setdefault precedence)。bundle_restore._restore_la round-trip props 欄位(JSONB persist + restore)。

詳細 root cause / fix options trade-off / Bug B vs Bug D 區別 → design.md §11.25。


2. Commits 清單

Repo Commit 內容
BE 本 commit fix: Bug D — cmmc adapter T7 emit LA + ParsedLeveragedAuthorization.props + write strategy props merge + design §11.25 + 本 SUMMARY
FE 無改動
jedi-* 無改動

push 狀態:1 commit 待 push(等 user 拍板)。


3. Verify Evidence

3.1 BE pytest (fix 後)

140 passed, 10 warnings in 0.89s
(覆蓋 test_cmmc_ssp_adapter_v3_bundle / test_ssp_write_strategy / test_parsed_bundle_normalizer
   / test_ssp_intermediate_v2_bundle / test_ssp_docx_import_app_service
   / test_ssp_import_pipeline_smoke / test_a4_reconciliation_leveraged
   / test_a4_reconciliation_orchestrator)

3.2 BE log(fix 後 E2E parse_job 148, mf 369, ssp 267)

parse_job 148: lev_auths=4, lev_svcs=4, components=4, parties=5
  (pre-fix 同 docx parse_job 146: lev_auths=1, lev_svcs=4 — 4 → 1 → fix 後 4 → 4)

3.3 DB(cmmgr SET app.is_super_admin='t'

SELECT id, title, props->>'category' AS category, props->>'protocol' AS protocol,
       props->>'fedramp_package_id' AS fedramp, props->>'impact_level' AS impact
  FROM oscal.ssp_leveraged_authorizations
 WHERE ssp_id=267
 ORDER BY id;
--  21 | MDR、病毒碼與威脅情資同步更新 |                 |                  | FR18078583629 | moderate
--  22 | Microsoft Windows Update      | service         | HTTPS (Port 443) |               |
--  23 | Fortinet                      | service         | HTTPS (Port 443) |               |
--  24 | 印表機                        | interconnection | 有線網路         |               |
-- (4 筆 — T6 一筆 FedRAMP + T7 三筆 external-service / interconnection)

3.4 FE 行為

mf f489a145-47da-4506-8675-5cf45a6f218b /template-edit 「外部利用服務 (4)」tab:

類別 服務名稱 狀態 描述 / 用途
— (T6 FedRAMP) MDR、病毒碼與威脅情資同步更新 運作中
service Microsoft Windows Update 運作中 系統安全性更新與補丁下載
service Fortinet 運作中 防火牆校時
互連 印表機 運作中 輸出紙本

截圖 .playwright-mcp/bug-d-fixed-leveraged-4-items.png 已存。User 視覺驗收 4 筆完整。


4. 殘留 / 不在 scope

項目 處理
T6 FedRAMP 行 category 顯示「—」 Cosmetic — T6 = OSCAL FedRAMP authorization 嚴格語意沒 category prop(只有 fedramp_package_id 區分)。FE 顯示「—」合理;未來若想顯示「FedRAMP 授權」label 可另開 Issue
Excel side ParsedLeveragedAuthorization.props 也接 product-specific fields Excel SHEET_LEVERAGED 每行 1 LA(無 T6/T7 拆分)目前沒踩 bug;但若 Excel template 加 category column 同樣會踩 — 列入 future cross-source consistency item
jedi-oscal _upsert_responsible_party silent-skip pattern 同 Bug B follow-up — jedi-oscal 進版時順手改 log.warning
Cross-source ParsedParty / ParsedLA adapter 共用 helper refactor 同 Bug B follow-up — Excel + docx + 未來 OSCAL JSON 各自 build ParsedParty / ParsedLA,沒共用 → 下次又會漏
進版(L8 jedi-oscal Nexus + L9 BE/FE 版號對齊) User 拍板才做 — Bug D fix 不涉 jedi-oscal 套件層
mf 367 (fc51dd08) 失敗 import 殘留 仍可手動刪,無害

5. 教訓(Bug D 單段)

  1. Adapter 兩條對稱 path 一定 cross-source check:T6 emit LA + Component,T7 卻只 emit Component — adapter 內「同概念多 path」必須一開始驗 user-facing UI 看到的最終 shape 一致。Phase 2 Task 8 落地時沒 surface gap,Phase 4 末才被踩到。
  2. 「FE preview 數字」跟「DB 寫入數字」必須 1:1:FE 預覽計數 (leveraged_services) 跟 BE confirm 寫入計數 (leveraged_authorizations) 不同 key 不同來源 → 顯示「外部利用服務 (4)」但實寫 1 筆。下次 parsed_result 放兩個語意接近 list 必須附 cross-check(要嘛單一 key,要嘛兩 key 同 length)。
  3. OSCAL 語意純粹 vs Product 語意現實:OSCAL leveraged_authorization 規範限 FedRAMP;product「外部利用服務」採廣義(含 printer)。不一致時優先 product 語意(user-facing concept = single source of truth),OSCAL 例外走 props JSONB 容納。
  4. Bug B 教訓繼續套用:「寫成功」計數會騙人 — Bug D fix 後我們即時 query DB 而非看 BE log written_las 計數,避免重蹈覆轍。

6. 給 user 的 hand-off summary

已修 + 已驗收

  1. BE adapter (domain/oscal/adapter/cmmc_ssp_adapter.py + domain/oscal/parser/ssp_intermediate.py + domain/oscal/import_pipeline/bundle_restore.py + domain/oscal/service/write_strategy/leveraged_write_strategy.py) — Table #7 也產 LA + props 欄位
  2. BE 單元測試 (tests/test_cmmc_ssp_adapter_v3_bundle.py + tests/test_ssp_write_strategy.py) — 共 140 passed
  3. E2E 用 user 桌面那份 亞航-CMMC-SSP-20260520-1會議討論版.docx 重跑完整 docx import flow(CMMC Level 1 / module_frame create mode)→ 新建 mf f489a145-47da-4506-8675-5cf45a6f218b → DB ssp_leveraged_authorizations 4 筆完整 → FE template-edit「外部利用服務 (4)」tab 4 列正確顯示
  4. design.md §11.25 補完整 root cause + fix options trade-off + 教訓
  5. 本 SUMMARY 涵蓋全套 fix + 驗證 + 殘留

動作項(user 拍板)

  1. push BE 本期 commit 到 origin/feature/ssp-oscal-alignment(user 一直 push 自己掌控)
  2. 後續若要 release 一版 → 走 release SOP(release_note + bump pyproject.toml + FE 對齊版號)
  3. 看完截圖 .playwright-mcp/bug-d-fixed-leveraged-4-items.png 驗收 UI

測試新建殘留資料

  • mf f489a145-47da-4506-8675-5cf45a6f218b (id=369) 「Bug D E2E - 亞航 CMMC SSP 測試」— user 可手動刪
  • parse_job 147 (failed CMMC L2 mismatch) + parse_job 148 (completed) 為驗證痕跡,可保留也可清

6.1 順手修的 cascade bugs (Bug E + Bug F)

E2E 用 user docx 跑 flow 過程在 Playwright console 看到 1 個 FE error + 多筆 i18n warning,順手修:

Bug E — ModuleFrameDocumentPoolPanel.vue TDZ ReferenceError

症狀:FE console ERROR fetchDocuments failed ReferenceError: Cannot access 'baseService' before initialization。template-edit「程序書文件池」tab 永遠無法 fetch documents(雖然 fallback catch 不阻 user,但功能廢)。

Root causeconst baseService = new BaseService() 之前 declared 在 line ~108(onSelectFiles 上方),但 fetchDocuments()watch(immediate: true, line ~85) 在 setup 階段立即呼叫 — 那時 baseService 還在 Temporal Dead Zone。

Fix:把 const baseService = new BaseService() 移到 State 區段(line ~55),確保 fetchDocuments / onSelectFiles 都能 access。

Bug F — i18n keys 缺漏(其中 system-user 是 Bug B fix 引發的 cascade)

症狀:FE console 多筆 [intlify] Not found 'lang.oscal_role.ssp_party_role.system-user.label' key in 'tw' locale messages + 'lang.module_frame.template_ssp_empty_hint' warning。

Root cause

  • system-user:Bug B fix(BE commit fef5eecf)把 docx 匯入 person 的 default role 設為 "system-user",但 FE oscal-role.json (zh-tw + en) 沒對應 label / description → 責任人員 tab render 時警告且顯示 raw key
  • template_ssp_empty_hintModuleFrameTemplateEditView.vue:2058 用此 key 但 module-frame.json 沒定義(雖有 ?? fallback hardcoded 中文字串但 console warning + 沒法 i18n 切換)

Fix:兩 locale 都補上對應 key。

檔案 改動
src/config/locales/i18n/{zh-tw,en}/oscal-role.json system-user label + description
src/config/locales/i18n/{zh-tw,en}/module-frame.json template_ssp_empty_hint

驗證:Playwright 重 navigate template-edit 頁,循序 click 10 tabs (基本資料 / 適用控制項 / 受評標的 / 責任單位 / 責任人員 / 設備 / 資訊系統 / 外部利用服務 / 元件清冊 / 程序書) → console 0 errors / 0 warnings(vs 修前 ERROR fetchDocuments failed + 多筆 i18n warnings)。

Bug E + F commit

FE commit c7d5791 —「fix(ssp-oscal-alignment): Bug E + F — DocumentPoolPanel TDZ + Bug B 引發的 i18n missing keys」

教訓:BE fix 改字串常量(如 default role string "system-user")必須同步 trace FE 是否有對應 i18n key — 否則 user 立即看到 raw key + console warning。Cross-stack consistency check 應該列入 Bug B / Bug D 同類 fix 的 follow-up checklist(跟 cross-source consistency check 平行)。


7. 為何 §11.22~§11.24 Bug B fix 沒順手解 Bug D

Bug B 修 party adapter chain(_dict_to_parsed_parties + _upsert_responsible_party),Bug D 修 leveraged adapter T6/T7 拆分(adapt_to_bundle.parsed_las emission)— 完全不重疊的 code path。User 觀感都是「docx N 筆 → 看到 M 筆 (M < N)」但 root cause 完全不同。

兩者共通教訓:adapter / extractor / write strategy 任何 chain,凡涉及「跨 table / 跨 source 拆兩 path」一定先 cross-source check user-facing UI。Bug B 教訓 2 (Cross-source feature fix audit) + Bug D 教訓 1 (Adapter 兩條對稱 path) 是同概念不同 facet — 列入未來 review checklist。