# 紀錄 — 框架維護（PDF 匯入 V1→V2）支援修補 + import-ssp 復原

> 狀態 **2026-06-16**。本檔記錄 2026-06-16 這個 session 做的兩塊：
> 1. **import-ssp 復原弧**（過夜自主完成，詳見同資料夾 SUMMARY + decision-log）
> 2. **框架維護 PDF 匯入 V1→V2 的一連串支援修補**（白天與 user 互動，本檔重點）
>
> 下個 session 要做**整合測試**。本檔幫忙定位：哪些已修、哪些已驗、哪些待驗、已知問題。
> 跳過 changelog / Notion / 對話紀錄（user 指示）。**未 push、未發 Nexus、stg/poc/prod migration 未套。**

---

## A. import-ssp 復原弧（過夜，已完成）
7 phase 全綠 + 兩段式 review + 過夜抓修兩個真 bug（D1 docx source_type、D3 by-component 敘述被丟）。
詳見：
- [`2026-06-16-import-ssp-restore-SUMMARY.md`](2026-06-16-import-ssp-restore-SUMMARY.md)
- [`2026-06-16-overnight-autonomous-decision-log.md`](2026-06-16-overnight-autonomous-decision-log.md)
- 架構分析 [`docs/analysis/2026-06-16-ssp-import-restore-w2-architecture-and-bugs.md`](../../../analysis/2026-06-16-ssp-import-restore-w2-architecture-and-bugs.md)
- **待 user real-DB HTTP smoke**（專案 266 / ssp 1482）— 留新 session。

---

## B. 框架維護 PDF 匯入 V1→V2 支援修補（本檔重點）

### 背景
有一條**平行 session**在做「框架維護 2a/2b + FE 套用」（commits `9973fc55`/`c7f962d5`/`ef600abf`/`470b8bf9`/`501cf537`、FE release v1.4.0）。FE 套用後一連串 error / 缺功能，本 session 逐一修，目標 = PDF 匯入框架版本的 V1 體驗在 V2 上補齊。

### 修了什麼（依時間 / 主題）
| # | 問題 | 修法 | commit |
|---|------|------|--------|
| 1 | 合規框架 `/oscal-frameworks` 拉不到資料 | `OscalFrameworkVersionResponseSchema` 在兩個 module 撞名（marshmallow RegistryError）→ `oscal_framework.py` 那個改名 `OscalFrameworkVersionTreeSchema` + 同檔引用改直接 class ref | BE `e5fac369` |
| 2 | participant 路徑炸 `metadata_domain_service not defined`（**pre-existing** Wave 2A 缺口）| oscal_container 補 `metadata_domain_service = providers.Object(None)`（participant service 該參數本來就 default None、未呼叫）| BE `e5fac369` |
| 3 | `/flow-engine/task/queue` 一直 500 | task_assignee 依賴鏈牽到 participant→oscal **更多**未接的 domain service（`assessment_plan_group_domain_service` 等）；DI 進方法前就失敗無法 try/except → **暫時止血回空陣列**（移除注入），原邏輯留註解待 2B | BE `742ba3f3` |
| 4 | 新增框架撞 code UNIQUE | `oscal.frameworks.code` 的 UNIQUE 移除（還原 V1：框架是參考資料、允許重複 code，如 CMMC L1/L2 同掛 CMMC_2）| BE `2352ee20` + 套件 model |
| 5 | 新增框架多一層「自動生版本」| `add_framework` 不再自動生版本（master 只是招牌；版本 L1/L2/L1.5 由第二層 UI 自己加）| BE `7a95b6bf` |
| 6 | 版號（main_version）應是字串非 FK | `frameworks` 加 `main_version` 字串欄（對齊 V1），建框架直接存；`_enrich` 優先用字串欄（舊資料 fallback 解 FK）| BE `a190befe` + 套件 `93d1a45` |
| 7 | guidance 沒爬出來 | (a) `_tree_to_dict` 從 assessment-method parts 撈 **EXAMINE** → guidance（還原 V1）；(b) `_persist_catalog` confirm 時寫 `guidance` part（編輯/瀏覽才讀得到）| BE `9ead048c` + `a20d6ce4` |
| 8 | 編輯/瀏覽看不到 PDF | V1 的 framework_version 有 `file_uid`，V2 漏建 → 加 `framework_versions.file_uid` 欄 + confirm 兩路徑搬 `job.file_path` → version；`_enrich`/`get_catalog_tree` 回應帶出 file_uid | BE `94a5f3e3`+`a5bd79bd` + 套件 `a0d9db0` |
| 9 | PDF preview 出不來（前端 blob 判型失敗）| pdf-preview 端點（MinIO 回 BytesIO）明確回 `mimetype=application/pdf`；FE 編輯頁改**直接 iframe :src**（對齊 Dialog，拿掉 axios-blob 判型層）| BE `6edca577` + FE `74eb50b`/`c96d189` |
| 10 | 條文預覽 AO 文字空白 | v2 parse 把 AO 文字放 `name`（非 description）→ FE 條文視圖 + Dialog 右半邊都改讀 `name`；群組標頭 null description 不顯示破折號 | FE `8144ff8`/`74eb50b` |
| 11 | 瀏覽 Dialog 右半邊空 | 它讀 `item.catalog`（版本列表只回 presence）→ 改成走 catalog-tree 端點取完整內容 + 組巢狀 | FE `74eb50b` |
| 12 | 編輯條文內容後不刷新 | `patchAndUpdate` 只 merge BE 回傳（常是 true）→ 改成先套使用者送出的 `fields`、BE 物件再疊 | FE `b07db99` |

