diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs index d1dead65a..260c1a90f 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs @@ -1473,9 +1473,7 @@ fn bridge_write_file(root: &Path, arguments: &Value) -> Value { return Err("工具参数 content 不能包含 NUL".to_string()); } reject_command_output_wrapper(content)?; - // Direct 写入原本用零等待取锁:任何重叠都在 24-42ms 内直接被判成"别人正在写", - // 而 `file.write / file.patch / file.delete` 等写入口用的是约 10 秒有界等待。 - // 这是用户直接触发、失败即整轮无法落盘的项目写入通道,必须和其它写入口同语义: + // 这是用户直接触发、失败即整轮无法落盘的项目写入通道: // 短暂重叠排队等成功,只有预算耗尽才报出带持锁方身份的错误。 let _lock = acquire_game_creator_agent_runtime_project_write_lock_with_wait( root, @@ -1501,10 +1499,7 @@ fn bridge_write_file(root: &Path, arguments: &Value) -> Value { } /// 项目写锁的有界等待是同步轮询(2_000 × 5ms,最多约 10 秒)。handler 是 async, -/// 直接在 handler 里走完整条写路径会占住一个 tokio worker:争用窗口内同一轮并行写多个 -/// 文件时会有多个 worker 被占,而这条 bridge 与只读端点、UI 命令共享同一个 runtime, -/// 正是 Issue #318 现场"只读工具全部正常"这条诊断特征会被破坏的情形。 -/// 因此整条写路径挪进阻塞线程池,等待语义与错误文案都不变。 +/// 整条写路径必须放进阻塞线程池,避免同步等待占用 tokio worker。 async fn bridge_write_file_in_blocking_pool(root: PathBuf, arguments: Value) -> Value { let task_root = root.clone(); match tokio::task::spawn_blocking(move || bridge_write_file(&task_root, &arguments)).await { @@ -2742,8 +2737,7 @@ mod tests { ); } - /// Issue #318 第 1 条验收:同一轮里并行的多个文件写必须排队成功, - /// 而不是互相报"项目正在被其他写操作占用"。 + /// 同一轮里并行的多个文件写必须排队成功,而不是互相报"项目正在被其他写操作占用"。 #[test] fn bridge_write_file_serializes_parallel_writes_in_one_round() { let temporary = tempfile::tempdir().expect("create parallel direct write root"); @@ -2788,8 +2782,7 @@ mod tests { } } - /// Issue #318 第 1 条验收:App 自己另一条写通道正在写该项目时, - /// Direct 写入必须等待后成功,而不是在 24-42ms 内被判成"别人正在写"。 + /// App 自己另一条写通道正在写该项目时,Direct 写入必须等待后成功。 #[test] fn bridge_write_file_waits_for_a_short_same_process_project_writer() { let temporary = tempfile::tempdir().expect("create contended direct write root"); @@ -2883,7 +2876,7 @@ mod tests { ); } - /// Issue #318 第 3 条验收:权限拒绝不得被投影成"被其他写操作占用"。 + /// 权限拒绝不得被投影成"被其他写操作占用"。 #[cfg(unix)] #[test] fn bridge_write_file_does_not_project_permission_denial_as_contention() { diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/project_gates.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/project_gates.rs index 95b17611d..17dbb57f2 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/project_gates.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/project_gates.rs @@ -1843,8 +1843,7 @@ fn acquire_game_creator_agent_runtime_project_write_lock_within( }; if attempt + 1 == max_attempts { // 等待预算耗尽才记一条:争用本身可能重试上千次,逐次记账会淹掉日志。 - // 这条记录回答的正是 Issue #318 现场缺的问题——"谁在持锁、是不是自己人", - // 以及等满预算之后这到底是争用还是权限拒绝(`projection=`)。 + // 这条记录保留持锁方身份和最终分类(`projection=`),便于排查等待耗尽。 // 单次试探(max_attempts == 1,例如 hydrate 的 try_acquire_*)根本没有等待: // 既不写 `wait_exhausted`(waitedMs≈0 会让"耗尽"这个词失去意义,而 hydrate // 每次状态变化都会撞一次锁,会把它变成噪声),也不做终态改判。