FR-051 實作計畫 — 規劃階段推進前置檢核

docs/features/FR-051-2607-planning-advance-prechecks/design.md。 跨 repo:BE(compliance-manager-be)+ FE(compliance-manager-fe)。E2E 後補(Phase 4 test-plan)。 只規劃不實作——本檔為 plan,user approve 後才進 Phase 5。


§1

CLAUDE.md 規範檢查(全 plan 適用)


§2

Step 0 — 前置查證(動手前必做,design §8)

BE

  1. 抽 1~2 份真實主流程模板 XML(DEV DB workflow_templates.xml 或資源庫),確認 userTask main_role 值域 == ParticipantRole 值(manager/auditor/reviewer/viewer)。
    • 若不符(存 stage 角色碼)→ 在 checker 加 normalize map,並在此 plan 補一列。
  2. advance_stage line 366-378 halt 分支,確認 handler_result(含新增 reasons)原樣回 FE,無 schema 白名單過濾。
  3. 確認 count_incomplete_prep_jobs 的「本輪 vs 專案」scope(design §8.2)——首輪按專案全部 in-scope prep-job,多輪列 follow-up。

輸出:Step 0 結論寫一段到本檔末「Step 0 查證結果」,再開工。


§3

BE 任務(compliance-manager-be)

Task B1 — domain count 方法(檢核 A 資料源)〔≤0.5d〕

  • infra/grc/repository/grc_project_repo_impl.py:新增 count_incomplete_prep_jobs(project_uid) -> int,reuse get_ap_dashboard 的 JOIN,COUNT(*) FILTER (WHERE je.status != COMPLETED AND je.type=USER)
  • domain/grc/service/grc_project_domain_service.py + repo interface:加對應 abstract + 委派。
  • :pytest 直接對 DEV 種子專案跑,比對 get_ap_dashboardnot_started+in_progress 一致。

Task B2 — PlanningReadinessChecker(新檔)〔0.5d〕

  • app/grc/service/planning_readiness_checker.py
    class PlanningReadinessChecker:
        def __init__(self, grc_project_domain_service, project_participant_domain_service,
                     audit_round_domain_service, workflow_execution_domain_service): ...
        def check(self, round_uid, project_uid) -> list[dict]:
            """回 [{code, context}];caller 須在 @transaction scope。fail-open:任一子檢查
            取不到資料 → skip 該項 + log warning,不阻推進。"""
    • 檢核 A:n = count_incomplete_prep_jobs(project_uid)n>0 → append {"code":"planning_prep_jobs_incomplete","context":{"count":n}}
    • 檢核 B:取本輪主流程 XML(audit_round → workflow_execution.workflow_template_xml)→ BpmnUtils(xml).jobsmain_role(split ",")去重 → required_rolesparticipant_roles = {p.role for p in participants}missing = required - participant;非空 → append {"code":"planning_roles_unassigned","context":{"roles":sorted(missing)}}
  • DIdi_containers/ grc container 註冊 checker(注入上述 4 domain service),注入進 LaunchAuditOnCompleteHandler
  • :unit test 三情境(都通過 / 只缺 job / 只缺角色 / 兩者皆缺)。

Task B3 — 掛進 LaunchAuditOnCompleteHandler〔0.25d〕

  • app/grc/service/oscal_stage_handlers.py::LaunchAuditOnCompleteHandler
    • __init__ 多注入 planning_readiness_checker
    • executeforce = bool((ctx or {}).get("force"))if not force: warnings = checker.check(round_uid, project_uid); if warnings: return {"warning":"planning_readiness","reasons":warnings};否則照舊 launch_audit
  • ⚠️ 守住 design §7 前提(review 調整 2)advance_stagenot forceprecondition 先於 handler 跑stage_advance_service.py:313-331)。planning 的 precondition RoundPrepTasksDoneCheck 必須維持 lenient(不能改嚴)——否則會在 handler warning 之前先硬擋,破壞「confirm 放行」語意。檢查全放 handler warning 層,precondition 一行都不動。
  • advance_stage 整合 test — force=false 有 warning → 回 {"advanced":false, handler_result.reasons=[...]}、BPMN 未推進;force=true → 正常推進。注意 test 必 patch logger(memory feedback_test_logger_patch_db_handler)。

Task B4 — BE 端 i18n / 常數〔0.1d〕

  • warning code 常數集中(如 common/enum 或 checker 內常數),確保與 FE i18n key 對得上。

§4

FE 任務(compliance-manager-fe)

