From 3ecff3f524f1367506405847dca60ecda345a030 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:18:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=9D=E7=95=99=E4=BA=A4=E4=BB=98=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E5=BA=8F=E5=88=97=E5=8C=96=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充交付账本和执行状态的 serde 原因 通过 Direct 执行回归测试 --- .../src-tauri/src/agent/direct_delivery.rs | 38 ++++++++++--------- .../src-tauri/src/agent/direct_execution.rs | 17 ++++++--- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_delivery.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_delivery.rs index 88ce1af67..da2c19017 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_delivery.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_delivery.rs @@ -301,13 +301,13 @@ struct InitialDelivery { fn initial_delivery_path(root: &Path) -> Result { let root = root .canonicalize() - .map_err(|_| "delivery-project-unavailable")?; + .map_err(|error| format!("delivery-project-unavailable:{error}"))?; let host = crate::game_creator_runtime_config_dir() .ok_or("delivery-host-unavailable: 缺少客户端私有目录")?; crate::ensure_game_creator_private_directory_tree(&host, "客户端交付目录")?; let host = host .canonicalize() - .map_err(|_| "delivery-host-unavailable")?; + .map_err(|error| format!("delivery-host-unavailable:{error}"))?; if host.starts_with(&root) { return Err("delivery-host-invalid: 权威交付状态不能位于项目内".into()); } @@ -322,14 +322,14 @@ fn initial_delivery_path(root: &Path) -> Result { fn read_initial_delivery(path: &Path, project_id: &str) -> Result, String> { if !path .try_exists() - .map_err(|_| "delivery-initial-state-unavailable")? + .map_err(|error| format!("delivery-initial-state-unavailable:{error}"))? { return Ok(None); } let value: InitialDelivery = serde_json::from_str( &crate::read_game_creator_private_file_to_string(path, "首次交付状态", 16 * 1024)?, ) - .map_err(|_| "delivery-initial-state-invalid")?; + .map_err(|error| format!("delivery-initial-state-invalid:{error}"))?; if value.schema_version != "agc-initial-web-delivery.v1" || value.project_id != project_id { return Err("delivery-initial-state-identity".into()); } @@ -358,7 +358,7 @@ fn initial_requirement_at( let receipt = resolve_local_project_path(root, ".agent/runtime/web-scaffold-preparation.json")?; if !receipt .try_exists() - .map_err(|_| "delivery-scaffold-receipt-unavailable")? + .map_err(|error| format!("delivery-scaffold-receipt-unavailable:{error}"))? { return Ok(false); } @@ -366,12 +366,12 @@ fn initial_requirement_at( let mut bytes = Vec::new(); file.take(16 * 1024 + 1) .read_to_end(&mut bytes) - .map_err(|_| "delivery-scaffold-receipt-unavailable")?; + .map_err(|error| format!("delivery-scaffold-receipt-unavailable:{error}"))?; if bytes.len() > 16 * 1024 { return Err("delivery-scaffold-receipt-invalid".into()); } - let receipt: Value = - serde_json::from_slice(&bytes).map_err(|_| "delivery-scaffold-receipt-invalid")?; + let receipt: Value = serde_json::from_slice(&bytes) + .map_err(|error| format!("delivery-scaffold-receipt-invalid:{error}"))?; let expected: BTreeMap<_, _> = crate::project::trusted_web_scaffold_files() .into_iter() .map(|(path, content)| (path, format!("{:x}", Sha256::digest(content.as_bytes())))) @@ -379,7 +379,8 @@ fn initial_requirement_at( if receipt["schemaVersion"] != "agc-web-scaffold-preparation.v1" || receipt["projectId"] != project.project_id || receipt["templateHashes"] - != serde_json::to_value(expected).map_err(|_| "delivery-scaffold-template-invalid")? + != serde_json::to_value(expected) + .map_err(|error| format!("delivery-scaffold-template-invalid:{error}"))? { return Err("delivery-scaffold-receipt-invalid".into()); } @@ -390,7 +391,7 @@ fn initial_requirement_at( project_id: project.project_id, completed: false, }) - .map_err(|_| "delivery-initial-state-invalid")?, + .map_err(|error| format!("delivery-initial-state-invalid:{error}"))?, "首次交付状态", )?; Ok(true) @@ -423,7 +424,7 @@ fn mark_initial_delivered(root: &Path, ledger: &ExecutionLedger) -> Result<(), S project_id: ledger.project_id.clone(), completed: true, }) - .map_err(|_| "delivery-initial-state-invalid")?, + .map_err(|error| format!("delivery-initial-state-invalid:{error}"))?, "首次交付状态", ) } @@ -438,18 +439,18 @@ pub(super) async fn register_contract( let input = input.clone(); tokio::task::spawn_blocking(move || { let state = session.snapshot()?; - if root.canonicalize().map_err(|_| "delivery-project-unavailable")? != session.root { return Err("delivery-project-identity".into()); } + if root.canonicalize().map_err(|error| format!("delivery-project-unavailable:{error}"))? != session.root { return Err("delivery-project-identity".into()); } let new_web = state.contract.as_ref().and_then(|value| value["newWebGame"].as_bool()).unwrap_or(state.requires_contract); let mut contract = normalized_contract(&input, new_web)?; contract.initial_artifact_hashes = if let Some(existing) = &state.contract { - serde_json::from_value::(existing.clone()).map_err(|_| "delivery-frozen-contract-invalid")?.initial_artifact_hashes + serde_json::from_value::(existing.clone()).map_err(|error| format!("delivery-frozen-contract-invalid:{error}"))?.initial_artifact_hashes } else { contract.requirements.iter().filter_map(|criterion| match criterion { Criterion::Artifact {id,path} => Some((id.clone(),artifact_hash(&root,path).ok())), _ => None, }).collect() }; - let value = serde_json::to_value(contract).map_err(|_| "delivery-contract-invalid")?; + let value = serde_json::to_value(contract).map_err(|error| format!("delivery-contract-invalid:{error}"))?; let frozen = session.freeze_contract(value)?; Ok(json!({"status":"frozen","contract":frozen,"next":"只推进已登记范围;使用托管构建/验证和固定场景提供真实证据"})) }).await.map_err(|error| format!("delivery-contract-worker-exited:{error}"))? @@ -534,8 +535,8 @@ fn assess(root: &Path, ledger: &ExecutionLedger) -> Result { checks: vec![json!({"id":"contract","passed":false,"detail":"尚未登记本轮验收合同"})], }); }; - let contract: FrozenContract = - serde_json::from_value(value.clone()).map_err(|_| "delivery-frozen-contract-invalid")?; + let contract: FrozenContract = serde_json::from_value(value.clone()) + .map_err(|error| format!("delivery-frozen-contract-invalid:{error}"))?; if contract.schema_version != CONTRACT_SCHEMA { return Err("delivery-frozen-contract-invalid".into()); } @@ -688,7 +689,10 @@ pub(super) async fn finish_sealing( if !first.ready || !final_check.ready || first.runtime_fingerprint != final_check.runtime_fingerprint || first.checks != final_check.checks { session.reopen_for_repair()?; return Ok(None); } - let contract: FrozenContract = serde_json::from_value(ledger.contract.clone().ok_or("delivery-contract-missing")?).map_err(|_| "delivery-contract-invalid")?; + let contract: FrozenContract = serde_json::from_value( + ledger.contract.clone().ok_or("delivery-contract-missing")?, + ) + .map_err(|error| format!("delivery-contract-invalid:{error}"))?; let report = format!("本轮已完成宿主验收。\n\n范围:{}\n\n{}\n\n执行/返修批次:{}/{}。仅证明以上登记范围;固定场景不代表完整长关卡已通关。执行预算按宿主观察的占用计入;第三方并发调用可能按组占用上界计入。", truncate_agent_runtime_text(&contract.scope,1200), final_check.checks.iter().map(|check|format!("- 已通过:{}",check["label"].as_str().unwrap_or("验收项"))).collect::>().join("\n"), ledger.used_passes,ledger.max_runs); session.complete(report.clone())?; diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_execution.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_execution.rs index b8c20cc82..059daae23 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_execution.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_execution.rs @@ -559,11 +559,15 @@ pub(super) fn open_with_analytics_at( "宿主执行状态", MAX_STATE_BYTES as u64, ) - .map_err(|_| { - "direct-execution-private-state: 无法安全读取宿主状态,禁止重置预算" + .map_err(|error| { + format!( + "direct-execution-private-state: 无法安全读取宿主状态,禁止重置预算:{error}" + ) })?, ) - .map_err(|_| "direct-execution-corrupt: 宿主执行状态无效,禁止重置预算")?, + .map_err(|error| { + format!("direct-execution-corrupt: 宿主执行状态无效,禁止重置预算:{error}") + })?, ) } else { None @@ -758,7 +762,7 @@ impl ExecutionSession { } next.revision = data.ledger.revision.saturating_add(1); let bytes = serde_json::to_vec(&next) - .map_err(|_| "direct-execution-persistence: 状态序列化失败")?; + .map_err(|error| format!("direct-execution-persistence: 状态序列化失败:{error}"))?; if bytes.len() > MAX_STATE_BYTES { return Err("direct-execution-capacity: 宿主状态超过保留上限".into()); } @@ -829,7 +833,10 @@ impl ExecutionSession { pub(super) fn update_plan(&self, plan: Value) -> Result { self.tick()?; if !plan.is_object() - || serde_json::to_vec(&plan).map_err(|_| "计划格式无效")?.len() > 32 * 1024 + || serde_json::to_vec(&plan) + .map_err(|error| format!("计划格式无效:{error}"))? + .len() + > 32 * 1024 { return Err("direct-plan-invalid: 计划必须为有界对象".into()); }