diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs index 8c8c4f998..d2c62d7d5 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs @@ -1609,6 +1609,24 @@ mod tests { let root = temporary.path().join("project"); init_local_game_project_at(&root, "manifest-dag-policy", "测试正式任务图等待") .expect("init manifest DAG policy project"); + let session_id = resolve_agent_conversation_session_id_at( + &root, + GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, + None, + true, + ) + .expect("resolve manifest DAG Supervisor session"); + append_unique_game_creator_agent_runtime_pending_task( + &root, + GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, + &session_id, + "测试正式任务图等待", + "manifest-dag-policy-run", + AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE, + Some(AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD), + None, + ) + .expect("queue trusted autonomous root task"); assert!(!autonomous_manifest_dag_in_progress_at(&root).expect("read pending DAG")); update_manifest_task_status_at( diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs index 98dfec582..6981f58f4 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs @@ -937,14 +937,16 @@ async fn missing_completed_visual_asset_fails_same_child_without_retry() { "missing visual output must fail the current logical task" ); - assert!(schedule_autonomous_game_build_ready_tasks_at( + let scheduled_after_failure = schedule_autonomous_game_build_ready_tasks_at( &root, GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, PARENT_RUN_ID, 1, ) - .expect("repeat scheduling after visual failure") - .is_empty()); + .expect("schedule other ready work after visual failure"); + assert!(scheduled_after_failure + .iter() + .all(|scheduled| scheduled.state.agent_id != CHILD_ID)); let records = read_all_game_creator_agent_runtime_tasks(&game_creator_agent_runtime_task_path( &root, CHILD_ID, )) diff --git a/apps/ai-game-creator-shell/src-tauri/src/tests/collaboration/supervisor_planning.rs b/apps/ai-game-creator-shell/src-tauri/src/tests/collaboration/supervisor_planning.rs index 641ce4acf..f2b2e0f69 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/tests/collaboration/supervisor_planning.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/tests/collaboration/supervisor_planning.rs @@ -300,55 +300,36 @@ async fn supervisor_autonomous_durable_batch_rejects_invalid_responsibilities_wi let AgentRuntimeProviderActionBatchPreparation::Ready(valid_batch) = preparation else { panic!("valid autonomous responsibilities must form a ready durable batch"); }; - assert_eq!(valid_batch.actions.len(), 2); + assert_eq!(valid_batch.actions.len(), 3); assert!(valid_batch.collaboration_contract.is_some()); let valid_batch_id = valid_batch.batch_id.clone(); - let mut quality_not_read_only = autonomous_initial_responsibility_actions_for_test( - "创建或修改 game/index.html,完成可直接试玩的游戏实现并执行静态验证。", - &["game/index.html"], - "直接修改 game/index.html,修复试玩阻塞并重新验证。", - &[], - ); - quality_not_read_only[1].input["acceptanceCriteria"] = serde_json::json!([ - "直接写入 game/index.html 修复试玩问题", - "修改后执行静态验证并交付新 revision" - ]); + let mut design_not_read_only = valid_autonomous_initial_responsibility_actions_for_test(); + design_not_read_only[0].input["task"] = serde_json::json!("直接修改项目并完成首轮策划实现。"); + design_not_read_only[0].input["acceptanceCriteria"] = serde_json::json!(["直接修改项目文件"]); + let mut design_with_artifacts = valid_autonomous_initial_responsibility_actions_for_test(); + design_with_artifacts[0].input["expectedArtifacts"] = + serde_json::json!(["game/game_design.md"]); + let mut art_missing_spec = valid_autonomous_initial_responsibility_actions_for_test(); + art_missing_spec[1].input["expectedArtifacts"] = serde_json::json!(["assets/art-preview.png"]); + let mut code_with_artifacts = valid_autonomous_initial_responsibility_actions_for_test(); + code_with_artifacts[2].input["expectedArtifacts"] = serde_json::json!(["game/index.html"]); let invalid_cases = vec![ ( - "code-missing-game-index", - "game/index.html", - autonomous_initial_responsibility_actions_for_test( - "创建可直接试玩的游戏实现并执行静态验证。", - &["game/main.js"], - "只读验收 game/index.html 的可玩性;不要修改任何项目文件。", - &[], - ), + "design-not-read-only", + "design-director", + design_not_read_only, ), ( - "code-read-only", - "code-prototype", - autonomous_initial_responsibility_actions_for_test( - "只读检查 game/index.html,不要修改任何项目文件。", - &["game/index.html"], - "只读验收 game/index.html 的可玩性;不要修改任何项目文件。", - &[], - ), - ), - ( - "quality-not-read-only", - "quality-review", - quality_not_read_only, - ), - ( - "quality-with-artifacts", + "design-with-artifacts", "expectedArtifacts", - autonomous_initial_responsibility_actions_for_test( - "创建或修改 game/index.html,完成可直接试玩的游戏实现并执行静态验证。", - &["game/index.html"], - "只读验收 game/index.html 的可玩性;不要修改任何项目文件。", - &["game/index.html"], - ), + design_with_artifacts, + ), + ("art-missing-spec", "assets/art-spec.png", art_missing_spec), + ( + "code-with-artifacts", + "expectedArtifacts", + code_with_artifacts, ), ]; for (case_name, expected_error, actions) in invalid_cases { @@ -389,12 +370,7 @@ async fn supervisor_autonomous_durable_batch_rejects_invalid_responsibilities_wi rewrite_autonomous_responsibility_batch_actions_for_test( &root, &mut legacy_v2_batch, - autonomous_initial_responsibility_actions_for_test( - "创建或修改 game/index.html,完成可直接试玩的游戏实现并执行静态验证。", - &["game/index.html"], - "直接修改 game/index.html,修复试玩阻塞并重新验证。", - &[], - ), + valid_autonomous_initial_responsibility_actions_for_test(), ); let legacy_v2_schema = "game-creator-provider-action-batch.v2"; legacy_v2_batch.schema_version = legacy_v2_schema.to_string(); @@ -600,68 +576,37 @@ async fn autonomous_game_build_repairs_supervisor_failed_playtest_stall_into_mut fs::remove_dir_all(root).ok(); } -fn autonomous_initial_responsibility_actions_for_test( - code_task: &str, - code_artifacts: &[&str], - quality_task: &str, - quality_artifacts: &[&str], -) -> Vec { +fn valid_autonomous_initial_responsibility_actions_for_test() -> Vec { vec![ - AgentRuntimeToolAction { - tool: "agent.delegate".to_string(), - reason: Some("委派程序 Agent 形成可玩入口".to_string()), - input: serde_json::json!({ - "agentId": "code-prototype", - "task": code_task, - "acceptanceCriteria": [ - "game/index.html 必须形成可直接试玩的完整入口", - "程序交付必须完成当前 revision 的静态验证" - ], - "expectedArtifacts": code_artifacts, - "repairOfDelegationId": null, - "runId": null - }), - }, - AgentRuntimeToolAction { - tool: "agent.delegate".to_string(), - reason: Some("委派质量 Agent 独立只读验收".to_string()), - input: serde_json::json!({ - "agentId": "quality-review", - "task": quality_task, - "acceptanceCriteria": [ - "只读核对可玩性、交互闭环和阻塞问题", - "返回可追溯的验收结论,不修改项目文件" - ], - "expectedArtifacts": quality_artifacts, - "repairOfDelegationId": null, - "runId": null - }), - }, + autonomous_initial_leader_responsibility_action_for_test("design-director", &[]), + autonomous_initial_leader_responsibility_action_for_test( + "art-director", + &["assets/art-spec.png"], + ), + autonomous_initial_leader_responsibility_action_for_test("code-director", &[]), ] } -fn valid_autonomous_initial_responsibility_actions_for_test() -> Vec { - autonomous_initial_responsibility_actions_for_test( - "创建或修改 game/index.html,完成可直接试玩的游戏实现并执行静态验证。", - &["game/index.html"], - "只读验收 game/index.html 的可玩性与交互闭环;不要修改任何项目文件。", - &[], - ) -} - -fn autonomous_art_director_responsibility_action_for_test( +fn autonomous_initial_leader_responsibility_action_for_test( + agent_id: &str, expected_artifacts: &[&str], ) -> AgentRuntimeToolAction { + let read_only = matches!(agent_id, "design-director" | "code-director"); AgentRuntimeToolAction { tool: "agent.delegate".to_string(), - reason: Some("委派美术总监生成统一视觉规范图".to_string()), + reason: Some("建立首批 Leader 专业规划".to_string()), input: serde_json::json!({ - "agentId": "art-director", - "task": "生成项目统一视觉规范图并写入项目资产目录。", - "acceptanceCriteria": [ - "使用画布生成接口产出后续 UI 与图集共用的规范图", - "生成结果必须登记为项目本地 icon-spec 资产" - ], + "agentId": agent_id, + "task": if read_only { + format!("由 {agent_id} 只读完成首轮专业规划,不得修改项目") + } else { + "生成首轮统一视觉规范图供后续专业 Agent 使用".to_string() + }, + "acceptanceCriteria": if read_only { + serde_json::json!(["只读输出专业规划,不得修改项目文件"]) + } else { + serde_json::json!(["生成并登记统一视觉规范图"]) + }, "expectedArtifacts": expected_artifacts, "repairOfDelegationId": null, "runId": null @@ -713,9 +658,7 @@ async fn supervisor_autonomous_initial_art_director_requires_canonical_art_spec_ ) .expect("start autonomous Supervisor runtime"); let mut actions = valid_autonomous_initial_responsibility_actions_for_test(); - actions.push(autonomous_art_director_responsibility_action_for_test(&[ - "assets/art-preview.png", - ])); + actions[1].input["expectedArtifacts"] = serde_json::json!(["assets/art-preview.png"]); let plan = supervisor_collaboration_plan_for_test(actions); let revision = read_game_creator_agent_runtime_project_revision(&root) .expect("read art contract project revision"); @@ -834,40 +777,21 @@ async fn supervisor_autonomous_initial_responsibilities_reject_invalid_plans_bef ) .expect("start autonomous Supervisor runtime"); - let code_missing_game_index = autonomous_initial_responsibility_actions_for_test( - "创建可直接试玩的游戏实现并执行静态验证。", - &["game/main.js"], - "只读验收 game/index.html 的可玩性;不要修改任何项目文件。", - &[], - ); - let code_read_only = autonomous_initial_responsibility_actions_for_test( - "只读检查 game/index.html,不要修改任何项目文件。", - &["game/index.html"], - "只读验收 game/index.html 的可玩性;不要修改任何项目文件。", - &[], - ); - let mut quality_not_read_only = autonomous_initial_responsibility_actions_for_test( - "创建或修改 game/index.html,完成可直接试玩的游戏实现并执行静态验证。", - &["game/index.html"], - "直接修改 game/index.html,修复试玩阻塞并重新验证。", - &[], - ); - quality_not_read_only[1].input["acceptanceCriteria"] = serde_json::json!([ - "直接写入 game/index.html 修复试玩问题", - "修改后执行静态验证并交付新 revision" - ]); - let quality_with_artifacts = autonomous_initial_responsibility_actions_for_test( - "创建或修改 game/index.html,完成可直接试玩的游戏实现并执行静态验证。", - &["game/index.html"], - "只读验收 game/index.html 的可玩性;不要修改任何项目文件。", - &["game/index.html"], - ); let valid = valid_autonomous_initial_responsibility_actions_for_test(); + let mut missing_design = valid.clone(); + missing_design.remove(0); + let mut design_not_read_only = valid.clone(); + design_not_read_only[0].input["task"] = serde_json::json!("直接修改项目完成策划实现"); + design_not_read_only[0].input["acceptanceCriteria"] = serde_json::json!(["直接修改项目文件"]); + let mut art_missing_spec = valid.clone(); + art_missing_spec[1].input["expectedArtifacts"] = serde_json::json!(["assets/art-preview.png"]); + let mut code_with_artifacts = valid.clone(); + code_with_artifacts[2].input["expectedArtifacts"] = serde_json::json!(["game/index.html"]); let responses = [ - ("code-missing-game-index", &code_missing_game_index), - ("code-read-only", &code_read_only), - ("quality-not-read-only", &quality_not_read_only), - ("quality-with-artifacts", &quality_with_artifacts), + ("missing-design", &missing_design), + ("design-not-read-only", &design_not_read_only), + ("art-missing-spec", &art_missing_spec), + ("code-with-artifacts", &code_with_artifacts), ("valid-responsibilities", &valid), ] .into_iter() @@ -904,7 +828,13 @@ async fn supervisor_autonomous_initial_responsibilities_reject_invalid_plans_bef .await .expect("repair invalid initial responsibilities") .expect("valid initial responsibility plan"); - assert_eq!(plan.actions, valid); + let mut expected_valid = valid.clone(); + expected_valid.sort_by(|left, right| { + left.input["agentId"] + .as_str() + .cmp(&right.input["agentId"].as_str()) + }); + assert_eq!(plan.actions, expected_valid); assert!(plan.response.is_empty()); let requests = (0..5) diff --git a/apps/ai-game-creator-shell/src-tauri/src/tests/provider.rs b/apps/ai-game-creator-shell/src-tauri/src/tests/provider.rs index 4f098396f..8c10a9fb5 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/tests/provider.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/tests/provider.rs @@ -4922,24 +4922,36 @@ async fn provider_transient_retry_autonomous_upstream_400_retries_with_bounded_b .expect("project init"); let delegate_function = native_runtime_function_name("agent.delegate").expect("delegate function"); - let code_arguments = serde_json::json!({ - "reason": "委派可玩原型实现", + let design_arguments = serde_json::json!({ + "reason": "委派策划 Leader", "input": { - "agentId": "code-prototype", - "task": "实现可直接试玩的游戏原型", - "acceptanceCriteria": ["项目能够启动并完成最小玩法闭环"], - "expectedArtifacts": ["game/index.html"], + "agentId": "design-director", + "task": "只读拆解首轮玩法目标和专业分工,不得修改项目", + "acceptanceCriteria": ["只读输出策划规划,不得修改项目文件"], + "expectedArtifacts": [], "repairOfDelegationId": null, "runId": null } }) .to_string(); - let quality_arguments = serde_json::json!({ - "reason": "委派独立质量评审", + let art_arguments = serde_json::json!({ + "reason": "委派美术 Leader", "input": { - "agentId": "quality-review", - "task": "只读评审可玩性与闯关闭环,不要修改任何项目文件", - "acceptanceCriteria": ["只读给出阻塞试玩的问题和验收结论"], + "agentId": "art-director", + "task": "生成首轮统一视觉规范图供后续专业 Agent 使用", + "acceptanceCriteria": ["生成并登记统一视觉规范图"], + "expectedArtifacts": ["assets/art-spec.png"], + "repairOfDelegationId": null, + "runId": null + } + }) + .to_string(); + let code_arguments = serde_json::json!({ + "reason": "委派程序 Leader", + "input": { + "agentId": "code-director", + "task": "只读拆解首轮程序实现边界,不得修改项目", + "acceptanceCriteria": ["只读输出程序规划,不得修改项目文件"], "expectedArtifacts": [], "repairOfDelegationId": null, "runId": null @@ -4947,16 +4959,21 @@ async fn provider_transient_retry_autonomous_upstream_400_retries_with_bounded_b }) .to_string(); let recovered_response = native_agent_tool_plan_chat_response_with_calls(vec![ + ( + "call-autonomous-upstream-400-design", + delegate_function.as_str(), + design_arguments, + ), + ( + "call-autonomous-upstream-400-art", + delegate_function.as_str(), + art_arguments, + ), ( "call-autonomous-upstream-400-code", delegate_function.as_str(), code_arguments, ), - ( - "call-autonomous-upstream-400-quality", - delegate_function.as_str(), - quality_arguments, - ), ]); let (request_notice_sender, request_notice_receiver) = mpsc::channel(); let base_url = spawn_mock_llm_upstream_400_then_raw_response( @@ -5042,7 +5059,7 @@ async fn provider_transient_retry_autonomous_upstream_400_retries_with_bounded_b assert!(request_notice_receiver .recv_timeout(Duration::from_millis(100)) .is_err()); - assert_eq!(plan.actions.len(), 2); + assert_eq!(plan.actions.len(), 3); assert!(plan .actions .iter() diff --git a/apps/ai-game-creator-shell/src-tauri/src/tests/runtime_actions/action_execution.rs b/apps/ai-game-creator-shell/src-tauri/src/tests/runtime_actions/action_execution.rs index cd234fa8e..c138e990a 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/tests/runtime_actions/action_execution.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/tests/runtime_actions/action_execution.rs @@ -707,7 +707,7 @@ async fn background_agent_runtime_can_schedule_ready_manifest_tasks() { )); let scheduled = - schedule_game_creator_agent_ready_tasks_at(&root, 0).expect("schedule ready tasks"); + schedule_game_creator_agent_ready_tasks_at(&root, 1).expect("schedule one ready task"); assert_eq!(scheduled.len(), 1); assert_eq!(scheduled[0].state.agent_id, "design-director"); assert_eq!(scheduled[0].state.source, "agent-ready-task-scheduler");