# Bug O — docx 重 import diff stepper 擴展 (4 keys + TabView) Handoff

> ## ✓ FIXED — 2026-05-25 收尾
>
> Bug O 已完工。收尾報告 `docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-o-FIXED-SUMMARY.md`，changelog `docs/changelog/2026-05-25-feat-bug-o-diff-stepper-4key-tabview.md`，design spec 更新 `design.md §11.34`（含 5 教訓 + commit clause）。
>
> 本檔保留作為「接手前 context handoff」歷史紀錄 — 內容反映開工前的設計假設，**實作過程中有偏差**（特別是 annotated key 改用 `<key>_diff` 獨立 suffix，跟原 plan 直接覆寫 raw list 的設計不同），偏差紀錄見 FIXED-SUMMARY + design.md §11.34 教訓段。
>
> 下期 follow-up：`docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-p-q-r-parties-form-handoff.md`（3 個本期 E2E 發現的 parties form bug）。

---


| 項目 | 內容 |
|---|---|
| 緣由 | User 對既有 mf 重跑 docx import 進 diff stepper，只看到 controls / objectives / parties 3 種 diff，**缺**：sc / components / leveraged_authorizations / inventory_items 4 key；跳過 diff 後 confirm 會 silently overwrite user 在 template-edit 已手動編輯的內容。並反映 UI stack 過長要改 TabView。|
| Branch | `feature/ssp-oscal-alignment`（接續 H-N arc）|
| 主要 file | BE: `ssp_docx_diff_service.py` + `ssp_docx_import_app_service.py` confirm decisions 過濾；FE: `sspDocxImportStore.js` + 4 個新 `*DiffSection.vue` + `DiffResolutionStep.vue` TabView refactor |
| 接手前必讀 | **本文件自包含** — 按 §0 順序讀本檔 + design.md §11.34 spec + 既有 PartyDiffCard / ControlDiffCard pattern 即可。**前提**：H-N batch commit 必須先 push 完，working tree 乾淨才開工 |
| 預估時間 | 6-10 小時（跨 BE + FE，4 新 diff section + TabView refactor + tests）|

---

## 0. 接手讀序（fresh session 必照順序）

1. **本文件** — Bug O 完整 context（4 個缺漏 key / 5 phase scope）
2. **`docs/features/FR-028-2605-ssp-oscal-alignment/design.md §11.34`** — full spec（root cause / scope / 不在 scope / cross-references）
3. **`docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-h-to-l-FULL-ARC-SUMMARY.md`** — 前一 session 累積 H-N 9 bugs 完整狀態（特別看「待 commit working tree」段確認 commit 完成）
4. **`app/oscal/service/ssp_docx_diff_service.py`** — 既有 BE diff annotation 結構（controls + parties pattern）
5. **`src/stores/sspDocxImportStore.js`** — FE diff store decisions 結構
6. **`src/components/grc/ssp-docx-import-v2/diff/`** 全資料夾：
   - `DiffResolutionStep.vue` (要 refactor 成 TabView)
   - `ControlDiffSection.vue` / `ControlDiffCard.vue` (pattern 抄)
   - `PartiesDiffSection.vue` / `PartyDiffCard.vue` (pattern 抄)
7. **`src/components/grc/ssp/SspComponentsLeveragedInventoryTab.vue`** — template-edit 端的 3 sub-panel 結構（diff section 視覺對齊用）
8. **`src/components/grc/ssp/SspBasicSection.vue`** — 受評標的 template-edit 端 8 欄位結構（SystemCharacteristicDiffSection 對齊）

讀完後跑 §6 Pre-flight + §7 Verify H-N close → §3 開工順位。

---

## 1. 症狀

### 1.1 User-facing

User 對既有 mf 重 import docx 後進 Step「解決差異」(DiffResolutionStep)：

