FR-038 稽核輪次流程引擎再整合 — 換 session Handoff(2026-06-16)

項目 內容
緣由 「新增專案 422」開查 → 演成「把 FR-026 BPMN 流程引擎從死掉的舊 AP retarget 到 v2 輪次」大重構;測試中又連帶修了 GRC 專案列表/刪除(Wave 2A dark)
BE branch feature/oscal-refactor勿切 branch;發現 branch 不對停下問 user)
FE repo ~/Projects/Billows/Audit-Manager/compliance-manager-fe/,同名 branch feature/oscal-refactor
接手前必讀 本文件 §0 讀序(先讀需求 WHY 再碰 code)
預估剩餘 FE round-flow 大遷移(大,數 hr)+ Task 8b commit + 文件收尾 + stg/poc migration
push 狀態 BE + FE 都有未 push commits → push 等 user 明示

🧭 原始需求 / WHY(先懂這個再動 code)

大圖:FR-038 是把 OSCAL 從 jedi_oscal v1 整碗換成 v2(Wave 2A 已把 BE boot 切到 jedi_oscal_v2,舊 v1 全剝離)。本棒處理其中一條線:稽核流程引擎

這條線的需求(user 對話拍板,2026-06-16)

  • 稽核流程範本 = 專案/輪次的「執行流程」(規劃→執行→稽核→缺失改善→結案),由 FR-026 用 BPMN 流程引擎驅動 AP 生命週期(見 docs/features/FR-026-2605-project-flow-engine-integrate/FINAL-SPEC-v1.0.md)。
  • v2 切換時,這套引擎被擱在已 dark 的舊 AP 上(孤兒)。目標:retarget 到 v2 first-class compliance.project_audit_rounds
  • 每個 audit_round 綁一支自己的稽核流程範本 snapshot(1:1,跟 round 綁一個 SSP 同道理)。範本掛輪次,不是專案。
  • v2 多了一關 AP 填寫(reviewed-controls/subjects/tasks,人工 author)→ 新增 ap_authoring stage(auditor 主責)。
  • 建專案 = clone 資源庫三件組 + 順帶建第一輪(initial)並綁所選流程範本(D6)。建專案頁維持「資源庫 + 稽核流程範本」雙下拉。
  • 覆核走 v2 多輪模型launch_reverify 另開 close-out 子輪 + parent_round_id),不是 FR-026 的 poam→audit 同輪迴圈。

canonical 鏈(D5 拍板)planning → ap_authoring → audit → poam → End (FR-026 的 task_execution 收進 planning;review 暫不納)

stage_code → round.status 對照(與已 shipped 的 v2 輪次狀態機 1:1,零 migration): planning→planningap_authoring→audit_planningaudit→auditingpoam→remediation、End→closed

決策清單(design 內有完整理由): | # | 決策 | |---|------| | D1 | 流程範本綁 project_audit_round(非專案)| | D2 | big-bang 一次到位(不分階段)| | D3 | ap_authoring main_role = auditor(暫定)| | D4 | 第一輪範本在「建專案」選;後續輪在「建輪次」選 | | D5 | stage 模型 planning→ap_authoring→audit→poam;task_execution 收進 planning | | D6 | 建專案順帶建第一輪 initial + 綁範本 | | 重要實作收斂 | 無 status derivation:handler 各自寫 status(與所進入 stage 一致);poam→End(非 loop)|

完整設計docs/features/FR-038-2606-oscal-redesign/round-flow-engine-reintegration-design.md(⚠️ §4.1b 還寫 derivation,實作改成 no-derivation,文件待收尾同步)


§0 接手讀序(按序,前 2 個是硬 gate)

  1. 🔒 本文件 §「原始需求 WHY」全讀 — 答不出下面自檢題別碰 code
  2. 🔒 round-flow-engine-reintegration-design.md §1~§4(問題/決策/目標架構)
  3. round-flow-engine-implementation-plan.md(BE 11 task,已全做)+ round-flow-engine-fe-plan.md(FE,Task 1 做完、2-5 未做)
  4. 主嫌路徑檔:app/flow_engine/service/stage_advance_service.py(已 retarget round_uid)、app/grc/service/oscal_stage_handlers.pyapp/grc/service/audit_round_app_service.py(create_round 綁範本 + 狀態機)
  5. GRC 列表/刪除修補:infra/grc/repository/grc_project_repo_impl.py(list_projects v2 重建)、app/grc/service/project_service.py(delete/batch_delete)

