M1C-0b:补齐静态委派状态前向兼容

- 未知 contractStatus 保留为 Unknown(raw) 并接入完成屏障、等待、返工与谱系门禁

- 补齐 planning Provider、自治 liveness 与终态扫描的 fail-closed 消费路径及回归

- 保持已知状态和损坏 sidecar 行为不变,更新技术方案与共享决策记录
This commit is contained in:
2026-08-15 07:04:40 +00:00
parent deae1e08ce
commit 89bbfd135a
10 changed files with 380 additions and 71 deletions
@@ -573,17 +573,21 @@ pub(in crate::agent) fn validate_agent_runtime_autonomous_plan_liveness_at(
&& agent_runtime_autonomous_supervisor_plan_prepares_repair(plan)
{
let barrier = static_delegate_completion_barrier_at(root, agent_id, run_id)?;
if barrier.ready_unclaimed_count > 0 || barrier.unobserved_claim_count > 0 {
if barrier.ready_unclaimed_count > 0
|| barrier.unobserved_claim_count > 0
|| barrier.unknown_contract_status_count > 0
{
let active_delegations =
active_static_delegate_delivery_count_at(root, agent_id, run_id)?;
if is_agent_runtime_autonomous_supervisor_delivery_convergence_plan(plan) {
return Ok(());
}
return Err(format!(
"{AGENT_RUNTIME_AUTONOMOUS_SUPERVISOR_DELIVERY_CONVERGENCE_LIVENESS_ERROR_PREFIX};当前父 run 在准备专业 repair 前仍有 activeDelegations={active_delegations}、readyUnclaimedReceipts={}、unobservedReceiptClaims={}、repairRequired={}。必须先只调用 agent.run_status(agentId=null、scope=all、delegationId=null)原子认领并观察已有回执,再基于权威合同准备 repair",
"{AGENT_RUNTIME_AUTONOMOUS_SUPERVISOR_DELIVERY_CONVERGENCE_LIVENESS_ERROR_PREFIX};当前父 run 在准备专业 repair 前仍有 activeDelegations={active_delegations}、readyUnclaimedReceipts={}、unobservedReceiptClaims={}、repairRequired={}、unknownContractStatus={}。必须先只调用 agent.run_status(agentId=null、scope=all、delegationId=null)原子认领并观察已有回执,再基于权威合同准备 repair",
barrier.ready_unclaimed_count,
barrier.unobserved_claim_count,
barrier.repair_required_count,
barrier.unknown_contract_status_count,
));
}
}
@@ -597,16 +601,18 @@ pub(in crate::agent) fn validate_agent_runtime_autonomous_plan_liveness_at(
let active_delegations = active_static_delegate_delivery_count_at(root, agent_id, run_id)?;
let must_claim_or_wait = barrier.ready_unclaimed_count > 0
|| barrier.unobserved_claim_count > 0
|| barrier.unknown_contract_status_count > 0
|| active_delegations >= 3;
if must_claim_or_wait {
if is_agent_runtime_autonomous_supervisor_delivery_convergence_plan(plan) {
return Ok(());
}
return Err(format!(
"{AGENT_RUNTIME_AUTONOMOUS_SUPERVISOR_DELIVERY_CONVERGENCE_LIVENESS_ERROR_PREFIX};当前父 run 的 activeDelegations={active_delegations}、waitingDelegations={}、readyUnclaimedReceipts={}、unobservedReceiptClaims={}。必须先只调用 agent.run_status(agentId=null、scope=all、delegationId=null)认领并观察 ready delivery,或等待已有委派推进;不得创建第四次 agent.delegate。收束后若项目 revision 已推进,再验证当前 revision",
"{AGENT_RUNTIME_AUTONOMOUS_SUPERVISOR_DELIVERY_CONVERGENCE_LIVENESS_ERROR_PREFIX};当前父 run 的 activeDelegations={active_delegations}、waitingDelegations={}、readyUnclaimedReceipts={}、unobservedReceiptClaims={}、unknownContractStatus={}。必须先只调用 agent.run_status(agentId=null、scope=all、delegationId=null)认领并观察 ready delivery,或等待已有委派推进;不得创建第四次 agent.delegate。收束后若项目 revision 已推进,再验证当前 revision",
barrier.waiting_count,
barrier.ready_unclaimed_count,
barrier.unobserved_claim_count,
barrier.unknown_contract_status_count,
));
}
}
@@ -883,11 +883,17 @@ pub(in crate::agent) fn isolated_join_barrier_has_waiting_groups(detail: &str) -
}
pub(in crate::agent) fn static_delegate_barrier_has_waiting_deliveries(detail: &str) -> bool {
detail
let waiting = detail
.split_whitespace()
.find_map(|part| part.strip_prefix("waitingDelegations="))
.and_then(|value| value.parse::<usize>().ok())
.is_some_and(|count| count > 0)
.is_some_and(|count| count > 0);
let unknown_contract_status = detail
.split_whitespace()
.find_map(|part| part.strip_prefix("unknownContractStatus="))
.and_then(|value| value.parse::<usize>().ok())
.is_some_and(|count| count > 0);
waiting || unknown_contract_status
}
pub(in crate::agent) fn static_delegate_barrier_requires_repair(detail: &str) -> bool {
@@ -86,6 +86,16 @@ pub(crate) fn capture_plan_provider_structured_injections_at(
if clarification_round == u32::MAX {
return Err("planning Provider 无法从委派链推导 clarificationRound".to_string());
}
if static_delegate_lineage_contains_unknown_contract_status(
&deliveries,
&session.latest_delegation_id,
)
.map_err(|error| format!("planning Provider 无法确认委派 contractStatus:{error}"))?
{
return Err(
"planning Provider 委派谱系含更新版本 contractStatus,当前版本拒绝继续".to_string(),
);
}
validate_plan_session_for_clarification_round(&session, clarification_round)
.map_err(|error| error.to_string())?;
let approval_observation = observations
@@ -2146,6 +2156,38 @@ mod tests {
assert!(validate_plan_gdd(&gdd).is_ok());
}
#[test]
fn provider_structured_injections_reject_unknown_delegate_lineage() {
let (root, context, _) = submit_fixture();
let mut delivery = new_static_delegate_delivery(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0b-provider-root-session",
&context.root_run_id,
"m1c0b-provider-parent-action",
&context.delegation_id,
GAME_CREATOR_PROJECT_PLANNING_AGENT_ID,
&context.session_id,
&context.created_by_run_id,
);
delivery.status = StaticDelegateDeliveryStatus::Ready;
delivery.terminal_status = Some("completed".to_string());
delivery.result_summary = Some("future status fixture".to_string());
let mut structured_result = StaticDelegateStructuredResult::default();
structured_result.contract_status =
StaticDelegateContractStatus::Unknown("future-contract-status".to_string());
delivery.structured_result = Some(structured_result);
write_static_delegate_delivery_at(&root, &delivery)
.expect("write unknown planning delivery");
let error = capture_plan_provider_structured_injections_at(&root, &context.session_id, &[])
.expect_err("unknown planning lineage must block Provider injection");
assert!(
error.contains("更新版本 contractStatus"),
"unexpected error: {error}"
);
cleanup_fixture(root);
}
#[test]
fn provider_binding_is_strict_and_recomputes_canonical_request_identity() {
let binding = provider_binding();
@@ -960,7 +960,7 @@ pub(crate) fn publish_game_creator_agent_delegate_result(
"contractStatus": delivery
.structured_result
.as_ref()
.map(|result| result.contract_status),
.map(|result| result.contract_status.clone()),
"artifactCount": delivery
.structured_result
.as_ref()
@@ -30,7 +30,7 @@ pub(in crate::agent) fn observe_claimed_static_delegate_contract_at(
"contractStatus": delivery
.structured_result
.as_ref()
.map(|result| result.contract_status),
.map(|result| result.contract_status.clone()),
}
}))
.map_err(|error| format!("序列化已认领委派合同失败:{error}"))?;
@@ -236,7 +236,7 @@ pub(crate) fn observe_agent_runtime_run_status(
"contractStatus": delivery
.structured_result
.as_ref()
.map(|result| result.contract_status),
.map(|result| result.contract_status.clone()),
"needsUserInput": delivery
.structured_result
.as_ref()
@@ -31,13 +31,58 @@ pub(crate) enum StaticDelegateDeliveryStatus {
Suppressed,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum StaticDelegateContractStatus {
EvidenceReady,
NeedsRepair,
NeedsUserInput,
UserRevisionRequested,
/// A structurally valid durable status introduced by a newer client.
///
/// The raw wire value is retained so an old client can safely read and
/// rewrite the record without silently changing the newer status.
Unknown(String),
}
impl StaticDelegateContractStatus {
pub(crate) fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown(_))
}
fn durable_value(&self) -> &str {
match self {
Self::EvidenceReady => "evidence-ready",
Self::NeedsRepair => "needs-repair",
Self::NeedsUserInput => "needs-user-input",
Self::UserRevisionRequested => "user-revision-requested",
Self::Unknown(value) => value,
}
}
}
impl serde::Serialize for StaticDelegateContractStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.durable_value())
}
}
impl<'de> serde::Deserialize<'de> for StaticDelegateContractStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <String as serde::Deserialize>::deserialize(deserializer)?;
Ok(match value.as_str() {
"evidence-ready" => Self::EvidenceReady,
"needs-repair" => Self::NeedsRepair,
"needs-user-input" => Self::NeedsUserInput,
"user-revision-requested" => Self::UserRevisionRequested,
_ => Self::Unknown(value),
})
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
@@ -171,6 +216,7 @@ pub(crate) struct StaticDelegateCompletionBarrier {
pub(crate) unobserved_claim_count: usize,
pub(crate) repair_required_count: usize,
pub(crate) user_input_required_count: usize,
pub(crate) unknown_contract_status_count: usize,
}
impl StaticDelegateCompletionBarrier {
@@ -180,20 +226,22 @@ impl StaticDelegateCompletionBarrier {
&& self.unobserved_claim_count == 0
&& self.repair_required_count == 0
&& self.user_input_required_count == 0
&& self.unknown_contract_status_count == 0
}
pub(crate) fn has_waiting(self) -> bool {
self.waiting_count > 0
self.waiting_count > 0 || self.unknown_contract_status_count > 0
}
pub(crate) fn detail(self) -> String {
format!(
"waitingDelegations={} · readyUnclaimedReceipts={} · unobservedReceiptClaims={} · repairRequired={} · userInputRequired={} · 必须认领专业 Agent 回执,并处理 needs-user-input 或对 needs-repair 原委派发起唯一返工后再继续",
"waitingDelegations={} · readyUnclaimedReceipts={} · unobservedReceiptClaims={} · repairRequired={} · userInputRequired={} · unknownContractStatus={} · 必须认领专业 Agent 回执,处理 needs-user-input/needs-repair,或升级客户端后再继续",
self.waiting_count,
self.ready_unclaimed_count,
self.unobserved_claim_count,
self.repair_required_count,
self.user_input_required_count
self.user_input_required_count,
self.unknown_contract_status_count
)
}
}
@@ -510,6 +558,16 @@ pub(crate) fn static_delegate_completion_barrier_at(
})
})
.count();
barrier.unknown_contract_status_count = deliveries
.iter()
.filter(|delivery| {
delivery.status == StaticDelegateDeliveryStatus::ClaimedByParent
&& delivery
.structured_result
.as_ref()
.is_some_and(|result| result.contract_status.is_unknown())
})
.count();
Ok(barrier)
}
@@ -1112,6 +1170,18 @@ fn static_delegate_original_is_user_revision_requested(
})
}
/// A newer durable contract status is intentionally not a repairable contract.
/// The old client may preserve and report it, but must not manufacture a
/// mutation under semantics it does not understand.
fn static_delegate_original_has_unknown_contract_status(
delivery: &StaticDelegateDeliveryRecord,
) -> bool {
delivery
.structured_result
.as_ref()
.is_some_and(|result| result.contract_status.is_unknown())
}
/// 沿 repair_of_delegation_id 反向重放整条链,现算目标 delivery 的
/// (repair_depth, clarification_round)。两个维度都是运行时派生值,故意不落盘:
/// - 链根(repair_of_delegation_id 为 None):depth = 0,round = 0。
@@ -1129,25 +1199,9 @@ pub(crate) fn static_delegate_lineage_counters(
deliveries: &[StaticDelegateDeliveryRecord],
delegation_id: &str,
) -> (u32, u32) {
let mut chain: Vec<&StaticDelegateDeliveryRecord> = Vec::new();
let mut seen_ids: std::collections::BTreeSet<&str> = std::collections::BTreeSet::new();
let mut current_id = delegation_id;
loop {
if !seen_ids.insert(current_id) || chain.len() >= STATIC_DELEGATE_LINEAGE_MAX_HOPS {
return (u32::MAX, u32::MAX);
}
let Some(node) = deliveries
.iter()
.find(|delivery| delivery.delegation_id == current_id)
else {
return (u32::MAX, u32::MAX);
};
chain.push(node);
match node.repair_of_delegation_id.as_deref() {
Some(parent_id) => current_id = parent_id,
None => break,
}
}
let Some(mut chain) = static_delegate_lineage_nodes(deliveries, delegation_id) else {
return (u32::MAX, u32::MAX);
};
// chain 目前是 [目标 .. 根],反转成 [根 .. 目标] 便于按 R1/R2/R3 正向传播。
chain.reverse();
let mut depth = 0u32;
@@ -1165,6 +1219,49 @@ pub(crate) fn static_delegate_lineage_counters(
(depth, round)
}
/// Return whether the target's durable lineage contains a status introduced by
/// a newer client. A malformed lineage is an error rather than a negative
/// answer: callers that use this as a mutation/Provider gate must fail closed.
pub(crate) fn static_delegate_lineage_contains_unknown_contract_status(
deliveries: &[StaticDelegateDeliveryRecord],
delegation_id: &str,
) -> Result<bool, String> {
let chain = static_delegate_lineage_nodes(deliveries, delegation_id)
.ok_or_else(|| "静态委派谱系无效,无法检查未知 contractStatus".to_string())?;
Ok(chain.iter().any(|delivery| {
delivery
.structured_result
.as_ref()
.is_some_and(|result| result.contract_status.is_unknown())
}))
}
fn static_delegate_lineage_nodes<'a>(
deliveries: &'a [StaticDelegateDeliveryRecord],
delegation_id: &str,
) -> Option<Vec<&'a StaticDelegateDeliveryRecord>> {
let mut chain: Vec<&StaticDelegateDeliveryRecord> = Vec::new();
let mut seen_ids: std::collections::BTreeSet<&str> = std::collections::BTreeSet::new();
let mut current_id = delegation_id;
loop {
if !seen_ids.insert(current_id) || chain.len() >= STATIC_DELEGATE_LINEAGE_MAX_HOPS {
return None;
}
let Some(node) = deliveries
.iter()
.find(|delivery| delivery.delegation_id == current_id)
else {
return None;
};
chain.push(node);
match node.repair_of_delegation_id.as_deref() {
Some(parent_id) => current_id = parent_id,
None => break,
}
}
Some(chain)
}
/// 澄清轮次上限只按发起请求所在 Run 的 source 区分(详见常量注释),
/// 与仓库已有的 game-chat 分支先例(见 agent/runtime_tools/delegation.rs 的
/// may_be_game_chat 判定)同源:source 缺失 binding 时按非 game-chat 处理。
@@ -1221,6 +1318,11 @@ pub(crate) fn validate_static_delegate_repair_request_at(
if original.target_agent_id != target_agent_id {
return Err("静态委派返工必须交回原专业 Agent".to_string());
}
if static_delegate_original_has_unknown_contract_status(original) {
return Err(
"静态委派原 delivery 的 contractStatus 由更新版本写入,当前版本拒绝返工".to_string(),
);
}
let (original_repair_depth, original_clarification_round) =
static_delegate_lineage_counters(&deliveries, &original.delegation_id);
if static_delegate_original_is_awaiting_clarification(original) {
@@ -1238,6 +1340,16 @@ pub(crate) fn validate_static_delegate_repair_request_at(
} else if original_repair_depth >= 1 {
return Err("静态委派返工深度最多为 1".to_string());
}
if static_delegate_lineage_contains_unknown_contract_status(
&deliveries,
&original.delegation_id,
)
.map_err(|_| "静态委派谱系计数无效,拒绝继续".to_string())?
{
return Err(
"静态委派原 delivery 所在谱系含更新版本 contractStatus,当前版本拒绝返工".to_string(),
);
}
if original.acceptance_criteria != acceptance_criteria
|| original.expected_artifacts != expected_artifacts
{
@@ -2856,22 +2968,48 @@ mod tests {
"超过 32-hop 安全阀必须 fail closed"
);
let encoded = serde_json::to_value(StaticDelegateContractStatus::UserRevisionRequested)
.expect("serialize user revision status");
assert_eq!(encoded, serde_json::json!("user-revision-requested"));
for (status, wire) in [
(
StaticDelegateContractStatus::EvidenceReady,
"evidence-ready",
),
(StaticDelegateContractStatus::NeedsRepair, "needs-repair"),
(
StaticDelegateContractStatus::NeedsUserInput,
"needs-user-input",
),
(
StaticDelegateContractStatus::UserRevisionRequested,
"user-revision-requested",
),
] {
let encoded = serde_json::to_value(&status).expect("serialize known status");
assert_eq!(encoded, serde_json::json!(wire));
assert_eq!(
serde_json::from_value::<StaticDelegateContractStatus>(encoded)
.expect("round-trip known status"),
status
);
}
let unknown_wire = "future-contract-status";
let unknown =
serde_json::from_value::<StaticDelegateContractStatus>(serde_json::json!(unknown_wire))
.expect("unknown durable contract status is preserved explicitly");
assert_eq!(
serde_json::from_value::<StaticDelegateContractStatus>(encoded)
.expect("round-trip user revision status"),
StaticDelegateContractStatus::UserRevisionRequested
unknown,
StaticDelegateContractStatus::Unknown(unknown_wire.to_string())
);
assert_eq!(
serde_json::to_value(&unknown).expect("serialize unknown status"),
serde_json::json!(unknown_wire)
);
assert!(
serde_json::from_value::<StaticDelegateContractStatus>(serde_json::json!(42)).is_err(),
"non-string durable contract status must still fail closed"
);
let unknown = serde_json::from_value::<StaticDelegateContractStatus>(serde_json::json!(
"future-contract-status"
))
.expect_err("unknown durable contract status must fail closed");
assert!(unknown.to_string().contains("unknown variant"));
// 额外钉住 durable sidecar 读取入口:未知状态不能在 JSON sidecar 层被吞掉或
// 降级成 Default(NeedsRepair)。
// Durable sidecar 的未知变体可读,但必须进入 barrier、拒绝返工,并在读-改-写后
// 保留原始 wire 字符串;不能吞掉或降级成 Default(NeedsRepair)。
let root = std::env::temp_dir().join(format!(
"genarrative-static-unknown-status-{}-{}",
std::process::id(),
@@ -2882,7 +3020,8 @@ mod tests {
));
init_local_game_project_at(&root, "m1c0-unknown-status", "未知静态委派状态解析测试")
.expect("project init");
let mut record = new_static_delegate_delivery(
let acceptance_criteria = vec!["保留未知合同状态".to_string()];
let mut record = new_static_delegate_delivery_with_contract(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-unknown-status-session",
"m1c0-unknown-status-run",
@@ -2891,30 +3030,113 @@ mod tests {
"design-director",
"m1c0-unknown-status-target-session",
"m1c0-unknown-status-target-run",
&acceptance_criteria,
&[],
None,
);
record.status = StaticDelegateDeliveryStatus::Ready;
record.status = StaticDelegateDeliveryStatus::ClaimedByParent;
record.terminal_status = Some("completed".to_string());
record.result_summary = Some("unknown status fixture".to_string());
record.claimed_by_action_id = Some("m1c0-unknown-status-claim-action".to_string());
let mut result = StaticDelegateStructuredResult::default();
result.contract_status = StaticDelegateContractStatus::UserRevisionRequested;
result.contract_status = StaticDelegateContractStatus::Unknown(unknown_wire.to_string());
record.structured_result = Some(result);
write_static_delegate_delivery_at(&root, &record).expect("write known status fixture");
write_static_delegate_delivery_at(&root, &record).expect("write unknown status fixture");
let loaded = read_static_delegate_delivery_at(&root, &record.delegation_id)
.expect("read unknown status fixture")
.expect("unknown status delivery exists");
assert_eq!(loaded, record);
let barrier = static_delegate_completion_barrier_at(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-unknown-status-run",
)
.expect("read unknown status completion barrier");
assert_eq!(barrier.unknown_contract_status_count, 1);
assert!(!barrier.is_clear());
assert!(barrier.has_waiting());
assert!(barrier.detail().contains("unknownContractStatus=1"));
let repair_error = validate_static_delegate_repair_request_at(
&root,
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-unknown-status-run",
"m1c0-unknown-status-repair-candidate",
"design-director",
&acceptance_criteria,
&[],
Some(&record.delegation_id),
)
.expect_err("unknown root status must reject the first repair unconditionally");
assert!(repair_error.contains("更新版本") && repair_error.contains("拒绝返工"));
// 读-改-写只改变外层字段时,未知 status 的 raw wire 值必须原样保留。
let mut rewritten = loaded;
rewritten.result_summary = Some("unknown status rewritten summary".to_string());
rewritten.updated_at = unix_timestamp();
write_static_delegate_delivery_at(&root, &rewritten).expect("rewrite unknown status");
let delivery_path = root
.join(STATIC_DELEGATE_DELIVERY_DIR)
.join(format!("{}.json", record.delegation_id));
let mut raw = serde_json::to_value(&record).expect("serialize status fixture");
raw["structuredResult"]["contractStatus"] = serde_json::json!("future-contract-status");
fs::write(
&delivery_path,
serde_json::to_vec(&raw).expect("serialize unknown status fixture"),
let rewritten_raw: serde_json::Value = serde_json::from_slice(
&fs::read(&delivery_path).expect("read rewritten unknown status sidecar"),
)
.expect("overwrite unknown status fixture");
let sidecar_error = read_static_delegate_delivery_at(&root, &record.delegation_id)
.expect_err("unknown durable status must fail at sidecar read");
assert!(
sidecar_error.contains("解析") && sidecar_error.contains("静态委派 delivery"),
"unexpected sidecar parse error: {sidecar_error}"
.expect("parse rewritten unknown status sidecar");
assert_eq!(
rewritten_raw["structuredResult"]["contractStatus"],
serde_json::json!(unknown_wire)
);
// lineage 中的 Unknown 按“其它”分支计数,不能被误识别成用户修订或澄清。
let mut unknown_lineage = deliveries[..2].to_vec();
unknown_lineage[0]
.structured_result
.as_mut()
.expect("lineage root structured result")
.contract_status = StaticDelegateContractStatus::Unknown(unknown_wire.to_string());
assert_eq!(
static_delegate_lineage_counters(&unknown_lineage, &ids[1]),
(1, 0)
);
assert!(static_delegate_lineage_contains_unknown_contract_status(
&unknown_lineage,
&ids[1]
)
.expect("inspect unknown lineage"));
// 损坏仍按原有整体 fail-closed 语义处理;即使目录里另有合法 delivery,也不能
// 跳过坏记录后继续计算 barrier/lineage。
let unrelated = new_static_delegate_delivery(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"m1c0-unrelated-session",
"m1c0-unrelated-run",
"m1c0-unrelated-action",
"m1c0-unrelated-delivery",
"art-director",
"m1c0-unrelated-target-session",
"m1c0-unrelated-target-run",
);
write_static_delegate_delivery_at(&root, &unrelated)
.expect("write unrelated valid delivery");
for (label, bytes) in [
("截断 JSON", b"{not-json".to_vec()),
("非 UTF-8", vec![b'{', 0xff, b'}']),
(
"超限 sidecar",
vec![b' '; STATIC_DELEGATE_DELIVERY_MAX_BYTES + 1],
),
] {
fs::write(&delivery_path, &bytes).expect("write damaged delivery sidecar");
let error = list_static_delegate_deliveries_at(&root)
.expect_err("damaged sidecar must lock the whole delivery directory");
assert!(
error.contains("静态委派 delivery"),
"{label} returned an unrelated error: {error}"
);
write_static_delegate_delivery_at(&root, &rewritten)
.expect("restore valid unknown status sidecar");
}
fs::remove_dir_all(root).ok();
}
}
@@ -322,6 +322,10 @@ pub(super) fn original_delivery_has_successful_repair(
claimed_deliveries: &[StaticDelegateDeliveryRecord],
) -> bool {
original.repair_of_delegation_id.is_none()
&& !original
.structured_result
.as_ref()
.is_some_and(|result| result.contract_status.is_unknown())
&& claimed_deliveries.iter().any(|candidate| {
candidate.repair_of_delegation_id.as_deref() == Some(original.delegation_id.as_str())
&& candidate.terminal_status.as_deref() == Some("completed")
@@ -1293,6 +1293,26 @@ fn original_specialist_failure_is_recoverable_but_repair_failure_closes() {
classify_failed_specialist(&parent, &child, Some(&repair), false),
SwarmSpecialistFailureDisposition::Failed
);
let mut successful_repair = repair.clone();
successful_repair.terminal_status = Some("completed".to_string());
let mut evidence_ready = StaticDelegateStructuredResult::default();
evidence_ready.contract_status = StaticDelegateContractStatus::EvidenceReady;
successful_repair.structured_result = Some(evidence_ready);
assert!(original_delivery_has_successful_repair(
&original,
&[successful_repair.clone()]
));
let mut unknown_original = original.clone();
let mut unknown_result = StaticDelegateStructuredResult::default();
unknown_result.contract_status =
StaticDelegateContractStatus::Unknown("future-contract-status".to_string());
unknown_original.structured_result = Some(unknown_result);
assert!(
!original_delivery_has_successful_repair(&unknown_original, &[successful_repair]),
"a newer contract status must not be classified as already repaired"
);
}
#[test]