修复M0A-2给做游戏路径引入的两处收束死路
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user