冷接自檢 4 問(答不出回去讀)

  1. 為何要把流程引擎 retarget?(→ FR-026 引擎被擱在 v2 已 dark 的 AP 上)
  2. 範本綁哪一層、幾比幾?(→ 綁 audit_round,1 round : 1 範本 snapshot : 1 workflow_execution)
  3. ap_authoring 是什麼、誰推?(→ AP 填寫,auditor)
  4. round.status 怎麼來?(→ handler 自己寫,對應所進入的 stage, derivation)

§1 目前狀態(哪些好了 / 哪些沒)

✅ 已完成 + committed(BE,round flow engine core,Tasks 1-10)

commit 內容
1fd604cc Task 1 schema:project_audit_rounds.{flow_template_snapshot_uid,workflow_execution_uid} + module_frames.audit_flow_template_uid
76252985 Task 2 error codes(GRC_ROUND_NO_WORKFLOW_BINDING / GRC_ROUND_PREP_TASKS_NOT_DONE)
5df81d25 Task 3 ap_authoring stage_object seed + planning 收斂
36d022e7 Task 4 builtin BPMN canonical(完整稽核/自我評估 改鏈;含審核/內部自查 deactivate)
0a132492 Tasks 5-10 engine core:stage_advance retarget round_uid、handlers wrap audit_round、preconditions、DI rewire、stage route round-scoped、poam→End
67ded923 handler 測試回歸修正

✅ 已完成 + committed(BE,GRC 列表/刪除 — 測試中發現 Wave 2A dark,連帶修)

commit 內容
b31dc79b 修 /projects/list DI(移除 ProjectService 死接線 associations.assessment_plan_task_domain_service)
e88e8d78 移除 grc 容器對 7 個 Wave 2A 已移除 oscal v1 provider 的死接線
c56927c4 重建 list_projects(v2):查 projects+extensions、軟刪排除、可見性、分頁 → 列表恢復顯示(只回未刪除)
987575a6 批次刪除 API(POST /grc/projects/batch-delete)
596d5417 修單筆刪除(delete_project 不再走 dark get_project)

✅ 已完成 + committed(FE)

commit 內容
175ca24 建專案改送 resource_library_uid + flow_template_uid(修 422,FE Task 1)
9ad582c 專案列表批次刪除 UI(多選 + 批次刪除按鈕)

⚠️ 已完成但「未 commit」(BE,Task 8b — 建專案順帶建第一輪 D6)

留在 working tree,下個 session 要 commit。 因為這 4 個檔在 session 開始前就有 user in-flight 未 commit 改動,無法用 git add 乾淨切開:

  • api/project/serializers/project.py(加 flow_template_uid、清掉舊 EXCLUDE)
  • api/project/routes/project_route.py(thread flow_template_uid + delete 傳 is_admin)
  • app/project/service/project_start_app_service.py(start_project 建 round 1 = _create_first_round,回 first_round_uid)
  • di_containers/project/project_containers.py(注入 audit_round_app_service)
  • test/test_project_start_creates_first_round.py(新檔,2 tests pass)

驗證 Task 8b 有效:DB 已驗 — 建專案 271 後 project_audit_rounds 有 round_no=1/initial/planning,flow_template_snapshot_uid + workflow_execution_uid 非空。

❌ 未開始(FE round-flow 大遷移 — 最大剩餘塊)