### Migrations（**DEV 已套，stg/poc/prod 待套**）
- `scripts/sql/2026-06-16-frameworks-code-drop-unique.sql`（DROP code UNIQUE + 非唯一索引）
- `scripts/sql/2026-06-16-frameworks-add-main-version-string.sql`（加 main_version 字串欄 + backfill）
- `scripts/sql/2026-06-16-framework-versions-add-file-uid.sql`（加 file_uid 欄）

### 套件（jedi_oscal_v2，dev path-dep，**未發 Nexus**）
`OscalFramework.main_version`（字串）、`OscalFrameworkVersion.file_uid`，三處 model/entity/mapper 同步。整弧完 + user 明示才 bump + 推 Nexus。

---

## 已驗證 vs 待驗證
**已驗（自動）**：每個 BE 改動 BOOT OK；部分用 rollback-isolated 真 DB smoke（add_framework 不生版本 / main_version 存字串 / get_catalog_tree 帶 file_uid）；guidance 用合成 `_tree_to_dict` 測；migration 套用 + backfill 驗。
**user 已手動驗（本 session 互動確認）**：新增框架（重複 code）、PDF 匯入帶 guidance、編輯頁 PDF + 即時刷新、瀏覽 Dialog PDF + 右半邊內容。
**待整合測試（新 session）**：完整端到端（新框架 → 加版本 → 匯入 PDF → 編輯 → 發布）跑一輪；多框架（ISO/NIST adapter 若有）；覆蓋模式（既有版本重匯入）。

## ⚠️ 已知問題 / follow-up（給整合測試 + 後續）
1. **participant ↔ oscal DI 缺口（最重要）**：`metadata_domain_service` 補了 None，但 task_assignee/participant 鏈還缺 `assessment_plan_group_domain_service` 等（Wave 2A 移除、待 2B 正式接 v2 service，非補 None）。目前 `/flow-engine/task/queue` 是**止血空陣列**；**其他走 project_participant_service 的 route（如 `api/participant/routes/*` 5 條）可能仍會炸**。整合測試碰到 participant 相關 500 多半是這個。**這是框架/2B session 的接線工作。** 我 curl 登入也回 COMMON_500001，疑似同源（但 user 瀏覽器已登入正常）。
2. **`frameworks.main_version_id`（FK）變冗餘**：改用 main_version 字串後，FK 已無用（與刪除/匯入邏輯有耦合故沒拔），框架 session 可另開 cleanup DROP。
3. **FE 新增框架表單仍有「版號」欄**：現在值會存進 main_version 字串（OK），要不要從表單拿掉是 FE 清理。
4. **DROP/UNIQUE 變更的多環境**：三支 migration 只套 DEV，上線前補 stg/poc/prod。

## ⏸ 等 user 明示（未做）
push（三 repo）/ 套件發 Nexus + pin 還原 / stg/poc/prod migration / changelog / Notion / 對話紀錄（user 本次明確跳過後三項）。

## 工作樹
- 主專案：`feature/oscal-refactor`，乾淨（`M pyproject.toml` = jedi_oscal_v2 dev path-dep，勿 commit）
- 套件：`feature/oscal-refactor`
- FE：（其 branch）；今日 4 個框架修 commit 已 commit 未 push
