From c89a13e682c42257d06ea15f714b787eb14cd142 Mon Sep 17 00:00:00 2001 From: Linghong Date: Mon, 17 Aug 2026 03:28:24 +0000 Subject: [PATCH] =?UTF-8?q?=E6=8A=8A=20Fast=20GDD=20=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E7=AD=89=E5=BE=85=E7=9A=84=E5=8F=AF=E6=81=A2=E5=A4=8D=E5=88=A4?= =?UTF-8?q?=E6=8D=AE=E9=92=89=E6=88=90=E4=B8=8D=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 判据在 task record 的 status 而不是 phase:审批等待只改 phase、保留 status=running,恢复扫描仍会拉起;真正的澄清等待才会把 status 一并写成 waiting-for-user-input。一旦有人把审批等待也写成后者,审批命令的通用 wake 会静默变成 no-op,用户点完批准/修改/退回不会有任何东西继续跑,且无报错。 Co-Authored-By: Claude Opus 5 --- .../src/agent/runtime_driver/recovery_scan.rs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs index 155bf3571..4827b3228 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs @@ -1770,3 +1770,76 @@ mod orphaned_external_generation_recovery_tests { ); } } + +#[cfg(test)] +mod plan_gdd_approval_wait_recovery_tests { + use super::*; + + /// Fast GDD 审批等待期,根 Supervisor 到底还能不能被恢复扫描拉起。 + /// + /// 判据不在 `phase` 上:`read_recoverable_game_creator_agent_runtime_task` 按 + /// task record 的 **status** 分类,而 record 的 status 由 + /// `game_creator_agent_runtime_task_status` 从 `state.status` 推导。审批等待 + /// (`main_loop` 的 `runtime.plan_gdd` 分支)只改 phase、保留 `status="running"`; + /// 真正的澄清等待(`action_projection`)才会把 status 一并写成 + /// `waiting-for-user-input`,那才是恢复扫描要让路的外部输入等待。 + /// + /// 这个区别决定了审批决定之后还有没有生产路径驱动父 run:审批命令只调通用 + /// wake,一旦有人把审批等待也写成 `status="waiting-for-user-input"`,通用 wake + /// 会静默变成 no-op,用户点了批准/修改/退回之后不会有任何东西继续跑。这条测试 + /// 把这个区别钉成不变量。 + #[test] + fn plan_gdd_approval_wait_stays_recoverable_while_real_user_input_wait_does_not() { + let temporary = crate::tests::canonical_test_tempdir("plan-gdd-approval-wait-recovery-"); + let root = temporary.path(); + init_local_game_project_at(root, "plan-gdd-approval-wait", "Fast GDD 审批等待恢复判定") + .expect("init project"); + let mut runtime = start_game_creator_agent_runtime_task_at( + root, + GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, + "推进立项策划", + "plan-gdd-approval-wait-run", + "agent-ready-task-scheduler", + "准备推进立项策划", + vec!["推进立项策划".to_string()], + ) + .expect("start plan-root supervisor runtime"); + + // main_loop.rs 的 Fast GDD 审批等待形状。 + runtime.status = "running".to_string(); + runtime.phase = "waiting-for-user-input".to_string(); + runtime.current_action = "等待 Fast GDD 审批决定".to_string(); + runtime.waiting_on = "用户在审批卡选择批准、修改或退回".to_string(); + runtime.updated_at = unix_timestamp(); + append_game_creator_agent_runtime_task(root, &runtime).expect("append approval wait task"); + refresh_game_creator_agent_runtime_task_queue(root, &mut runtime) + .expect("refresh approval wait task queue"); + write_game_creator_agent_runtime_state(root, &runtime).expect("write approval wait state"); + + let task = + read_recoverable_runnable_game_creator_agent_runtime_task(root, &runtime.agent_id) + .expect("read approval wait task") + .expect("审批等待态必须仍可被恢复扫描拉起,否则审批决定后没有生产路径驱动父 run"); + assert_eq!(task.status, "running"); + assert_eq!(task.phase, "waiting-for-user-input"); + assert!(has_recoverable_game_creator_agent_background_tasks_at(root) + .expect("public preflight must agree with the recoverable task read")); + + // 对照组:真正的用户输入等待把 status 一并写成 waiting-for-user-input。 + runtime.status = "waiting-for-user-input".to_string(); + runtime.updated_at = unix_timestamp(); + append_game_creator_agent_runtime_task(root, &runtime) + .expect("append user input wait task"); + refresh_game_creator_agent_runtime_task_queue(root, &mut runtime) + .expect("refresh user input wait task queue"); + write_game_creator_agent_runtime_state(root, &runtime) + .expect("write user input wait state"); + + assert!( + read_recoverable_runnable_game_creator_agent_runtime_task(root, &runtime.agent_id) + .expect("read user input wait task") + .is_none(), + "status 写成 waiting-for-user-input 才是恢复扫描让路的外部输入等待" + ); + } +}