SSP 重新匯入差異審查 (SSP Update Diff) — Final Summary

Task arc:2026-05-06 ~ 2026-05-07 Branchfeature/ssp-update-diff (BE + FE 同名 branch);E2E tests on main of compliance-manager-test 核心需求:SSP 重新上傳 docx 時,user 看不到「DB 現值 vs docx 新值」的差異 → 直接 confirm 等於無提示覆蓋手動編輯內容。新增 Step 2「解決差異」頁,讓 user 逐項決策(保留 / 採用 / 跳過 / 清空 / 移除)。 完成度:BE 35 + FE 60 + Test repo 1 = 96 commits;feature 主體 + Phase 5 E2E + code review (Critical 6 / Important 12) 全部完成。


1. 已產出 commits(依 repo 分)

BE (compliance-manager-be) — 35 commits

Branch base: 5a57a77 (上一個 task arc 收口) → HEAD: bd15b11

Phase 範圍 代表 commits
0. Spec & Plan brainstorm + writing-plans + plan reviewer 兩輪 05c7f59
Phase 1 BE diff service 7 個 sub-task:compute_default_action_for_text / match_parties / compute_party_diff / build_diff_summary / annotate_parse_result / wire AppService / changelog 8bf52ca, ddbe690, 5e9a7e8, 569e6fd, e0b5f0a, d0cadac, e6929c6, c4956eb, 521caaa, 5b9de1b
Phase 2 BE confirm 擴充 5 個 sub-task:error codes / payload schema / filter+write / regression test / changelog f8e3346, a5795fa, 7407779, 3d75593, ea5a18e
Smoke test 修正期 12+ commits(current_obj injection / module_frame parties / email alias / party_type-aware / nickname enrichment / gone-as-diff / responsible_party update / party append fix / normalize / no-default for added) 127d092, 7269f99, 07bfb39, 5f5af92, 94b9a3b, 766eb3d, 7903d5e, 32cddd9, 74ea5f3, 453ea66, 57289be, b467d93, 08ec299
Code review 修正期(2026-05-07) 4 commits:critical 6 (C1-C6) / important 12 (I1-I12) / 加 §I4+I10 gone parties unlink path / §I9 adapter failure surfacing 49c8b8c, 09fc125, 94a53fa, bd15b11
Spec / docs 同步 design.md 1.1 + §11 reconciliation + plan post-impl note + review report 入版控 (含於 49c8b8c, 08872cb)

FE (compliance-manager-fe) — 60 commits

Branch base: 0d98408 → HEAD: 871a535

Phase 範圍 代表 commits
Phase 3 FE 元件 7 個 sub-task:jsdiff util / Pinia store / DiffSectionShell / ControlDiffCard + ObjectiveDiffRow / PartyDiffCard + PartiesDiffSection / ControlDiffSection + DiffResolutionStep / changelog 7a43bbe, b67b525, 00f33f4, 1e4d073, 7bbb5dd, 7604c48, ad5dc5a
Phase 4 FE 整合 5 個 sub-task:stepper 接線 / ControlImplSection readonly / PartiesSection readonly / ImportOutline summary / changelog 845c217, 0a717dd, 0c8fe93, 7aba95c, 7acbd14
Smoke test 修正期 — diff feature ~30 commits(Step 2 條件 / cancel / Steps activeStep / parse shape flatten / payload parties_decisions / outlined / 配色 / nested cards 扁平化 / unselectable / pending decided 互斥 / bulk apply cascade / 顯示 responsible-organization / 衝突文案 / 略 ) 012214e, d79fe33, e2675aa, 14afe65, b89ce29, e14156a, c25f0f0, e46a079, c0a0751, 45eeb2b, d46c02a, e72acc7, 4adfe39, 0f44726, 3c3f73c, 3e93f17, e8800bf, c79f787, 7c08cec, 299febf, e4f7a6b, 7eb687a, a084a68, 5186a7b, 08a102d, 94cd2b6, bdb5133, 778b71a, 3e636b8, 09235e6, c6c1787
module-frame template-edit 既有頁修正 7 commits(AO 進度條 / 沿用控制項狀態 / dropdown null bug / inherit fallback / placeholder / unfilled AO 點擊 jump+flash / 進度算法對齊 描述語意) f853e97, 66f8da8, f42e5f8, 80531aa, 57770b3, cf4c7e2, 6a3a0ab, bd5ab06
Code review 修正期(2026-05-07) 4 commits:C1-C3 critical fixes / I12 confirm dialog / I4+I10 gone parties remove UI / I9 adapter banner a06a943, 93c2045, aac8677, c1e2b14
Stepper UX 收尾 + 釘 banner orphan 3 commits:banner wiring / stepper hide Step 2 in create / update-mode 固定 3 步 + changelog backfill f771ba3, dacbf6c, 0153ac7, 871a535

