# 過夜自主收尾決策記錄 — SSP 差異更新匯入復原

> 狀態 2026-06-16（凌晨自主作業）。user 2026-06-15 深夜授權「持續做完、不能進行的用推薦模式並記錄、權限開放、明早驗收」。
> 本檔記錄**所有自主作出的決策**，供 user 明早驗收時對照。原則：安全優先、不逾越（不 push / 不發 Nexus / 不起服務 / 不寫真 DB 非 rollback / 不盲建 FE UI）。

## 範圍邊界（自主作業的紅線）
| 動作 | 做不做 | 理由 |
|------|--------|------|
| 端到端整合測試（rollback-isolated 真 DB） | ✅ 做 | 補 per-phase mock 鄰居的縫隙；rollback 不污染資料 |
| serializer drift 對齊（FE-informed） | ✅ 做 | 先 grep FE 確認實際值再改，非盲改 |
| e2e BDD 場景（測試專案） | ✅ 寫，不執行 | 執行需起服務 → 留 user |
| 收尾文件（changelog/analysis/SUMMARY/Notion/design 同步） | ✅ 做 + commit | user 要求「做完 + 記錄」；不 push |
| 起 HTTP 服務跑 smoke | ❌ | 規範「服務 user 自起」+ user 明早親自驗收 |
| 對 ssp 1482 真寫入（非 rollback） | ❌ | 怕污染 demo 資料 |
| push（兩 repo）/ 發 Nexus / pin 還原 | ❌ | 規範「push/發版等 user 明示」 |
| 建 FE 專案 SSP 匯入 UI 頁 | ❌ | 大、需 user 看設計；記錄為 follow-up |

## 決策記錄（依時間）
（隨作業逐條補入）

### D1 — source_type `project_ssp` vs `ssp`：BE app service 入口正規化（真 bug，非 cosmetic）
**發現**：grep FE 後確認 `SspDocxImportPage.vue:135` 在「更新專案 SSP」時送 `source_type='project_ssp'`，走**通用** docx route `/ssp-docx-imports/parse`（`api.js:224`，FE 沒用 implementer 新建的 scoped docx route）。docx serializer 已允許 `project_ssp`，但 BE docx app service `_VALID_SOURCE_TYPES={"module_frame","ssp"}` 會把 `project_ssp` 當無效 → **422**。→ 真實 FE 的 docx 專案 SSP 匯入是壞的（單元測試用 `'ssp'` 沒抓到）。excel 專案匯入走 scoped route（route 內強制 `'ssp'`）不受影響。
**FE 現況**：excel 專案 = scoped route forced `'ssp'`；docx 專案 = 通用 route + FE 送 `'project_ssp'`。兩格式值不一致是既有狀態。
**決策**：BE 兩個 import app service 在入口（manager gate + dispatch 之前）把 `project_ssp` 與 `ssp` 正規化成同一 canonical（視為「專案 SSP」）。理由：(1) 修好真實 FE docx 壞掉的路徑；(2) 不改 FE（FE 已定 `project_ssp`）；(3) 不動 excel scoped route 的 `'ssp'`；(4) gate 在正規化後一樣對 `project_ssp` 生效，無繞過。被排除：改 FE 送 `ssp`（FE 已出貨 `project_ssp`、docx serializer 也是）；全面 canonical 成 `project_ssp`（要改 excel scoped route + 更多面，風險大）。
**回退條件**：若日後決定全系統統一成單一值，可把正規化收斂掉。
**狀態**：✅ 完成，commit `acc72361`。正規化在 docx app service `:120`（gate `:137` 前）+ excel + 兩邊 confirm 防禦性正規化。16 tests（含非 manager + project_ssp → Forbidden）、BOOT OK、baseline 零回歸。gate 順序我親讀確認無繞過。

### D2 — 端到端整合測試的 DB 隔離方式
**發現**：主專案 `test/` 無 conftest；複用 v2 套件 `jedi-oscal-v2/tests/conftest.py::db_session` 的 rollback pattern，inline 在測試檔（開 `SessionLocal()`、綁 `session_context`、`SET LOCAL app.is_super_admin='t'` 繞 RLS、teardown `rollback()`）。在**低層**（adapters/merge/import_ssp/snapshot 直接呼叫，繞過 `@transaction` 否則會 commit）測，DEV DB 不可達則整檔 skip。
**隔離驗證**：跑前後 `oscal.system_security_plans` count/maxid 完全相同（ssp=55 maxid=1487）→ 零持久化。
**commit** `dcaa6e6d`（`test/test_ssp_import_pipeline_integration.py`，4 tests）。

