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

> **Task arc**：2026-05-06 ~ 2026-05-07
> **Branch**：`feature/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.py`（`ParsedParty.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.vue`（`inheritWhenNull` + `parentStatus` props）
  - `src/views/module_frame/ModuleFrameTemplateEditView.vue`（AO 進度條 / Dropdown sentinel / unfilled AO 點擊 jump+flash）
  - `package.json`（`diff` 5.x 依賴）
  - `src/config/locales/i18n/{zh-tw,en}/module-frame.json`（`placeholder_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 failure** — `session_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）
4. ~80 commits / ~10 changelogs — consolidated changelog 待補
5. i18n key 「解決差異」hard-code（Phase 4.1 TODO）
6. AO row 用 `v-show` 而非 `v-if` filter（全 skip 時 section header 仍顯示空）
7. 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
- [ ] BE deploy 前確認 `common/code/grc_error_code.py` 新增 3 個 codes 序號未跟其他 PR 衝突（current: 400022 / 400023 / 409021）
- [ ] FE deploy `package.json` 新增 `diff` 依賴 → 部署環境 `npm install` 拉到
- [ ] BE deploy 後重啟 — `_load_current_parties` / `_filter_parties_for_write` / `_inject_current_objective_descriptions` 都需新 service code 載入
- [ ] FE 部署後檢查 dark theme 配色（`var(--surface-card)` / `var(--surface-border)` 在主題 token 下渲染正確）

### 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 可分批跟進。
