FR-038 框架維護 2a/2b 再啟用 — 驗收報告

自主執行(2026-06-16,user 不在場)完成。決策全用建議值並記錄於 docs/analysis/2026-06-16-framework-maintenance-2a2b-decisions.md。 本報告供隔日驗收。所有 commit 已落地、未 push

0. 一句話結論

框架管理整條(框架 CRUD/刪除 → 版本管理 → catalog 文章式編輯 → 兩階段 PDF 匯入) 全部接回 v2 底層、路由註冊在舊 URL、FE 零改動。BOOT OK、真實 CMMC L2 PDF e2e 通過、 pytest 零新回歸。FE 不需改任何一行,重啟 BE 即可驗。

1. 完成範圍

區塊 FE 頁面 狀態 端點(舊 URL,FE 既有)
框架 CRUD + 刪除 ComplianceFrameworkManage POST /oscal-frameworks(list)、GET /oscal-frameworks/menuPOST /oscal-framework(create)、GET/PUT/DELETE /oscal-framework/<uid>
P1 版本管理 2a ComplianceFrameworkVersionManage POST /oscal-framework-versions(list)、GET/POST/PUT/DELETE /oscal-framework-version[/<uid>]
P2 catalog 編輯 2b FrameworkVersionEditView GET /oscal-framework-version/<uid>/catalog-tree、`PUT/DELETE /oscal-catalog-group
P3 兩階段 PDF 匯入 FrameworkImportPage GET /oscal-framework-parse-jobsPOST /oscal-framework-parse-jobs/parseGET/DELETE /oscal-framework-parse-jobs/<uid>POST .../<uid>/confirm

2. 核心做法

  • FE 零改動原則:底層全換 jedi_oscal_v2,但路由註冊回舊 URL + 主專案 app service 當 adapter 對齊舊 request/response shape。
  • 不 import v1 jedi_oscal(撞 v2 MetaData 會炸 boot):三組 service 全部 v1-clean 重寫,只接 v2。
  • v2 套件異動(dev path-dep,未發 Nexus):
    • FrameworkService:補 delete_framework(靠 CASCADE)、update_versiondelete_version
    • framework_versions 加回 release_date 欄(model/entity/mapper)
  • 不需新增 v2 method 的部分:P2 直接用 v2 catalog repos;P3 重用 get_oscal_parser_adapter + CatalogService.add_catalog

3. 驗證結果

項目 結果
create_app() BOOT ✅ OK(含全部 16 條框架路由註冊在舊 URL)
P1 read/write smoke ✅ 版本 list(樹/parent/children)+ create/update/delete(含 release_date)round-trip
P2 read/write smoke ✅ catalog-tree(control desc 從 statement part、AO 從 parts)+ control/AO 改寫還原
P3 真實 CMMC L2 PDF e2e ✅ parse=14 groups/110 controls/320 AO → confirm 全落地 + release_date → catalog 持久化數量一致 → cleanup
pytest(全套) ✅ 42 failed + 50 errors ≤ baseline 47f/50e(零新回歸,失敗反少 5)

4. 已知限制(v2 正規化結構無對應欄位 → 讀回 None、寫入忽略)

OSCAL v2 是正規化模型,下列 v1 扁平欄位在 v2 無對應 slot,刻意不臆造儲存位置(避免埋髒資料):

欄位 說明
control guidance CMMC catalog 無 guidance part
group description v2 catalog_groups 無 description 欄
AO description v2 AO part 單一 prose(AO 文字對映到 name↔︎prose)

影響:FE 這些欄位編輯後不會保存(顯示空白)。控制項主要描述(control.description ↔︎ statement part)、 控制標題、AO 文字、群組名稱都正常可編可存。若客戶需要 guidance/group-description,需另開 FR 在 v2 catalog 加欄或 part(schema 變更)。

5. ⚠️ 待辦(需 user 處理)

  1. stg / poc / prod 跑兩支 migration(DEV 已套):
    • scripts/sql/2026-06-16-fr038-framework-versions-add-release-date.sql
    • scripts/sql/2026-06-16-fr038-recreate-framework-parse-jobs.sql
    • 一律 psql --single-transaction -v ON_ERROR_STOP=1 -f <檔> 用 cmmgr。BE 必須同步上新版(含 v2 套件 path-dep 改動)否則炸。
  2. v2 套件發 Nexus:整個框架維護 + 既有 import-ssp 完成後,user 明示才 bump + 推(dev path-dep 期間勿發)。pyproject.toml path-dep 勿 commit。
  3. push:BE + 套件 commit 都未 push,等 user 明示。
  4. 重啟 BE 才會生效(無 hot reload)。

6. Commit 清單(未 push)

主專案 BE(branch feature/oscal-refactor):

  • ef600abf 框架 CRUD 接回舊 URL + 補 delete + 版本管理 2a
  • c7f962d5 框架維護 2b — catalog 文章式編輯
  • 9973fc55 框架維護 — 兩階段 PDF 匯入 parse-job

註:log 中 acc72361 / 6388a1dc(SSP 匯入相關)非本次工作,疑來自平行 session;本次框架維護僅上述 3 commit。

v2 套件jedi-oscal-v2,branch feature/oscal-refactor,dev path-dep):

  • ceb1b3a FrameworkService delete/version CRUD + framework_versions.release_date
  • 1427956 delete_framework 改靠 CASCADE(修 FK)

7. 隔日手測 checklist(重啟 BE 後)

進「合規框架」頁逐項點:

  1. 框架:列表載入 / 新增 / 編輯 / 刪除(draft)
  2. 版本管理(點框架名進入):版本列表(樹狀父子)/ 新增版本(含發行日期)/ 編輯 / 刪除
  3. catalog 編輯(版本「編輯內容」):tree 載入 / 改 control 標題+描述 / 改 AO 文字 / 存檔
  4. 兩階段 PDF 匯入:上傳 CMMC L2 PDF → 預覽(14 領域/110 控制/320 AO)→ 確認 → 新版本帶 catalog
  5. 確認上述操作 FE 無需改 code、無 console error

8. 決策索引

完整決策(含取捨理由、反悔成本)見 docs/analysis/2026-06-16-framework-maintenance-2a2b-decisions.md: D0~D1(共用方向)、P1-1~6(版本管理)、P2-1~8(catalog 編輯,含 uid=str(id)、part 對映)、P3-1~7(parse-job)。