### D3 — 🔴 整合測試抓到真實產品 bug：專案 SSP 更新時 by-component 敘述被丟棄
**Bug**：Model C 真實路徑（re-parse → merge → `import_ssp(update)` 進 living SSP）下，use_docx 控制項敘述覆寫 + 未匹配段落手動指派**都不 persist**。SoA props（IR 上的 status/applicability）會寫，但實作**敘述文字**（存在 by-component.description）被靜默丟。
**根因**：`parsed_docx_to_oscal_ssp`/excel adapter 每次 parse mint 全新 this-system component uuid；by-component soft-ref 指它。`import_ssp(update)` 用 `(title,type)` upsert component、保留既有 uuid，`comp_map` 只映既有 uuid（`oscal_io_service.py:1118-1121`）→ 新 by-component uuid 找不到 → skip（log: `by-component skipped — component-uuid not found`）。living SSP 既有 uuid 永不等於新 parse → 敘述永遠掉。per-phase 單元測試 mock 鄰居所以沒抓到；整合測試（真 import_ssp）抓到。
**為何嚴重**：這是功能主要用途（更新專案 SSP 敘述）。不修，明早 smoke 會「匯入回傳成功 counts 但敘述沒變」。
**決策（Option B，套件層 `import_ssp` update path）**：upsert component 時把**傳入 dict 的 component uuid**（by-components 參照的那個）也映進 `comp_map` → 解析出的 component id；新舊 uuid 並存指同一 id。對既有 + 新 SSP 都成立（component 靠 title,type 配對與 uuid 無關）。
**被排除**：Option A（adapter 用 deterministic 穩定 uuid）—— 救不了既有隨機 uuid 的 living SSP（如 1482），只對全新建立的 SSP 有效。Option C（by-component 改用 (title,type) 對齊）—— 較侵入、改 schema 語意。
**授權依據**：user 2026-06-15 已點頭本弧套件改動 + 今晚「持續做完、推薦模式」。
**狀態**：✅ 完成。package commit `bdfe780`（comp_map 同時映傳入 uuid，`oscal_io_service.py:1101` 捕捉 incoming_uuid、`:1135` `setdefault(incoming_uuid, saved.id)`）；main commit `93df50b4`（整合測試 SEAM 翻正：敘述覆寫 + 手動指派現在 persist）。package 17 tests、整合 4 passed 隔離再驗、**我親驗 BOOT OK（canonical env，subagent 的 boot-fail 是它漏帶 recursionlimit）**、baseline 91/97 comm 零新回歸。聚焦 review Approved（setdefault first-wins 正確、create byte-identical）。
**驗收重點**：這是讓你明早 smoke「敘述真的有更新」的關鍵修復。

### D4 — 收尾文件處理方式（誠實標註 smoke 待驗）
**決策**：撰寫 changelog / analysis / SUMMARY / design 同步 / plan 狀態並 commit（user 授權「持續做完」），但**全部誠實標註「real-DB HTTP smoke 待 user 明早驗收」**，不標 FIXED/verified。**不 push**（規範等 user）。**不建 Notion「Done」任務**（外部發佈 + 未經 smoke verify，逾越；列為 user 驗收後的收尾步驟）。**FE scoped UI 不建**（大、需 user 看設計；列 follow-up）。
**被排除**：標 FIXED / 建 Notion Done —— 未 smoke 前等於宣稱已驗，違反 CLAUDE.md「FIXED 沒 verify = 撒謊」。

### D5 — e2e BDD（測試專案）+ FE scoped UI：刻意延後，不過夜盲做
**決策**：兩項都**不在過夜做**，列 follow-up 留有 user 在場的 session。
- **e2e BDD**（compliance-manager-test）：需起服務才能跑；且場景要對 FE 實際流程/selector，過夜寫了無法驗證、易與真實不符（反而誤導）。手測 checklist（SUMMARY §未驗證）已覆蓋同範圍，user 明早可直接照走。
- **FE 專案 SSP 匯入獨立 UI 頁**：FE docx 已走既有通用頁（送 `project_ssp`，D1 修好後可通）；excel 有 scoped service。是否要獨立入口是 UX 設計題，需 user 看設計，不過夜盲建（memory：FE 設計需謹慎）。
**推薦模式**：明早 smoke 通過後，若要 e2e，有 user 起服務、一起對 FE 流程寫＋跑。

---

## 過夜總結（給 user 晨間驗收）
- 7 phase 全綠 + 兩段式 review；過夜**自主抓到並修兩個真 bug**（D1 docx 契約 422、D3 核心 by-component 敘述被丟）—— 都是明早 smoke 會踩的。
- 端到端整合測試（rollback、零污染）覆蓋 upsert/keep_current/snapshot/manual 核心 seam。
- BOOT OK（親驗）、baseline 91/97 零新回歸。
- 收尾文件全寫＋commit（changelog/analysis/SUMMARY/本記錄/plan/design），**誠實標 smoke 待驗**。
- **未做（等你）**：push / Nexus / FE 對齊 / Notion / e2e / FE UI —— 全列 follow-up。
- `pyproject.toml`（dev path-dep）維持 working tree 未 commit。
