本檔的「下一棒 = B2」已過時:B2 / B3a / B4.1 已 shipped(commits 2ba7a7c4、8111626e)。 下一棒 = B4.2 → B5,請改讀 2026-06-15-B4.2-B5-START-HERE-handoff.md。 本檔保留作 B1/B2 開工依據與 disable 清單參照。
給下個 session 的 prompt:「讀
docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-14-2B-START-HERE-handoff.md,先過 §0 讀序硬 gate 懂需求,跑 §4 pre-flight 確認現況,再接手 B2(B1 已完成):依 §3 re-enable 計畫 + B1 的 canonical 模式(§1c)在 v2 上逐一重建。」 本檔自包含。所有狀態為 2026-06-14 verified。
| 項目 | 值 |
|---|---|
| 進度 | 2A ✅ shipped+closed · 2.0b ✅ defer-DROP · B1 ✅ 完成(框架 CRUD+匯入 + 資源庫三件組) · 下一棒 = B2(專案成立 clone 三件組) |
| 主專案 branch / HEAD | feature/oscal-refactor / edab519e(B 進度 commits:37962f7d→befbbc58→3512e4f9 + docs) |
| 主專案 working tree | 乾淨,只有 M pyproject.toml(jedi-oscal-v2 dev path-dep,照規範勿 commit) |
| ⚠️ **v2 套件有未 commit 的 dev 改動(B 依賴,別搞丟/別發版) | repo ~/Projects/Jedicogy/module/jedi-python-package(branch feature/oscal-refactor)working tree:M jedi-oscal-v2/.../catalog/catalog_service.py(add_catalog + import_catalog_from_pdf/excel + metadata NN 預設)M jedi-oscal-v2/.../framework/framework_service.py(get_version)?? jedi-oscal-v2/tests/catalog/test_catalog_service_import.pydev path-dep(主專案 pyproject 指本地源碼),BE 重啟即生效;v2 測試 197 passed。發版(bump+推 Nexus+主專案 pin)等整個 feature 完成 + user 明示才做。** |
| My Jobs / GRC 產品 | 2A 後 GRC 大面 dark;B1 已 re-enable 框架+資源庫 9 條 route;其餘 B2~B5 route 仍在 §5.4 disable 清單,逐棒 re-enable |
| 跑得起來嗎 | create_app() BOOT OK on v2;python -m pytest test/ = 56 failed + 50 errors(=106,與 2A 後持平、零新回歸)+ 9 skipped |
| 項目 | 值 |
|---|---|
| 緣由 | FR-038 OSCAL 重設計。2A 地基翻轉已完成(BE 已 boot 在 jedi_oscal_v2 上,B1~B5 業務全 disable)。2B = 在 v2 地基上把 B1~B5 業務真正落地。 |
| branch | feature/oscal-refactor(兩 repo) |
| 2A 狀態 | ✅ 完成:create_app() BOOT OK on v2、pytest 106 紅 ≤ baseline 117(zero new regression)、My Jobs view 回 131 列。見 2A handoff ✓ FIXED 標頭 + §5.4 disable 清單。 |
| 2B 範圍 | 依 design §4.x 把 B1~B5 業務 service 接真 v2,逐一 re-enable 2A disable 的 route/provider/service/test。FE 是 2C/Wave 3。 |
一句話:2A 只是把 BE 從舊套件翻到 v2、讓它能 boot;2A 之後整個 GRC 產品是 dark 的(framework/資源庫/專案成立/SSP/AP/AR/POA&M/輪次 endpoint 全 404 或 stub 空回)。2B 就是把這些業務在 v2 的新模型上重新實作,讓產品恢復功能並落地新設計(三層 clone/snapshot 邊界 + 稽核輪次 first-class + Phase 3 補齊)。
新模型 WHY 與細節(必讀,與 2A 同一份 gate):
requirement-analysis.md §0~§4(舊 6 問題 / 三層四邊界 / 輪次 7 態狀態機 / 新舊業務流程對照 / Q1 job 綁定)design.md §3(engagement 模型 initial/surveillance/close-out ↔︎ AP/AR/ar_results)、§4.2(v2 套件對外 service 簽章契約 — 2B 接 v2 的依據)、§4.4(套件 vs 主專案邊界)、§5(API spec 骨架)api-contract.md §1~§10(B1~B5 端點清單 — 2B 照此重建 route)冷接自檢:你應能回答 ① 三層四 clone/snapshot 邊界是哪四個 ② engagement 模型怎麼把 round ↔︎ AP ↔︎ AR ↔︎ ar_results wire 起來 ③ 哪些留主專案、哪些在套件(design §4.4)④ My Jobs 為何綁 SSP 控制項而非 AP task。答不出 → 回去讀。
wave2-migration-plan.md(B1~B5 phase 拆解)di_containers/oscal/oscal_containers.py。2B 直接消費這些(或加 repo 注入)。jedi_oscal 0.0.22),但任何 import 它的檔都會與 v2 collide → 2B 重建時一律接 v2,禁止再 import 舊 jedi_oscal。oscal schema 48 表(v2)、compliance.project_audit_rounds 在;v2 業務表多為空(SSP/AP/AR=0 列)→ 2B 的專案成立 clone 流程會把資料灌進去。oscal.ssp_control_implementations(Q1)。assessment_plan_extensions:決議 defer DROP 到 B3(不 rebuild FK、不現在 DROP)。理由:目前無 broken FK 卡 boot(fk_ape 早已消失)、表 inert(193 列舊資料)但仍有 live-ish consumer stage_advance_service。等 B3 project_audit_rounds 取代 + stage_advance_service 重寫後再跑安全四查 DROP。完整取捨見 docs/analysis/2026-06-14-fr038-2.0b-assessment-plan-extensions-defer-drop.md。CatalogService 沒有建立/匯入 catalog 的方法、OscalIoService 沒有 import_ssp → B1 匯入端點無法做。已在 jedi-oscal-v2 補:
CatalogService.add_catalog(catalog: CatalogEntity) -> CatalogEntity(persist catalog→groups→controls→parts,caller 持 @transaction)CatalogService.import_catalog_from_pdf(stream, framework_code, start_page=None, end_page=None, metadata=None) -> CatalogEntityCatalogService.import_catalog_from_excel(stream, framework_code, metadata=None) -> CatalogEntitycatalog.oscal_metadata / catalog.groups / catalog.controls(flat) / group.controls / control.parts;FK:group.catalog_id、control.catalog_id+catalog_group_id、part.catalog_control_id。OscalIoService.import_ssp_docx/excel(SSP 匯入大;資源庫可先用空 SSP 範本 + clone 起步,非 B2~B5 e2e 必需)→ 需要時再補套件。oscal_container.framework_service(v2 FrameworkService:add/get/list/update_framework、add_framework_version、list_versions、publish_version);資源庫建立走 oscal_container.oscal_snapshot_service.clone_resource_library + profile_service.resolve_profile;框架版本匯入走 catalog_service.import_catalog_from_pdf/excel 後回填 framework_versions.catalog_id。端點清單 + request/response shape 見 api-contract §1/§2。舊 framework route 檔(api/oscal/routes/framework/*)仍在但 import 舊 jedi_oscal → 2B 重寫成 v2 或新建,再從 EXCLUDE_MODULES + api/oscal/__init__.py create_module 恢復註冊。37962f7d CRUD + befbbc58 匯入):
app/oscal/service/framework_app_service.py 包 v2 FrameworkService + CatalogService。oscal_framework_route 重寫 + re-enable(/oscal/frameworks/list、/menu、/oscal/framework/<uid>、/oscal/frameworks)。import_framework_version(PDF/Excel → CatalogService.import_catalog_from_* → 建 framework_version 回填 catalog_id),route oscal_import_route 重寫 + re-enable(POST /oscal/framework-versions/import)。real CMMC L2 PDF 實測 → 14 groups / 110 controls / 1988 parts 正確落地。3512e4f9)— B1 整體完成:
app/oscal/service/resource_library_app_service.py:建立資源庫 = 選 framework_version → oscal_clone_service.clone_catalog_tree(邊界① catalog 副本)→ profile_service.add_profile(ProfileImportEntity 指向 cloned catalog + include 規則)→ ssp_service.add_empty_ssp(空範本,需先建 metadata)→ 寫 compliance.module_frames link record。無 schema 變更(module_frames 已有 oscal_framework_version_uid/oscal_profile_uid/template_ssp_id;catalog 副本 id 經 profile import 取得,不另存欄位)。/oscal/resource-libraries[/list]、/oscal/resource-library/<uid>[/publish]。user.tenant_id;無 user context 時 session_scope else 開 super_admin 繞 RLS(smoke 可直接跑)。OscalIoService.import_ssp_*(follow-up)。CatalogService.add_catalog+import_catalog_from_pdf/excel、add_catalog 補 metadata NN 預設、FrameworkService.get_version。發版時機:feature 全完成 + user 明示。project_oscal_v1_v2_cannot_coexist):2B 任何新 code 只接 jedi_oscal_v2。重新 enable 一個 service 前,確認它內部 import 的是 v2,不是被 2A stub 的舊符號。python -m pytest test/ 比現況(106 紅)多紅。| Phase | 內容 | 對應 2A disable | 主要 v2 service(design §4.2) |
|---|---|---|---|
| B1 | 合規框架 + 資源庫 API 對齊 v2(profile resolve、資源庫三件組 snapshot) | api.oscal framework/catalog/profile route + module_frame 資源庫 service | FrameworkService / CatalogService / ProfileService / OscalSnapshotService |
| B2 | 專案成立流程重寫(clone 資源庫三件組、AP/AR 延後建) | oscal_project_service / module_frame_template_copy | OscalSnapshotService.clone_resource_library + SspService |
| B3 | SSP 維護 + 啟動稽核 snapshot + project_audit_rounds 狀態機(7 態) |
api.oscal ssp_* route + ssp_versioning | SspService 子物件 CRUD + snapshot_ssp;輪次狀態機留主專案 |
| B4 | AP 後端(草稿生成 + reviewed-controls/subjects/tasks) | api.oscal ap route | AssessmentPlanService + ApDraftService |
| B5 | AR 後端(AO 全量判定矩陣 + 風險總結)+ POA&M + 結案/覆核 | api.oscal ar route + grc poam/stage handlers | AssessmentResultService + AssessmentRiskService + PoamService |
| My Jobs | view control_* 接 v2 SSP 控制項;專案成立時建 job(綁 SSP 控制項,Q1) | vw_user_job_queue + task_assignee | — |
各 phase re-enable 時:(1) 把對應 leaf service 的 stub 換真 v2 實作 → (2) container 重接 v2 provider → (3) route 從 EXCLUDE_MODULES 移除 + create_module 恢復註冊 → (4) 移除對應 test skip 並補 v2 測試 → (5) 跑 boot + pytest 驗不多紅。
cd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be
git branch --show-current # feature/oscal-refactor
poetry run python -c "import jedi_oscal_v2; print('v2 OK')"
# BE boot(補 dummy env,否則撞 jedi_issue / deepcopy 假象)
set -a; source .env; set +a 2>/dev/null
export GITLAB_API_VERSION=4 GITLAB_URL=http://localhost GITLAB_PRIVATE_TOKEN=dummy GITHUB_PRIVATE_TOKEN=dummy
poetry run python -c "import eventlet; eventlet.monkey_patch(all=False, socket=True); import sys; sys.setrecursionlimit(5000); from core.app_factory import create_app; create_app(); print('BOOT OK')"
poetry run python -m pytest test/ -q -p no:cacheprovider --continue-on-collection-errors 2>&1 | tail -1 # 56 failed,405 passed,9 skipped,50 errorscanonical 模式(B1 已驗,照抄):新 app service 包 v2 primitive(補主專案職責 pager/filter/uid/RLS/tenant)→ OscalContainer wire → route 重寫/新建 → EXCLUDE_MODULES 移除 + api/oscal/__init__.py(或對應 api 模組)create_module 恢復註冊 → boot + app-service smoke(用真資料) + pytest(不多紅)+ 顯式 git add commit。參考 commit 37962f7d/3512e4f9 怎麼接。
B2 要做的(requirement §4.3 + design §3 + api-contract §3.2):
app/project/service/oscal_project_service.py 的 start_oscal_project → start_project:
oscal_container.oscal_snapshot_service.clone_resource_library(catalog_id, profile_id, ssp_id)(邊界② 脫鉤公版)→ 得專案自己的三件組副本。compliance.projects 主檔 + participant + 為專案 SSP 控制項建 workflow job(Q1,專案成立就建,非 AP task)。clone_resource_library 簽章 = (catalog_id, profile_id, ssp_id)(不是 framework_version_uid;主專案先從資源庫 module_frame 解出三件組 id)。oscal_profile_uid(→ profile → profile_imports.source_catalog_id = catalog)、template_ssp_id(= ssp)。catalog 經 profile import 取。project_route / oscal_project_service 在 2A 被 gut(stub),B2 是把它接回 v2,不是從零;先讀 2A 對它做了什麼(§5.4 D 表)。pyproject.toml 改回 pin。看本檔 + §0 讀序 + 跑 §4 → 能確認 2A 地基(v2 boots、舊套件已剝離)、知道 2B 是「在 v2 上重建 B1~B5」非「翻 import」、知道 disable 清單是 re-enable 地圖、知道每 phase 接哪個 v2 service。