**目前看到**：
```
[解決差異 step]
  └─ 控制條文：X 已修改 ...  (badge)
  └─ Parties：Y 新增 ...    (badge)
  └─ ControlDiffSection（一串 ControlDiffCard）
  └─ PartiesDiffSection（一串 PartyDiffCard）
```

**期望看到**：
```
[解決差異 step]
  └─ <TabView>
       ├─ 控制條文 (X)
       ├─ 評估目標 (Y)
       ├─ 參與人員與單位 (Z)
       ├─ 受評標的 (1 if changed)
       ├─ 系統依賴元件 (W)
       ├─ 外部正式授權服務 (V)
       └─ 資產清冊 (U)
  </TabView>
```

每 tab header 含 changed/added/gone count badge；無 diff 的 tab disable 或隱藏。

### 1.2 BE 證據

```bash
# 看現有 BE diff_summary 確認 4 key 缺漏
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -A -c "
SET app.is_super_admin='t';
SELECT id, jsonb_pretty(parsed_result->'diff_summary') FROM oscal.ssp_docx_parse_jobs WHERE id=(SELECT MAX(id) FROM oscal.ssp_docx_parse_jobs);
"
# 預期看到 diff_summary 只有 controls / objectives / parties 3 key
# 缺 sc / components / leveraged_authorizations / inventory_items
```

### 1.3 風險

跳過 diff stepper（或 stepper 對 4 key 沒 expose 決策）後 confirm，BE write strategy 直接用 docx 端值覆寫既有 SSP — **user 在 template-edit 已手動編輯的 leveraged / components / inventory / sc 被 silently overwritten**。

---

## 2. 前次教訓（H-N arc 累積，避免重蹈）

1. **Bug L-2 教訓** — FE 編輯路徑跟 BE 寫入路徑 key 必須對齊（v2 leveraged_services vs v3 leveraged_authorizations + components）。本 Bug O 要在 BE diff annotation + FE store + FE UI + BE confirm 4 處同步擴展 v3 keys。
2. **Bug M 教訓** — 整條 chain 缺一斷一（extractor / dataclass / adapter / confirm wire / write filter 五層全要動）。本 Bug O 同樣是 chain bug — BE diff service / FE store / FE UI / BE confirm 過濾任一漏掉都 fail。
3. **Bug J 教訓** — `try/except: pass` silent failure 讓 bug 隱形數週。本 Bug O 寫 BE confirm decision filter 時必加 log.warning 而非默吞。
4. **Bug K 教訓** — 不該重複 wrapper / routing 概念成獨立 domain。Diff service 對 4 key 應該共用 annotation pattern (mirror parties)，不該每 key 寫獨立 service。
5. **「user 看不到的 bug 不是沒 bug」** — diff stepper 4 key 缺漏 ship 過去 phase 4，user 一直沒做重 import 才沒踩到。本 Bug O fix 完必跑既有 mf 重 import 全 path verify。

---

## 3. 開工順位（建議按此順序）

1. **跑 §6 Pre-flight + §7 Verify H-N close**（10 分鐘）
2. **Enter plan mode + 寫 implementation plan**（per CLAUDE.md「新功能開發走標準 SOP」）— 確認 BE annotation pattern / FE decision schema / TabView UI 取捨
3. **BE Phase O-A**：擴展 `ssp_docx_diff_service` — 4 key annotation + summary
4. **BE Phase O-B**：擴展 `ssp_docx_import_app_service.confirm_import` decisions 過濾
5. **FE Phase O-C**：擴展 `sspDocxImportStore` decisions schema
6. **FE Phase O-D**：4 個新 diff section 元件（mirror PartyDiffCard / ControlDiffCard pattern）
7. **FE Phase O-E**：DiffResolutionStep TabView refactor
8. **FE Phase O-F**：i18n key 加（tab header / diff card labels / 對齊既有 ssp_docx_import 命名）
9. **Tests**：BE diff service test + FE store unit test
10. **E2E Playwright**：對既有 mf 跑重 import → 確認 6 tab 全 render + 各自決策路徑通

