保留交付执行序列化错误
补充交付账本和执行状态的 serde 原因 通过 Direct 执行回归测试
This commit is contained in:
@@ -301,13 +301,13 @@ struct InitialDelivery {
|
||||
fn initial_delivery_path(root: &Path) -> Result<PathBuf, String> {
|
||||
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<PathBuf, String> {
|
||||
fn read_initial_delivery(path: &Path, project_id: &str) -> Result<Option<InitialDelivery>, 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::<FrozenContract>(existing.clone()).map_err(|_| "delivery-frozen-contract-invalid")?.initial_artifact_hashes
|
||||
serde_json::from_value::<FrozenContract>(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<Assessment, String> {
|
||||
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::<Vec<_>>().join("\n"), ledger.used_passes,ledger.max_runs);
|
||||
session.complete(report.clone())?;
|
||||
|
||||
@@ -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<Value, String> {
|
||||
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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user