Bind public Eyes evidence to requested sources
This commit is contained in:
parent
a4a41006a2
commit
3d13b1eac2
2 changed files with 217 additions and 36 deletions
|
|
@ -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::<BTreeSet<_>>();
|
||||
let packet_ids = packet
|
||||
.source_lookup_receipt_ids
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.collect::<BTreeSet<_>>();
|
||||
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::<Vec<_>>();
|
||||
let published_ids = published
|
||||
.iter()
|
||||
.map(|lookup| lookup.receipt_id.as_str())
|
||||
.collect::<BTreeSet<_>>();
|
||||
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::<EyesEvidencePacket>("eyes-9")?, eyes);
|
||||
assert_eq!(
|
||||
cache.get_required::<ContinuityRecoveryReceipt>("continuity-9")?,
|
||||
continuity
|
||||
);
|
||||
assert!(
|
||||
cache
|
||||
.get::<EpiphanyThreadStateEntry>(THREAD_STATE_KEY)?
|
||||
|
|
|
|||
|
|
@ -3649,6 +3649,64 @@ fn validate_frontier_research_launch_carrier(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn frontier_research_request_for_launch(
|
||||
cache: &CultCache,
|
||||
launch: &EpiphanyRuntimeWorkerLaunchRequest,
|
||||
) -> Result<Option<RepoFrontierResearchRequest>> {
|
||||
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::<RepoFrontierResearchRequest>(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<Path>,
|
||||
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<Vec<EyesSourceLookupReceipt>> {
|
||||
|
|
@ -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<Vec<EyesSourceLookupReceipt>> {
|
||||
validate_non_empty(worker_job_id, "frontier public source worker job id")?;
|
||||
let launch = cache
|
||||
.get::<EpiphanyRuntimeWorkerLaunchRequest>(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::<BTreeSet<_>>();
|
||||
let observed = lookups
|
||||
.iter()
|
||||
.map(|lookup| lookup.source_ref.clone())
|
||||
.collect::<BTreeSet<_>>();
|
||||
if observed != expected {
|
||||
let missing = expected.difference(&observed).cloned().collect::<Vec<_>>();
|
||||
let unrequested = observed.difference(&expected).cloned().collect::<Vec<_>>();
|
||||
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",
|
||||
|
|
|
|||
Reference in a new issue