use super::*; use serde_json::Value; use sha2::{Digest, Sha256}; const SUPERVISOR_COLLABORATION_POLICY_SCHEMA_VERSION: &str = "game-creator-supervisor-collaboration-policy.v1"; const SUPERVISOR_COLLABORATION_CONTRACT_SCHEMA_VERSION: &str = "game-creator-supervisor-collaboration-contract.v1"; const SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_SCHEMA_VERSION: &str = "game-creator-supervisor-collaboration-policy-snapshot.v1"; const SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_BINDING_SCHEMA_VERSION: &str = "game-creator-supervisor-collaboration-policy-snapshot-binding.v1"; pub(crate) const SUPERVISOR_COLLABORATION_POLICY_RELATIVE_PATH: &str = ".agent/collaboration-policy.json"; const SUPERVISOR_COLLABORATION_POLICY_MAX_BYTES: usize = 16 * 1024; const SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_MAX_BYTES: usize = 24 * 1024; const SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_BINDING_MAX_BYTES: usize = 8 * 1024; const SUPERVISOR_COLLABORATION_POLICY_BINDING_INITIAL_BATCH: &str = "initial-collaboration-batch"; const SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_BATCH: &str = "legacy-provider-batch-contract"; const SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_CURRENT: &str = "legacy-current-project-policy"; const SUPERVISOR_COLLABORATION_MAX_STATIC_DELEGATES: usize = 3; const SUPERVISOR_COLLABORATION_MAX_ISOLATED_CHILDREN: usize = 3; const SUPERVISOR_COLLABORATION_MAX_ISOLATED_GROUPS_BEFORE_CLAIM: usize = 16; #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] pub(crate) enum SupervisorInitialCollaborationWave { #[default] Auto, Static, Isolated, Mixed, } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub(crate) struct SupervisorCollaborationPolicy { pub(crate) schema_version: String, #[serde(default)] pub(crate) required_initial_wave: SupervisorInitialCollaborationWave, #[serde(default)] pub(crate) min_static_delegates: usize, #[serde(default)] pub(crate) required_static_agent_ids: Vec, #[serde(default)] pub(crate) min_isolated_children: usize, #[serde(default, skip_serializing_if = "is_zero")] pub(crate) min_isolated_groups_before_claim: usize, #[serde(default = "default_orchestrator_only_after_delegation")] pub(crate) orchestrator_only_after_delegation: bool, } impl Default for SupervisorCollaborationPolicy { fn default() -> Self { Self { schema_version: SUPERVISOR_COLLABORATION_POLICY_SCHEMA_VERSION.to_string(), required_initial_wave: SupervisorInitialCollaborationWave::Auto, min_static_delegates: 0, required_static_agent_ids: Vec::new(), min_isolated_children: 0, min_isolated_groups_before_claim: 0, orchestrator_only_after_delegation: true, } } } fn autonomous_game_build_supervisor_collaboration_policy( _root: &Path, ) -> SupervisorCollaborationPolicy { SupervisorCollaborationPolicy::default() } #[derive(Clone, Debug)] struct SupervisorCollaborationUnboundPolicy { policy: SupervisorCollaborationPolicy, source: &'static str, project_policy_status: &'static str, project_policy_present: bool, } fn read_supervisor_collaboration_unbound_policy_for_run_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> Result { let (run_profile, _) = agent_runtime_run_profile_identity_at(root, parent_agent_id, parent_run_id, None, None)?; let autonomous_supervisor = parent_agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID && run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD; let policy_path = root.join(SUPERVISOR_COLLABORATION_POLICY_RELATIVE_PATH); match fs::symlink_metadata(&policy_path) { Ok(_) => { let policy = read_supervisor_collaboration_policy_at(root)?; return Ok(SupervisorCollaborationUnboundPolicy { policy, source: "project-policy-unbound", project_policy_status: "current", project_policy_present: true, }); } Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} Err(error) => { return Err(format!( "读取 Project Supervisor 协作策略文件状态失败:{error}" )); } } if autonomous_supervisor { return Ok(SupervisorCollaborationUnboundPolicy { policy: normalize_supervisor_collaboration_policy( autonomous_game_build_supervisor_collaboration_policy(root), )?, source: "autonomous-run-default", project_policy_status: "absent", project_policy_present: false, }); } Ok(SupervisorCollaborationUnboundPolicy { policy: SupervisorCollaborationPolicy::default(), source: "project-policy-unbound", project_policy_status: "current", project_policy_present: false, }) } #[derive(Clone, Debug, Default, Eq, PartialEq)] pub(crate) struct SupervisorCollaborationState { pub(crate) initial_static_agent_ids: Vec, pub(crate) isolated_group_count: usize, pub(crate) isolated_child_count: usize, pub(crate) max_isolated_children_per_group: usize, } impl SupervisorCollaborationState { pub(crate) fn has_collaboration(&self) -> bool { !self.initial_static_agent_ids.is_empty() || self.isolated_group_count > 0 } } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub(crate) struct SupervisorCollaborationContract { pub(crate) schema_version: String, pub(crate) policy: SupervisorCollaborationPolicy, pub(crate) policy_fingerprint: String, pub(crate) initial_wave: bool, pub(crate) initial_static_agent_ids: Vec, pub(crate) repair_delegate_count: usize, pub(crate) isolated_spawn_count: usize, pub(crate) isolated_child_count: usize, pub(crate) contract_fingerprint: String, } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub(crate) struct SupervisorCollaborationPolicySnapshot { pub(crate) schema_version: String, pub(crate) project_id: String, pub(crate) parent_agent_id: String, pub(crate) parent_run_id: String, pub(crate) bound_from: String, pub(crate) policy: SupervisorCollaborationPolicy, pub(crate) policy_fingerprint: String, pub(crate) snapshot_fingerprint: String, pub(crate) bound_at: u64, } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub(crate) struct SupervisorCollaborationPolicySnapshotBinding { pub(crate) schema_version: String, pub(crate) project_id: String, pub(crate) parent_agent_id: String, pub(crate) parent_run_id: String, pub(crate) bound_from: String, pub(crate) policy_fingerprint: String, pub(crate) snapshot_fingerprint: String, pub(crate) bound_at: u64, } #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct SupervisorCollaborationPolicyResolution { pub(crate) policy: SupervisorCollaborationPolicy, pub(crate) policy_fingerprint: String, pub(crate) snapshot_fingerprint: Option, pub(crate) binding_source: Option, pub(crate) source: &'static str, pub(crate) project_policy_status: &'static str, pub(crate) current_project_policy_fingerprint: Option, } #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct SupervisorCollaborationViolation { pub(crate) summary: String, pub(crate) detail: String, } #[derive(Clone, Debug, Default, Eq, PartialEq)] pub(crate) struct SupervisorCollaborationPreflight { pub(crate) contract: Option, pub(crate) force_durable_batch: bool, pub(crate) violation: Option, } #[derive(Clone, Debug, Default, Eq, PartialEq)] struct SupervisorCollaborationActionSummary { initial_static_agent_ids: Vec, repair_delegate_count: usize, isolated_spawn_count: usize, isolated_child_count: usize, has_project_mutation: bool, } impl SupervisorCollaborationActionSummary { fn has_collaboration_action(&self) -> bool { !self.initial_static_agent_ids.is_empty() || self.repair_delegate_count > 0 || self.isolated_spawn_count > 0 } } fn default_orchestrator_only_after_delegation() -> bool { true } fn is_zero(value: &usize) -> bool { *value == 0 } pub(crate) fn read_supervisor_collaboration_policy_at( root: &Path, ) -> Result { let policy = read_agent_runtime_json_sidecar_with_max_bytes::( root, SUPERVISOR_COLLABORATION_POLICY_RELATIVE_PATH, "Project Supervisor 协作策略", SUPERVISOR_COLLABORATION_POLICY_MAX_BYTES, )? .unwrap_or_default(); normalize_supervisor_collaboration_policy(policy) } pub(crate) fn write_supervisor_collaboration_policy_at( root: &Path, policy: SupervisorCollaborationPolicy, ) -> Result { let policy = normalize_supervisor_collaboration_policy(policy)?; write_agent_runtime_json_sidecar_with_max_bytes( root, SUPERVISOR_COLLABORATION_POLICY_RELATIVE_PATH, "Project Supervisor 协作策略", &policy, SUPERVISOR_COLLABORATION_POLICY_MAX_BYTES, )?; Ok(policy) } pub(crate) fn render_supervisor_collaboration_policy_for_prompt_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> Result { let resolution = resolve_supervisor_collaboration_policy_for_run_at(root, parent_agent_id, parent_run_id)?; serde_json::to_string_pretty(&resolution.policy) .map_err(|error| format!("序列化 Project Supervisor 协作策略失败:{error}")) } fn supervisor_collaboration_policy_snapshot_path_component(value: &str, fallback: &str) -> String { let normalized = agent_runtime_confirmation_path_component(value, fallback); if normalized == value { return normalized; } let readable = normalized.chars().take(80).collect::(); format!("{readable}--{:x}", Sha256::digest(value.as_bytes())) } fn supervisor_collaboration_policy_snapshot_relative_path( parent_agent_id: &str, parent_run_id: &str, ) -> String { format!( ".agent/runtime/collaboration-policy-snapshots/{}/{}.json", supervisor_collaboration_policy_snapshot_path_component(parent_agent_id, "agent"), supervisor_collaboration_policy_snapshot_path_component(parent_run_id, "run") ) } fn supervisor_collaboration_policy_snapshot_binding_relative_path( parent_agent_id: &str, parent_run_id: &str, ) -> String { format!( ".agent/runtime/collaboration-policy-snapshot-bindings/{}/{}.json", supervisor_collaboration_policy_snapshot_path_component(parent_agent_id, "agent"), supervisor_collaboration_policy_snapshot_path_component(parent_run_id, "run") ) } fn supervisor_collaboration_policy_snapshot_lock_id( parent_agent_id: &str, parent_run_id: &str, ) -> String { format!( "snapshot-{:x}", Sha256::digest(format!("{parent_agent_id}\0{parent_run_id}").as_bytes()) ) } pub(crate) fn supervisor_collaboration_policy_snapshot_path( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> PathBuf { root.join(supervisor_collaboration_policy_snapshot_relative_path( parent_agent_id, parent_run_id, )) } pub(crate) fn supervisor_collaboration_policy_snapshot_binding_path( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> PathBuf { root.join( supervisor_collaboration_policy_snapshot_binding_relative_path( parent_agent_id, parent_run_id, ), ) } fn supervisor_collaboration_policy_snapshot_fingerprint( snapshot: &SupervisorCollaborationPolicySnapshot, ) -> Result { let identity = serde_json::to_vec(&serde_json::json!({ "schemaVersion": snapshot.schema_version, "projectId": snapshot.project_id, "parentAgentId": snapshot.parent_agent_id, "parentRunId": snapshot.parent_run_id, "boundFrom": snapshot.bound_from, "policy": snapshot.policy, "policyFingerprint": snapshot.policy_fingerprint, })) .map_err(|error| format!("序列化 Project Supervisor 协作策略快照指纹失败:{error}"))?; Ok(format!("{:x}", Sha256::digest(identity))) } fn validate_supervisor_collaboration_policy_snapshot( root: &Path, snapshot: &SupervisorCollaborationPolicySnapshot, parent_agent_id: &str, parent_run_id: &str, ) -> Result<(), String> { if snapshot.schema_version != SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_SCHEMA_VERSION { return Err(format!( "不支持的 Project Supervisor 协作策略快照版本:{}", snapshot.schema_version )); } if parent_agent_id != GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID || snapshot.parent_agent_id != parent_agent_id || snapshot.parent_run_id != parent_run_id || parent_run_id.trim().is_empty() { return Err("Project Supervisor 协作策略快照父 run 身份不匹配".to_string()); } if snapshot.project_id != game_creator_agent_runtime_context_project_id(root)? { return Err("Project Supervisor 协作策略快照项目身份不匹配".to_string()); } if !matches!( snapshot.bound_from.as_str(), SUPERVISOR_COLLABORATION_POLICY_BINDING_INITIAL_BATCH | SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_BATCH | SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_CURRENT ) { return Err("Project Supervisor 协作策略快照绑定来源无效".to_string()); } let normalized_policy = normalize_supervisor_collaboration_policy(snapshot.policy.clone())?; if normalized_policy != snapshot.policy { return Err("Project Supervisor 协作策略快照包含未规范化策略".to_string()); } let policy_fingerprint = supervisor_collaboration_policy_fingerprint(&snapshot.policy)?; if snapshot.policy_fingerprint != policy_fingerprint { return Err("Project Supervisor 协作策略快照的策略指纹不匹配".to_string()); } if snapshot.bound_at == 0 || snapshot.snapshot_fingerprint != supervisor_collaboration_policy_snapshot_fingerprint(snapshot)? { return Err("Project Supervisor 协作策略快照身份指纹已变化".to_string()); } Ok(()) } fn supervisor_collaboration_policy_snapshot_binding( snapshot: &SupervisorCollaborationPolicySnapshot, ) -> SupervisorCollaborationPolicySnapshotBinding { SupervisorCollaborationPolicySnapshotBinding { schema_version: SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_BINDING_SCHEMA_VERSION.to_string(), project_id: snapshot.project_id.clone(), parent_agent_id: snapshot.parent_agent_id.clone(), parent_run_id: snapshot.parent_run_id.clone(), bound_from: snapshot.bound_from.clone(), policy_fingerprint: snapshot.policy_fingerprint.clone(), snapshot_fingerprint: snapshot.snapshot_fingerprint.clone(), bound_at: snapshot.bound_at, } } fn validate_supervisor_collaboration_policy_snapshot_binding( root: &Path, binding: &SupervisorCollaborationPolicySnapshotBinding, parent_agent_id: &str, parent_run_id: &str, ) -> Result<(), String> { if binding.schema_version != SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_BINDING_SCHEMA_VERSION { return Err(format!( "不支持的 Project Supervisor 协作策略快照绑定记录版本:{}", binding.schema_version )); } if parent_agent_id != GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID || binding.parent_agent_id != parent_agent_id || binding.parent_run_id != parent_run_id || parent_run_id.trim().is_empty() { return Err("Project Supervisor 协作策略快照绑定记录父 run 身份不匹配".to_string()); } if binding.project_id != game_creator_agent_runtime_context_project_id(root)? { return Err("Project Supervisor 协作策略快照绑定记录项目身份不匹配".to_string()); } if !matches!( binding.bound_from.as_str(), SUPERVISOR_COLLABORATION_POLICY_BINDING_INITIAL_BATCH | SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_BATCH | SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_CURRENT ) || !matches!(binding.policy_fingerprint.len(), 64) || !binding .policy_fingerprint .bytes() .all(|byte| byte.is_ascii_hexdigit()) || !matches!(binding.snapshot_fingerprint.len(), 64) || !binding .snapshot_fingerprint .bytes() .all(|byte| byte.is_ascii_hexdigit()) || binding.bound_at == 0 { return Err("Project Supervisor 协作策略快照绑定记录内容无效".to_string()); } Ok(()) } pub(crate) fn read_supervisor_collaboration_policy_snapshot_binding_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> Result, String> { let relative_path = supervisor_collaboration_policy_snapshot_binding_relative_path( parent_agent_id, parent_run_id, ); let Some(binding) = read_agent_runtime_json_sidecar_with_max_bytes( root, &relative_path, "Project Supervisor 协作策略快照绑定记录", SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_BINDING_MAX_BYTES, )? else { return Ok(None); }; validate_supervisor_collaboration_policy_snapshot_binding( root, &binding, parent_agent_id, parent_run_id, )?; Ok(Some(binding)) } fn write_supervisor_collaboration_policy_snapshot_binding_at( root: &Path, snapshot: &SupervisorCollaborationPolicySnapshot, ) -> Result { let binding = supervisor_collaboration_policy_snapshot_binding(snapshot); let relative_path = supervisor_collaboration_policy_snapshot_binding_relative_path( &snapshot.parent_agent_id, &snapshot.parent_run_id, ); write_agent_runtime_json_sidecar_with_max_bytes( root, &relative_path, "Project Supervisor 协作策略快照绑定记录", &binding, SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_BINDING_MAX_BYTES, )?; let persisted = read_supervisor_collaboration_policy_snapshot_binding_at( root, &snapshot.parent_agent_id, &snapshot.parent_run_id, )? .ok_or_else(|| "Project Supervisor 协作策略快照绑定记录写入后不存在".to_string())?; if persisted != binding { return Err("Project Supervisor 协作策略快照绑定记录并发写入后内容冲突".to_string()); } Ok(binding) } pub(crate) fn read_supervisor_collaboration_policy_snapshot_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> Result, String> { let relative_path = supervisor_collaboration_policy_snapshot_relative_path(parent_agent_id, parent_run_id); let Some(snapshot) = read_agent_runtime_json_sidecar_with_max_bytes( root, &relative_path, "Project Supervisor 协作策略快照", SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_MAX_BYTES, )? else { return Ok(None); }; validate_supervisor_collaboration_policy_snapshot( root, &snapshot, parent_agent_id, parent_run_id, )?; Ok(Some(snapshot)) } pub(crate) fn bind_supervisor_collaboration_policy_snapshot_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, policy: &SupervisorCollaborationPolicy, bound_from: &str, ) -> Result { let policy = normalize_supervisor_collaboration_policy(policy.clone())?; if !matches!( bound_from, SUPERVISOR_COLLABORATION_POLICY_BINDING_INITIAL_BATCH | SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_BATCH | SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_CURRENT ) { return Err("Project Supervisor 协作策略快照绑定来源无效".to_string()); } if parent_agent_id != GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID || parent_run_id.trim().is_empty() { return Err("只能为有效的 Project Supervisor 父 run 绑定协作策略快照".to_string()); } let lock_id = supervisor_collaboration_policy_snapshot_lock_id(parent_agent_id, parent_run_id); let _binding_lock = try_acquire_game_creator_agent_delegation_lock_with_wait( root, &lock_id, "collaboration-policy-snapshot", )? .ok_or_else(|| "Project Supervisor 协作策略快照并发绑定冲突:正被其他进程绑定".to_string())?; let existing_binding = read_supervisor_collaboration_policy_snapshot_binding_at( root, parent_agent_id, parent_run_id, )?; if let Some(existing) = read_supervisor_collaboration_policy_snapshot_at(root, parent_agent_id, parent_run_id)? { if existing.policy != policy { return Err("Project Supervisor 协作策略快照已绑定且与待恢复策略冲突".to_string()); } let expected_binding = supervisor_collaboration_policy_snapshot_binding(&existing); match existing_binding { Some(binding) if binding != expected_binding => { return Err("Project Supervisor 协作策略快照与绑定记录冲突".to_string()); } Some(_) => {} None => { write_supervisor_collaboration_policy_snapshot_binding_at(root, &existing)?; } } return Ok(existing); } let policy_fingerprint = supervisor_collaboration_policy_fingerprint(&policy)?; let (effective_bound_from, bound_at) = if let Some(binding) = existing_binding.as_ref() { if binding.policy_fingerprint != policy_fingerprint { return Err("Project Supervisor 协作策略快照绑定记录与待恢复策略冲突".to_string()); } let existing_batch_derived = matches!( binding.bound_from.as_str(), SUPERVISOR_COLLABORATION_POLICY_BINDING_INITIAL_BATCH | SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_BATCH ); let requested_batch_derived = matches!( bound_from, SUPERVISOR_COLLABORATION_POLICY_BINDING_INITIAL_BATCH | SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_BATCH ); if binding.bound_from != bound_from && !(existing_batch_derived && requested_batch_derived) { return Err("Project Supervisor 协作策略快照绑定来源与恢复来源冲突".to_string()); } (binding.bound_from.clone(), binding.bound_at) } else { (bound_from.to_string(), unix_timestamp()) }; let mut snapshot = SupervisorCollaborationPolicySnapshot { schema_version: SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_SCHEMA_VERSION.to_string(), project_id: game_creator_agent_runtime_context_project_id(root)?, parent_agent_id: parent_agent_id.to_string(), parent_run_id: parent_run_id.to_string(), bound_from: effective_bound_from, policy_fingerprint, policy, snapshot_fingerprint: String::new(), bound_at, }; snapshot.snapshot_fingerprint = supervisor_collaboration_policy_snapshot_fingerprint(&snapshot)?; if existing_binding.as_ref().is_some_and(|binding| { binding != &supervisor_collaboration_policy_snapshot_binding(&snapshot) }) { return Err("Project Supervisor 协作策略快照无法按原绑定记录恢复".to_string()); } let relative_path = supervisor_collaboration_policy_snapshot_relative_path(parent_agent_id, parent_run_id); write_agent_runtime_json_sidecar_with_max_bytes( root, &relative_path, "Project Supervisor 协作策略快照", &snapshot, SUPERVISOR_COLLABORATION_POLICY_SNAPSHOT_MAX_BYTES, )?; let persisted = read_supervisor_collaboration_policy_snapshot_at(root, parent_agent_id, parent_run_id)? .ok_or_else(|| "Project Supervisor 协作策略快照写入后不存在".to_string())?; if persisted != snapshot { return Err("Project Supervisor 协作策略快照并发绑定后内容冲突".to_string()); } if existing_binding.is_none() { write_supervisor_collaboration_policy_snapshot_binding_at(root, &snapshot)?; } Ok(snapshot) } pub(crate) fn bind_supervisor_collaboration_policy_snapshot_for_new_batch_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, policy: &SupervisorCollaborationPolicy, ) -> Result { let policy = normalize_supervisor_collaboration_policy(policy.clone())?; bind_supervisor_collaboration_policy_snapshot_at( root, parent_agent_id, parent_run_id, &policy, SUPERVISOR_COLLABORATION_POLICY_BINDING_INITIAL_BATCH, ) } pub(crate) fn recover_supervisor_collaboration_policy_snapshot_from_batch_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, policy: &SupervisorCollaborationPolicy, ) -> Result { bind_supervisor_collaboration_policy_snapshot_at( root, parent_agent_id, parent_run_id, policy, SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_BATCH, ) } pub(crate) fn resolve_supervisor_collaboration_policy_for_run_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> Result { if let Some(snapshot) = read_supervisor_collaboration_policy_snapshot_at(root, parent_agent_id, parent_run_id)? { let snapshot = bind_supervisor_collaboration_policy_snapshot_at( root, parent_agent_id, parent_run_id, &snapshot.policy, &snapshot.bound_from, )?; return supervisor_collaboration_policy_resolution_from_snapshot(root, snapshot); } let existing_binding = read_supervisor_collaboration_policy_snapshot_binding_at( root, parent_agent_id, parent_run_id, )?; if let Some(snapshot) = recover_supervisor_collaboration_policy_snapshot_from_pending_batch_if_any_at( root, parent_agent_id, parent_run_id, )? { return supervisor_collaboration_policy_resolution_from_snapshot(root, snapshot); } if existing_binding.is_some() { return Err( "Project Supervisor 协作策略快照绑定记录存在,但快照与可信 v2 batch 均不可恢复" .to_string(), ); } let unbound_policy = read_supervisor_collaboration_unbound_policy_for_run_at( root, parent_agent_id, parent_run_id, )?; let policy = unbound_policy.policy.clone(); let state = read_supervisor_collaboration_state_at(root, parent_agent_id, parent_run_id)?; if state.has_collaboration() { if !game_creator_agent_runtime_run_is_non_terminal_for_collaboration_migration_at( root, parent_agent_id, parent_run_id, )? { return Err( "只有仍在运行且没有可信 v2 contract 的旧 Project Supervisor 父 run 才能迁移当前项目策略" .to_string(), ); } let snapshot = bind_supervisor_collaboration_policy_snapshot_at( root, parent_agent_id, parent_run_id, &policy, SUPERVISOR_COLLABORATION_POLICY_BINDING_LEGACY_CURRENT, )?; return Ok(SupervisorCollaborationPolicyResolution { policy: snapshot.policy, policy_fingerprint: snapshot.policy_fingerprint, snapshot_fingerprint: Some(snapshot.snapshot_fingerprint), binding_source: Some(snapshot.bound_from), source: "legacy-run-migration", project_policy_status: "matched", current_project_policy_fingerprint: Some(supervisor_collaboration_policy_fingerprint( &policy, )?), }); } Ok(SupervisorCollaborationPolicyResolution { policy_fingerprint: supervisor_collaboration_policy_fingerprint(&policy)?, policy, snapshot_fingerprint: None, binding_source: None, source: unbound_policy.source, project_policy_status: unbound_policy.project_policy_status, current_project_policy_fingerprint: None, }) } fn supervisor_collaboration_policy_resolution_from_snapshot( root: &Path, snapshot: SupervisorCollaborationPolicySnapshot, ) -> Result { let current_policy = read_supervisor_collaboration_unbound_policy_for_run_at( root, &snapshot.parent_agent_id, &snapshot.parent_run_id, ); let (project_policy_status, current_project_policy_fingerprint) = match current_policy { Ok(current) => { let fingerprint = current .project_policy_present .then(|| supervisor_collaboration_policy_fingerprint(¤t.policy)) .transpose()?; let status = if current.policy == snapshot.policy { "matched" } else { "drifted" }; (status, fingerprint) } Err(_) => ("unreadable", None), }; Ok(SupervisorCollaborationPolicyResolution { policy: snapshot.policy, policy_fingerprint: snapshot.policy_fingerprint, snapshot_fingerprint: Some(snapshot.snapshot_fingerprint), binding_source: Some(snapshot.bound_from), source: "run-snapshot", project_policy_status, current_project_policy_fingerprint, }) } pub(crate) fn supervisor_collaboration_policy_status_for_run_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> Result { let resolution = resolve_supervisor_collaboration_policy_for_run_at(root, parent_agent_id, parent_run_id)?; Ok(format!( "source={} · bindingSource={} · policyFingerprint={} · snapshotFingerprint={} · projectPolicyStatus={} · currentProjectPolicyFingerprint={}", resolution.source, resolution.binding_source.as_deref().unwrap_or("none"), resolution.policy_fingerprint, resolution.snapshot_fingerprint.as_deref().unwrap_or("none"), resolution.project_policy_status, resolution .current_project_policy_fingerprint .as_deref() .unwrap_or("unavailable"), )) } pub(crate) fn read_supervisor_collaboration_state_at( root: &Path, parent_agent_id: &str, parent_run_id: &str, ) -> Result { let initial_static_agent_ids = static_delegate_target_agent_ids_at(root, parent_agent_id, parent_run_id)?; let isolated = isolated_agent_group_summary_at(root, parent_agent_id, parent_run_id)?; Ok(SupervisorCollaborationState { initial_static_agent_ids, isolated_group_count: isolated.group_count, isolated_child_count: isolated.child_count, max_isolated_children_per_group: isolated.max_child_count, }) } pub(crate) fn supervisor_collaboration_policy_fingerprint( policy: &SupervisorCollaborationPolicy, ) -> Result { let bytes = serde_json::to_vec(policy) .map_err(|error| format!("序列化 Project Supervisor 协作策略指纹失败:{error}"))?; Ok(format!("{:x}", Sha256::digest(bytes))) } pub(crate) fn preflight_supervisor_collaboration_plan( agent_id: &str, actions: &[AgentRuntimeToolAction], policy: &SupervisorCollaborationPolicy, state: &SupervisorCollaborationState, ) -> Result { if agent_id != GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID { return Ok(SupervisorCollaborationPreflight::default()); } let policy = normalize_supervisor_collaboration_policy(policy.clone())?; let initial_wave = !state.has_collaboration(); if actions.is_empty() { if initial_wave && supervisor_collaboration_policy_has_initial_requirements(&policy) { return Ok(SupervisorCollaborationPreflight { violation: Some(SupervisorCollaborationViolation { summary: "Project Supervisor 首批协作不能停留在计划更新".to_string(), detail: "当前父 run 尚无协作事实,且项目 policy 明确要求首批专业协作;首批不能停留在计划更新,必须在同一 Provider 批次完整提交协作。".to_string(), }), ..SupervisorCollaborationPreflight::default() }); } return Ok(SupervisorCollaborationPreflight::default()); } let summary = summarize_supervisor_collaboration_actions(actions)?; let has_collaboration_action = summary.has_collaboration_action(); if !initial_wave && has_collaboration_action && supervisor_collaboration_policy_has_initial_requirements(&policy) { if let Some(detail) = supervisor_collaboration_initial_wave_gap(&policy, state) { return Ok(SupervisorCollaborationPreflight { violation: Some(SupervisorCollaborationViolation { summary: "Project Supervisor 首批协作不能跨 Provider 批次补齐".to_string(), detail: format!( "当前父 run 已有部分首批协作事实,但仍不满足项目合同:{detail}。只能恢复原 Provider action 批次或进入人工核对,不能另起批次补齐。" ), }), ..SupervisorCollaborationPreflight::default() }); } } if policy.orchestrator_only_after_delegation && summary.has_project_mutation && (state.has_collaboration() || has_collaboration_action) { return Ok(SupervisorCollaborationPreflight { violation: Some(SupervisorCollaborationViolation { summary: "Project Supervisor 已进入协作编排,不能直接修改项目".to_string(), detail: "当前父 run 已有协作事实,或本批次正在创建协作;请把项目修改交给专业 Agent,Supervisor 只继续委派、读取、认领回执和验证。".to_string(), }), ..SupervisorCollaborationPreflight::default() }); } if initial_wave && supervisor_collaboration_policy_has_initial_requirements(&policy) && (has_collaboration_action || summary.has_project_mutation) { let candidate_state = SupervisorCollaborationState { initial_static_agent_ids: summary.initial_static_agent_ids.clone(), isolated_group_count: summary.isolated_spawn_count, isolated_child_count: summary.isolated_child_count, max_isolated_children_per_group: summary.isolated_child_count, }; if let Some(detail) = supervisor_collaboration_initial_wave_gap(&policy, &candidate_state) { return Ok(SupervisorCollaborationPreflight { violation: Some(SupervisorCollaborationViolation { summary: "Project Supervisor 首批协作不满足项目合同".to_string(), detail, }), ..SupervisorCollaborationPreflight::default() }); } } if !has_collaboration_action { return Ok(SupervisorCollaborationPreflight::default()); } let contract = build_supervisor_collaboration_contract(&policy, initial_wave, &summary)?; Ok(SupervisorCollaborationPreflight { contract: Some(contract), force_durable_batch: true, violation: None, }) } pub(crate) fn validate_supervisor_collaboration_contract( actions: &[AgentRuntimeToolAction], current_policy: &SupervisorCollaborationPolicy, contract: &SupervisorCollaborationContract, expected_initial_wave: Option, ) -> Result<(), String> { if contract.schema_version != SUPERVISOR_COLLABORATION_CONTRACT_SCHEMA_VERSION { return Err(format!( "不支持的 Project Supervisor 协作合同版本:{}", contract.schema_version )); } let policy = normalize_supervisor_collaboration_policy(current_policy.clone())?; if contract.policy != policy { return Err("Project Supervisor 协作策略在 Provider 批次执行前已变化".to_string()); } let policy_fingerprint = supervisor_collaboration_policy_fingerprint(&policy)?; if contract.policy_fingerprint != policy_fingerprint { return Err("Project Supervisor 协作合同的策略指纹不匹配".to_string()); } if expected_initial_wave.is_some_and(|expected| contract.initial_wave != expected) { return Err("Project Supervisor 协作合同的 initialWave 与当前父 run 不一致".to_string()); } let summary = summarize_supervisor_collaboration_actions(actions)?; if contract.initial_static_agent_ids != summary.initial_static_agent_ids || contract.repair_delegate_count != summary.repair_delegate_count || contract.isolated_spawn_count != summary.isolated_spawn_count || contract.isolated_child_count != summary.isolated_child_count { return Err("Project Supervisor 协作合同与 Provider 动作组成不一致".to_string()); } if policy.orchestrator_only_after_delegation && summary.has_project_mutation && summary.has_collaboration_action() { return Err("Project Supervisor 协作批次混入了项目 mutation".to_string()); } if contract.initial_wave && supervisor_collaboration_policy_has_initial_requirements(&policy) { let candidate_state = SupervisorCollaborationState { initial_static_agent_ids: summary.initial_static_agent_ids.clone(), isolated_group_count: summary.isolated_spawn_count, isolated_child_count: summary.isolated_child_count, max_isolated_children_per_group: summary.isolated_child_count, }; if let Some(detail) = supervisor_collaboration_initial_wave_gap(&policy, &candidate_state) { return Err(format!("Project Supervisor 协作合同不完整:{detail}")); } } let fingerprint = supervisor_collaboration_contract_fingerprint(contract)?; if contract.contract_fingerprint != fingerprint { return Err("Project Supervisor 协作合同指纹已变化".to_string()); } Ok(()) } pub(crate) fn supervisor_collaboration_completion_gap( policy: &SupervisorCollaborationPolicy, state: &SupervisorCollaborationState, ) -> Option { if let Some(detail) = supervisor_collaboration_initial_wave_gap(policy, state) { return Some(detail); } if state.isolated_group_count < policy.min_isolated_groups_before_claim { return Some(format!( "minIsolatedGroupsBeforeClaim={} · isolatedGroups={}/{}", policy.min_isolated_groups_before_claim, state.isolated_group_count, policy.min_isolated_groups_before_claim, )); } None } pub(crate) fn supervisor_collaboration_initial_wave_gap( policy: &SupervisorCollaborationPolicy, state: &SupervisorCollaborationState, ) -> Option { let required_static_count = required_static_delegate_count(policy); let required_isolated_count = required_isolated_child_count(policy); let actual_static = state .initial_static_agent_ids .iter() .cloned() .collect::>(); let missing_static_agents = policy .required_static_agent_ids .iter() .filter(|agent_id| !actual_static.contains(*agent_id)) .cloned() .collect::>(); if actual_static.len() >= required_static_count && state.max_isolated_children_per_group >= required_isolated_count && missing_static_agents.is_empty() { return None; } Some(format!( "requiredInitialWave={:?} · static={}/{} · isolatedChildrenPerGroup={}/{} · isolatedChildrenTotal={} · missingStaticAgents={}", policy.required_initial_wave, actual_static.len(), required_static_count, state.max_isolated_children_per_group, required_isolated_count, state.isolated_child_count, if missing_static_agents.is_empty() { "none".to_string() } else { missing_static_agents.join(",") } )) } pub(crate) fn supervisor_collaboration_policy_has_initial_requirements( policy: &SupervisorCollaborationPolicy, ) -> bool { required_static_delegate_count(policy) > 0 || required_isolated_child_count(policy) > 0 } pub(crate) fn is_supervisor_orchestrator_project_mutation_tool(tool: &str) -> bool { matches!( tool.trim(), "file.write" | "file.patch" | "file.delete" | "project.patchset" | "project.restore" | "project.git_commit" | "command.exec" | "command.start" | "command.stdin" | "canvas.asset_generate" ) } fn normalize_supervisor_collaboration_policy( mut policy: SupervisorCollaborationPolicy, ) -> Result { if policy.schema_version != SUPERVISOR_COLLABORATION_POLICY_SCHEMA_VERSION { return Err(format!( "不支持的 Project Supervisor 协作策略版本:{}", policy.schema_version )); } if policy.min_static_delegates > SUPERVISOR_COLLABORATION_MAX_STATIC_DELEGATES { return Err(format!( "minStaticDelegates 不能超过 {SUPERVISOR_COLLABORATION_MAX_STATIC_DELEGATES}" )); } if policy.min_isolated_children > SUPERVISOR_COLLABORATION_MAX_ISOLATED_CHILDREN { return Err(format!( "minIsolatedChildren 不能超过 {SUPERVISOR_COLLABORATION_MAX_ISOLATED_CHILDREN}" )); } if policy.min_isolated_groups_before_claim > SUPERVISOR_COLLABORATION_MAX_ISOLATED_GROUPS_BEFORE_CLAIM { return Err(format!( "minIsolatedGroupsBeforeClaim 不能超过 {SUPERVISOR_COLLABORATION_MAX_ISOLATED_GROUPS_BEFORE_CLAIM}" )); } let mut required_ids = std::collections::BTreeSet::new(); for agent_id in policy.required_static_agent_ids { let agent_id = normalize_game_creator_runtime_agent_id(&agent_id)?; if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID || agent_id.starts_with("child-") { return Err( "requiredStaticAgentIds 只能包含静态专业 Agent,不能包含 Supervisor 或动态 child" .to_string(), ); } required_ids.insert(agent_id); } if required_ids.len() > SUPERVISOR_COLLABORATION_MAX_STATIC_DELEGATES { return Err(format!( "requiredStaticAgentIds 不能超过 {SUPERVISOR_COLLABORATION_MAX_STATIC_DELEGATES} 项" )); } policy.required_static_agent_ids = required_ids.into_iter().collect(); let required_action_slots = required_static_delegate_count(&policy) .saturating_add(usize::from(required_isolated_child_count(&policy) > 0)); if required_action_slots > AGENT_RUNTIME_BACKGROUND_TOOL_ACTION_LIMIT { return Err(format!( "协作策略至少需要 {required_action_slots} 个 Provider action,超过单批次上限 {AGENT_RUNTIME_BACKGROUND_TOOL_ACTION_LIMIT}" )); } Ok(policy) } fn required_static_delegate_count(policy: &SupervisorCollaborationPolicy) -> usize { let mode_minimum = usize::from(matches!( policy.required_initial_wave, SupervisorInitialCollaborationWave::Static | SupervisorInitialCollaborationWave::Mixed )); mode_minimum .max(policy.min_static_delegates) .max(policy.required_static_agent_ids.len()) } fn required_isolated_child_count(policy: &SupervisorCollaborationPolicy) -> usize { let mode_minimum = usize::from(matches!( policy.required_initial_wave, SupervisorInitialCollaborationWave::Isolated | SupervisorInitialCollaborationWave::Mixed )); mode_minimum.max(policy.min_isolated_children) } fn summarize_supervisor_collaboration_actions( actions: &[AgentRuntimeToolAction], ) -> Result { let mut summary = SupervisorCollaborationActionSummary::default(); let mut static_agents = std::collections::BTreeSet::new(); for action in actions { let tool = action.tool.trim(); summary.has_project_mutation |= is_supervisor_orchestrator_project_mutation_tool(tool); match tool { "agent.delegate" => { let input = action.input.as_object().ok_or_else(|| { "Project Supervisor agent.delegate input 必须是 object".to_string() })?; let agent_id = input .get("agentId") .or_else(|| input.get("agent_id")) .and_then(Value::as_str) .ok_or_else(|| "Project Supervisor agent.delegate 缺少 agentId".to_string())?; let agent_id = normalize_game_creator_runtime_agent_id(agent_id)?; if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID || agent_id.starts_with("child-") { return Err( "Project Supervisor agent.delegate 只能指向静态专业 Agent".to_string() ); } let expected_artifacts = input .get("expectedArtifacts") .or_else(|| input.get("expected_artifacts")) .and_then(Value::as_array) .into_iter() .flatten(); for artifact in expected_artifacts.filter_map(Value::as_str) { let expected_owner = match artifact.trim() { "assets/art-spec.png" => Some("art-director"), "assets/ui-prototype.png" => Some("design-foundation"), "assets/art-spritesheet.png" => Some("art-asset-plan"), _ => None, }; if expected_owner.is_some_and(|owner| owner != agent_id) { return Err(format!( "Project Supervisor 视觉委派不能跨 owner 声明固定产物:{artifact} 必须交给 {}", expected_owner.expect("checked visual artifact owner") )); } } let repair = input .get("repairOfDelegationId") .or_else(|| input.get("repair_of_delegation_id")) .and_then(Value::as_str) .is_some_and(|value| !value.trim().is_empty()); if repair { summary.repair_delegate_count = summary.repair_delegate_count.saturating_add(1); } else { static_agents.insert(agent_id); } } "agent.spawn_isolated" => { let input = action.input.as_object().ok_or_else(|| { "Project Supervisor agent.spawn_isolated input 必须是 object".to_string() })?; if input.get("joinMode").and_then(Value::as_str) != Some("all") { return Err( "Project Supervisor agent.spawn_isolated 只允许 joinMode=all".to_string(), ); } let children = input .get("children") .and_then(Value::as_array) .ok_or_else(|| { "Project Supervisor agent.spawn_isolated 缺少 children".to_string() })?; if children.is_empty() || children.len() > SUPERVISOR_COLLABORATION_MAX_ISOLATED_CHILDREN { return Err(format!( "Project Supervisor agent.spawn_isolated children 必须在 1-{SUPERVISOR_COLLABORATION_MAX_ISOLATED_CHILDREN} 之间" )); } summary.isolated_spawn_count = summary.isolated_spawn_count.saturating_add(1); if summary.isolated_spawn_count > 1 { return Err( "Project Supervisor 单个 Provider 批次最多包含一个 agent.spawn_isolated" .to_string(), ); } summary.isolated_child_count = children.len(); } _ => {} } } summary.initial_static_agent_ids = static_agents.into_iter().collect(); Ok(summary) } pub(crate) fn supervisor_collaboration_actions_require_contract( actions: &[AgentRuntimeToolAction], ) -> Result { summarize_supervisor_collaboration_actions(actions) .map(|summary| summary.has_collaboration_action()) } fn build_supervisor_collaboration_contract( policy: &SupervisorCollaborationPolicy, initial_wave: bool, summary: &SupervisorCollaborationActionSummary, ) -> Result { let policy_fingerprint = supervisor_collaboration_policy_fingerprint(policy)?; let mut contract = SupervisorCollaborationContract { schema_version: SUPERVISOR_COLLABORATION_CONTRACT_SCHEMA_VERSION.to_string(), policy: policy.clone(), policy_fingerprint, initial_wave, initial_static_agent_ids: summary.initial_static_agent_ids.clone(), repair_delegate_count: summary.repair_delegate_count, isolated_spawn_count: summary.isolated_spawn_count, isolated_child_count: summary.isolated_child_count, contract_fingerprint: String::new(), }; contract.contract_fingerprint = supervisor_collaboration_contract_fingerprint(&contract)?; Ok(contract) } fn supervisor_collaboration_contract_fingerprint( contract: &SupervisorCollaborationContract, ) -> Result { let identity = serde_json::to_vec(&serde_json::json!({ "schemaVersion": contract.schema_version, "policy": contract.policy, "policyFingerprint": contract.policy_fingerprint, "initialWave": contract.initial_wave, "initialStaticAgentIds": contract.initial_static_agent_ids, "repairDelegateCount": contract.repair_delegate_count, "isolatedSpawnCount": contract.isolated_spawn_count, "isolatedChildCount": contract.isolated_child_count, })) .map_err(|error| format!("序列化 Project Supervisor 协作合同指纹失败:{error}"))?; Ok(format!("{:x}", Sha256::digest(identity))) } #[cfg(test)] mod tests { use super::*; fn delegate(agent_id: &str, repair: Option<&str>) -> AgentRuntimeToolAction { AgentRuntimeToolAction { tool: "agent.delegate".to_string(), reason: Some("委派专业交付".to_string()), input: serde_json::json!({ "agentId": agent_id, "task": "完成专业交付", "acceptanceCriteria": ["交付可验收"], "expectedArtifacts": [], "repairOfDelegationId": repair, "runId": null, }), } } fn spawn(children: usize) -> AgentRuntimeToolAction { AgentRuntimeToolAction { tool: "agent.spawn_isolated".to_string(), reason: Some("并行隔离检查".to_string()), input: serde_json::json!({ "children": (0..children).map(|index| serde_json::json!({ "templateAgentId": "code-prototype", "task": format!("检查 {index}"), "acceptanceCriteria": ["检查完成"], "expectedArtifacts": [], "writeScopes": [format!("game/check-{index}/**")], })).collect::>(), "joinMode": "all", }), } } fn verify() -> AgentRuntimeToolAction { AgentRuntimeToolAction { tool: "project.verify".to_string(), reason: Some("验证项目".to_string()), input: serde_json::json!({ "script": "test", "expectedCommand": "cargo test", "timeoutSeconds": 120, }), } } fn mixed_policy() -> SupervisorCollaborationPolicy { SupervisorCollaborationPolicy { required_initial_wave: SupervisorInitialCollaborationWave::Mixed, min_static_delegates: 2, required_static_agent_ids: vec![ "art-director".to_string(), "design-director".to_string(), ], min_isolated_children: 2, ..SupervisorCollaborationPolicy::default() } } #[test] fn supervisor_collaboration_policy_default_json_and_fingerprints_remain_v1_compatible() { const OLD_DEFAULT_POLICY_JSON: &str = "{\"schemaVersion\":\"game-creator-supervisor-collaboration-policy.v1\",\"requiredInitialWave\":\"auto\",\"minStaticDelegates\":0,\"requiredStaticAgentIds\":[],\"minIsolatedChildren\":0,\"orchestratorOnlyAfterDelegation\":true}"; const OLD_DEFAULT_POLICY_FINGERPRINT: &str = "9962617595c7d20ea24d7b18b4f77eac2160edaf0daaf83960d5c92014e8bd2b"; const OLD_DEFAULT_CONTRACT_FINGERPRINT: &str = "90845e3e0f817ac99fb4f4574eae1662235739752268c4d9884f815518eab2bb"; let policy = SupervisorCollaborationPolicy::default(); assert_eq!( serde_json::to_string(&policy).expect("serialize default policy"), OLD_DEFAULT_POLICY_JSON ); assert_eq!( serde_json::from_str::(OLD_DEFAULT_POLICY_JSON) .expect("deserialize old default policy"), policy ); assert_eq!( supervisor_collaboration_policy_fingerprint(&policy) .expect("fingerprint default policy"), OLD_DEFAULT_POLICY_FINGERPRINT ); let actions = vec![delegate("design-director", None)]; let contract = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &actions, &policy, &SupervisorCollaborationState::default(), ) .expect("preflight default policy") .contract .expect("default policy contract"); let contract_json = serde_json::to_value(&contract).expect("serialize default contract"); assert!(contract_json["policy"] .get("minIsolatedGroupsBeforeClaim") .is_none()); assert_eq!(contract.policy_fingerprint, OLD_DEFAULT_POLICY_FINGERPRINT); assert_eq!( contract.contract_fingerprint, OLD_DEFAULT_CONTRACT_FINGERPRINT ); } #[test] fn supervisor_collaboration_policy_initial_mixed_wave_allows_one_staged_group() { let policy = SupervisorCollaborationPolicy { min_isolated_groups_before_claim: 2, ..mixed_policy() }; let actions = vec![ delegate("design-director", None), delegate("art-director", None), spawn(2), ]; let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &actions, &policy, &SupervisorCollaborationState::default(), ) .expect("preflight staged mixed wave"); assert!(result.violation.is_none()); let contract = result.contract.expect("staged mixed contract"); validate_supervisor_collaboration_contract(&actions, &policy, &contract, Some(true)) .expect("validate staged mixed contract"); } #[test] fn supervisor_collaboration_policy_completion_requires_staged_isolated_groups() { let policy = SupervisorCollaborationPolicy { min_isolated_groups_before_claim: 2, ..mixed_policy() }; let mut state = SupervisorCollaborationState { initial_static_agent_ids: vec![ "art-director".to_string(), "design-director".to_string(), ], isolated_group_count: 1, isolated_child_count: 2, max_isolated_children_per_group: 2, }; let gap = supervisor_collaboration_completion_gap(&policy, &state) .expect("one isolated group must leave a completion gap"); assert!(gap.contains("isolatedGroups=1/2")); state.isolated_group_count = 2; state.isolated_child_count = 4; assert!(supervisor_collaboration_completion_gap(&policy, &state).is_none()); } #[test] fn supervisor_collaboration_policy_nonzero_group_requirement_round_trips_stably() { let policy = SupervisorCollaborationPolicy { min_isolated_groups_before_claim: 2, ..SupervisorCollaborationPolicy::default() }; let json = serde_json::to_string(&policy).expect("serialize nonzero group policy"); assert!(json.contains("\"minIsolatedGroupsBeforeClaim\":2")); let decoded = serde_json::from_str::(&json) .expect("deserialize nonzero group policy"); assert_eq!(decoded, policy); assert_eq!( supervisor_collaboration_policy_fingerprint(&policy) .expect("fingerprint nonzero group policy"), "7c2764dde3a5be4a62e11f0acdb7e1b2f6445377f1afdc548e1b7e137ce9fc84" ); assert_eq!( supervisor_collaboration_policy_fingerprint(&decoded) .expect("fingerprint round-tripped group policy"), "7c2764dde3a5be4a62e11f0acdb7e1b2f6445377f1afdc548e1b7e137ce9fc84" ); } #[test] fn supervisor_collaboration_policy_rejects_excessive_group_requirement() { let policy = SupervisorCollaborationPolicy { min_isolated_groups_before_claim: SUPERVISOR_COLLABORATION_MAX_ISOLATED_GROUPS_BEFORE_CLAIM + 1, ..SupervisorCollaborationPolicy::default() }; let error = normalize_supervisor_collaboration_policy(policy) .expect_err("excessive group requirement must fail"); assert!(error.contains("minIsolatedGroupsBeforeClaim 不能超过 16")); } #[test] fn supervisor_collaboration_policy_blocks_empty_required_initial_wave() { let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[], &mixed_policy(), &SupervisorCollaborationState::default(), ) .expect("preflight empty required initial wave"); let violation = result .violation .expect("required initial wave must reject empty actions"); assert_eq!( violation.summary, "Project Supervisor 首批协作不能停留在计划更新" ); assert!(violation.detail.contains("首批不能停留在计划更新")); assert!(violation .detail .contains("必须在同一 Provider 批次完整提交协作")); assert!(result.contract.is_none()); assert!(!result.force_durable_batch); } #[test] fn supervisor_collaboration_policy_allows_empty_actions_after_collaboration() { let state = SupervisorCollaborationState { initial_static_agent_ids: vec!["design-director".to_string()], ..SupervisorCollaborationState::default() }; let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[], &mixed_policy(), &state, ) .expect("preflight empty actions after collaboration"); assert_eq!(result, SupervisorCollaborationPreflight::default()); } #[test] fn supervisor_collaboration_policy_allows_empty_actions_without_initial_requirements() { let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[], &SupervisorCollaborationPolicy::default(), &SupervisorCollaborationState::default(), ) .expect("preflight empty actions without initial requirements"); assert_eq!(result, SupervisorCollaborationPreflight::default()); } #[test] fn supervisor_collaboration_policy_blocks_partial_mixed_wave() { let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[delegate("design-director", None)], &mixed_policy(), &SupervisorCollaborationState::default(), ) .expect("preflight partial mixed wave"); assert!(result.violation.is_some()); assert!(result.contract.is_none()); assert!(!result.force_durable_batch); } #[test] fn supervisor_collaboration_policy_accepts_complete_mixed_wave() { let actions = vec![ delegate("design-director", None), delegate("art-director", None), spawn(2), ]; let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &actions, &mixed_policy(), &SupervisorCollaborationState::default(), ) .expect("preflight complete mixed wave"); assert!(result.violation.is_none()); assert!(result.force_durable_batch); let contract = result.contract.expect("durable collaboration contract"); validate_supervisor_collaboration_contract( &actions, &mixed_policy(), &contract, Some(true), ) .expect("validate complete mixed contract"); } #[test] fn supervisor_collaboration_policy_allows_later_isolated_group_after_complete_wave() { let state = SupervisorCollaborationState { initial_static_agent_ids: vec![ "art-director".to_string(), "design-director".to_string(), ], isolated_group_count: 1, isolated_child_count: 2, max_isolated_children_per_group: 2, }; let actions = vec![spawn(1)]; let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &actions, &mixed_policy(), &state, ) .expect("preflight later isolated group"); assert!(result.violation.is_none()); assert!(result.force_durable_batch); let contract = result.contract.expect("later collaboration contract"); assert!(!contract.initial_wave); assert_eq!(contract.isolated_spawn_count, 1); assert_eq!(contract.isolated_child_count, 1); validate_supervisor_collaboration_contract( &actions, &mixed_policy(), &contract, Some(false), ) .expect("validate later isolated contract"); } #[test] fn supervisor_collaboration_policy_rejects_cross_batch_initial_wave_completion() { let state = SupervisorCollaborationState { initial_static_agent_ids: vec!["design-director".to_string()], ..SupervisorCollaborationState::default() }; let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[delegate("art-director", None), spawn(2)], &mixed_policy(), &state, ) .expect("preflight cross-batch initial wave completion"); let violation = result .violation .expect("partial initial wave must not be completed by a new batch"); assert!(violation.summary.contains("不能跨 Provider 批次补齐")); assert!(result.contract.is_none()); assert!(!result.force_durable_batch); } #[test] fn supervisor_collaboration_policy_blocks_mutation_with_collaboration() { let mut actions = vec![delegate("design-director", None)]; actions.push(AgentRuntimeToolAction { tool: "file.write".to_string(), reason: Some("总控直接写项目".to_string()), input: serde_json::json!({"path":"game/out.txt","content":"blocked"}), }); let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &actions, &SupervisorCollaborationPolicy::default(), &SupervisorCollaborationState::default(), ) .expect("preflight collaboration mutation"); assert!(result.violation.is_some()); } #[test] fn supervisor_collaboration_policy_allows_repair_and_verify_after_delegation() { let state = SupervisorCollaborationState { initial_static_agent_ids: vec!["design-director".to_string()], isolated_group_count: 0, isolated_child_count: 0, max_isolated_children_per_group: 0, }; let actions = vec![delegate("design-director", Some("delivery-1")), verify()]; let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &actions, &SupervisorCollaborationPolicy::default(), &state, ) .expect("preflight repair and verify"); assert!(result.violation.is_none()); assert!(result.force_durable_batch); } #[test] fn supervisor_collaboration_policy_rejects_contract_fingerprint_tampering() { let actions = vec![delegate("design-director", None)]; let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &actions, &SupervisorCollaborationPolicy::default(), &SupervisorCollaborationState::default(), ) .expect("preflight contract"); let mut contract = result.contract.expect("contract"); contract.contract_fingerprint = "0".repeat(64); assert!(validate_supervisor_collaboration_contract( &actions, &SupervisorCollaborationPolicy::default(), &contract, Some(true), ) .expect_err("tampered contract must fail") .contains("合同指纹")); } #[test] fn supervisor_collaboration_policy_default_has_no_completion_gap() { assert!(supervisor_collaboration_completion_gap( &SupervisorCollaborationPolicy::default(), &SupervisorCollaborationState::default(), ) .is_none()); } #[test] fn supervisor_collaboration_policy_classifies_process_stdin_as_mutation() { assert!(is_supervisor_orchestrator_project_mutation_tool( "command.stdin" )); assert!(!is_supervisor_orchestrator_project_mutation_tool( "command.poll" )); assert!(!is_supervisor_orchestrator_project_mutation_tool( "command.terminate" )); } #[test] fn supervisor_collaboration_policy_requires_isolated_minimum_in_one_group() { let policy = SupervisorCollaborationPolicy { required_initial_wave: SupervisorInitialCollaborationWave::Isolated, min_isolated_children: 3, ..SupervisorCollaborationPolicy::default() }; let split_groups = SupervisorCollaborationState { isolated_group_count: 3, isolated_child_count: 3, max_isolated_children_per_group: 1, ..SupervisorCollaborationState::default() }; assert!(supervisor_collaboration_completion_gap(&policy, &split_groups).is_some()); let complete_group = SupervisorCollaborationState { isolated_group_count: 1, isolated_child_count: 3, max_isolated_children_per_group: 3, ..SupervisorCollaborationState::default() }; assert!(supervisor_collaboration_completion_gap(&policy, &complete_group).is_none()); } #[test] fn supervisor_collaboration_policy_rejects_multiple_isolated_spawns_per_batch() { let error = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[spawn(1), spawn(1)], &SupervisorCollaborationPolicy::default(), &SupervisorCollaborationState::default(), ) .expect_err("multiple isolated groups must not share one provider batch"); assert!(error.contains("最多包含一个")); } #[test] fn supervisor_collaboration_policy_classifies_snake_case_repair_as_repair() { let mut action = delegate("design-director", None); let input = action.input.as_object_mut().expect("delegate input"); input.remove("repairOfDelegationId"); input.insert( "repair_of_delegation_id".to_string(), Value::String("delivery-original".to_string()), ); let result = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[action], &SupervisorCollaborationPolicy::default(), &SupervisorCollaborationState::default(), ) .expect("preflight snake-case repair"); let contract = result.contract.expect("repair contract"); assert!(contract.initial_static_agent_ids.is_empty()); assert_eq!(contract.repair_delegate_count, 1); } #[test] fn supervisor_collaboration_policy_rejects_dynamic_child_as_static_delegate() { let error = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[delegate("child-code-prototype-1", None)], &SupervisorCollaborationPolicy::default(), &SupervisorCollaborationState::default(), ) .expect_err("dynamic child must not count as static delegate"); assert!(error.contains("静态专业 Agent")); } #[test] fn supervisor_collaboration_policy_rejects_cross_owner_visual_artifacts() { let mut action = delegate("art-director", None); action.input["expectedArtifacts"] = serde_json::json!([ "assets/art-spec.png", "assets/ui-prototype.png", "assets/art-spritesheet.png" ]); let error = preflight_supervisor_collaboration_plan( GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, &[action], &SupervisorCollaborationPolicy::default(), &SupervisorCollaborationState::default(), ) .expect_err("cross-owner visual artifacts must fail before dispatch"); assert!(error.contains("不能跨 owner")); assert!(error.contains("assets/ui-prototype.png")); assert!(error.contains("design-foundation")); } }