fix: validate Soul plan adoption across model transition
This commit is contained in:
parent
d2538d4641
commit
d3bfbda0c5
6 changed files with 103 additions and 30 deletions
|
|
@ -2,7 +2,8 @@ use anyhow::{Result, anyhow};
|
|||
use epiphany_core::{
|
||||
RepoFrontierExecutionAmendment, RepoFrontierRoute, amend_repo_frontier_execution,
|
||||
apply_supervisor_modeling_acceptance_correction, read_coordinator_state,
|
||||
runtime_repo_model_admission_receipt, runtime_repo_model_admission_review, runtime_spine_cache,
|
||||
runtime_repo_frontier_plan_decision, runtime_repo_model_admission_receipt,
|
||||
runtime_repo_model_admission_review, runtime_spine_cache,
|
||||
};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{collections::BTreeMap, env, path::PathBuf};
|
||||
|
|
@ -83,6 +84,11 @@ fn main() -> Result<()> {
|
|||
.ok_or_else(|| anyhow!("route {route_id} admission receipt is absent"))?;
|
||||
let review = runtime_repo_model_admission_review(&store, &admission.review_id)?
|
||||
.ok_or_else(|| anyhow!("route {route_id} admission review is absent"))?;
|
||||
let plan_decision = if admission.frontier_plan_decision_id.is_empty() {
|
||||
None
|
||||
} else {
|
||||
runtime_repo_frontier_plan_decision(&store, &admission.frontier_plan_decision_id)?
|
||||
};
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&serde_json::json!({
|
||||
|
|
@ -93,6 +99,12 @@ fn main() -> Result<()> {
|
|||
"admissionResultId": admission.result_id,
|
||||
"admissionReviewId": admission.review_id,
|
||||
"admissionSource": review.admission_source,
|
||||
"admissionPreviousRevision": admission.previous_revision,
|
||||
"admissionPreviousHash": admission.previous_hash,
|
||||
"admissionAdmittedRevision": admission.admitted_revision,
|
||||
"admissionAdmittedHash": admission.admitted_hash,
|
||||
"frontierPlanDecisionId": admission.frontier_plan_decision_id,
|
||||
"frontierPlanDecision": plan_decision,
|
||||
"frontierItemId": route.frontier_item_id,
|
||||
"frontierItemHash": route.frontier_item_hash,
|
||||
"originalAction": plan.action,
|
||||
|
|
|
|||
|
|
@ -2583,6 +2583,29 @@ pub(crate) mod tests {
|
|||
let route = select_and_commit_repo_frontier_route(&store, "2026-07-15T09:00:06Z")?;
|
||||
assert_eq!(route.adopted_plan.as_ref(), Some(adopted));
|
||||
assert_eq!(route.source_scope, adopted.safe_paths);
|
||||
let admission_review = crate::runtime_repo_model_admission_review(
|
||||
&store,
|
||||
&admission.review_id,
|
||||
)?
|
||||
.expect("plan decision admission review");
|
||||
crate::coordinator_launch_context::validate_frontier_plan_decision_chain(
|
||||
&decision,
|
||||
&admission,
|
||||
&admission_review,
|
||||
&route,
|
||||
)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let mut substituted_route = route.clone();
|
||||
substituted_route.model_hash = "substituted-admitted-model".into();
|
||||
assert!(
|
||||
crate::coordinator_launch_context::validate_frontier_plan_decision_chain(
|
||||
&decision,
|
||||
&admission,
|
||||
&admission_review,
|
||||
&substituted_route,
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
let grant = crate::substrate_gate_coordinator_implementation_grant(
|
||||
"adopt-hands-grant".into(),
|
||||
"adopt-hands-job".into(),
|
||||
|
|
|
|||
|
|
@ -202,32 +202,12 @@ pub fn append_verification_hands_receipt_context(
|
|||
)
|
||||
.map_err(|error| format!("failed to load Soul frontier plan decision: {error}"))?
|
||||
.ok_or_else(|| "Soul frontier admission has no persisted plan decision".to_string())?;
|
||||
let source_matches = match decision.decision_source.as_ref() {
|
||||
Some(crate::RepoFrontierPlanDecisionSource::MindWorker { result_id, job_id }) => {
|
||||
admission_receipt.result_id.as_deref() == Some(result_id.as_str())
|
||||
&& admission_review.result_id.as_deref() == Some(result_id.as_str())
|
||||
&& admission_review.job_id.as_deref() == Some(job_id.as_str())
|
||||
}
|
||||
Some(crate::RepoFrontierPlanDecisionSource::AuthenticatedOperatorReview { .. }) => {
|
||||
admission_receipt.result_id.is_none()
|
||||
&& admission_review.result_id.is_none()
|
||||
&& admission_review.job_id.is_none()
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
if decision.decision != crate::RepoFrontierPlanDecision::Adopt
|
||||
|| decision.model_admission_receipt_id != admission_receipt.receipt_id
|
||||
|| decision.model_revision != admission_receipt.previous_revision
|
||||
|| decision.model_hash != admission_receipt.previous_hash
|
||||
|| decision.frontier_item_id != route.frontier_item_id
|
||||
|| decision.frontier_item_hash != route.frontier_item_hash
|
||||
|| !source_matches
|
||||
{
|
||||
return Err(
|
||||
"Soul frontier plan decision does not exactly bind admission, route, and decision source"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
validate_frontier_plan_decision_chain(
|
||||
&decision,
|
||||
&admission_receipt,
|
||||
&admission_review,
|
||||
&route,
|
||||
)?;
|
||||
plan_decision_chain = Some(decision);
|
||||
} else if let Some(admission_result_id) = admission_receipt.result_id.as_deref() {
|
||||
let modeling_acceptances = state
|
||||
|
|
@ -509,6 +489,51 @@ pub fn append_verification_hands_receipt_context(
|
|||
Ok(context)
|
||||
}
|
||||
|
||||
pub(crate) fn validate_frontier_plan_decision_chain(
|
||||
decision: &crate::RepoFrontierPlanDecisionReceipt,
|
||||
admission_receipt: &crate::RepoModelAdmissionReceipt,
|
||||
admission_review: &crate::RepoModelAdmissionReview,
|
||||
route: &crate::RepoFrontierRoute,
|
||||
) -> Result<(), String> {
|
||||
let source_matches = match decision.decision_source.as_ref() {
|
||||
Some(crate::RepoFrontierPlanDecisionSource::MindWorker { result_id, job_id }) => {
|
||||
admission_receipt.result_id.as_deref() == Some(result_id.as_str())
|
||||
&& admission_review.result_id.as_deref() == Some(result_id.as_str())
|
||||
&& admission_review.job_id.as_deref() == Some(job_id.as_str())
|
||||
}
|
||||
Some(crate::RepoFrontierPlanDecisionSource::AuthenticatedOperatorReview { .. }) => {
|
||||
admission_receipt.result_id.is_none()
|
||||
&& admission_review.result_id.is_none()
|
||||
&& admission_review.job_id.is_none()
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
if decision.decision != crate::RepoFrontierPlanDecision::Adopt
|
||||
|| decision.model_admission_receipt_id != admission_receipt.receipt_id
|
||||
|| decision.model_revision != admission_receipt.previous_revision
|
||||
|| decision.model_hash != admission_receipt.previous_hash
|
||||
|| decision.frontier_item_id != route.frontier_item_id
|
||||
|| admission_receipt.admitted_revision != route.model_revision
|
||||
|| admission_receipt.admitted_hash != route.model_hash
|
||||
|| !matches!(
|
||||
&admission_receipt.purpose,
|
||||
crate::RepoModelPatchPurpose::AdoptFrontierPlan {
|
||||
planning_request_id,
|
||||
candidate_id,
|
||||
..
|
||||
} if planning_request_id == &decision.planning_request_id
|
||||
&& candidate_id == &decision.candidate_id
|
||||
)
|
||||
|| !source_matches
|
||||
{
|
||||
return Err(
|
||||
"Soul frontier plan decision does not exactly bind admission, route, and decision source"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn has_complete_hands_consequence_after_latest_accepted_boundary(
|
||||
runtime_store_path: &Path,
|
||||
state: &EpiphanyThreadState,
|
||||
|
|
|
|||
|
|
@ -1497,8 +1497,9 @@ an authenticated operator supplied the decision.
|
|||
|
||||
Soul selects and verifies provenance by typed route anatomy. A non-empty
|
||||
`frontier_plan_decision_id` requires the exact decision receipt, Adopt verdict,
|
||||
admission receipt binding, base model revision/hash, frontier identity/hash,
|
||||
and exact Mind-worker result/job or authenticated-operator source. Only an
|
||||
admission receipt binding, pre-admission model revision/hash and frontier
|
||||
identity, admission-purpose planning/candidate identity, post-admission route
|
||||
model revision/hash, and exact Mind-worker result/job or authenticated-operator source. Only an
|
||||
ordinary worker-result admission may require the Modeling acceptance,
|
||||
Mind-state-commit, and gateway-review chain. Execution amendments retain their
|
||||
separate exact receipt chain.
|
||||
|
|
@ -1511,7 +1512,10 @@ validation path despite the legacy generic `WorkerResult` source tag.
|
|||
Forbidden writer: Soul cannot infer Modeling from result presence, and a plan
|
||||
decision cannot bypass provenance merely because its admission and review
|
||||
contain a model-produced result. Missing, substituted, non-Adopt, or
|
||||
route-mismatched decisions fail closed before worker launch.
|
||||
route-mismatched decisions fail closed before worker launch. The decision's
|
||||
pre-adoption frontier-item hash is deliberately not equated with the route's
|
||||
post-adoption frontier-item hash; Mind's admitted patch is the transition that
|
||||
changes that item.
|
||||
|
||||
## Exact release build cache ownership
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,15 @@ The active source repair types all new plan admissions as
|
|||
uses the already-persisted `frontier_plan_decision_id` as the compatibility
|
||||
discriminator for v53. It does not rewrite evidence or weaken Soul.
|
||||
|
||||
Authenticated release `sha256-53dc65ecf1971fc39a535c3a597723e02ebe73a28936581441b4fe007c98c601`
|
||||
from `d2538d46` proved the typed branch live, then failed closed because its
|
||||
first exact-binding predicate incorrectly equated the decision's pre-adoption
|
||||
frontier hash with the route's post-adoption hash. The follow-up source repair
|
||||
binds the decision to the pre-admission model, the admission purpose to the
|
||||
planning request and candidate, and the route to the admitted model. A focused
|
||||
positive/negative test proves the real transition passes and a substituted
|
||||
admitted route hash fails.
|
||||
|
||||
## Build-time finding
|
||||
|
||||
Commit `0f0b006d` made the three named owner manifests drive separate locked Cargo
|
||||
|
|
|
|||
Binary file not shown.
Reference in a new issue