---

## 4. 該讀的檔案 / 預期改動範圍

### 4.1 BE 端

| 檔案 | 為何 read / 改 |
|---|---|
| `app/oscal/service/ssp_docx_diff_service.py` (line 130+ for parties pattern) | 主改 — 加 4 key annotation + summary。Parties 是現有 pattern reference |
| `app/oscal/service/ssp_docx_import_app_service.py` (confirm_import line 505~, `_apply_v2_bundle_overrides` line 820~) | 主改 — 接 4 個新 decision payload + filter parsed_result by decision before write |
| `app/oscal/service/ssp_import_confirm_service.py` (if used in confirm path) | 確認 4 key write strategy 已 ship + 怎麼接 decision filter |
| `tests/test_ssp_docx_diff_service.py` | 加 4 key annotation + summary test cases |

### 4.2 FE 端

| 檔案 | 為何 read / 改 |
|---|---|
| `src/stores/sspDocxImportStore.js` (line 19+ decisions struct) | 主改 — 加 4 sub-map + init + payload build |
| `src/components/grc/ssp-docx-import-v2/diff/DiffResolutionStep.vue` | 主改 — TabView refactor wrap 既有 2 + 新 4 section |
| `src/components/grc/ssp-docx-import-v2/diff/PartyDiffCard.vue` | Pattern reference — 抄結構建 4 個新 card |
| `src/components/grc/ssp-docx-import-v2/diff/ControlDiffCard.vue` | 同上 |
| `src/components/grc/ssp-docx-import-v2/diff/DiffSectionShell.vue` | 看 section 統一外殼 pattern |
| **新檔**：`SystemCharacteristicDiffSection.vue` / `ComponentsDiffSection.vue` / `LeveragedAuthorizationsDiffSection.vue` / `InventoryItemsDiffSection.vue` | 主要新增 |
| `src/components/grc/ssp/SspBasicSection.vue` | 受評標的欄位結構參考 (8 fields)|
| `src/components/grc/ssp/SspComponentsLeveragedInventoryTab.vue` | 3 sub-panel 結構參考（component_type 14 enum / LA fields / inventory fields）|

### 4.3 跨 repo 必做

- 改 FE 前必 read `~/Projects/Billows/Audit-Manager/compliance-manager-fe/CLAUDE.md`（per memory `feedback_cross_repo_read_claude_md_first`）— 特別「Add / Create Action Button Style」(per Bug L) + Loading / 等待狀態規範 + PrimeVue 3.53 quirks

---

## 5. Pre-flight Command（必跑）

```bash
# 1. 兩 repo branch + working tree
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-be branch --show-current
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-fe branch --show-current
# 預期：兩個都 feature/ssp-oscal-alignment

git -C ~/Projects/Billows/Audit-Manager/compliance-manager-be status --short
# 預期：clean (H-N 已 commit + push)；或只有 dev-only path dep pyproject.toml
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-fe status --short
# 預期：clean

# 2. local vs origin
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-be log origin/feature/ssp-oscal-alignment..HEAD --oneline
# 預期 0 commits ahead (H-N pushed)
git -C ~/Projects/Billows/Audit-Manager/compliance-manager-fe log origin/feature/ssp-oscal-alignment..HEAD --oneline
# 預期 0 commits ahead

# 3. BE / FE listener
lsof -t -i:8000 && echo "BE up" || echo "BE down — restart needed"
lsof -t -i:5180 && echo "FE up" || echo "FE down"

# 4. BE pytest smoke (含 Bug H-N 改動 verify still passing)
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
poetry run pytest tests/test_ssp_docx_import_app_service.py \
  tests/test_ssp_docx_diff_service.py \
  tests/test_docx_section_extractors.py \
  tests/test_cmmc_ssp_adapter_v3_bundle.py \
  tests/test_ssp_write_strategy.py \
  tests/test_ssp_leveraged_app_service.py \
  tests/test_system_characteristic_write_strategy.py -q
# 預期 ~200 passed
```

