# 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 cause**：`cmmc_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=title`。`ParsedLeveragedAuthorization` 新增 `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'`）

```sql
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 cause**：`const 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_hint`：`ModuleFrameTemplateEditView.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。