Test repo (compliance-manager-test) — 1 commit

  • a6fc678 (main):Phase 5 E2E — requirements + 4 BDD scenarios + page object + step defs(dry-run 通過)

總計:96 commits 在這個 task arc(BE 35 + FE 60 + Test 1)

Working tree 全乾淨(只剩 BE M CLAUDE.md 是 user 預存的不相關修改)。


2. 改動範圍 — 檔案級

BE (compliance-manager-be)

  • 新增
    • app/oscal/service/ssp_docx_diff_service.py(diff 計算純邏輯類,6 個方法)
    • tests/test_ssp_docx_diff_service.py(28 unit tests)
    • tests/test_ssp_docx_import_diff_response.py(integration tests,DB log handler infra 失敗待後修)
    • tests/test_ssp_docx_import_create_no_diff.py(regression test)
    • docs/features/FR-025-2605-ssp-update-diff/(design.md / implementation-plan.md / raw-requirement.md)
    • docs/changelog/2026-05-06-feat-ssp-update-diff-be-phase1.md / phase2.md
    • docs/changelog/2026-05-06-fix-ssp-*.md(多筆 hotfix changelog)
    • docs/conversation-history/2026-05-06-ssp-update-diff/(7 parts + summary)
  • 修改
    • app/oscal/service/ssp_docx_import_app_service.py(注入 diff service / _load_current_parties / _inject_current_objective_descriptions / _filter_parties_for_write / _validate_decisions / _enrich_party_user_org_names
    • domain/oscal/parser/ssp_intermediate.pyParsedParty.target_party_uid
    • domain/oscal/strategy/module_frame_write_strategy.py + ssp_write_strategy.py_upsert_party 新增 target_uid 優先 + 既有 link 改 idempotent return)
    • api/oscal/serializers/ssp/ssp_docx_import.py(response schema 新欄位 + confirm payload parties_decisions
    • di_containers/oscal/oscal_containers.py(user / org_unit / diff service 注入)
    • common/code/grc_error_code.py(3 個新 GRC error codes)

FE (compliance-manager-fe)

  • 新增
    • src/utils/textDiff.js(jsdiff 包裝)
    • src/stores/sspDocxImportStore.js(Pinia store)
    • src/components/grc/ssp-docx-import-v2/diff/(6 個新 Vue 元件 — DiffResolutionStep / DiffSectionShell / ControlDiffSection / ControlDiffCard / ObjectiveDiffRow / PartiesDiffSection / PartyDiffCard,FieldRow 中途新增後又 inline 化刪除)
    • docs/changelog/2026-05-06-feat-ssp-update-diff-fe-phase3.md / phase4.md
    • docs/changelog/2026-05-06-tweak-*.md / style-*.md
  • 修改
    • src/components/grc/ssp-docx-import-v2/SspDocxImportPage.vue(stepper + viewModel.parties flatten + onCancel/onConfirm 改寫)
    • src/components/grc/ssp-docx-import-v2/sections/ControlImplSection.vue(接 store decisions、AO 顯示來源依 user 決策、移除 readonly gate)
    • src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue(拿掉 HIDDEN_ROLES filter / 接 store decisions)
    • src/components/grc/ssp-docx-import-v2/sections/ImportOutline.vue(diff summary banner)
    • src/components/grc/SspStatusTag.vueinheritWhenNull + parentStatus props)
    • src/views/module_frame/ModuleFrameTemplateEditView.vue(AO 進度條 / Dropdown sentinel / unfilled AO 點擊 jump+flash)
    • package.jsondiff 5.x 依賴)
    • src/config/locales/i18n/{zh-tw,en}/module-frame.jsonplaceholder_pick_status

3. 行為差異(before vs after)

