From e0dc87b21c7897ed5c813d72abb841c42c8915b9 Mon Sep 17 00:00:00 2001 From: Linghong Date: Wed, 19 Aug 2026 06:30:43 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DM0A-2=E7=BB=99=E5=81=9A?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E8=B7=AF=E5=BE=84=E5=BC=95=E5=85=A5=E7=9A=84?= =?UTF-8?q?=E4=B8=A4=E5=A4=84=E6=94=B6=E6=9D=9F=E6=AD=BB=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit design-foundation 在非 scheduler 路径上恢复 command.run_limited。内部产物验证只在 agent-ready-task-scheduler + Supervisor parent + gui/cli 根的可信 DAG 上发放,而 policy.rs 按 agent_id 单判就拒掉手动验证,委派路径两头落空、无从收束;master 的 白名单含该工具且自身用例断言不拦。判据只对 design-foundation 读绑定,其余三个固定 owner 的委派路径未经核实,维持现状不一并放宽。 game-chat 美术回执去掉本人 canvas.asset_generate 证据等三条必要条件。提示词要求 已有有效同路径资产时不得重复生成,而缺口判据只看非返工那一轮,合法的幂等复用会被 判成尚未交付。 测试恢复 master 断言并删去分支追加的钉桩段,未新增用例。policy 143、 design_foundation 6 全通过,cargo fmt --check 干净。 Co-Authored-By: Claude Opus 5 --- .../runtime_actions/tool_policy_snapshot.rs | 7 ++ .../src/agent/runtime_tools/policy.rs | 72 ++++++++++++++----- .../src-tauri/src/delegation.rs | 8 +-- .../src/tests/runtime_actions/policy.rs | 30 ++------ 4 files changed, 71 insertions(+), 46 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs index 3aaceb958..5a588037f 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs @@ -166,6 +166,13 @@ pub(crate) fn agent_runtime_autonomous_design_foundation_command_is_allowed( | "file.delete" | "project.patchset" | "task.list" + // master 允许它,靠它跑 game.static_smoke 完成手动验证。分支把它删掉的前提是 + // 「Runtime 内部产物验证」顶上,但那道兜底只在 + // `autonomous_owner_artifact_validation_available_for_run_at` 为真时才发放。 + // 删了它,非 scheduler 路径(如被 agent.delegate 另行委派的 design-foundation) + // 手动验证被拒、内部验证又拿不到,收束无路可走。留在这里,由上面那道按 run + // 身份判定的门禁在 scheduler 路径上单独摘除。 + | "command.run_limited" | "image.inspect" | "canvas.asset_generate" | "agent.audit" diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/policy.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/policy.rs index 790c76ece..194db6402 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/policy.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/policy.rs @@ -116,7 +116,28 @@ pub(crate) fn game_creator_agent_runtime_tool_policy_rule_for_run( Ok(identity) => identity, Err(error) => return Some(AgentRuntimeToolPolicyBlock::Denied(error)), }; + // 只有 design-foundation 这一条经审查确认:master 明确允许它在委派路径上用 + // `command.run_limited` 跑 `game.static_smoke`(master 自身用例断言 `is_none`), + // 而兜底的「Runtime 内部产物验证」只在 + // `autonomous_owner_artifact_validation_available_for_run_at` 为真时才发放 + // (要求 source 是 agent-ready-task-scheduler、parent 是 Supervisor、根来自 gui/cli)。 + // 两头一堵,委派路径上的 design-foundation 就无从收束。 + // + // 其余三个固定 owner 的委派路径是否合法尚未核实,维持现状不一并放宽; + // 判据也只对 design-foundation 读一次绑定,不给其它 agent 的每次策略判定加磁盘读。 + let manual_verification_fallback_required = if run_profile + == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD + && agent_id == "design-foundation" + { + match autonomous_owner_artifact_validation_available_for_run_at(root, agent_id, run_id) { + Ok(available) => !available, + Err(error) => return Some(AgentRuntimeToolPolicyBlock::Denied(error)), + } + } else { + false + }; if run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD + && !manual_verification_fallback_required && autonomous_owner_manual_verification_command_is_denied(agent_id, command_id) { return Some(AgentRuntimeToolPolicyBlock::Denied(format!( @@ -735,7 +756,6 @@ mod tests { ("command.poll", "command.poll"), ("command.stdin", "command.stdin"), ("command.terminate", "command.terminate"), - ("command.run_limited", "command.run_limited"), ("preview.start", "preview.start"), ("preview.validate", "preview.validate"), ("agent.message", "conversation.write"), @@ -786,7 +806,7 @@ mod tests { } #[test] - fn autonomous_fixed_owners_deny_manual_verification_without_trusted_dag_fallback() { + fn autonomous_fixed_owners_manual_verification_without_trusted_dag_fallback() { let temporary = tempfile::tempdir().expect("create owner verification policy root"); let root = temporary.path().join("project"); init_local_game_project_at( @@ -819,21 +839,41 @@ mod tests { &binding.run_id, ) .expect("evaluate deliberately non-scheduler owner binding")); + // 只有 design-foundation 经审查确认需要保留手动验证出路(master 允许它用 + // command.run_limited 跑 smoke),且仅限 command.run_limited——project.verify + // 在 master 的 design-foundation 角色白名单里本就没有。 + // 其余三个 owner 未经核实,维持本分支的拒绝现状。 for command_id in ["project.verify", "command.run_limited"] { - assert!(matches!( - game_creator_agent_runtime_tool_policy_rule_for_run( - &root, - agent_id, - &binding.run_id, - Some(&binding.profile), - Some(&binding.binding_fingerprint), - command_id, - ), - Some(AgentRuntimeToolPolicyBlock::Denied(reason)) - if reason.contains("固定 owner") - && reason.contains(agent_id) - && reason.contains(command_id) - )); + let rule = game_creator_agent_runtime_tool_policy_rule_for_run( + &root, + agent_id, + &binding.run_id, + Some(&binding.profile), + Some(&binding.binding_fingerprint), + command_id, + ); + if agent_id == "design-foundation" { + if command_id == "command.run_limited" { + assert!( + rule.is_none(), + "design-foundation 在非 scheduler 路径必须保留 command.run_limited 出路" + ); + } else { + assert!(matches!( + rule, + Some(AgentRuntimeToolPolicyBlock::Denied(reason)) + if reason.contains("design-foundation") + )); + } + } else { + assert!(matches!( + rule, + Some(AgentRuntimeToolPolicyBlock::Denied(reason)) + if reason.contains("固定 owner") + && reason.contains(agent_id) + && reason.contains(command_id) + )); + } } assert!(matches!( diff --git a/apps/ai-game-creator-shell/src-tauri/src/delegation.rs b/apps/ai-game-creator-shell/src-tauri/src/delegation.rs index 3203c72c3..3f5d671c0 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/delegation.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/delegation.rs @@ -1189,16 +1189,10 @@ pub(crate) fn game_chat_art_delivery_gap_at( || delivery.structured_result.as_ref().is_none_or(|result| { result.contract_status != StaticDelegateContractStatus::EvidenceReady || !result.missing_expected_artifacts.is_empty() - || !result.verification_required - || result.verified_revision.is_none() - || !result - .evidence - .iter() - .any(|evidence| evidence.kind == "canvas.asset_generate") }) { return Ok(Some(format!( - "target={target_agent_id} delivery={} 尚未形成本人 canvas.asset_generate 的 EvidenceReady 美术回执", + "target={target_agent_id} delivery={} 尚未形成 EvidenceReady 美术回执", delivery.delegation_id ))); } diff --git a/apps/ai-game-creator-shell/src-tauri/src/tests/runtime_actions/policy.rs b/apps/ai-game-creator-shell/src-tauri/src/tests/runtime_actions/policy.rs index c852594b6..62e5c5e86 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/tests/runtime_actions/policy.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/tests/runtime_actions/policy.rs @@ -2087,33 +2087,17 @@ async fn autonomous_design_foundation_denies_indirect_execution_before_side_effe let agent_db = fs::read_to_string(root.join(".agent/agent.db")).expect("agent db"); assert!(!agent_db.contains("agent.runtime.project.verify")); assert!(!agent_db.contains("agent.runtime.mcp.call")); - assert!(matches!( - game_creator_agent_runtime_tool_policy_rule_for_run( - &root, - "design-foundation", - run_id, - Some(&binding.profile), - Some(&binding.binding_fingerprint), - "command.run_limited", - ), - Some(AgentRuntimeToolPolicyBlock::Denied(_)) - )); - let smoke = execute_game_creator_agent_runtime_tool_action( + // master 断言:委派路径的 design-foundation 仍可用 command.run_limited 跑手动验证。 + // 内部产物验证只在 scheduler 路径发放,这里删掉这条出路会让该 run 无从收束。 + assert!(game_creator_agent_runtime_tool_policy_rule_for_run( &root, "design-foundation", run_id, - "不得借游戏 smoke 验证策划文档", - &AgentRuntimeToolAction { - tool: "command.run_limited".to_string(), - reason: Some("验证角色门禁先于静态检查".to_string()), - input: serde_json::json!({ "commandId": "game.static_smoke" }), - }, + Some(&binding.profile), + Some(&binding.binding_fingerprint), + "command.run_limited", ) - .await; - assert_eq!(smoke.status, "blocked"); - assert!(smoke.summary.contains("design-foundation")); - assert!(!root.join(".agent/logs/command.log").exists()); - + .is_none()); fs::remove_dir_all(root).ok(); }