---

## 6. Verify H-N 確實 close（必跑，避免回頭追）

```bash
# 6.1 Bug H-6 cleanup verify — 4 OSCAL 表 org_unit_id 應全 NULL
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -A -c "
SET app.is_super_admin='t';
SELECT 'parties' AS tbl, count(*) AS bad FROM oscal.oscal_parties WHERE org_unit_id IS NOT NULL
UNION ALL SELECT 'leveraged', count(*) FROM oscal.ssp_leveraged_authorizations WHERE org_unit_id IS NOT NULL
UNION ALL SELECT 'components', count(*) FROM oscal.ssp_components WHERE org_unit_id IS NOT NULL
UNION ALL SELECT 'inventory', count(*) FROM oscal.ssp_inventory_items WHERE org_unit_id IS NOT NULL;
"
# 預期 4 row all bad=0

# 6.2 Bug I verify — mf 372 / 373 應只剩 T6 LA (1 筆 MDR)
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -A -c "
SET app.is_super_admin='t';
SELECT mf.uid, COUNT(la.id) AS la_count
  FROM public.module_frames mf
  JOIN oscal.profiles p ON p.uid::text=mf.oscal_profile_uid
  JOIN oscal.system_security_plans s ON s.profile_id=p.id
  LEFT JOIN oscal.ssp_leveraged_authorizations la ON la.ssp_id=s.id
 WHERE mf.uid IN ('2c4ba0b8-52e2-4e90-a8fc-df8dcf481002', 'b006fc6c-28ae-4190-bdab-d930903e6314')
 GROUP BY mf.uid;
"
# 預期 2 row la_count=1
```

---

## 7. 行為規範重要提醒（per CLAUDE.md + memory）

- **永不切 branch**（per CLAUDE.md / `feedback_no_branch_switch`）
- **可自行 commit，不自動 push**（per CLAUDE.md / `feedback_stage_commit_no_ask`）
- **跨 repo 改 FE 前必 read FE CLAUDE.md**（per memory `feedback_cross_repo_read_claude_md_first`）
- **改 BE service 層後必提醒 user 重啟**（per memory `feedback_be_restart_after_service_change`）— BE diff service 改動需重啟
- **PrimeVue 3.53 quirks**（per memory）：本 Bug O 用 TabView + DataTable + Dropdown — 注意 Dropdown null bug（用 sentinel string + v-model wrapper）+ TabView `:active-step` 不是 `:active-index`
- **DB 兩張表都 verify 才算 done**（§2 教訓）— 對既有 mf 跑重 import + 各自決策路徑 (use_docx / keep_current / skip) 都 verify DB write 結果
- **不要把 bug 標 caveat 甩給 user**（直面修，不迴避）
- **changelog 收尾 batch 寫**（user 說「summary / 告一段落」才寫）
- **不要晶晶體**（per memory `feedback_no_chinglish`）
- **plan 假設先 verify**（per memory `feedback_plan_vs_reality_verify_first`）— BE diff annotation pattern 對 4 key 是否真適用，先實證再 plan
- **subagent dispatch 必加「git add 顯式檔名,禁用 -am」**（per memory `feedback_subagent_explicit_git_add`）
- **新增類按鈕用 outlined**（per Bug L + FE CLAUDE.md「Add / Create Action Button Style」section）

---

## 8. 收尾流程（fix 完後）

1. **design.md §11.34 完成段** — 補「Bug O 教訓」具體內容、commit clause、driver 等
2. **更新 §11 index reference** — 移除 §11.34 末尾「⏸ DEFERRED」標註
3. **`handoff/2026-05-25-bug-o-FIXED-SUMMARY.md`** 短收尾報告（mirror `2026-05-25-bug-h-to-l-FULL-ARC-SUMMARY.md` 樣式）
4. **commit + push**（problem-driven minimal commits — BE / FE 各一）
5. **問 user 確認 push 規模**