Before(feature 上之前)

  • 重新匯入 docx 直接覆蓋 — 沒提醒 user 哪些手動編輯會被洗掉
  • module_frame import 流程:上傳 → 預覽/編輯 → 確認
  • AO 進度條把「有 record 但 description 空」算 filled
  • AO Dropdown「沿用控制項狀態」value=null 會顯示 placeholder「未設定」(PrimeVue bug)
  • Parties section 隱藏 responsible-organization 角色

After

  • 重新匯入 docx:上傳 → 新增 Step 2 解決差異 → 編輯 → 確認
  • Stepper 固定 by mode(update mode 永遠 3 步,create mode 2 步) — 進入流程一眼知道有幾步
  • Step 2 對 user 顯示:
    • 每條 control / objective / party 的「現值(DB) vs 新值(docx)」side-by-side
    • 文字 word-level red/green highlight
    • 4 種 diff_status:changed(已修改)/ added(新增)/ gone(將清空)/ unchanged(隱藏)
    • Smart default:changed/gone → 預設 keep_current;added → user 必選(無預設)
    • filter:全部 / 待決策 / 已決策(互斥語意)
    • bulkApply:全部採用新值 / 全部保留現值(含 confirm dialog 防呆 + cascade 到 AOs)
  • Step 3 編輯頁:use_docx 用新值、其他用現值;可隨時 inline edit 蓋過決策
  • Confirm:parties 用 target_party_uid 精準 update,不再 append 新 party
  • Gone parties unlink(review I4+I10):user 對 gone party 選「移除」→ confirm 時刪除 responsible_party link record(保留 oscal_parties row 給其他 SSP/MF 共用)
  • Adapter failure surfacing(review I9):v2 adapter 解析失敗時 BE 在 response 標 adapter_status per section + adapter_error user-friendly 訊息;FE 顯示橘色 banner + 若 parties 失敗則 hard block confirm(disable button)
  • Step 3 → Step 1 confirm dialog(review I12):放棄 parse + 決策前跳防呆 dialog
  • Diff summary banner 顯示在 Step 3(review C1 fix):之前 ImportOutline orphan 沒人 render,現在直接 inline 接到 SspDocxImportPage
  • AO 進度條 / 「未描述」badge 對齊:只看 implementation_description 是否非空
  • AO Dropdown 用 sentinel 字串解決 PrimeVue null bug,inherit 標籤顯示「繼承:[父狀態]」(如父亦未設則 warning「繼承(控制項未設定)」)
  • Parties section 顯示所有 role 包含 responsible-organization
  • 點擊未設定 AO 清單 → 自動 jump 到父 control + 展開該 AO + scroll into view + 黃色 flash 1.5 秒

API 變更(向下相容)

  • GET /ssp-docx-import/<uid> response 新增:has_diff / diff_summary / matched_controls[].diff_status / default_action / objectives[].diff_status / parties[] / adapter_status (per-section) / adapter_error (per-section)
  • POST /ssp-docx-import/<uid>/confirm payload 新增:parties_decisions(optional,default [])— action 對 gone parties 多支援「移除 link」語意
  • 既有 create flow 完全不變(has_diff=false / parties_decisions=[])

重要 deviations 從 design / plan

  1. ResponsiblePartyEntity 是 link record 不是 full party — strategy join PartyEntity(plan 假設錯)
  2. Email field 命名 email vs email_address 兩邊不對稱 — 加 alias normalize
  3. Party_type-aware diff(person 不比 org_unit_id / org 不比 user_id)— 防 metadata 誤判
  4. target_party_uid 加到 ParsedParty — 解決 update by name 失敗的 append bug
  5. AO progress 算法只看 description(match「未描述」badge 語意)— 不看 status 欄位
  6. PrimeVue Steps prop 是 activeStep 不是 activeIndex(plan 寫錯)

4. 規範文件清單

Spec / Plan / Issue

  • compliance-manager-be/docs/features/FR-025-2605-ssp-update-diff/design.md(spec,含 §3 smart default / §4.4 strategy)
  • compliance-manager-be/docs/features/FR-025-2605-ssp-update-diff/implementation-plan.md(5 phases / 23+ tasks)
  • compliance-manager-be/docs/features/FR-025-2605-ssp-update-diff/raw-requirement.md(原始 user 需求)

