同步自主首批任务图测试夹具
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Successful in 2m46s
Project CI / Native shell tests (pull_request) Successful in 10m54s

补齐可信 Supervisor 根任务与三 Leader 首批合同测试数据

更新视觉失败和 Ready 任务调度断言以匹配最新任务图

修复 Provider 重试恢复响应仍使用旧底层 Agent 合同的问题
This commit is contained in:
2026-08-03 14:21:30 +08:00
parent bda0d0d398
commit d8064eff49
5 changed files with 124 additions and 157 deletions
@@ -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(
@@ -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,
))
@@ -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<AgentRuntimeToolAction> {
fn valid_autonomous_initial_responsibility_actions_for_test() -> Vec<AgentRuntimeToolAction> {
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<AgentRuntimeToolAction> {
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)
@@ -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()
@@ -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");