FR-050 稽核輪次階段回退(Stage Rollback)— Design

緣由:user 2026-07-18 提出——目前專案流程階段推進後無法退回,實務上不切實際(稽核實務本來就迭代:啟動稽核後發現 scope 缺漏、送審後被要求重寫現況說明等)。需求 = 有條件的返回上一步 + 完整留痕狀態:五決策已拍板(§2),設計骨架落定;implementation plan 待開工時做前提查證後撰寫。 業界對照:Qualtrics / ServiceNow GRC / Archer 均有 stage regression(reopen / send back / return to draft),皆綁 justification 必填——本案形態與業界一致。

1. 為什麼要做

現行推進(app/flow_engine/service/stage_advance_service.py:185 advance_stage)一次做三件事:業務 handler 副作用(寫 OSCAL state)→ BPMN 節點完成(complete_main_workflow_job)→ 輪次 status 前進。三者皆只有前向路徑(狀態機由 DB CHECK 強制值域)。按錯 / 需求變更時唯一出路是整輪作廢重開專案,成本不成比例。

回退最大的反對理由「稽核有效性被質疑」由三條設計合力關掉:closed 不可退 + 不刪資料只作廢標記 + UI 可見時間軸——回退本身成為稽核軌跡的一部分,不是漏洞

2. 決策紀錄(user 2026-07-18 全數拍板)

# 決策 結論
1 回退範圍 只允許退回相鄰上一步;跨階段 = 連按多次,每次各留一筆記錄
2 closed 輪次 不可回退。已結案要重來走既有模型(開新輪次 / 新專案)
3 auditing 回退時已填 AR 保留 + 標記(不作廢重建;稽核員已填內容不丟失)
4 回退權限 manager only(比照 force=true 先例,stage_advance_service.py:297
5 記錄形式 UI 可見的「階段歷程」時間軸(誰 / 何時 / 何為 / 為何退),非僅 system_logs

3. 回退邊界與副作用處置(開工前提查證要逐項 live 對)

推進的逆向不是均勻的,每個邊界成本不同(以下座標 2026-07-18 grep 核實):

回退邊界 推進時副作用(handler) 難度 副作用處置原則
audit_planning → planning launch_auditaudit_round_app_service.py:352):snapshot living SSP + clone 程序書池 + 建 AP 草稿 frozen SSP / AP 草稿作廢標記(superseded,不物理刪);living SSP 本未動,退回後重推會產生新 snapshot
auditing → audit_planning start_auditing:建 AR 全量矩陣 AR 已填內容保留 + 標記(決策 3);重推 start_auditing 時的「既有 AR 矩陣」處置為 plan 前提查證重點
remediation → auditing finalize_audit:AR 定版 + not_met 生 POA&M POA&M 可能已在整改——作廢標記 + 已有整改紀錄保留;此邊界是否 v1 就開放,plan 時再裁
closed → * close_round 禁止 決策 2

鐵則:回退絕不物理刪資料,一律「作廢/superseded 標記 + 留痕」——與 FR-049 快照哲學同源(歷史不漂移)。

4. 設計骨架

4.1 BE

  • 新 endpointPOST /project/<uid>/audit-round/<uid>/stage/rollback,body 必填 reason(比照 review reject 必填 comment 的驗證,stage_advance_service.py:264)。
  • StageRollbackService(flow_engine app service,鏡像 StageAdvanceService 結構):manager 檢查 → 解析當前 stage → 查上一 stage → dispatch rollback handler(stage_registry 加 rollback_handler_key,僅對允許回退的邊界註冊)→ BPMN 回退 → 輪次 status 回寫 → 落歷程記錄。
  • BPMN 回退是唯一技術未知數ReviewDecisionHandler 註解提及「reject 走 BPMN reverse flow」但 builtin 未接——jedi-flow-engine 是否支援重開 COMPLETED job 為 plan 前提查證第一項;若不支援,fallback=不倒退 BPMN 節點、以「重建當前 stage 的 job execution」語意實作(對 UI 等價)。
  • 稽核埋點:鏡像 [AUDIT:STAGE_ADVANCE] pattern(requested / denied / completed),新 event_code STAGE_ROLLBACK_* 進 system_logs。

4.2 階段歷程(決策 5 的資料源)

  • 新表 compliance.round_stage_transitions(round_id、from_stage、to_stage、direction(advance/rollback)、operator、reason(rollback 必填)、created_at)——advance 也同步落(歷程要完整,不能只記 rollback)。
  • 既有 advance 路徑補一行落表(非破壞性 delta)。
  • 查詢 endpoint:GET .../stage/transitions 回時間軸。

4.3 FE

  • FlowPhaseBanner 加「返回上一階段」入口(manager only 可見)→ dialog 必填原因 → 呼叫 rollback。
  • 階段時間軸元件(banner 展開或獨立 tab):垂直 timeline,advance/rollback 雙色,rollback 顯示 reason。

5. Not in scope

  • 跨階段一鍵回退(= 連按多次)
  • closed 輪次重開(決策 2)
  • 回退的雙人複核(v1 manager 單人;未來若客戶要求四眼原則再開)
  • remediation → auditing 邊界是否 v1 開放,plan 時裁決

6. 驗收情境(草案,plan 時細化)

  1. planning 推進到 audit_planning 後回退 → frozen SSP/AP 標 superseded、輪次回 planning、重推產生新 snapshot、時間軸兩筆(advance + rollback 含 reason)。
  2. auditing 回退 → 已填 AR 保留可見(帶標記)、回 audit_planning 可改 AP、重推後稽核員接續。
  3. 非 manager 按回退 → 403;reason 空 → 400。
  4. closed 輪次無回退入口(API 打也擋)。
  5. 時間軸完整呈現多次往返(誰/何時/方向/原因),順序正確。
  6. 回退不物理刪任何資料(DB 對帳:僅新增標記欄/記錄,零 DELETE)。