Changelogs(依 phase)

  • BE Phase 1: 2026-05-06-feat-ssp-update-diff-be-phase1.md
  • BE Phase 2: 2026-05-06-feat-ssp-update-diff-be-phase2.md
  • BE hotfix: fix-ssp-docx-load-current-parties-module-frame.md / fix-ssp-party-diff-normalization.md / fix-ssp-update-diff-party-append-bug.md / tweak-ssp-update-diff-added-no-default-action.md
  • FE Phase 3: 2026-05-06-feat-ssp-update-diff-fe-phase3.md
  • FE Phase 4: 2026-05-06-feat-ssp-update-diff-fe-phase4.md
  • FE hotfix: style-ssp-diff-softer-column-tints.md / tweak-ssp-docx-hide-unused-party-fields.md / 2026-05-07-fix-stepper-fixed-by-mode.md

Coverage gap:96 commits / ~12 changelogs。多數 smoke test + review 期間的 hotfix 沒個別 changelog。若需嚴格依 CLAUDE.md 規範,後續可補一份 consolidated「2026-05-07-fix-ssp-update-diff-review-iterations.md」整合所有未文件化 commits。

Code review 報告(2026-05-07)

  • 位置compliance-manager-be/docs/review/2026-05-07-ssp-update-diff-review.md(已入版控於 commit 49c8b8c
  • Reviewer:Claude Code 5-agent parallel review (code-reviewer×2 / silent-failure-hunter / type-design-analyzer / comment-analyzer)
  • 發現:Critical 6 (C1-C6) / Important 12 (I1-I12) / Type design 建議 / Comment 待清理 / Minor 12+ / 驗證 §11 12 條 deviations 全對齊
  • 修正狀態:Critical 6 全部完成(BE 49c8b8c / FE a06a943);Important I4+I10+I9+I12 完成;其餘 Important 多半 narrow bare except — 已於 BE 09fc125 處理大部分

對話紀錄

  • compliance-manager-be/docs/conversation-history/2026-05-06-ssp-update-diff/
    • 00-summary.md(背景 subagent 寫的 7-part 索引)
    • part-01-of-07-brainstorm-and-spec.md(73 user / 75 assistant 段)
    • part-02-of-07-writing-plans-and-review.md
    • part-03-of-07-phase1-2-be-execution.md
    • part-04-of-07-phase3-4-fe-execution.md
    • part-05-of-07-integration-debugging-1.md
    • part-06-of-07-ux-polish.md
    • part-07-of-07-final-debug-and-archive.md
    • 約 70 個真實 user 訊息 / 684 個 assistant 條目

5. 已知 follow-up(未處理)

中優先(影響日常使用)

  1. tests/test_ssp_docx_import_diff_response.py 7+ 既有 fixture failuresession_scope mock 跟 system_log_service @transaction 衝撞 → 整檔不能 run。Diff service 自身 28 unit tests 全綠,但 integration 層信號失能。修法:fixture 加 patch DB log handler。
  2. AO 「沿用」狀態 reload 後消失 — BE 對「未設定」跟「主動沿用」都存 null,user 選 inherit 後存檔 → 下次 reload 看到 placeholder「請選擇實施狀態」需要再選一次。要根除需 BE schema 加新欄位區分。
  3. Phase 5 E2E 自動化測試 — ✅ 已完成(commit a6fc678 in compliance-manager-test repo main branch)。Cucumber dry-run 4/4 scenarios + 29/29 steps 全 resolved。Live run 卡在 staging fixture 狀態(需新增 ASIA-CMMC-SSP-DRAFT-DIFF.docx fixture 到 test repo + 「test-only」乾淨 framework 版本),UI proxy 替代 DB 對帳是已接受的 trade-off。詳見 §6 部署 handover。
  4. Code review Critical 6 + Important 12 — ✅ 已完成(review report 詳見 docs/review/2026-05-07-ssp-update-diff-review.md)。BE 4 commits + FE 4 commits 修完,包含 C4 confirm 吞 DB 錯 / C5 GET 沒 check_permission / C6 annotated=None fallback / I4+I10 gone parties unlink path / I9 adapter failure surfacing。

低優先(technical debt / polish)

  1. ~80 commits / ~10 changelogs — consolidated changelog 待補
  2. i18n key 「解決差異」hard-code(Phase 4.1 TODO)
  3. AO row 用 v-show 而非 v-if filter(全 skip 時 section header 仍顯示空)
  4. design.md / implementation-plan.md 部分內容跟最終實作有落差(responsible_party shape / parties enrichment 流程 / Steps prop name),文件未回填修正

Plan-stage open issue(已 resolved by deviation)

  • responsible_party_domain_service.list_by_context() API 名稱 → 改用 BaseRepoImpl.list_by(QueryEntity) standard pattern
  • module_frame_party_service 是否可重用 enrichment 邏輯 → mirror 同 pattern 加進 SspDocxImportAppService

6. 部署 handover

Pre-deploy checklist

Smoke test 路徑(人工,已通)

  1. Update mode + 有差異:合規資源庫 → 已存在的框架 → 批次維護 → 從文件匯入 → 上傳 docs/reference/ASIA-CMMC-SSP-DRAFT-DIFF.docx → Step 2 出現 → 切「待決策」filter → 對 changed/added/gone 各做幾筆決策 → 下一步 → Step 3 verify 顯示 → 確認匯入 → DB 對帳
  2. Create mode regression:建新框架(無 SSP 既有資料)→ 上傳同 docx → Step 2 應 skip → 直接 Step 3 → 確認 → DB 應全寫入
  3. Cancel 流程:在 Step 1/2/3 任一步點「取消」→ 應 router.push 回 module-frame-template-edit/<uid>,不殘留 parse_job

自動化 E2E(Phase 5,已完成)

位置:compliance-manager-test repo (commit a6fc678 on main)

  • docs/requirements/module-frame/01-ssp-update-diff.md — requirements
  • features/regression/module-frame/04-ssp-update-diff.feature — 4 BDD scenarios (@regression @module-frame @ssp-update-diff @P0/@P1)
  • pages/module-frame/SspDocxImportDiffPage.js — page object (313 lines)
  • steps/module-frame/ssp-update-diff.steps.js — 29 step defs

跑法:npm run test:e2e -- --tags @ssp-update-diff

待補的 fixture(live run 才能完整跑通):

  • 在 test repo fixtures/cmmc/imports/ssp-docx/ASIA-CMMC-SSP-DRAFT-DIFF.docx(從 BE repo docs/reference/ 複製過去)
  • staging 開「test-only」乾淨版本 framework,避免 has_diff=true 干擾 create mode scenario

Rollback path

  • BE:feature/ssp-update-diff revert 整個 branch(30 commits 都跟此 feature 相關)
  • FE:同樣 revert(注意 module-frame template-edit 的 AO 進度條 / Dropdown / 「沿用」邏輯改動是順手 fix 的既有 bug,rollback 會 regress 那些)
  • 資料:confirm 過的 docx update 沒有 BE 端的「undo」機制 — DB 寫入是 final

監控

  • BE log [ssp-docx-import] / [ssp-confirm] / [party-diff] 三類 prefix 用於診斷
    • _load_current_parties 印 DB 載入的 parties names
    • compute_party_diff 印 changed 欄位細節
    • _filter_parties_for_write 印 decisions map + 寫入流程

7. Lessons learned

  1. Plan 跟 reality 落差 — 多次發現 implementation plan 寫的方法名 / 欄位 / 流程跟 codebase 實際不符。後續 plan-stage 的 recon 要 grep 實際 code,不要光看 design.md。
  2. Smoke test 期間 user 回報 ≠ 真 bug — 有幾次 user 反應的「沒生效」其實是 BE 沒重啟。要明確跟 user 說「BE 改了要重啟」否則 false positive 浪費時間。
  3. PrimeVue 細節activeStep vs activeIndex / SelectButton unselectable=true default / Dropdown null value bug — 都是踩雷地雷區,每個元件第一次用要先看 source。
  4. Diff vs Edit 兩階段視角衝突:BE annotate filter unchanged 對 Step 2 對,但 Step 3 需要全部 → annotate 應只標不 filter,FE 各自 filter。
  5. 資安:先做 user_id 顯示 fix(✓ 已連結 取代 (user 17)),後加 nickname enrichment 作真正 UX 升級。

狀態:feature 主體驗收 OK,可上線。剩 follow-up 1-3 是 nice-to-have 可分批跟進。