FE 整個還是 AP-centric(route param apUidapListRoundSwitcherBar、Banner 掛 :ap-uid)。round flow 在 UI 還不能跑。要做(見 round-flow-engine-fe-plan.md Task 2-5 + audit-round-fe-migration-plan.md):

  • Banner/stage retarget apUidroundUidStageService.js / useStageInfo.js / FlowPhaseBanner.vue,BE 端點已是 /project/<uid>/audit-round/<round_uid>/stage/{info,advance}
  • AP 填寫畫面(ap_authoring routed stage,route /round/:roundUid/ap
  • round-scoped routes + RoundSwitcher 改吃 audit-rounds
  • 掛 Banner 的 view 改傳 roundUid + i18n(precondition reason key 在 FE 渲染)

§2 前次(本 session)踩過的雷,別重蹈

  1. GRC 業務層在 Wave 2A 被大量 darkgrc_project_repo_impl 多個 method return []/None、grc 容器 provider 接了已移除的 oscal v1 provider(lazy resolve,踩到才炸)。改 GRC 任何 read/list 前先 grep # FR-038 2A dark 標記 + 確認 grc→oscal provider 引用都有定義for p in $(grep -oE 'oscal_container\.[a-z_]+' di_containers/grc/grc_containers.py|sort -u); do ...)。
  2. RLS 不是 projects 列表的過濾機制compliance.projects 的 RLS relrowsecurity=f(沒啟用);列表過濾是 app 層 GRC visibility filter(user_id/is_admin + participant)。別再往 RLS/tenant context 鑽(我繞了一大圈才發現)。
  3. data.id 在 FE 專案列表 = project UID(serializer id = obj.uid),不是整數 id。批次刪除送 data.id 即 uid。
  4. sandbox 無法 create_app() 完整 boot(jedi_issue 要 gitlab/github token + dependency_injector deepcopy quirk,pre-existing,stash 我的改動後 baseline 一樣炸)。驗 boot 要在真實環境;sandbox 只能 import smoke + 單元測試。

§3 下一棒主要工作 + 風險

工作 A:commit Task 8b(小,先做)

4 個 entangled 檔 + 1 test。先跟 user 確認他 in-flight 的改動要不要一起 commit,再決定怎麼切(顯式 git add、禁 -am)。

工作 B:FE round-flow 大遷移(大)

round-flow-engine-fe-plan.md Task 2-5。這是讓 round flow 在 UI 能跑的關鍵。風險:Banner prop rename ripple 到所有掛 Banner 的 view;需 round-scoped routing 地基(migration plan Step 0)。開工前讀 FE CLAUDE.md(646 行,跨 repo 鐵則)

工作 C:BE 收尾文件(等 user 下令)

  • design §4.1b 改 no-derivation(目前還寫 derivation)
  • changelog(本 arc 多主題 batch)
  • 把本 handoff 標 ✓ + 寫 SUMMARY
  • Notion 任務

§4 刻意 defer / lenient 的東西(標明復原依據,別當「不需要」)

項目 現況 復原依據
round_prep_tasks_done precondition lenient(一律 ok) prep job 是專案層級(_generate_prep_jobs),「哪些算本輪 prep」scoping 未定 → 待定後補 job-completion 檢查
all_poam_closed / all_controls_verdict_filled precondition lenient v2 POA&M/AR Banner 檢查 follow-up;close_round/finalize 自身仍強制
4 個直接轉換 route(launch-audit/start-auditing/finalize/close) 未退役(no-derivation 下不會壞,只 bypass BPMN) O2:跟 FE 遷移一起退,統一走 stage/advance
builtin「完整稽核流程(含審核)」+「內部自查流程」 deactivate(is_active=false) 含 review(D5 延後)/ 無 audit(不合 ap_authoring 拓撲)→ 補 review-stage / planning→end 形狀後 reactivate
專案列表 AP 進度統計(completion_rate/control 數/task 進度) 回 0/None v1 統計子查詢移除;待 v2 輪次層接上重算
design §4.1b derivation 段 文件寫 derivation,code 是 no-derivation 收尾時改文件對齊 code

§5 Migration 狀態(4 支,dev 已套,stg/poc 待遷移)

  • scripts/sql/2026-06-16-fr038-round-flow-binding.sql(round 兩欄 + module_frames 欄)
  • scripts/sql/2026-06-16-fr038-round-stage-seed.sql(ap_authoring stage + planning 收斂)
  • scripts/sql/2026-06-16-fr038-builtin-flow-canonical.sql(builtin BPMN)
  • scripts/sql/2026-06-16-fr038-poam-to-end.sql(poam→End)

⚠️ schema_migrations 欄位是 filename/note(不是 version,我踩過)。套法:psql -U cmmgr -p 25432 -d <db> --single-transaction -v ON_ERROR_STOP=1 -f <檔>


§6 Pre-flight Command(下個 session 開工必跑)

cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git branch --show-current          # 應為 feature/oscal-refactor,不對停下問 user
git log --oneline -6 | cat         # 對照 §1 commit 清單
git status --short | grep -vE '^\?\? docs/'   # 確認 Task 8b 4 檔 + test 仍在 working tree
# 取 DB 密碼(cmmgr/cm_app 同組,查 .env DB_SECRET)
PW=$(python3 -c "import json;[print(json.loads(l.split('=',1)[1].strip().strip(chr(34))).get('rds_master_password','')) for l in open('.env') if l.startswith('DB_SECRET=')]")
# 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 -m pytest test/test_oscal_stage_handlers.py test/test_create_round_flow_binding.py test/test_project_start_creates_first_round.py -q

§7 Verify 本期成果(DB,下個 session 確認沒走回頭路)

-- 建專案有建第一輪 + 綁範本(D6 + create_round 綁定)
SELECT round_no, round_type, status, flow_template_snapshot_uid IS NOT NULL AS has_flow, workflow_execution_uid IS NOT NULL AS has_wf
  FROM compliance.project_audit_rounds ORDER BY id DESC LIMIT 3;
-- canonical stage 鏈
SELECT code, complete_handler_key, precondition_key, allowed_successors FROM compliance.stage_objects WHERE code IN ('planning','ap_authoring','audit','poam') ORDER BY sort;
-- 列表只回未刪除(27 筆);軟刪 175 筆
SELECT count(*) FILTER (WHERE pe.deleted_at IS NULL) non_deleted, count(*) FILTER (WHERE pe.deleted_at IS NOT NULL) soft_deleted
  FROM compliance.projects p LEFT JOIN compliance.project_extensions pe ON pe.project_id=p.id;

直接打 API 驗(dev turnstile 收 dummy):POST /api/1.0/login {"username":"blsadmin","password":"<查 .env / reference_dev_login>","cf_turnstile_token":"dummy","provider":"password"} → 取 access_token → POST /api/1.0/grc/projects/listAuthorization: Bearer + X-Tenant-Id:102 + X-Org-Unit-Id:98

§8 行為規範提醒(適用本 arc)

  • 不切 branchpush 等 user 明示;跨 repo 各自 commit、顯式 git add 禁 -am
  • 改 BE service/DI → 提醒 user 重啟 BE(無 hot reload);服務都 user 自己起
  • 動 FE 前先讀 FE CLAUDE.md(646 行)
  • 改套件先走 poetry path dep(本期沒動套件)
  • 不晶晶體;plan 假設先 verify 才開工
  • 收尾類動作等 user 明確下令(changelog/SUMMARY/design §4.1b 同步/Notion)

§9 不在本期 scope(別順手做)

  • v2 輪次層的 AP 進度統計重算(列表那些 0/None)
  • review stage / 含審核 builtin 復原
  • 4 直接轉換 route 退役(跟 FE 遷移一起)
  • prep-task gate 真實檢查
  • stg/poc migration(user 排程)

§10 給 fresh session 的超短 prompt

讀 docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-16-round-flow-reintegration-handoff.md,
先讀「🧭 原始需求 WHY」+ §0 自檢 4 問答得出再開工。本 arc:FR-026 流程引擎已 retarget 到 v2
audit_round(BE Tasks 1-10 done + committed);GRC 列表/刪除已修。剩:①commit Task 8b(4 entangled
檔,先問 user in-flight 改動)②FE round-flow 大遷移(Banner→round + AP 填寫畫面,FE 還全是 AP-centric)。
跑 §6 pre-flight + §7 verify 確認現況,不切 branch、push 等 user。