| 項目 | 內容 |
|---|---|
| 緣由 | 接續 round-flow 再整合:把專案生命週期 FE 從舊 AP-centric 引擎逐頁遷到 v2 audit_round + living/round SSP |
| BE branch | feature/oscal-refactor(勿切 branch;branch 不對停下問 user) |
| FE branch | 同名 feature/oscal-refactor(~/Projects/Billows/Audit-Manager/compliance-manager-fe/) |
| 下一棒主任務 | 專案規劃頁 ProjectPlanningView 資料沒帶出來 → 比照總覽遷到 v2 SSP control-tree |
| push 狀態 | BE + FE 都有未 push commits → push 等 user 明示 |
| 驗證狀態 | 本弧全部已 user 手測通過(含 per-AO 任務 / get_job drawer,2026-06-17) |
大圖:FR-038 把 OSCAL 從 jedi_oscal v1 整碗換 v2。本弧處理 專案生命週期 FE 逐頁從舊 AP 引擎遷到 v2 audit_round。GRC 業務層在 Wave 2A 被大量 dark,這弧一頁一頁把資料源接回 v2。
這條線的需求(user 對話拍板):
compliance.project_extensions.living_ssp_id)。launch_audit 時 snapshot living SSP → project_audit_rounds.ssp_id,邊界③)。round-flow-engine-reintegration-design.md(D5 stage 模型 / 邊界③ snapshot / status 對照表)。SSP-source 切換在 FE 怎麼自然落地(關鍵): 總覽 / 規劃頁的 sspUid computed 都是
sspUid = currentRound.ssp_uid ?? project.ssp.id ?? project.ssp_uid
round.ssp_id 為 NULL(snapshot 還沒做)→ fallback 到 project.ssp.id(= living SSP uuid,BE get_project 已回,見下)→ 自動拉 living SSP。round.ssp_id 有值(frozen snapshot)→ currentRound.ssp_uid 有值 → 自動拉該 round frozen SSP。所以「同一段 sspUid 邏輯」就涵蓋兩階段,不需 if/else 判 stage。規劃頁照抄總覽即可。
冷接自檢 4 問(答不出回去讀設計):
project_extensions.living_ssp_id)launch_audit snapshot 後,round.ssp_id 有值)sspUid = round.ssp_uid ?? project.ssp.id,planning 輪 round.ssp_uid 為 null 自動 fallback)ProjectAuditorOverview,§3 參考實作)round-flow-engine-reintegration-design.md §1~§4(D5 stage / 邊界③ / status 對照)ProjectAuditorOverview.vue 的 round-scoped 遷移(已完成,照它做規劃頁)| commit | 內容 |
|---|---|
49aca1ab |
批次刪除掛掉 — batch_delete_projects 補 @transaction |
9a617180 |
建專案順帶建第一輪 initial + 綁流程範本(D6, Task 8b) |
901d173(FE) |
建專案後導到 round-scoped 總覽(project-auditor-overview-round + first_round_uid) |
| commit | 內容 |
|---|---|
e200b01(FE) |
/project/projects/:id 列表來源 AP→POST /projects/<id>/audit-rounds/list;建輪次 / launch-audit / launch-reverify;整列可點進總覽 |
| commit | 內容 |
|---|---|
03fb83bf |
get_project_by_uid 從 dark 重建(v2)— 修總覽「找不到專案」;回 living SSP uuid(project_extensions.living_ssp_id → project.ssp.id) |
fbebdee(FE) |
總覽入口吃 roundUid(route project-auditor-overview-round = /project/projects/:id/round/:roundUid);Banner retarget round-scoped(StageService/FlowPhaseBanner 改 /audit-round/<roundUid>/stage/{info,advance}) |
98b33fc3 |
新 BE 端點 GET /ssp/<ssp_uid>/control-tree:v2 catalog(群組→控制項→AO part)+ SSP 現況說明(implemented_requirements + statements)組樹;防重複 IR(偏好有描述那筆) |
c014197(FE) |
總覽 fetchGroups 改吃 /ssp/<sspUid>/control-tree、fetchControlDetail 從已載入控制項取 |
| commit | 內容 |
|---|---|
d8c1d4fe |
project_start._generate_prep_jobs 重寫:每 AO 用自己的 workflow-template-uid(資源庫 _init_ao_workflows 寫進 cloned catalog part.props)clone 一份定版(source_template_uid 指原始),回填 part.props 指向 cloned uid;get_control_tree 補 AO jobs(cloned wf uid → workflow_executions → job_executions);get_job 從 dark 重建(最小 GrcJobEntity)+ route None→404 guard |
✅ user 手測通過(重啟 BE + 建新專案 → AO 顯示「收證據」任務 + 點任務 drawer 正常開)。 既有舊專案(舊單一 template、part.props 未回填)不會顯示 per-AO 任務 → 測試一律用新專案。
ProjectPlanningView 資料沒帶出來症狀:/project/projects/<id>/ap/<apUid>/planning 整頁資料沒帶出來(控制項 / 現況說明 / SSP)。
root cause(同總覽舊版):ProjectPlanningView.vue 仍是 AP-centric:
route.params.apUid + apSegment = /ap/<apUid>,打舊 dark 的 /grc/.../control-groups 引擎 → 空。sspUid computed 已是對的 pattern(currentAp.ssp_uid ?? project.ssp?.id ?? project.ssp_uid),但 project.ssp 要 BE get_project 回(已修,03fb83bf)+ 入口要改吃 roundUid。做法 = 照 §3 參考實作(總覽剛遷好的那套):
/project/projects/:id/round/:roundUid/planning,名 project-planning-round),或讓現有 view 同時吃 route.params.roundUid(總覽用 isRoundRoute + selectedApUid = roundUid ?? apUid 雙吃法,見 ProjectAuditorOverview.vue:44)。總覽 / 列表的「規劃」按鈕導過去帶 roundUid。/grc/.../control-groups 改吃 GET /ssp/<sspUid>/control-tree(總覽 fetchGroups 範本,ProjectAuditorOverview.vue 內)。SSP 現況說明編輯沿用既有 SspService(/ssp/<uid>/control-implementation/...,是活的、可寫)。sspUid computed 保持 round.ssp_uid ?? project.ssp.id——planning 輪自動拉 living SSP、launch_audit 後自動拉 round frozen SSP(見 WHY 節)。編輯:living SSP 階段可寫(/ssp/<living_uid>/control-implementation PUT,Upsert 活的);round frozen 階段「如果可以」可寫(frozen 是否唯讀依設計,design §4.2 deep_clone_ssp 是可寫副本,但稽核階段是否鎖定待 user 確認)。注意(同總覽踩過):
<FlowPhaseBanner>,改傳 :round-uid(Banner 已支援 roundUid 優先、apUid deprecated fallback)。ProjectAuditorOverview.vue(FE,剛遷好) 是規劃頁的範本,關鍵段:
ProjectAuditorOverview.vue:44 — isRoundRoute + selectedApUid = roundUid ?? apUid 雙吃入口ProjectAuditorOverview.vue fetchApList() — round route 時改撈 audit-rounds/list 並 map 成 header/dropdown shapeProjectAuditorOverview.vue fetchGroups() — 改吃 GET /ssp/<sspUid>/control-tree,populate groups + controlsMap + syncControlMapssspUid computed — selectedAp.ssp_uid ?? project.ssp?.id ?? project.ssp_uidBE 端點(都活的):
GET /ssp/<ssp_uid>/control-tree — 群組→控制項→AO(含現況說明 + AO 任務)GET/PUT /ssp/<ssp_uid>/control-implementation/<control_identifier> — 單控制項現況(讀/Upsert)PUT /ssp/<ssp_uid>/control-implementation/<ctrl>/objective/<stmt> — AO 層現況GET /grc/project/<uid>(ProjectResponseSchema)回 project.ssp = {id: living_ssp_uuid}get_project_by_uid / get_job / grc_job_repo 多處 return None/[](標 FR-038 2A)。改 GRC read 前先 grep dark 標記,dark 的就比照 list_projects 重建(查 v2 表 + 套可見性 + enrich)。project_start 同時「clone 範本 SSP 帶描述 IR」+「_init_ssp_control_implementations 建空白 IR」→ 每控制項兩筆 IR(一有描述、一空白)。讀取路徑要偏好有描述那筆(get_control_tree / _ir_for_control 已做)。root cause(project_start 重複建 IR)待獨立修 + 既有資料清理——見 §6 follow-up。workflow_execution_control_mappings 只有 control_id + ao_part_id(無 project_id),跨專案同 catalog 會撞。改用「per-project cloned template 專案唯一」當 scope(part.props.workflow-template-uid 回填 cloned uid → 該 template 的 workflow_execution 即本專案的)。compliance schema(workflow_templates / workflow_executions / job_executions / task_assignees)。api/oscal/__init__.py 與 user in-flight 糾纏:該檔同時有「我的 SspControlTreeRoute」+「user 的 ModuleFrameTemplateSspResource」→ commit 時用 git apply --cached 切自己的 hunk(互動式 git add -p 在此環境不可用)。cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git branch --show-current # 應 feature/oscal-refactor
git log --oneline -8 | cat # 對照 §1 commit 清單
git status --short | grep -vE '^\?\? docs/' # 應只剩 user in-flight(api/oscal/__init__.py、module_frame/* 等)
# import smoke(sandbox 無法完整 boot)
set -a; source .env; set +a; export GITLAB_API_VERSION=4 GITLAB_URL=x GITLAB_PRIVATE_TOKEN=x GITHUB_PRIVATE_TOKEN=x
.venv/bin/python -c "import app.oscal.service.ssp_control_implementation_service, app.project.service.project_start_app_service; print('import OK')"Verify 本期成果(重啟 BE 後,建新專案 → 進 round 總覽):
generate_prep_jobs ... jobs=N cloned_templates=N (per-AO 專屬流程,對齊 V1 Step 8)_init_ssp_control_implementations(建空白 IR)兩邊都建 → 每控制項兩筆 IR。需改 project_start 擇一 + 寫既有專案空白重複 IR 清理 SQL。(本期只在讀取路徑 robust)讀 docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-17-round-flow-fe-migration-handoff.md,
先讀「🧭 原始需求 WHY」+ §0 自檢 4 問答得出再開工。本弧:專案生命週期 FE 逐頁從舊 AP 引擎
遷到 v2 audit_round + living/round SSP(總覽已遷好,§3 參考實作)。下一棒:專案規劃頁
ProjectPlanningView 資料沒帶出來 → 比照總覽改吃 /ssp/<sspUid>/control-tree + roundUid 入口;
sspUid = round.ssp_uid ?? project.ssp.id 自動切換 living/round SSP(planning 拉主專案 SSP、
launch_audit 後拉 round frozen SSP)。跑 §5 pre-flight。不切 branch、push 等 user。