緣由:user 2026-07-18 提出——目前專案流程階段推進後無法退回,實務上不切實際(稽核實務本來就迭代:啟動稽核後發現 scope 缺漏、送審後被要求重寫現況說明等)。需求 = 有條件的返回上一步 + 完整留痕。 狀態:五決策已拍板(§2),設計骨架落定;implementation plan 待開工時做前提查證後撰寫。 業界對照:Qualtrics / ServiceNow GRC / Archer 均有 stage regression(reopen / send back / return to draft),皆綁 justification 必填——本案形態與業界一致。
現行推進(app/flow_engine/service/stage_advance_service.py:185 advance_stage)一次做三件事:業務 handler 副作用(寫 OSCAL state)→ BPMN 節點完成(complete_main_workflow_job)→ 輪次 status 前進。三者皆只有前向路徑(狀態機由 DB CHECK 強制值域)。按錯 / 需求變更時唯一出路是整輪作廢重開專案,成本不成比例。
回退最大的反對理由「稽核有效性被質疑」由三條設計合力關掉:closed 不可退 + 不刪資料只作廢標記 + UI 可見時間軸——回退本身成為稽核軌跡的一部分,不是漏洞。
| # | 決策 | 結論 |
|---|---|---|
| 1 | 回退範圍 | 只允許退回相鄰上一步;跨階段 = 連按多次,每次各留一筆記錄 |
| 2 | closed 輪次 | 不可回退。已結案要重來走既有模型(開新輪次 / 新專案) |
| 3 | auditing 回退時已填 AR | 保留 + 標記(不作廢重建;稽核員已填內容不丟失) |
| 4 | 回退權限 | manager only(比照 force=true 先例,stage_advance_service.py:297) |
| 5 | 記錄形式 | UI 可見的「階段歷程」時間軸(誰 / 何時 / 何為 / 為何退),非僅 system_logs |
推進的逆向不是均勻的,每個邊界成本不同(以下座標 2026-07-18 grep 核實):
| 回退邊界 | 推進時副作用(handler) | 難度 | 副作用處置原則 |
|---|---|---|---|
| audit_planning → planning | launch_audit(audit_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 快照哲學同源(歷史不漂移)。
POST /project/<uid>/audit-round/<uid>/stage/rollback,body 必填 reason(比照 review reject 必填 comment 的驗證,stage_advance_service.py:264)。rollback_handler_key,僅對允許回退的邊界註冊)→ BPMN 回退 → 輪次 status 回寫 → 落歷程記錄。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。compliance.round_stage_transitions(round_id、from_stage、to_stage、direction(advance/rollback)、operator、reason(rollback 必填)、created_at)——advance 也同步落(歷程要完整,不能只記 rollback)。GET .../stage/transitions 回時間軸。