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 證據

# 看現有 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(必跑)

# 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(必跑,避免回頭追)

# 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。