格式化 AGC Rust 模块
按当前 rustfmt 规则整理 Agent Runtime、配置、预览、会话与相关测试的代码布局。 本提交不改变运行逻辑,保持 AGC Rust 工作区格式门禁可通过。
This commit is contained in:
@@ -48,7 +48,9 @@ pub(in crate::agent) fn validate_agent_runtime_autonomous_complete_game_index_wr
|
||||
) -> Result<(), String> {
|
||||
if path
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map_or(true, |path| !is_agent_runtime_game_entry_relative_path(path))
|
||||
.map_or(true, |path| {
|
||||
!is_agent_runtime_game_entry_relative_path(path)
|
||||
})
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
@@ -793,7 +795,7 @@ fn autonomous_manifest_parent_has_active_ready_task_at(
|
||||
let records =
|
||||
latest_game_creator_agent_runtime_tasks(read_all_game_creator_agent_runtime_tasks(
|
||||
&game_creator_agent_runtime_task_path(root, task_id),
|
||||
)?);
|
||||
)?);
|
||||
for record in records {
|
||||
if record.source != "agent-ready-task-scheduler"
|
||||
|| game_creator_agent_runtime_terminal_status(&record).is_some()
|
||||
@@ -806,10 +808,9 @@ fn autonomous_manifest_parent_has_active_ready_task_at(
|
||||
// binding's root id solely to associate an in-flight child with
|
||||
// this root; never reject or block the child for a mismatch.
|
||||
if record.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD {
|
||||
let parent_hint_matches =
|
||||
record.parent_agent_id.as_deref()
|
||||
== Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID)
|
||||
&& record.parent_run_id.as_deref() == Some(parent_run_id);
|
||||
let parent_hint_matches = record.parent_agent_id.as_deref()
|
||||
== Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID)
|
||||
&& record.parent_run_id.as_deref() == Some(parent_run_id);
|
||||
let binding_root_matches = read_game_creator_agent_runtime_run_profile_binding(
|
||||
root,
|
||||
&record.agent_id,
|
||||
@@ -821,8 +822,7 @@ fn autonomous_manifest_parent_has_active_ready_task_at(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if record.parent_agent_id.as_deref()
|
||||
!= Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID)
|
||||
if record.parent_agent_id.as_deref() != Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID)
|
||||
|| record.parent_run_id.as_deref() != Some(parent_run_id)
|
||||
{
|
||||
continue;
|
||||
|
||||
+2
-1
@@ -338,7 +338,8 @@ pub(in crate::agent) fn validate_agent_runtime_pending_tool_action_record(
|
||||
validate_agent_runtime_pending_goal_binding(pending)?;
|
||||
match pending.planning_session_binding.as_ref() {
|
||||
Some(binding) => {
|
||||
validate_plan_provider_session_binding(binding).map_err(|error| error.to_string())?;
|
||||
validate_plan_provider_session_binding(binding)
|
||||
.map_err(|error| error.to_string())?;
|
||||
if pending.action.tool.trim() != PLAN_SUBMIT_GDD_TOOL
|
||||
|| binding.agent_id != pending.agent_id
|
||||
|| binding.task_id != pending.task_id
|
||||
|
||||
+15
-14
@@ -541,14 +541,14 @@ pub(crate) async fn prepare_game_creator_agent_runtime_provider_action_batch_wit
|
||||
}
|
||||
if !relaxed_autonomous {
|
||||
if let Some(violation) = collaboration_preflight.violation {
|
||||
return Ok(AgentRuntimeProviderActionBatchPreparation::Blocked(
|
||||
AgentRuntimeToolObservation {
|
||||
tool: "runtime.collaboration_policy".to_string(),
|
||||
status: "blocked".to_string(),
|
||||
summary: violation.summary,
|
||||
detail: Some(violation.detail),
|
||||
},
|
||||
));
|
||||
return Ok(AgentRuntimeProviderActionBatchPreparation::Blocked(
|
||||
AgentRuntimeToolObservation {
|
||||
tool: "runtime.collaboration_policy".to_string(),
|
||||
status: "blocked".to_string(),
|
||||
summary: violation.summary,
|
||||
detail: Some(violation.detail),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
if provider_action_batch_is_not_needed(
|
||||
@@ -594,12 +594,13 @@ pub(crate) async fn prepare_game_creator_agent_runtime_provider_action_batch_wit
|
||||
"当前 Agent 身份不允许执行该原始工具".to_string(),
|
||||
)
|
||||
});
|
||||
let art_director_canvas_only_block = agent_runtime_autonomous_art_director_canvas_only_action_block(
|
||||
&runtime.agent_id,
|
||||
task,
|
||||
action.tool.trim(),
|
||||
)
|
||||
.map(|observation| AgentRuntimeToolPolicyBlock::Denied(observation.summary));
|
||||
let art_director_canvas_only_block =
|
||||
agent_runtime_autonomous_art_director_canvas_only_action_block(
|
||||
&runtime.agent_id,
|
||||
task,
|
||||
action.tool.trim(),
|
||||
)
|
||||
.map(|observation| AgentRuntimeToolPolicyBlock::Denied(observation.summary));
|
||||
let isolated_scope_block = if runtime.agent_id.starts_with("child-") {
|
||||
validate_isolated_agent_tool_scope_at(
|
||||
root,
|
||||
|
||||
+13
-15
@@ -439,19 +439,18 @@ pub(in crate::agent) async fn request_game_creator_agent_background_tool_plan_at
|
||||
let code_prototype_requires_static_smoke = !relaxed_autonomous
|
||||
&& run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
&& agent_id == "code-prototype";
|
||||
let verified_delivery = if !relaxed_autonomous
|
||||
&& run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
{
|
||||
let verification_gate =
|
||||
read_game_creator_agent_runtime_verification_gate(root, agent_id, run_id)?;
|
||||
runtime_owner_artifact_validation_available
|
||||
|| agent_runtime_autonomous_verified_delivery_allows_plan_completion(
|
||||
agent_id,
|
||||
&verification_gate,
|
||||
)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let verified_delivery =
|
||||
if !relaxed_autonomous && run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD {
|
||||
let verification_gate =
|
||||
read_game_creator_agent_runtime_verification_gate(root, agent_id, run_id)?;
|
||||
runtime_owner_artifact_validation_available
|
||||
|| agent_runtime_autonomous_verified_delivery_allows_plan_completion(
|
||||
agent_id,
|
||||
&verification_gate,
|
||||
)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let allow_runtime_plan_completion = read_only_delivery || verified_delivery;
|
||||
let autonomous_project_verify_available = relaxed_autonomous
|
||||
|| run_profile != AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
@@ -758,8 +757,7 @@ pub(in crate::agent) async fn request_game_creator_agent_background_tool_plan_at
|
||||
};
|
||||
let parsed = match parsed {
|
||||
Ok((parsed, source_payload))
|
||||
if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID
|
||||
&& !relaxed_autonomous =>
|
||||
if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID && !relaxed_autonomous =>
|
||||
{
|
||||
let collaboration_policy =
|
||||
resolve_supervisor_collaboration_policy_for_run_at(root, agent_id, run_id)?
|
||||
|
||||
@@ -245,36 +245,39 @@ pub(in crate::agent) fn resume_game_creator_agent_finalization_at(
|
||||
let assistant_exists =
|
||||
game_creator_agent_runtime_finalization_assistant_exists(root, &journal)?;
|
||||
if !relaxed_autonomous {
|
||||
match classify_game_creator_agent_runtime_finalization_goal_snapshot_at(root, &journal, &state)?
|
||||
{
|
||||
AgentRuntimeFinalizationGoalSnapshotRelation::Matches => {}
|
||||
AgentRuntimeFinalizationGoalSnapshotRelation::StaleRevision {
|
||||
journal_revision,
|
||||
current_revision,
|
||||
} if journal.status == AGENT_RUNTIME_FINALIZATION_STATUS_PREPARED && !assistant_exists => {
|
||||
remove_game_creator_agent_runtime_finalization_recovery_sidecars(
|
||||
root,
|
||||
&journal.agent_id,
|
||||
&journal.run_id,
|
||||
)?;
|
||||
let _ = append_agent_db_record(
|
||||
root,
|
||||
serde_json::json!({
|
||||
"recordType": "agent.runtime.background_task.finalization_stale_recovered",
|
||||
"agentId": journal.agent_id,
|
||||
"taskId": journal.task_id,
|
||||
"sessionId": journal.session_id,
|
||||
"runId": journal.run_id,
|
||||
"source": journal.source,
|
||||
"summary": "Goal revision 已更新,旧最终回复已丢弃",
|
||||
"journalGoalRevision": journal_revision,
|
||||
"currentGoalRevision": current_revision,
|
||||
}),
|
||||
);
|
||||
return Ok(AgentRuntimeFinalizationResume::NotFound(runtime_lock));
|
||||
}
|
||||
relation => {
|
||||
let detail = match relation {
|
||||
match classify_game_creator_agent_runtime_finalization_goal_snapshot_at(
|
||||
root, &journal, &state,
|
||||
)? {
|
||||
AgentRuntimeFinalizationGoalSnapshotRelation::Matches => {}
|
||||
AgentRuntimeFinalizationGoalSnapshotRelation::StaleRevision {
|
||||
journal_revision,
|
||||
current_revision,
|
||||
} if journal.status == AGENT_RUNTIME_FINALIZATION_STATUS_PREPARED
|
||||
&& !assistant_exists =>
|
||||
{
|
||||
remove_game_creator_agent_runtime_finalization_recovery_sidecars(
|
||||
root,
|
||||
&journal.agent_id,
|
||||
&journal.run_id,
|
||||
)?;
|
||||
let _ = append_agent_db_record(
|
||||
root,
|
||||
serde_json::json!({
|
||||
"recordType": "agent.runtime.background_task.finalization_stale_recovered",
|
||||
"agentId": journal.agent_id,
|
||||
"taskId": journal.task_id,
|
||||
"sessionId": journal.session_id,
|
||||
"runId": journal.run_id,
|
||||
"source": journal.source,
|
||||
"summary": "Goal revision 已更新,旧最终回复已丢弃",
|
||||
"journalGoalRevision": journal_revision,
|
||||
"currentGoalRevision": current_revision,
|
||||
}),
|
||||
);
|
||||
return Ok(AgentRuntimeFinalizationResume::NotFound(runtime_lock));
|
||||
}
|
||||
relation => {
|
||||
let detail = match relation {
|
||||
AgentRuntimeFinalizationGoalSnapshotRelation::StaleRevision {
|
||||
journal_revision,
|
||||
current_revision,
|
||||
@@ -284,14 +287,16 @@ pub(in crate::agent) fn resume_game_creator_agent_finalization_at(
|
||||
AgentRuntimeFinalizationGoalSnapshotRelation::Conflict(detail) => detail,
|
||||
AgentRuntimeFinalizationGoalSnapshotRelation::Matches => unreachable!(),
|
||||
};
|
||||
let error = format!("Agent Runtime finalization 恢复已阻断:Goal 快照冲突:{detail}");
|
||||
record_game_creator_agent_runtime_finalization_pending(root, &state, &error);
|
||||
return read_game_creator_agent_runtime_at(root, agent_id)
|
||||
.map(AgentRuntimeFinalizationResume::Blocked);
|
||||
}
|
||||
let error =
|
||||
format!("Agent Runtime finalization 恢复已阻断:Goal 快照冲突:{detail}");
|
||||
record_game_creator_agent_runtime_finalization_pending(root, &state, &error);
|
||||
return read_game_creator_agent_runtime_at(root, agent_id)
|
||||
.map(AgentRuntimeFinalizationResume::Blocked);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !relaxed_autonomous && assistant_exists
|
||||
if !relaxed_autonomous
|
||||
&& assistant_exists
|
||||
&& !state_reconstructed_from_task
|
||||
&& !game_creator_agent_runtime_finalization_plan_matches_state(&journal, &state)
|
||||
{
|
||||
@@ -415,11 +420,13 @@ pub(in crate::agent) fn resume_game_creator_agent_finalization_at(
|
||||
goal_contract_acceptance_completion_blocker_at_locked(&root, &state)
|
||||
{
|
||||
Some(blocker)
|
||||
} else if let Some(blocker) = agent_runtime_non_verification_completion_blocker_at_locked(
|
||||
&root,
|
||||
&journal.agent_id,
|
||||
&journal.run_id,
|
||||
) {
|
||||
} else if let Some(blocker) =
|
||||
agent_runtime_non_verification_completion_blocker_at_locked(
|
||||
&root,
|
||||
&journal.agent_id,
|
||||
&journal.run_id,
|
||||
)
|
||||
{
|
||||
Some(blocker)
|
||||
} else if let Some(blocker) =
|
||||
autonomous_game_build_completion_blocker_at_locked(&root, &state)
|
||||
|
||||
@@ -810,101 +810,15 @@ async fn run_game_creator_agent_background_task_pass_without_deadline(
|
||||
// impossible for a relaxed run to read the DAG and accidentally
|
||||
// re-enter `waiting-for-manifest-tasks`.
|
||||
if !relaxed_autonomous {
|
||||
let autonomous_root_goal_contract_persisted =
|
||||
if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID
|
||||
&& runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
{
|
||||
match autonomous_root_goal_contract_persisted_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&runtime.run_id,
|
||||
) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("读取自主构建根 Goal Contract 门失败:{error}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let autonomous_manifest_parent_can_wait =
|
||||
autonomous_root_goal_contract_persisted
|
||||
&& agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID
|
||||
&& runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
&& !game_creator_agent_runtime_provider_action_batch_exists(
|
||||
&root,
|
||||
&agent_id,
|
||||
&runtime.run_id,
|
||||
)
|
||||
&& supervisor_collaboration_policy_completion_blocker_at_locked(
|
||||
&root,
|
||||
&agent_id,
|
||||
&runtime.run_id,
|
||||
)
|
||||
.is_none()
|
||||
&& isolated_join_completion_blocker_at(&root, &agent_id, &runtime.run_id)
|
||||
.is_none()
|
||||
&& static_delegate_completion_blocker_at(&root, &agent_id, &runtime.run_id)
|
||||
.is_none();
|
||||
if autonomous_manifest_parent_can_wait {
|
||||
let manifest_state_before_schedule = match autonomous_manifest_dag_state_at(&root) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("读取自主构建 manifest 等待屏障失败:{error}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
// 已登记但损坏的派生视觉需要先由父 Run 规划修复,因此此时不再调度新的
|
||||
// manifest child;但已经持久化并运行的 child 仍是当前 DAG 的活跃工作,
|
||||
// 父 Run 必须继续等待,不能提前结束。
|
||||
// 同理,已有失败任务时只能等待已在途 child 收束或立即安全失败,不能再
|
||||
// 启动新的 pending sibling 并用它遮蔽原始失败。
|
||||
let manifest_scheduler_blocked = matches!(
|
||||
&manifest_state_before_schedule,
|
||||
AutonomousManifestDagState::Completed
|
||||
| AutonomousManifestDagState::Failed { .. }
|
||||
) || autonomous_registered_derived_visuals_block_manifest_scheduler_at(
|
||||
&root, &runtime,
|
||||
);
|
||||
let scheduled_ready_tasks = if manifest_scheduler_blocked {
|
||||
Vec::new()
|
||||
} else {
|
||||
match schedule_autonomous_game_build_ready_tasks_at(
|
||||
let autonomous_root_goal_contract_persisted = if agent_id
|
||||
== GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID
|
||||
&& runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
{
|
||||
match autonomous_root_goal_contract_persisted_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&runtime.run_id,
|
||||
3,
|
||||
) {
|
||||
Ok(tasks) => tasks,
|
||||
Err(error) => {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("调度自主构建 manifest 任务失败:{error}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
let manifest_state = if matches!(
|
||||
&manifest_state_before_schedule,
|
||||
AutonomousManifestDagState::Failed { .. }
|
||||
) {
|
||||
manifest_state_before_schedule
|
||||
} else if scheduled_ready_tasks.is_empty() {
|
||||
match autonomous_manifest_dag_state_at(&root) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
@@ -912,68 +826,155 @@ async fn run_game_creator_agent_background_task_pass_without_deadline(
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("读取自主构建 manifest 等待屏障失败:{error}"),
|
||||
&format!("读取自主构建根 Goal Contract 门失败:{error}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AutonomousManifestDagState::InProgress
|
||||
false
|
||||
};
|
||||
let manifest_parent_must_wait = matches!(
|
||||
&manifest_state,
|
||||
AutonomousManifestDagState::InProgress
|
||||
| AutonomousManifestDagState::Failed {
|
||||
has_active_children: true,
|
||||
..
|
||||
}
|
||||
);
|
||||
if manifest_parent_must_wait {
|
||||
let blocker =
|
||||
autonomous_game_build_completion_blocker_at_locked(&root, &runtime)
|
||||
.unwrap_or_else(|| AgentRuntimeToolObservation {
|
||||
tool: "runtime.autonomous_completion".to_string(),
|
||||
status: "blocked".to_string(),
|
||||
summary: "自主构建 manifest DAG 尚未完成".to_string(),
|
||||
detail: Some("manifest 子任务仍在运行".to_string()),
|
||||
});
|
||||
if let Err(error) = persist_waiting_autonomous_manifest_parent_context_at(
|
||||
let autonomous_manifest_parent_can_wait = autonomous_root_goal_contract_persisted
|
||||
&& agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID
|
||||
&& runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
&& !game_creator_agent_runtime_provider_action_batch_exists(
|
||||
&root,
|
||||
&mut runtime,
|
||||
&task,
|
||||
&plan,
|
||||
&mut observations,
|
||||
loop_index,
|
||||
&mut context_tracker,
|
||||
blocker,
|
||||
&agent_id,
|
||||
&runtime.run_id,
|
||||
)
|
||||
&& supervisor_collaboration_policy_completion_blocker_at_locked(
|
||||
&root,
|
||||
&agent_id,
|
||||
&runtime.run_id,
|
||||
)
|
||||
.is_none()
|
||||
&& isolated_join_completion_blocker_at(&root, &agent_id, &runtime.run_id)
|
||||
.is_none()
|
||||
&& static_delegate_completion_blocker_at(&root, &agent_id, &runtime.run_id)
|
||||
.is_none();
|
||||
if autonomous_manifest_parent_can_wait {
|
||||
let manifest_state_before_schedule =
|
||||
match autonomous_manifest_dag_state_at(&root) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("读取自主构建 manifest 等待屏障失败:{error}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
// 已登记但损坏的派生视觉需要先由父 Run 规划修复,因此此时不再调度新的
|
||||
// manifest child;但已经持久化并运行的 child 仍是当前 DAG 的活跃工作,
|
||||
// 父 Run 必须继续等待,不能提前结束。
|
||||
// 同理,已有失败任务时只能等待已在途 child 收束或立即安全失败,不能再
|
||||
// 启动新的 pending sibling 并用它遮蔽原始失败。
|
||||
let manifest_scheduler_blocked = matches!(
|
||||
&manifest_state_before_schedule,
|
||||
AutonomousManifestDagState::Completed
|
||||
| AutonomousManifestDagState::Failed { .. }
|
||||
)
|
||||
|| autonomous_registered_derived_visuals_block_manifest_scheduler_at(
|
||||
&root, &runtime,
|
||||
);
|
||||
let scheduled_ready_tasks = if manifest_scheduler_blocked {
|
||||
Vec::new()
|
||||
} else {
|
||||
match schedule_autonomous_game_build_ready_tasks_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&runtime.run_id,
|
||||
3,
|
||||
) {
|
||||
Ok(tasks) => tasks,
|
||||
Err(error) => {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("调度自主构建 manifest 任务失败:{error}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
let manifest_state = if matches!(
|
||||
&manifest_state_before_schedule,
|
||||
AutonomousManifestDagState::Failed { .. }
|
||||
) {
|
||||
manifest_state_before_schedule
|
||||
} else if scheduled_ready_tasks.is_empty() {
|
||||
match autonomous_manifest_dag_state_at(&root) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("读取自主构建 manifest 等待屏障失败:{error}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AutonomousManifestDagState::InProgress
|
||||
};
|
||||
let manifest_parent_must_wait = matches!(
|
||||
&manifest_state,
|
||||
AutonomousManifestDagState::InProgress
|
||||
| AutonomousManifestDagState::Failed {
|
||||
has_active_children: true,
|
||||
..
|
||||
}
|
||||
);
|
||||
if manifest_parent_must_wait {
|
||||
let blocker =
|
||||
autonomous_game_build_completion_blocker_at_locked(&root, &runtime)
|
||||
.unwrap_or_else(|| AgentRuntimeToolObservation {
|
||||
tool: "runtime.autonomous_completion".to_string(),
|
||||
status: "blocked".to_string(),
|
||||
summary: "自主构建 manifest DAG 尚未完成".to_string(),
|
||||
detail: Some("manifest 子任务仍在运行".to_string()),
|
||||
});
|
||||
if let Err(error) = persist_waiting_autonomous_manifest_parent_context_at(
|
||||
&root,
|
||||
&mut runtime,
|
||||
&task,
|
||||
&plan,
|
||||
&mut observations,
|
||||
loop_index,
|
||||
&mut context_tracker,
|
||||
blocker,
|
||||
) {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("持久化 manifest 父 run 等待状态失败:{error}"),
|
||||
);
|
||||
}
|
||||
return AgentBackgroundTaskOutcome::WaitingForManifestTasks;
|
||||
}
|
||||
if let AutonomousManifestDagState::Failed {
|
||||
failed_task_ids, ..
|
||||
} = manifest_state
|
||||
{
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("持久化 manifest 父 run 等待状态失败:{error}"),
|
||||
&format!(
|
||||
"自主构建 manifest 专业任务失败;failedTaskIds={}",
|
||||
failed_task_ids.join(",")
|
||||
),
|
||||
);
|
||||
}
|
||||
return AgentBackgroundTaskOutcome::WaitingForManifestTasks;
|
||||
}
|
||||
if let AutonomousManifestDagState::Failed {
|
||||
failed_task_ids, ..
|
||||
} = manifest_state
|
||||
{
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!(
|
||||
"自主构建 manifest 专业任务失败;failedTaskIds={}",
|
||||
failed_task_ids.join(",")
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut resumed_provider_batch = if game_creator_agent_runtime_provider_action_batch_exists(
|
||||
&root,
|
||||
&agent_id,
|
||||
@@ -1706,7 +1707,9 @@ async fn run_game_creator_agent_background_task_pass_without_deadline(
|
||||
&agent_id,
|
||||
&runtime.run_id,
|
||||
)
|
||||
.or_else(|| process_session_completion_blocker_at(&root, &agent_id, &runtime.run_id))
|
||||
.or_else(|| {
|
||||
process_session_completion_blocker_at(&root, &agent_id, &runtime.run_id)
|
||||
})
|
||||
.or_else(|| autonomous_game_build_completion_blocker_at_locked(&root, &runtime))
|
||||
} else {
|
||||
structured_plan_completion_blocker(&runtime)
|
||||
@@ -1731,7 +1734,9 @@ async fn run_game_creator_agent_background_task_pass_without_deadline(
|
||||
.or_else(|| {
|
||||
game_creator_agent_goal_completion_blocker_at_locked(&root, &runtime)
|
||||
})
|
||||
.or_else(|| goal_contract_acceptance_completion_blocker_at_locked(&root, &runtime))
|
||||
.or_else(|| {
|
||||
goal_contract_acceptance_completion_blocker_at_locked(&root, &runtime)
|
||||
})
|
||||
.or_else(|| {
|
||||
supervisor_collaboration_policy_completion_blocker_at_locked(
|
||||
&root,
|
||||
@@ -1790,26 +1795,22 @@ async fn run_game_creator_agent_background_task_pass_without_deadline(
|
||||
match art_wait_state {
|
||||
AutonomousCodePrototypeArtAssetWaitState::Waiting => {
|
||||
let next_loop_index = loop_index.saturating_add(1);
|
||||
if let Err(error) =
|
||||
persist_waiting_autonomous_manifest_child_context_at(
|
||||
&root,
|
||||
&mut runtime,
|
||||
&task,
|
||||
&plan,
|
||||
&mut observations,
|
||||
next_loop_index,
|
||||
&mut context_tracker,
|
||||
blocker,
|
||||
)
|
||||
{
|
||||
if let Err(error) = persist_waiting_autonomous_manifest_child_context_at(
|
||||
&root,
|
||||
&mut runtime,
|
||||
&task,
|
||||
&plan,
|
||||
&mut observations,
|
||||
next_loop_index,
|
||||
&mut context_tracker,
|
||||
blocker,
|
||||
) {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!(
|
||||
"持久化 code-prototype 美术依赖等待状态失败:{error}"
|
||||
),
|
||||
&format!("持久化 code-prototype 美术依赖等待状态失败:{error}"),
|
||||
);
|
||||
}
|
||||
return AgentBackgroundTaskOutcome::WaitingForManifestTasks;
|
||||
|
||||
@@ -178,13 +178,13 @@ async fn drive_waiting_autonomous_manifest_parent_wake_pass_with_budget(
|
||||
reconciliation_delay_ms: u64,
|
||||
request_deferred_rerun: bool,
|
||||
) -> Result<(), String> {
|
||||
if read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)?
|
||||
.is_some_and(|task| {
|
||||
if read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)?.is_some_and(
|
||||
|task| {
|
||||
task.status == "running"
|
||||
&& task.phase == "waiting-for-manifest-tasks"
|
||||
&& autonomous_relaxed_run_profile(&task.run_profile)
|
||||
})
|
||||
{
|
||||
},
|
||||
) {
|
||||
// Free-form autonomous runs never reconcile a manifest barrier. The
|
||||
// phase can only be legacy durable state, so make a bounded attempt to
|
||||
// resume it and leave any lane contention for the ordinary recovery
|
||||
@@ -317,7 +317,10 @@ pub(in crate::agent) fn autonomous_manifest_ready_task_waiting_child_record(
|
||||
&& task.source == "agent-ready-task-scheduler"
|
||||
&& task.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
|
||||
&& task.parent_agent_id.as_deref() == Some(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID)
|
||||
&& task.parent_run_id.as_deref().is_some_and(|value| !value.trim().is_empty())
|
||||
&& task
|
||||
.parent_run_id
|
||||
.as_deref()
|
||||
.is_some_and(|value| !value.trim().is_empty())
|
||||
&& task.delegation_id.is_none()
|
||||
}
|
||||
|
||||
@@ -327,9 +330,9 @@ async fn drive_waiting_autonomous_manifest_child_wake_pass(
|
||||
run_id: &str,
|
||||
) -> Result<(), String> {
|
||||
for _ in 0..AUTONOMOUS_MANIFEST_PARENT_WAKE_MAX_ATTEMPTS {
|
||||
let Some(task) = read_latest_game_creator_agent_runtime_task_by_run_id(
|
||||
root, agent_id, run_id,
|
||||
)? else {
|
||||
let Some(task) =
|
||||
read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
if !autonomous_manifest_ready_task_waiting_child_record(&task) {
|
||||
|
||||
@@ -1628,23 +1628,24 @@ pub(in crate::agent) fn resume_game_creator_agent_background_tasks_unredacted_at
|
||||
continue;
|
||||
}
|
||||
AutonomousCodePrototypeArtAssetWaitState::NotWaiting => {
|
||||
let parent_agent_id = task.parent_agent_id.as_deref().ok_or_else(|| {
|
||||
"code-prototype 等待态缺少 parentAgentId".to_string()
|
||||
})?;
|
||||
let parent_run_id = task.parent_run_id.as_deref().ok_or_else(|| {
|
||||
"code-prototype 等待态缺少 parentRunId".to_string()
|
||||
})?;
|
||||
let parent_agent_id = task
|
||||
.parent_agent_id
|
||||
.as_deref()
|
||||
.ok_or_else(|| "code-prototype 等待态缺少 parentAgentId".to_string())?;
|
||||
let parent_run_id = task
|
||||
.parent_run_id
|
||||
.as_deref()
|
||||
.ok_or_else(|| "code-prototype 等待态缺少 parentRunId".to_string())?;
|
||||
// The child execution lane is held by this recovery
|
||||
// scan. Release it before the parent scheduler tries
|
||||
// to reacquire the deterministic child lane.
|
||||
drop(runtime_lock);
|
||||
let scheduled_ready_tasks =
|
||||
schedule_autonomous_game_build_ready_tasks_at(
|
||||
root,
|
||||
parent_agent_id,
|
||||
parent_run_id,
|
||||
3,
|
||||
)?;
|
||||
let scheduled_ready_tasks = schedule_autonomous_game_build_ready_tasks_at(
|
||||
root,
|
||||
parent_agent_id,
|
||||
parent_run_id,
|
||||
3,
|
||||
)?;
|
||||
if !scheduled_ready_tasks.is_empty() {
|
||||
resumed.push(read_game_creator_agent_runtime_at(root, &agent_id)?);
|
||||
}
|
||||
|
||||
+8
-6
@@ -60,8 +60,10 @@ pub(in crate::agent) fn autonomous_relaxed_run_at(
|
||||
{
|
||||
return Ok(autonomous_relaxed_run_profile(&binding.profile));
|
||||
}
|
||||
Ok(read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)?
|
||||
.is_some_and(|task| autonomous_relaxed_run_profile(&task.run_profile)))
|
||||
Ok(
|
||||
read_latest_game_creator_agent_runtime_task_by_run_id(root, agent_id, run_id)?
|
||||
.is_some_and(|task| autonomous_relaxed_run_profile(&task.run_profile)),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn autonomous_game_build_root_run_active_at(root: &Path) -> bool {
|
||||
@@ -669,11 +671,11 @@ pub(in crate::agent) fn autonomous_code_prototype_art_asset_wait_state_at(
|
||||
.map(|task| task.status.clone())
|
||||
.ok_or_else(|| "当前 seed manifest 缺少 art-asset-plan".to_string())?;
|
||||
match art_status {
|
||||
GameCreationAppTaskStatus::Failed => Ok(
|
||||
AutonomousCodePrototypeArtAssetWaitState::DependencyFailed(
|
||||
GameCreationAppTaskStatus::Failed => {
|
||||
Ok(AutonomousCodePrototypeArtAssetWaitState::DependencyFailed(
|
||||
"art-asset-plan 已失败,code-prototype 不能继续等待美术回执".to_string(),
|
||||
),
|
||||
),
|
||||
))
|
||||
}
|
||||
GameCreationAppTaskStatus::Pending
|
||||
| GameCreationAppTaskStatus::Running
|
||||
| GameCreationAppTaskStatus::WaitingForConfirmation => {
|
||||
|
||||
@@ -5066,7 +5066,10 @@ mod tests {
|
||||
assert_eq!(parse(&bytes).expect("parse input"), value);
|
||||
let mut newline = bytes.clone();
|
||||
newline.push(b'\n');
|
||||
assert_eq!(parse(&newline).unwrap_err().code(), "PLAN_NON_CANONICAL_BYTES");
|
||||
assert_eq!(
|
||||
parse(&newline).unwrap_err().code(),
|
||||
"PLAN_NON_CANONICAL_BYTES"
|
||||
);
|
||||
let mut object = serde_json::from_slice::<serde_json::Value>(&bytes).expect("input json");
|
||||
object["projectId"] = serde_json::Value::String("forged-project".to_string());
|
||||
let forged = serde_json::to_vec(&object).expect("forged input");
|
||||
|
||||
@@ -220,9 +220,7 @@ impl CliCommand {
|
||||
| Self::PlanGddDecide { project_path, .. }
|
||||
| Self::PreviewServe { project_path }
|
||||
| Self::AgentRun { project_path, .. } => Some((project_path, false)),
|
||||
| Self::LlmStatus
|
||||
| Self::RunnerStatus
|
||||
| Self::RunnerShutdownIfIdle => None,
|
||||
Self::LlmStatus | Self::RunnerStatus | Self::RunnerShutdownIfIdle => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,12 +481,9 @@ pub(crate) fn game_creator_project_manifest_error(root: &Path) -> Option<String>
|
||||
|
||||
pub(crate) fn recent_game_creator_run_trace(root: &Path) -> Option<GameCreationAgentRunTrace> {
|
||||
let trace_path = root.join(".agent/run.latest.json");
|
||||
let content = crate::read_game_creator_private_file_to_string(
|
||||
&trace_path,
|
||||
"最近运行状态",
|
||||
256 * 1024,
|
||||
)
|
||||
.ok()?;
|
||||
let content =
|
||||
crate::read_game_creator_private_file_to_string(&trace_path, "最近运行状态", 256 * 1024)
|
||||
.ok()?;
|
||||
serde_json::from_str::<GameCreationAgentRunTrace>(&content).ok()
|
||||
}
|
||||
|
||||
@@ -530,11 +527,9 @@ pub(crate) async fn pick_local_project_directory(
|
||||
// The native picker is the explicit user-selection boundary. Prepare the
|
||||
// selected root before returning it so inspect/create/open never races the
|
||||
// first ACL read.
|
||||
if let Err(error) = crate::prepare_game_creator_project_root_for_read(
|
||||
&path,
|
||||
true,
|
||||
"用户选择项目目录",
|
||||
) {
|
||||
if let Err(error) =
|
||||
crate::prepare_game_creator_project_root_for_read(&path, true, "用户选择项目目录")
|
||||
{
|
||||
#[cfg(windows)]
|
||||
crate::revoke_game_creator_user_selected_path(&path);
|
||||
return Err(error);
|
||||
@@ -563,11 +558,9 @@ pub(crate) async fn pick_local_file(app: tauri::AppHandle) -> Result<Option<Stri
|
||||
.map_err(|error| format!("读取本地文件失败:{error}"))?;
|
||||
#[cfg(windows)]
|
||||
crate::register_game_creator_user_selected_path(&path, false);
|
||||
if let Err(error) = crate::prepare_game_creator_user_selected_path_for_read(
|
||||
&path,
|
||||
false,
|
||||
"用户选择文件",
|
||||
) {
|
||||
if let Err(error) =
|
||||
crate::prepare_game_creator_user_selected_path_for_read(&path, false, "用户选择文件")
|
||||
{
|
||||
#[cfg(windows)]
|
||||
crate::revoke_game_creator_user_selected_path(&path);
|
||||
return Err(error);
|
||||
|
||||
@@ -1905,16 +1905,8 @@ fn install_agent_runtime_async_runtime_with_deep_stack() {
|
||||
|
||||
#[cfg(windows)]
|
||||
fn run_windows_acl_repair_if_requested(args: &[String]) -> Option<i32> {
|
||||
let [
|
||||
command,
|
||||
path,
|
||||
target_user_sid_flag,
|
||||
target_user_sid,
|
||||
authorization_flag,
|
||||
nonce,
|
||||
scope_flag,
|
||||
scope_value,
|
||||
] = args
|
||||
let [command, path, target_user_sid_flag, target_user_sid, authorization_flag, nonce, scope_flag, scope_value] =
|
||||
args
|
||||
else {
|
||||
if args.first().map(String::as_str) == Some("--repair-private-acl") {
|
||||
eprintln!(
|
||||
@@ -1942,13 +1934,11 @@ fn run_windows_acl_repair_if_requested(args: &[String]) -> Option<i32> {
|
||||
return Some(1);
|
||||
}
|
||||
};
|
||||
let result = config::consume_windows_acl_repair_authorization(
|
||||
&path,
|
||||
target_user_sid,
|
||||
nonce,
|
||||
scope,
|
||||
)
|
||||
.and_then(|()| config::repair_game_creator_private_acl_for_user_sid(&path, target_user_sid));
|
||||
let result =
|
||||
config::consume_windows_acl_repair_authorization(&path, target_user_sid, nonce, scope)
|
||||
.and_then(|()| {
|
||||
config::repair_game_creator_private_acl_for_user_sid(&path, target_user_sid)
|
||||
});
|
||||
match result {
|
||||
Ok(()) => Some(0),
|
||||
Err(error) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use sha2::{Digest, Sha256};
|
||||
use serde::Deserialize;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -8,10 +8,8 @@ use std::sync::{Mutex, OnceLock};
|
||||
/// Debug-only fixture hook used by the deterministic AGC E2E. The hook takes
|
||||
/// a path, rather than credentials on argv, so a child Runner can inherit the
|
||||
/// test identity without putting the bearer token in process listings.
|
||||
pub(crate) const PLATFORM_SESSION_FIXTURE_ENV: &str =
|
||||
"GENARRATIVE_AGC_PLATFORM_SESSION_FIXTURE";
|
||||
const PLATFORM_SESSION_FIXTURE_SCHEMA_VERSION: &str =
|
||||
"genarrative-agc-platform-session-fixture.v1";
|
||||
pub(crate) const PLATFORM_SESSION_FIXTURE_ENV: &str = "GENARRATIVE_AGC_PLATFORM_SESSION_FIXTURE";
|
||||
const PLATFORM_SESSION_FIXTURE_SCHEMA_VERSION: &str = "genarrative-agc-platform-session-fixture.v1";
|
||||
const PLATFORM_SESSION_FIXTURE_MAX_BYTES: u64 = 16 * 1024;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -144,8 +142,8 @@ fn validate_fixture_path(config_dir: &Path, fixture_path: &Path) -> Result<PathB
|
||||
}
|
||||
|
||||
fn read_fixture_file(path: &Path) -> Result<Vec<u8>, String> {
|
||||
let before = fs::symlink_metadata(path)
|
||||
.map_err(|_| "平台登录态 fixture 文件不可读取".to_string())?;
|
||||
let before =
|
||||
fs::symlink_metadata(path).map_err(|_| "平台登录态 fixture 文件不可读取".to_string())?;
|
||||
if metadata_is_link_or_reparse(&before) || !before.is_file() {
|
||||
return Err("平台登录态 fixture 必须是普通文件".to_string());
|
||||
}
|
||||
@@ -169,7 +167,8 @@ fn read_fixture_file(path: &Path) -> Result<Vec<u8>, String> {
|
||||
if metadata_is_link_or_reparse(&opened) || !opened.is_file() || opened.len() > before.len() {
|
||||
return Err("平台登录态 fixture 文件身份校验失败".to_string());
|
||||
}
|
||||
let mut bytes = Vec::with_capacity(opened.len().min(PLATFORM_SESSION_FIXTURE_MAX_BYTES) as usize);
|
||||
let mut bytes =
|
||||
Vec::with_capacity(opened.len().min(PLATFORM_SESSION_FIXTURE_MAX_BYTES) as usize);
|
||||
file.take(PLATFORM_SESSION_FIXTURE_MAX_BYTES + 1)
|
||||
.read_to_end(&mut bytes)
|
||||
.map_err(|_| "读取平台登录态 fixture 失败".to_string())?;
|
||||
@@ -188,10 +187,18 @@ fn parse_platform_session_fixture(bytes: &[u8]) -> Result<PlatformSessionFixture
|
||||
if fixture.generation == 0 {
|
||||
return Err("平台登录态 fixture generation 无效".to_string());
|
||||
}
|
||||
if fixture.user_id.chars().any(|character| character.is_control()) {
|
||||
if fixture
|
||||
.user_id
|
||||
.chars()
|
||||
.any(|character| character.is_control())
|
||||
{
|
||||
return Err("平台登录态 fixture 用户身份无效".to_string());
|
||||
}
|
||||
if fixture.access_token.chars().any(|character| character.is_control()) {
|
||||
if fixture
|
||||
.access_token
|
||||
.chars()
|
||||
.any(|character| character.is_control())
|
||||
{
|
||||
return Err("平台登录态 fixture 凭据无效".to_string());
|
||||
}
|
||||
Ok(fixture)
|
||||
|
||||
@@ -1486,7 +1486,8 @@ fn canonical_preview_path(root: &Path, file_path: &Path) -> Result<PathBuf, Stri
|
||||
.iter()
|
||||
.any(|protected| component.eq_ignore_ascii_case(protected))
|
||||
});
|
||||
if !protected_root_component && content_type(&canonical_file) != "application/octet-stream" {
|
||||
if !protected_root_component && content_type(&canonical_file) != "application/octet-stream"
|
||||
{
|
||||
return Ok(canonical_file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,11 +560,9 @@ pub(crate) fn create_game_creator_agent_session_at(
|
||||
conversation_path.display()
|
||||
)
|
||||
})?;
|
||||
if let Err(error) = harden_new_game_creator_private_path(
|
||||
&conversation_path,
|
||||
false,
|
||||
"对话记录",
|
||||
) {
|
||||
if let Err(error) =
|
||||
harden_new_game_creator_private_path(&conversation_path, false, "对话记录")
|
||||
{
|
||||
drop(file);
|
||||
let _ = fs::remove_file(&conversation_path);
|
||||
return Err(error);
|
||||
|
||||
@@ -79,7 +79,10 @@ async fn autonomous_manifest_parent_wake_budget_exhaustion_is_projected() {
|
||||
// exhaustion into a hard reconciliation failure; they remain waiting for the
|
||||
// deterministic child scheduler/recovery scan to make progress.
|
||||
assert_eq!(state.status, "running");
|
||||
assert!(matches!(state.phase.as_str(), "planning" | "waiting-for-manifest-tasks"));
|
||||
assert!(matches!(
|
||||
state.phase.as_str(),
|
||||
"planning" | "waiting-for-manifest-tasks"
|
||||
));
|
||||
assert!(state.error.is_none());
|
||||
|
||||
fs::remove_dir_all(root).ok();
|
||||
|
||||
@@ -1168,7 +1168,10 @@ async fn command_output_read_allows_same_agent_history_and_rejects_cross_agent_a
|
||||
)
|
||||
.expect("start source command run");
|
||||
let source_runtime = wait_for_agent_runtime_idle(&root, "code-prototype");
|
||||
assert!(matches!(source_runtime.phase.as_str(), "completed" | "planning"));
|
||||
assert!(matches!(
|
||||
source_runtime.phase.as_str(),
|
||||
"completed" | "planning"
|
||||
));
|
||||
let source_records = read_agent_db_records_for_test(&root);
|
||||
let source_receipt = source_records
|
||||
.iter()
|
||||
|
||||
@@ -5653,7 +5653,10 @@ setInterval(() => {{}}, 1000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(cancellation_output.contains(READY_SENTINEL), "{cancellation_output}");
|
||||
assert!(
|
||||
cancellation_output.contains(READY_SENTINEL),
|
||||
"{cancellation_output}"
|
||||
);
|
||||
terminate_process_sessions_for_run_at(&root, AGENT_ID, RUN_ID)
|
||||
.expect("cancel cleanup terminates active process");
|
||||
assert!(!has_active_process_sessions_at(&root).expect("cancel cleanup terminal"));
|
||||
|
||||
@@ -6202,8 +6202,14 @@ async fn agent_loop_writes_spec_findings_and_retries_generator() {
|
||||
assert!(first_agenda.contains("mode: initial"));
|
||||
assert!(first_agenda.contains("activeTasks: design-director"));
|
||||
assert!(first_agenda.contains("wave 1: design-director"));
|
||||
assert!(first_agenda.contains("wave 11: publish-package"), "{first_agenda}");
|
||||
assert!(first_agenda.contains("wave 12: preview-playtest"), "{first_agenda}");
|
||||
assert!(
|
||||
first_agenda.contains("wave 11: publish-package"),
|
||||
"{first_agenda}"
|
||||
);
|
||||
assert!(
|
||||
first_agenda.contains("wave 12: preview-playtest"),
|
||||
"{first_agenda}"
|
||||
);
|
||||
let first_task_graph: Value = serde_json::from_str(
|
||||
&fs::read_to_string(root.join(".agent/passes/pass-1/task-graph.json"))
|
||||
.expect("task graph 1"),
|
||||
|
||||
Reference in New Issue
Block a user