From 0a19a0e1c9520f7ed3531ee5312e96840eac72cd Mon Sep 17 00:00:00 2001 From: menghao Date: Tue, 11 Aug 2026 18:04:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=99=BA=E8=83=BD=E4=BD=93?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6=E6=B5=8B=E8=AF=95=E5=A4=B9=E5=85=B7?= =?UTF-8?q?=E7=AB=9E=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 让回执测试显式持有并等待父子执行通道释放 隔离异步后台任务与临时项目销毁之间的锁竞争 固定非阻塞 HTTP 夹具读取语义并保留有界超时 --- .../src/agent/generation/canvas_generation.rs | 3 + .../agent/runtime_driver/main_loop_tests.rs | 81 ++++++++++++++++--- .../autonomous_completion_contract_tests.rs | 4 + 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/generation/canvas_generation.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/generation/canvas_generation.rs index ffa9d901e..a87121eca 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/generation/canvas_generation.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/generation/canvas_generation.rs @@ -5707,6 +5707,9 @@ mod canvas_generation_tests { }; fn read_test_http_request(stream: &mut std::net::TcpStream) -> String { + stream + .set_nonblocking(false) + .expect("set request stream blocking"); stream .set_read_timeout(Some(Duration::from_secs(10))) .expect("set request read timeout"); 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 ceb4dc3d3..2f2fee623 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 @@ -254,11 +254,17 @@ fn persist_game_chat_main_asset_audit_and_route( .expect("persist game-chat main asset route"); } -fn game_chat_main_art_child_fixture( +fn game_chat_main_art_child_fixture_with_lane( root: &Path, target_agent_id: &str, missing_slots: &[&str], -) -> (AgentRuntimeState, AgentRuntimeState, String) { + hold_child_lane: bool, +) -> ( + AgentRuntimeState, + AgentRuntimeState, + String, + Option, +) { init_local_game_project_at(root, "game-chat-art-child", "单主美术委派") .expect("init game-chat art child project"); let root_run_id = "game-chat-main-art-root"; @@ -310,6 +316,11 @@ fn game_chat_main_art_child_fixture( ) .expect("persist game-chat main route"); + let child_lane = hold_child_lane.then(|| { + try_acquire_game_creator_agent_runtime_task_lock(root, target_agent_id) + .expect("acquire delegated art child lane") + .expect("delegated art child lane available") + }); let action_id = format!("game-chat-main-delegate-{target_agent_id}"); let observation = observe_agent_runtime_agent_delegate( root, @@ -335,11 +346,27 @@ fn game_chat_main_art_child_fixture( ) .expect("read delegated art child") .expect("delegated art child exists"); - ( - main, - agent_runtime_state_from_task_record(&child), - delegation_id, - ) + let mut child = agent_runtime_state_from_task_record(&child); + if child_lane.is_some() { + child.status = "running".to_string(); + child.phase = "planning".to_string(); + child.current_action = "执行受限美术子任务".to_string(); + append_game_creator_agent_runtime_task(root, &child) + .expect("persist running delegated art child"); + write_game_creator_agent_runtime_state(root, &child) + .expect("persist running delegated art runtime"); + } + (main, child, delegation_id, child_lane) +} + +fn game_chat_main_art_child_fixture( + root: &Path, + target_agent_id: &str, + missing_slots: &[&str], +) -> (AgentRuntimeState, AgentRuntimeState, String) { + let (main, child, delegation_id, _child_lane) = + game_chat_main_art_child_fixture_with_lane(root, target_agent_id, missing_slots, false); + (main, child, delegation_id) } fn game_chat_main_without_asset_audit_fixture(root: &Path) -> String { @@ -360,8 +387,12 @@ fn game_chat_main_without_asset_audit_fixture(root: &Path) -> String { async fn game_chat_main_agent_delegates_only_real_missing_art_and_limits_children_to_assets() { let temporary = tempfile::tempdir().expect("create game-chat art child root"); let root = temporary.path().join("project"); - let (_main, child, _delegation_id) = - game_chat_main_art_child_fixture(&root, "art-asset-plan", &["core-spritesheet"]); + let (_main, child, _delegation_id, _child_lane) = game_chat_main_art_child_fixture_with_lane( + &root, + "art-asset-plan", + &["core-spritesheet"], + true, + ); assert_eq!(child.agent_id, "art-asset-plan"); assert_eq!(child.source, "agent-delegate"); assert_eq!(child.parent_agent_id.as_deref(), Some("code-prototype")); @@ -684,8 +715,14 @@ fn game_chat_main_agent_rejects_unneeded_or_wrong_art_delegation() { fn game_chat_art_receipt_wakes_the_same_code_prototype_run() { let temporary = tempfile::tempdir().expect("create game-chat receipt wake root"); let root = temporary.path().join("project"); - let (mut main, mut child, delegation_id) = - game_chat_main_art_child_fixture(&root, "art-asset-plan", &["core-spritesheet"]); + let (mut main, mut child, delegation_id, child_lane) = + game_chat_main_art_child_fixture_with_lane( + &root, + "art-asset-plan", + &["core-spritesheet"], + true, + ); + let _child_lane = child_lane.expect("delegated art child lane is held"); main.status = "running".to_string(); main.phase = "waiting-for-delegate-receipts".to_string(); main.current_action = "等待临时美术 Agent 回执".to_string(); @@ -705,6 +742,9 @@ fn game_chat_art_receipt_wakes_the_same_code_prototype_run() { ) .expect("read completed art child") .expect("completed art child exists"); + let parent_lane = try_acquire_game_creator_agent_runtime_task_lock(&root, "code-prototype") + .expect("acquire waiting main-agent execution lane") + .expect("waiting main-agent execution lane is free"); publish_game_creator_agent_delegate_result( &root, &child_task, @@ -759,6 +799,7 @@ fn game_chat_art_receipt_wakes_the_same_code_prototype_run() { assert_eq!(duplicate.status, "failed", "{duplicate:?}"); assert!(duplicate.summary.contains("最多委派一次")); + drop(parent_lane); let waiting = read_latest_game_creator_agent_runtime_task_by_run_id( &root, "code-prototype", @@ -768,6 +809,19 @@ fn game_chat_art_receipt_wakes_the_same_code_prototype_run() { .expect("waiting main task exists"); let wake_started = wake_waiting_static_delegate_parent_run_at(&root, &waiting) .expect("wake same main run after art receipt"); + let release_deadline = std::time::Instant::now() + std::time::Duration::from_secs(5); + loop { + if game_creator_agent_runtime_task_lock_is_available(&root, "code-prototype") + .expect("probe resumed main-agent lane release") + { + break; + } + assert!( + std::time::Instant::now() < release_deadline, + "resumed main-agent lane did not release before fixture teardown" + ); + std::thread::sleep(std::time::Duration::from_millis(20)); + } let resumed = read_all_game_creator_agent_runtime_tasks(&game_creator_agent_runtime_task_path( &root, "code-prototype", @@ -783,7 +837,10 @@ fn game_chat_art_receipt_wakes_the_same_code_prototype_run() { #[test] fn autonomous_parent_waits_for_active_child_while_registered_derived_visuals_need_repair() { - let root = std::env::temp_dir().join(format!( + let temporary_root = std::env::temp_dir() + .canonicalize() + .expect("canonicalize system temporary directory"); + let root = temporary_root.join(format!( "genarrative-agent-main-loop-legacy-{}-{}", std::process::id(), std::time::SystemTime::now() diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion_contract_tests.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion_contract_tests.rs index ae2270cb0..4b5443051 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion_contract_tests.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion_contract_tests.rs @@ -1084,6 +1084,10 @@ fn game_chat_pure_continue_inherits_failed_root_semantics_and_manifest_progress( "继续完成继承的现有游戏目标", ) .expect("persist continued root Supervisor decision"); + let _code_prototype_lane = + try_acquire_game_creator_agent_runtime_task_lock(&root, "code-prototype") + .expect("acquire continued main-agent execution lane") + .expect("continued main-agent execution lane is free"); let scheduled = schedule_autonomous_game_build_ready_tasks_at( &root, GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,