---

## 9. 不在 Bug O scope

- preview 階段 user 在 SystemCharacteristicSection / LeveragedSection 等編輯後的 content_overrides 路徑（Bug L-2 + M-B 已解，跟 diff stepper 不同 phase）
- v2 legacy `leveraged_services` key（已 Bug L 廢棄）
- Excel import diff 對齊（Excel 走自己 import path，未來再對齊）
- `template_module_frame_id` entity field 沒 wire（Bug K1 留 follow-up）
- jedi-common / jedi-oscal 進版 + BE/FE 版號對齊（H-N batch commit 後 user 拍板）
- 對話歷史 export `docs/conversation-history/` (H-N session 收尾時觸發)

---

## 10. 給 fresh session 的超短 prompt（user 用這個貼）

```
請閱讀交接文件,了解現況,並接手 Bug O 開發。

交接文件路徑：
docs/features/FR-028-2605-ssp-oscal-alignment/handoff/2026-05-25-bug-o-diff-stepper-expansion-handoff.md

按本檔 §0 接手讀序執行 — 必跑 §5 Pre-flight + §6 Verify H-N close →
§3 開工順位。注意 §2 前次教訓 + §7 行為規範。
前次 (Bug H ~ N) 已全 close + commit。本 Bug O 範圍：BE diff service
擴展 4 key annotation + FE store decisions 擴展 + 4 新 diff section 元件
+ DiffResolutionStep TabView refactor + BE confirm 接新 decisions filter。
依 §3 順序，第 2 步 enter plan mode 寫完整 implementation plan 給 user
approve 才開工。
```

---

## 11. H-N session 累積待 commit 內容（H-N 已 verify 通過，user 拍板才 commit + push）

**H-N 改動清單（per `2026-05-25-bug-h-to-l-FULL-ARC-SUMMARY.md`）**：
- BE 7 檔 + jedi-common 1 + jedi-oscal 4（含 SspContextResolver 新 + sc write 新加 + extractor enhance + adapter revert + 5 個既有 service 改）
- FE 9 檔（含 LeveragedSection rewrite + SspComponentsLeveragedInventoryTab Card→Table + SystemCharacteristicSection 新 + SspDocxImportPage wiring + 2 i18n + CLAUDE.md 新規範）
- DB 4 表清理 + mf 372/373 T7 LA cleanup
- design.md §11.26~33（含本期 §11.34 spec）
- handoff/2026-05-25-bug-h-to-l-FULL-ARC-SUMMARY.md
- handoff/2026-05-25-bug-o-diff-stepper-expansion-handoff.md（本文件）

**push 建議**（per H-N SUMMARY §6）：
1. BE 1 commit（含 design.md + handoff 兩份）
2. FE 1 commit
3. jedi-common 1 commit
4. jedi-oscal 1 commit
5. BE pyproject.toml **不 commit**（dev-only path dep，等 jedi-* 進版才改回 pin 一起 commit）

---

## 12. Bug O 跟 H-N arc 的關聯

Bug O 是 H-N 整段 arc 的**第 10 個 follow-up**，緣由是 user 對既有 mf 重 import docx 時的 diff stepper coverage gap — 跟 H-N 的「single import 完整 chain fix」是同 feature 不同 mode（update vs create）。

H-N arc 把 create mode 的 chain 全打通了（parties / sc / components / leveraged / inventory / cleanup / template SSP endpoint 全通），update mode 的 diff stepper 是另一條路 — diff service 只 cover 2 source（controls / parties）跟 4 種寫入 path（sc + components + leveraged + inventory）不對等。Bug O 補完。

完成後 docx import 整段 user-facing chain（create + update mode）就 complete v1 階段，可開始考慮 Phase 5 OSCAL Export deferred plan。