Task F1 — useStageInfo 透出 reasons〔0.25d〕

  • src/composables/useStageInfo.js::advance:BE handler_result.reasons 透到回傳 result.reasons(現只透 needsConfirmation/warningMessage)。

Task F2 — FlowPhaseBanner 聚合彌窗〔0.5d〕

  • src/components/grc/FlowPhaseBanner.vue::doAdvanceneedsConfirmation 分支(review 確認 :442-450 存在、accept 帶 force:true 重打):
    • ⚠️ 必保留無-reasons fallback(review 調整 1):新 warning shape 是 {"warning":"planning_readiness","reasons":[...]}沒有 message key。F2 邏輯 =「result.reasons 有值 → 聚合渲染;否則走既有 warningMessage / default_message fallback」。不可只寫 reasons 分支——要保住其他 handler 未來回單句 warning(如既有 launch_audit incomplete_tasks 提示)的契約不被破壞。
    • reasons 有值時:逐條 map 成訊息(planning_prep_jobs_incompletet('lang.flow_engine.readiness.prep_jobs_incomplete', {count})planning_roles_unassignedt('...roles_unassigned', {roles: roles.map(r=>t('lang.grc_participants.role_'+r)).join('、')}))→ 換行/列表聚合成 message
    • ConfirmDialog header/accept/reject 用 lang.flow_engine.readiness.confirm_*
    • accept → doAdvance({...payload, force:true})(既有)。
  • 兩頁自動涵蓋:ProjectPlanningView / ProjectAuditorOverview 都嵌 Banner,零額外改。

Task F3 — i18n key(3 語)〔0.15d〕

  • src/config/locales/i18n/{en,tw,cn}/(對應 flow_engine 檔)新增:
    • flow_engine.readiness.prep_jobs_incomplete還有 {count} 個規劃階段任務尚未完成
    • flow_engine.readiness.roles_unassigned流程需要「{roles}」角色,但專案參與人員尚未指派,流程可能無法進行
    • flow_engine.readiness.confirm_header / confirm_accept仍要推進)/ confirm_reject取消
  • 確認 grc_participants.role_auditor / role_reviewer 等既有角色譯名存在(無則補)。

§5

執行順序 / 依賴

Step 0 查證
   ↓
B1(count) → B2(checker,依 B1) → B3(掛 handler,依 B2) → B4(BE i18n 常數)
   ↓(BE pytest 綠)
F1(useStageInfo) → F2(Banner 彌窗,依 F1) → F3(FE i18n)
   ↓
manual smoke(規劃頁 + 總覽頁,both)→ Phase 4 test-plan → E2E
  • B1~B4 可先於 FE;FE F1→F2 有序,F3 可並行。
  • 回滾:每 Task 一 commit,可獨立 revert(B3 是唯一改既有行為點,revert 即恢復無檢核)。

§6

完成標準


§7

Step 0 查證結果

2026-07-21 首腦 review 已預驗銷掉三項中的兩項,Step 0 剩一項必做。

✅ 已預驗銷掉(不用重做)

  • Step 0-2 handler_result 透出鏈 ✅:advance_stage halt 分支(stage_advance_service.py:366-378)原樣回整個 handler_result;route 序列化 StageAdvanceResponseSchema.handler_result = fields.Dict(allow_none=True)api/flow_engine/serializers/stage_advance.py:73),Dict 不濾 key → 新增 reasons 可原樣到 FE。
  • force 從 ctx 讀 ✅:advance_stage 開頭已有 ctx.setdefault("force", force),handler execute(ctx=ctx) 拿得到 → B3 寫法(force = bool(ctx.get("force")))成立。
  • FE 掛點 ✅:useStageInfo.js:67-73 / FlowPhaseBanner.vue:442-450 needsConfirmation 分支存在、accept 帶 force:true 重打 → F1/F2 落點正確。

✅ Step 0-1 main_role 值域(2026-07-21 已驗,live DEV DB)

  • seed 檔(scripts/sql/seeds/bpmn/builtin-*.bpmn):main_role{manager, auditor, reviewer}
  • live DEV DB compliance.flow_templates.bpmn_xml(含專案 cloned 模板,11/12 模板有 main_role)distinct 值域 + 筆數:manager×21 / reviewer×11 / auditor×6
  • 結論:值域 == ParticipantRole(manager/auditor/reviewer),全部合法。viewer 從不作 main_role(合理,觀察者不驅動階段)→ checker 不需 normalize map,B2 檢核 B 直接 set 相減即可。

⏳ Step 0-3(設計已定,無需驗):首輪按專案全量 prep-job,多輪情境列 follow-up。

Step 0 全數清空,可進 Phase 5 實作。