From 3d13b1eac28db843ae64baeb35ea4a0461cf49de Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 13 Aug 2026 21:26:24 +0200 Subject: [PATCH] Bind public Eyes evidence to requested sources --- epiphany-core/src/coordinator_acceptance.rs | 91 ++++++++--- epiphany-core/src/runtime_spine.rs | 162 +++++++++++++++++--- 2 files changed, 217 insertions(+), 36 deletions(-) diff --git a/epiphany-core/src/coordinator_acceptance.rs b/epiphany-core/src/coordinator_acceptance.rs index a7f47d77..831d279e 100644 --- a/epiphany-core/src/coordinator_acceptance.rs +++ b/epiphany-core/src/coordinator_acceptance.rs @@ -4,6 +4,7 @@ use crate::*; use anyhow::Context; use epiphany_state_model::EpiphanyThreadState; use sha2::{Digest, Sha256}; +use std::collections::BTreeSet; use std::path::Path; #[derive(Debug, Clone, PartialEq, Eq)] @@ -676,17 +677,67 @@ fn commit_state_with_mind_witness( store_path, expected_state, )?; + let mut eyes_packet_jobs = BTreeSet::new(); for prerequisite in prerequisites { - if let EpiphanyAcceptancePrerequisite::EyesSourceLookup(document) = prerequisite { - let authenticated = crate::runtime_spine::authenticated_public_source_lookups_for_worker( - &cache, - &document.source_job_id, - )?; - if !authenticated.iter().any(|candidate| candidate == document) { + if let EpiphanyAcceptancePrerequisite::Eyes(packet) = prerequisite { + if packet.source_role_id != "research" + || !eyes_packet_jobs.insert(packet.source_job_id.clone()) + { return Err(anyhow::anyhow!( - "Eyes source lookup lost its authenticated authority before Mind commit" + "Mind commit requires one exact Research packet per source job" )); } + let authenticated = + crate::runtime_spine::authenticated_requested_public_source_lookups_for_worker( + &cache, + &packet.source_job_id, + )?; + let authenticated_ids = authenticated + .iter() + .map(|lookup| lookup.receipt_id.as_str()) + .collect::>(); + let packet_ids = packet + .source_lookup_receipt_ids + .iter() + .map(String::as_str) + .collect::>(); + let published = prerequisites + .iter() + .filter_map(|candidate| match candidate { + EpiphanyAcceptancePrerequisite::EyesSourceLookup(lookup) + if lookup.source_job_id == packet.source_job_id => + { + Some(lookup) + } + _ => None, + }) + .collect::>(); + let published_ids = published + .iter() + .map(|lookup| lookup.receipt_id.as_str()) + .collect::>(); + if packet.source_lookup_receipt_ids.len() != packet_ids.len() + || published.len() != published_ids.len() + || packet_ids != authenticated_ids + || published_ids != authenticated_ids + || authenticated.iter().any(|lookup| { + !packet.source_refs.iter().any(|source_ref| source_ref == &lookup.source_ref) + || !published.iter().any(|candidate| *candidate == lookup) + }) + { + return Err(anyhow::anyhow!( + "Eyes packet lost exact frontier public-source coverage before Mind commit" + )); + } + } + } + for prerequisite in prerequisites { + if let EpiphanyAcceptancePrerequisite::EyesSourceLookup(document) = prerequisite + && !eyes_packet_jobs.contains(&document.source_job_id) + { + return Err(anyhow::anyhow!( + "Eyes source lookup has no owning Research packet" + )); } } let (review_envelope, _) = cache.prepare_entry(&mind_review.gateway_id, mind_review)?; @@ -1109,18 +1160,17 @@ mod tests { Vec::new(), "2026-07-11T00:00:00Z".to_string(), ); - let eyes = EyesEvidencePacket { - schema_version: EYES_EVIDENCE_PACKET_SCHEMA_VERSION.to_string(), - packet_id: "eyes-9".to_string(), + let continuity = ContinuityRecoveryReceipt { + schema_version: CONTINUITY_RECOVERY_RECEIPT_SCHEMA_VERSION.to_string(), + receipt_id: "continuity-9".to_string(), source_result_id: "result-9".to_string(), source_job_id: "job-9".to_string(), - source_role_id: "research".to_string(), - evidence_ids: Vec::new(), - observation_ids: Vec::new(), - source_refs: Vec::new(), - source_lookup_receipt_ids: Vec::new(), + binding_id: "reorientation-worker".to_string(), + mode: "resume".to_string(), + checkpoint_still_valid: "true".to_string(), summary: "looked".to_string(), - uncertainty: "none".to_string(), + next_safe_move: "continue".to_string(), + files_inspected: Vec::new(), emitted_at: "2026-07-11T00:00:00Z".to_string(), contract: "test".to_string(), }; @@ -1131,10 +1181,15 @@ mod tests { &state, &review, &commit, - &[EpiphanyAcceptancePrerequisite::Eyes(eyes.clone())], + &[EpiphanyAcceptancePrerequisite::Continuity( + continuity.clone(), + )], )?; let cache = coordinator_acceptance_cache(&store)?; - assert_eq!(cache.get_required::("eyes-9")?, eyes); + assert_eq!( + cache.get_required::("continuity-9")?, + continuity + ); assert!( cache .get::(THREAD_STATE_KEY)? diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index 82e37a30..24c4794d 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -3649,6 +3649,64 @@ fn validate_frontier_research_launch_carrier( Ok(()) } +fn frontier_research_request_for_launch( + cache: &CultCache, + launch: &EpiphanyRuntimeWorkerLaunchRequest, +) -> Result> { + let document: EpiphanyWorkerLaunchDocument = + rmp_serde::from_slice(&launch.launch_document_msgpack)?; + validate_frontier_research_launch_carrier( + &launch.role, + &launch.binding_id, + launch.repo_frontier_research_request_id.as_deref(), + &document, + )?; + let Some(request_id) = launch.repo_frontier_research_request_id.as_deref() else { + return Ok(None); + }; + let request = cache + .get::(request_id)? + .ok_or_else(|| anyhow!("frontier Research launch lost its typed request"))?; + let runtime = require_identity(cache)?; + let expected_request_id = crate::frontier_research_request_id( + &request.runtime_id, + &request.model_hash, + &request.frontier_item_hash, + ); + if request.schema_version != REPO_FRONTIER_RESEARCH_REQUEST_SCHEMA_VERSION + || request.contract != REPO_FRONTIER_RESEARCH_REQUEST_CONTRACT + || request.request_id != request_id + || request.request_id != expected_request_id + || request.runtime_id != runtime.runtime_id + || request.model_hash.is_empty() + || request.admission_receipt_id.is_empty() + || request.frontier_item_id.is_empty() + || request.frontier_item_hash.is_empty() + || request.thread_id.is_empty() + || chrono::DateTime::parse_from_rfc3339(&request.requested_at).is_err() + || request.source_scope.is_empty() + || !safe_sorted_unique_paths(&request.source_scope) + || !request.public_source_refs.windows(2).all(|pair| pair[0] < pair[1]) + || !request.public_source_refs.iter().all(|source_ref| { + crate::ImmutableGithubSource::parse(source_ref) + .map(|source| source.to_string() == *source_ref) + .unwrap_or(false) + }) + { + return Err(anyhow!( + "frontier Research launch names an invalid typed request" + )); + } + let carried = match document { + EpiphanyWorkerLaunchDocument::Role(document) => document.frontier_research_context, + EpiphanyWorkerLaunchDocument::Reorient(_) => None, + }; + if carried.as_ref() != Some(&crate::RepoFrontierResearchContextProjection::from(&request)) { + return Err(anyhow!("frontier Research launch carries substituted context")); + } + Ok(Some(request)) +} + pub fn runtime_authenticated_public_source_lookups_for_worker( store_path: impl AsRef, worker_job_id: &str, @@ -3657,10 +3715,10 @@ pub fn runtime_authenticated_public_source_lookups_for_worker( let mut cache = runtime_spine_cache(store_path)?; cache.pull_all_backing_stores()?; require_identity(&cache)?; - authenticated_public_source_lookups_for_worker(&cache, worker_job_id) + authenticated_requested_public_source_lookups_for_worker(&cache, worker_job_id) } -pub(crate) fn authenticated_public_source_lookups_for_worker( +fn authenticated_public_source_lookup_receipts_for_worker( cache: &CultCache, worker_job_id: &str, ) -> Result> { @@ -3783,6 +3841,39 @@ pub(crate) fn authenticated_public_source_lookups_for_worker( Ok(lookups) } +/// Authenticate the complete public-source consequence of one Research +/// attempt. The typed frontier request owns the allowed set; a capability +/// grant or a valid receipt cannot widen it, and an omitted lookup cannot +/// silently satisfy it. +pub(crate) fn authenticated_requested_public_source_lookups_for_worker( + cache: &CultCache, + worker_job_id: &str, +) -> Result> { + validate_non_empty(worker_job_id, "frontier public source worker job id")?; + let launch = cache + .get::(worker_job_id)? + .ok_or_else(|| anyhow!("frontier public source worker has no launch request"))?; + let request = frontier_research_request_for_launch(cache, &launch)?; + let lookups = + authenticated_public_source_lookup_receipts_for_worker(cache, worker_job_id)?; + let expected = request + .into_iter() + .flat_map(|request| request.public_source_refs) + .collect::>(); + let observed = lookups + .iter() + .map(|lookup| lookup.source_ref.clone()) + .collect::>(); + if observed != expected { + let missing = expected.difference(&observed).cloned().collect::>(); + let unrequested = observed.difference(&expected).cloned().collect::>(); + return Err(anyhow!( + "frontier Research public source coverage is not exact; missing={missing:?}; unrequested={unrequested:?}" + )); + } + Ok(lookups) +} + fn public_source_json_string<'a>(value: &'a serde_json::Value, field: &str) -> Result<&'a str> { value .get(field) @@ -8744,26 +8835,15 @@ pub fn runtime_has_uncovered_actionable_eyes_frontier( let Some(request) = current.pop() else { return runtime_has_actionable_eyes_frontier(runtime_store); }; - let expected_projection = crate::RepoFrontierResearchContextProjection::from(&request); let mut matching_jobs = BTreeSet::new(); for launch in &launches { if launch.repo_frontier_research_request_id.as_deref() != Some(&request.request_id) { continue; } - let document: EpiphanyWorkerLaunchDocument = - rmp_serde::from_slice(&launch.launch_document_msgpack)?; - validate_frontier_research_launch_carrier( - &launch.role, - &launch.binding_id, - launch.repo_frontier_research_request_id.as_deref(), - &document, - )?; - let carried = match document { - EpiphanyWorkerLaunchDocument::Role(document) => document.frontier_research_context, - EpiphanyWorkerLaunchDocument::Reorient(_) => None, - }; - if carried.as_ref() != Some(&expected_projection) { - return Err(anyhow!("frontier Research launch carries substituted context")); + let carried_request = frontier_research_request_for_launch(&cache, launch)? + .ok_or_else(|| anyhow!("frontier Research launch lost its typed request"))?; + if carried_request != request { + return Err(anyhow!("frontier Research launch carries substituted request")); } matching_jobs.insert(launch.job_id.as_str()); } @@ -19881,7 +19961,30 @@ pub(crate) mod tests { }, )?; let state = crate::EpiphanyThreadState::default(); - let launch = crate::build_epiphany_role_launch_request( + let source_ref = concat!( + "github://openai/openai-openapi@", + "0123456789abcdef0123456789abcdef01234567/openapi.yaml" + ); + let research_request = RepoFrontierResearchRequest { + schema_version: REPO_FRONTIER_RESEARCH_REQUEST_SCHEMA_VERSION.into(), + request_id: crate::frontier_research_request_id( + "epiphany-public-tool-test", + "model-public-tool", + "frontier-hash-public-tool", + ), + model_revision: 1, + model_hash: "model-public-tool".into(), + admission_receipt_id: "admission-public-tool".into(), + frontier_item_id: "frontier-public-tool".into(), + frontier_item_hash: "frontier-hash-public-tool".into(), + source_scope: vec!["openapi.yaml".into()], + requested_at: "2026-08-11T00:00:00Z".into(), + runtime_id: "epiphany-public-tool-test".into(), + thread_id: "thread-public-tool".into(), + contract: REPO_FRONTIER_RESEARCH_REQUEST_CONTRACT.into(), + public_source_refs: vec![source_ref.into()], + }; + let mut launch = crate::build_epiphany_role_launch_request( "thread-public-tool", crate::EpiphanyRoleResultRoleId::Research, Some(state.revision), @@ -19889,6 +19992,17 @@ pub(crate) mod tests { &state, ) .map_err(anyhow::Error::msg)?; + launch.repo_frontier_research_request_id = Some(research_request.request_id.clone()); + match &mut launch.launch_document { + EpiphanyWorkerLaunchDocument::Role(document) => { + document.frontier_research_context = + Some(crate::RepoFrontierResearchContextProjection::from(&research_request)); + } + EpiphanyWorkerLaunchDocument::Reorient(_) => unreachable!(), + } + let mut cache = runtime_spine_cache(&store)?; + cache.pull_all_backing_stores()?; + cache.put(&research_request.request_id, &research_request)?; let plan = crate::plan_coordinator_job_launch( &state, &launch, @@ -19904,6 +20018,18 @@ pub(crate) mod tests { &plan, "2026-08-11T00:00:01Z".to_string(), )?; + let before_missing_lookup = runtime_spine_backing_store(&store)?.pull_all()?; + assert!( + runtime_authenticated_public_source_lookups_for_worker( + &store, + "worker-public-tool" + ) + .is_err() + ); + assert_eq!( + runtime_spine_backing_store(&store)?.pull_all()?, + before_missing_lookup + ); let mut model_request = EpiphanyModelRequest::new( "request-public-tool",