From fc5d1a4a844cffbfb275d175df117f4e092b2236 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 18 Aug 2026 01:34:31 +0200 Subject: [PATCH] Migrate Verification to keyed Mind authority --- epiphany-core/src/agent_launch.rs | 13 +- .../src/bin/epiphany-mvp-coordinator.rs | 75 +- epiphany-core/src/bin/epiphany-mvp-status.rs | 19 +- epiphany-core/src/coordinator_acceptance.rs | 150 +-- epiphany-core/src/coordinator_launch.rs | 3 + .../src/coordinator_launch_context.rs | 729 +------------ epiphany-core/src/current_work.rs | 966 ++++++++++++++++-- epiphany-core/src/lib.rs | 5 +- epiphany-core/src/mind_documents.rs | 73 ++ epiphany-core/src/reasoning_context.rs | 7 + epiphany-core/src/resident_self.rs | 73 +- epiphany-core/src/runtime_spine.rs | 517 +++++++++- epiphany-core/src/runtime_worker_attempt.rs | 10 + epiphany-core/src/soul_gateway.rs | 6 +- epiphany-core/src/substrate_gate.rs | 2 + epiphany-core/src/surfaces/coordinator.rs | 243 +---- .../src/surfaces/coordinator_contract.rs | 14 +- epiphany-core/src/surfaces/mod.rs | 3 + epiphany-core/src/surfaces/worker_launch.rs | 22 + .../src/bin/epiphany-openai-runtime.rs | 9 + epiphany-openai-runtime/src/lib.rs | 51 +- 21 files changed, 1702 insertions(+), 1288 deletions(-) diff --git a/epiphany-core/src/agent_launch.rs b/epiphany-core/src/agent_launch.rs index 21ea4723..c7471679 100644 --- a/epiphany-core/src/agent_launch.rs +++ b/epiphany-core/src/agent_launch.rs @@ -561,10 +561,6 @@ pub fn epiphany_role_launch_output_schema(role_id: EpiphanyRoleResultRoleId) -> "additionalProperties": false } }); - if role_id == EpiphanyRoleResultRoleId::Verification { - properties["verificationRequestId"] = serde_json::json!({"type": "string", "minLength": 1}); - properties["frontierRouteId"] = serde_json::json!({"type": "string", "minLength": 1}); - } let mut required = vec![ "roleId", "verdict", @@ -572,10 +568,6 @@ pub fn epiphany_role_launch_output_schema(role_id: EpiphanyRoleResultRoleId) -> "nextSafeMove", "filesInspected", ]; - if role_id == EpiphanyRoleResultRoleId::Verification { - required.push("verificationRequestId"); - required.push("frontierRouteId"); - } if role_id == EpiphanyRoleResultRoleId::Imagination { if let Some(map) = properties.as_object_mut() { map.insert( @@ -1095,6 +1087,7 @@ pub fn build_epiphany_frontier_plan_mind_launch_request( frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1146,6 +1139,7 @@ pub fn build_epiphany_frontier_plan_mind_launch_request( admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }) } @@ -1197,6 +1191,7 @@ pub fn build_epiphany_role_launch_request_with_dynamic_context( frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1247,6 +1242,7 @@ pub fn build_epiphany_role_launch_request_with_dynamic_context( admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }) } @@ -1411,6 +1407,7 @@ pub fn build_epiphany_job_launch_request( admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, } } diff --git a/epiphany-core/src/bin/epiphany-mvp-coordinator.rs b/epiphany-core/src/bin/epiphany-mvp-coordinator.rs index c5cc2ffe..54dcb4f1 100644 --- a/epiphany-core/src/bin/epiphany-mvp-coordinator.rs +++ b/epiphany-core/src/bin/epiphany-mvp-coordinator.rs @@ -513,7 +513,7 @@ fn run_coordinator(args: &Args) -> Result { let current = epiphany_core::project_current_work(&runtime_store)? .proposal_modeling .filter(|work| { - work.action == epiphany_core::EpiphanyModelingContinuationAction::Launch + work.action == epiphany_core::EpiphanyAgentPassContinuationAction::Launch }) .ok_or_else(|| anyhow!("proposal Modeling request is not launchable"))?; if current.request.request_id != request_id { @@ -836,6 +836,45 @@ fn run_coordinator(args: &Args) -> Result { steps.push(step); continue; } + if role_id == "verification" + && let Some(job_id) = + epiphany_core::current_frontier_verification_review_job_id( + &runtime_store, + )? + { + let result = + epiphany_core::runtime_role_worker_result(&runtime_store, &job_id)? + .ok_or_else(|| { + anyhow!("frontier Verification review lost its typed result") + })?; + push_event( + &mut step, + json!({"type": "frontierVerificationResult", "roleId": role_id, "result": result}), + ); + if !args.auto_review { + final_action = json!({ + "action": "reviewVerificationResult", + "reason": "The exact frontier Verification result awaits Soul admission.", + "runtimeJobId": job_id, + }); + append_operator_step_jsonl(&steps_path, &step)?; + steps.push(step); + break; + } + let accepted = epiphany_core::accept_frontier_verification_result( + &runtime_store, + &job_id, + &now(), + )?; + push_event( + &mut step, + json!({"type": "frontierVerificationAccept", "roleId": role_id, "commit": accepted}), + ); + final_status = collect_coordinator_status(&runtime_store, &thread_id)?; + append_operator_step_jsonl(&steps_path, &step)?; + steps.push(step); + continue; + } if role_id == "modeling" && let Some(job_id) = epiphany_core::current_proposal_modeling_review_job_id(&runtime_store)? @@ -1124,6 +1163,7 @@ fn run_coordinator(args: &Args) -> Result { None }; let thread_free_role = role_id == "research" + || role_id == "verification" || (role_id == "modeling" && (proposal_modeling_request_id.is_some() || status["currentWork"]["bodyModeling"].is_object() @@ -1137,6 +1177,15 @@ fn run_coordinator(args: &Args) -> Result { "bindingId": epiphany_core::EPIPHANY_RESEARCH_ROLE_BINDING_ID, "backendJobId": job_id, }) + } else if role_id == "verification" { + let job_id = epiphany_core::launch_current_frontier_verification_work( + &runtime_store, + &now(), + )?; + json!({ + "bindingId": epiphany_core::EPIPHANY_VERIFICATION_ROLE_BINDING_ID, + "backendJobId": job_id, + }) } else if role_id == "modeling" && let Some(request_id) = proposal_modeling_request_id { @@ -1144,7 +1193,7 @@ fn run_coordinator(args: &Args) -> Result { .proposal_modeling .filter(|work| { work.action - == epiphany_core::EpiphanyModelingContinuationAction::Launch + == epiphany_core::EpiphanyAgentPassContinuationAction::Launch }) .ok_or_else(|| anyhow!("proposal Modeling work is not launchable"))?; if current.request.request_id != request_id { @@ -1779,10 +1828,15 @@ fn launch_role( "Research launch must use the keyed frontier Research current-work owner" )); } + if role == epiphany_core::EpiphanyRoleResultRoleId::Verification { + return Err(anyhow!( + "Verification launch must use the keyed frontier Verification current-work owner" + )); + } let expected_revision = expected_revision.and_then(|value| u64::try_from(value).ok()); let focus = epiphany_core::role_launch_context_focus(&state, epiphany_core::epiphany_role_label(role)); - let mut context = if role == epiphany_core::EpiphanyRoleResultRoleId::Modeling { + let context = if role == epiphany_core::EpiphanyRoleResultRoleId::Modeling { epiphany_core::render_modeling_launch_dynamic_prompt_context( runtime_store, local_verse_store, @@ -1799,14 +1853,6 @@ fn launch_role( } .map_err(anyhow::Error::msg)?; let dynamic_context_rendered_ms = started.elapsed().as_millis(); - if role == epiphany_core::EpiphanyRoleResultRoleId::Verification { - context = epiphany_core::append_verification_hands_receipt_context( - context, - runtime_store, - &state, - ) - .map_err(anyhow::Error::msg)?; - } let role_context_augmented_ms = started.elapsed().as_millis(); let request = epiphany_core::build_epiphany_role_launch_request_with_dynamic_context( thread_id, @@ -1933,6 +1979,11 @@ fn accept_role( .state()? .ok_or_else(|| anyhow!("cannot accept role without native coordinator state"))?; let role = parse_role_id(role_id)?; + if role == epiphany_core::EpiphanyRoleResultRoleId::Verification { + return Err(anyhow!( + "Verification acceptance must use the keyed frontier Verification current-work owner" + )); + } let accepted = service.accept_role( thread_id, &state, @@ -2889,7 +2940,6 @@ mod tests { for required in [ "render_launch_dynamic_prompt_context", "render_modeling_launch_dynamic_prompt_context", - "append_verification_hands_receipt_context", ".launch_job(", ".accept_role(", ] { @@ -2901,6 +2951,7 @@ mod tests { for forbidden in [ concat!("append_modeling_work_loop_telemetry_", "context"), concat!("accepted_research_is_newest_", "unmodeled_boundary"), + concat!("append_verification_hands_receipt_", "context"), ] { assert!(!source.contains(forbidden)); } diff --git a/epiphany-core/src/bin/epiphany-mvp-status.rs b/epiphany-core/src/bin/epiphany-mvp-status.rs index accbb7ea..a6410fdb 100644 --- a/epiphany-core/src/bin/epiphany-mvp-status.rs +++ b/epiphany-core/src/bin/epiphany-mvp-status.rs @@ -471,16 +471,6 @@ fn run_native_status(args: &Args, include_auxiliary_status: bool) -> Result { - epiphany_core::has_complete_hands_consequence_after_latest_accepted_boundary( - &runtime_store_path, - state, - ) - .map_err(anyhow::Error::msg)? - } - None => false, - }; let hands_frontier_ready = current_work.hands_frontier_ready; let research_continuation_action = current_work.research_continuation_action; let frontier_planning_eligibility = epiphany_core::runtime_repo_frontier_planning_eligibility( @@ -534,8 +524,7 @@ fn run_native_status(args: &Args, include_auxiliary_status: bool) -> Result Result Result anyhow::Result<()> { - if finding.verification_request_id.as_deref() != Some(request.request_id.as_str()) - || finding.frontier_route_id.as_deref() != Some(request.route_id.as_str()) - { - return Err(anyhow::anyhow!( - "Verification finding does not echo its exact frontier request and route" - )); - } - Ok(()) -} - pub fn completed_role_finding( runtime_store_path: Option<&Path>, state: &EpiphanyThreadState, @@ -568,10 +520,6 @@ pub fn read_accepted_coordinator_state( #[derive(Debug, Clone, PartialEq, Eq)] pub enum EpiphanyAcceptancePrerequisite { - SoulFrontierVerdict { - verdict: SoulVerdictReceipt, - modeling_request: crate::RepoFrontierModelingRequest, - }, Continuity(ContinuityRecoveryReceipt), } @@ -602,35 +550,11 @@ fn commit_state_with_mind_witness( store_path, expected_state, )?; - for prerequisite in prerequisites { - if let EpiphanyAcceptancePrerequisite::SoulFrontierVerdict { - verdict, - modeling_request, - } = prerequisite - && crate::runtime_spine::derive_repo_frontier_modeling_request(&cache, verdict)? - != *modeling_request - { - return Err(anyhow::anyhow!( - "Soul verdict and frontier Modeling request lost exact typed causality" - )); - } - } let (review_envelope, _) = cache.prepare_entry(&mind_review.gateway_id, mind_review)?; let (commit_envelope, _) = cache.prepare_entry(&commit_receipt.receipt_id, commit_receipt)?; let mut batch = vec![review_envelope, commit_envelope]; for prerequisite in prerequisites { let envelope = match prerequisite { - EpiphanyAcceptancePrerequisite::SoulFrontierVerdict { - verdict, - modeling_request, - } => { - batch.push( - cache - .prepare_entry(&modeling_request.request_id, modeling_request)? - .0, - ); - cache.prepare_entry(&verdict.receipt_id, verdict)?.0 - } EpiphanyAcceptancePrerequisite::Continuity(document) => { cache.prepare_entry(&document.receipt_id, document)?.0 } @@ -848,66 +772,6 @@ fn role_label_lower(role_id: EpiphanyRoleResultRoleId) -> &'static str { #[cfg(test)] mod tests { use super::*; - use sha2::Digest; - - #[test] - fn verification_finding_refuses_swapped_request_or_route() { - let request = RepoFrontierVerificationRequest { - schema_version: REPO_FRONTIER_VERIFICATION_REQUEST_SCHEMA_VERSION.to_string(), - request_id: "verification-request-1".to_string(), - route_id: "frontier-route-1".to_string(), - model_projection_digest: format!("sha256:{}", "a".repeat(64)), - model_source_documents: vec![crate::EpiphanyMindDocumentVersion { - store_id: "epiphany-mind".into(), - document_type: "epiphany.mind.repo_model.identity.v1".into(), - document_key: crate::REPO_MODEL_IDENTITY_KEY.into(), - schema_id: Some("EpiphanyRepoModelIdentityDocument".into()), - payload_msgpack: vec![1], - payload_sha256: format!("sha256:{:x}", sha2::Sha256::digest([1])), - }], - frontier_item_id: "frontier-1".to_string(), - frontier_item_hash: "frontier-hash".to_string(), - hands_intent_id: "intent-1".to_string(), - hands_review_id: "review-1".to_string(), - hands_patch_receipt_id: "patch-1".to_string(), - hands_command_receipt_id: "command-1".to_string(), - hands_commit_receipt_id: "commit-1".to_string(), - requested_at: "2026-07-13T00:00:00Z".to_string(), - contract: REPO_FRONTIER_VERIFICATION_REQUEST_CONTRACT.to_string(), - }; - let mut finding = EpiphanyRoleFindingInterpretation { - verdict: Some("pass".to_string()), - summary: Some("verified".to_string()), - next_safe_move: None, - checkpoint_summary: None, - scratch_summary: None, - files_inspected: Vec::new(), - frontier_node_ids: Vec::new(), - evidence_ids: Vec::new(), - artifact_refs: Vec::new(), - runtime_result_id: Some("result-1".to_string()), - runtime_job_id: Some("job-1".to_string()), - open_questions: Vec::new(), - evidence_gaps: Vec::new(), - risks: Vec::new(), - state_patch: None, - repo_model_mutation_proposal: None, - self_patch: None, - self_persistence: None, - job_error: None, - item_error: None, - verification_request_id: Some(request.request_id.clone()), - frontier_route_id: Some(request.route_id.clone()), - proposal_modeling_request_id: None, - }; - validate_verification_finding_binding(&finding, &request).expect("exact binding"); - - finding.frontier_route_id = Some("frontier-route-2".to_string()); - assert!(validate_verification_finding_binding(&finding, &request).is_err()); - finding.frontier_route_id = Some(request.route_id.clone()); - finding.verification_request_id = Some("verification-request-2".to_string()); - assert!(validate_verification_finding_binding(&finding, &request).is_err()); - } #[test] fn completed_finding_admission_refuses_missing_binding() { diff --git a/epiphany-core/src/coordinator_launch.rs b/epiphany-core/src/coordinator_launch.rs index d97cee21..5f5a7043 100644 --- a/epiphany-core/src/coordinator_launch.rs +++ b/epiphany-core/src/coordinator_launch.rs @@ -301,6 +301,9 @@ fn commit_coordinator_job_launch_in_cache( .clone(), repo_frontier_modeling_request_id: request.repo_frontier_modeling_request_id.clone(), repo_frontier_research_request_id: request.repo_frontier_research_request_id.clone(), + repo_frontier_verification_request_id: request + .repo_frontier_verification_request_id + .clone(), created_at: created_at.clone(), }, )?; diff --git a/epiphany-core/src/coordinator_launch_context.rs b/epiphany-core/src/coordinator_launch_context.rs index d4ce1a8d..bc55a015 100644 --- a/epiphany-core/src/coordinator_launch_context.rs +++ b/epiphany-core/src/coordinator_launch_context.rs @@ -1,32 +1,18 @@ -use crate::EPIPHANY_CULTMESH_INTERNAL_VERSE_ID; -use crate::EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_SCHEMA_VERSION; -use crate::EpiphanyCultMeshWorkLoopTelemetryEntry; use crate::EpiphanyMemoryContextPacket; use crate::EpiphanyMemoryContextQuery; use crate::EpiphanyMemoryProfile; use crate::EpiphanyPromptContextInput; use crate::EpiphanyRepoModelView; -use crate::RuntimeHandsReceiptChainSummary; use crate::assemble_repo_model_view; use crate::load_epiphany_cultmesh_cluster_topology; use crate::load_epiphany_cultmesh_status; use crate::plan_memory_graph_context_cut; use crate::query_epiphany_local_verse_context; use crate::render_epiphany_prompt_context; -use crate::runtime_hands_command_receipt; -use crate::runtime_hands_commit_receipt; -use crate::runtime_hands_patch_receipt; -use crate::runtime_latest_hands_receipt_chain_after; -use crate::write_epiphany_cultmesh_work_loop_telemetry; -use chrono::SecondsFormat; -use chrono::Utc; use epiphany_state_model::EpiphanyThreadState; use sha2::Digest; -use std::fs; use std::path::Path; use std::path::PathBuf; -use std::process::Command; -use uuid::Uuid; pub const EPIPHANY_LOCAL_VERSE_RUNTIME_ID: &str = "epiphany-local"; @@ -155,215 +141,6 @@ fn render_launch_dynamic_prompt_context_with_snapshot( )) } -pub fn append_verification_hands_receipt_context( - mut context: String, - runtime_store_path: &Path, - state: &EpiphanyThreadState, -) -> Result { - let accepted_at = latest_accepted_modeling_or_verification_timestamp(state).ok_or_else(|| { - "Soul launch requires an accepted Modeling or Verification boundary before Hands consequence review".to_string() - })?; - let chain = runtime_latest_hands_receipt_chain_after(runtime_store_path, accepted_at) - .map_err(|error| format!("failed to load Hands receipt chain for Soul: {error}"))? - .ok_or_else(|| { - "Soul launch requires a complete Hands patch, command, and commit receipt chain after the latest accepted Modeling or Verification boundary".to_string() - })?; - let verification_request = crate::commit_repo_frontier_verification_request_for_chain( - runtime_store_path, - &chain, - &Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true), - ) - .map_err(|error| format!("failed to commit Soul frontier verification request: {error}"))?; - let route = - crate::runtime_repo_frontier_route(runtime_store_path, &verification_request.route_id) - .map_err(|error| format!("failed to reload Soul frontier route: {error}"))? - .ok_or_else(|| "Soul frontier route disappeared after request commit".to_string())?; - let current_model = assemble_repo_model_view(runtime_store_path) - .map_err(|error| format!("failed to load current keyed RepoModel for Soul: {error}"))?; - let current_item = - validate_current_frontier_route(&route, ¤t_model, Some(&verification_request))?; - let (plan_decision, execution_amendment) = - load_route_plan_authority(runtime_store_path, &route, ¤t_model)?; - let telemetry = work_loop_telemetry_from_hands_chain( - &chain, - runtime_store_path, - state, - accepted_at, - Vec::new(), - ); - let telemetry_store = local_verse_store_path(runtime_store_path); - write_epiphany_cultmesh_work_loop_telemetry(&telemetry_store, telemetry.clone()).map_err( - |error| { - format!( - "failed to write work-loop telemetry to internal CultMesh store {}: {error}", - telemetry_store.display() - ) - }, - )?; - context.push_str("\n\n\n"); - context.push_str(&format!( - "verificationRequestId: {}\nfrontierRouteId: {}\nfrontierItemId: {}\nrouteQuestion: {}\nrouteGap: {}\n", - verification_request.request_id, - route.route_id, - route.frontier_item_id, - route.question, - route.gap, - )); - context.push_str("modelAuthorityChain:\n"); - context.push_str(&format!( - "- currentKeyedView: projectionDigest={} sourceDocuments={}\n", - current_model.projection_digest, - render_repo_model_source_documents(¤t_model.source_documents), - )); - context.push_str(&format!( - "- frontierRoute: schemaVersion={} routeId={} modelProjectionDigest={} modelSourceDocuments={} frontierItemId={} frontierItemHash={} selectedAt={}\n", - route.schema_version, - route.route_id, - route.model_projection_digest, - render_repo_model_source_documents(&route.model_source_documents), - route.frontier_item_id, - route.frontier_item_hash, - route.selected_at, - )); - context.push_str(&format!( - "- verificationRequestBasis: modelProjectionDigest={} modelSourceDocuments={}\n", - verification_request.model_projection_digest, - render_repo_model_source_documents(&verification_request.model_source_documents), - )); - context.push_str(&format!( - "- currentFrontierItem: id={} question={} gap={} sourceScope={}\n", - current_item.id, - current_item.question, - current_item.gap, - current_item.source_scope.join(" | "), - )); - if let Some(decision) = plan_decision { - context.push_str(&format!( - "- frontierPlanDecision: schemaVersion={} decisionId={} planningRequestId={} candidateId={} candidateSha256={} modelProjectionDigest={} modelSourceDocuments={} frontierItemId={} frontierItemHash={} decision={:?} decisionSource={:?} decidedAt={}\n", - decision.schema_version, - decision.decision_id, - decision.planning_request_id, - decision.candidate_id, - decision.candidate_sha256, - decision.model_projection_digest, - render_repo_model_source_documents(&decision.model_source_documents), - decision.frontier_item_id, - decision.frontier_item_hash, - decision.decision, - decision.decision_source, - decision.decided_at, - )); - } - if let Some(amendment) = execution_amendment { - context.push_str(&format!( - "- executionAmendment: schemaVersion={} amendmentId={} replacedRouteId={} sourceActorId={} commandId={} admissionId={} packetSha256={} previousModelProjectionDigest={} previousModelSourceDocuments={} admittedModelProjectionDigest={} admittedModelSourceDocuments={} replacementAction={} replacementCommand={} rationale={} amendedAt={}\n", - amendment.schema_version, - amendment.amendment_id, - amendment.replaced_route_id, - amendment.source_actor_id, - amendment.command_id, - amendment.admission_id, - amendment.packet_sha256, - amendment.previous_model_projection_digest, - render_repo_model_source_documents(&amendment.previous_model_source_documents), - amendment.admitted_model_projection_digest, - render_repo_model_source_documents(&amendment.admitted_model_source_documents), - amendment.replacement_action, - amendment.replacement_command, - amendment.rationale, - amendment.amended_at, - )); - } - if let Some(plan) = route.adopted_plan.as_ref() { - context.push_str(&format!( - "adoptedCandidateSha256: {}\nadoptedAction: {}\nadoptedCommand: {}\neffectiveAction: {}\neffectiveCommand: {}\nexecutionAmendmentId: {}\nadoptedChecks: {}\nadoptedStopConditions: {}\nadoptedRollbackSteps: {}\nadoptedCommitMessage: {}\n", - plan.candidate_sha256, - plan.action, - plan.command, - plan.effective_action(), - plan.effective_command(), - plan.execution_amendment - .as_ref() - .map(|amendment| amendment.amendment_id.as_str()) - .unwrap_or(""), - plan.checks.join(" | "), - plan.stop_conditions.join(" | "), - plan.rollback_steps.join(" | "), - plan.commit_message, - )); - } - context.push_str( - "Soul is reviewing typed internal CultMesh telemetry for concrete Hands consequence evidence produced after the latest accepted Verification finding.\n", - ); - context.push_str(&render_work_loop_telemetry(&telemetry)); - context.push_str("- resolvedReceiptPayloads:\n"); - context.push_str(&format!( - " - patch: schemaVersion={} changedPaths={}\n", - chain.patch_schema_version, - chain.changed_paths.join(", ") - )); - context.push_str(&format!( - " - command: schemaVersion={} command=`{}` exitCode={} stdoutArtifact={} stderrArtifact={}\n", - chain.command_schema_version, - chain.command, - chain.exit_code, - chain.stdout_artifact, - chain.stderr_artifact - )); - context.push_str(&format!( - " - commit: schemaVersion={} commitSha={} branch={} changedPaths={}\n", - chain.commit_schema_version, - chain.commit_sha, - chain.branch, - chain.changed_paths.join(", ") - )); - context.push_str("- artifactPreviews:\n"); - context.push_str(&format!( - " - stdout: {}\n", - artifact_preview(&chain.stdout_artifact) - )); - context.push_str(&format!( - " - stderr: {}\n", - artifact_preview(&chain.stderr_artifact) - )); - if !chain.changed_paths.is_empty() { - context.push_str("- changedPaths:\n"); - for path in chain.changed_paths { - context.push_str(&format!(" - {path}\n")); - } - } - context.push_str(&format!("- summary: {}\n", chain.summary)); - context.push_str("- sourceRefs:\n"); - context.push_str(" - epiphany-core/src/bin/epiphany-hands-action.rs\n"); - context.push_str(" - epiphany-core/src/hands_gateway.rs\n"); - context.push_str(" - epiphany-core/src/runtime_spine.rs\n"); - context.push_str(" - epiphany-core/src/bin/epiphany-mvp-coordinator.rs\n"); - context.push_str(" - epiphany-core/src/coordinator_launch.rs\n"); - context.push_str(" - epiphany-core/src/coordinator_launch_context.rs\n"); - context.push_str(" - epiphany-core/src/surfaces/coordinator.rs\n"); - context.push_str("- sourcePathProof:\n"); - context.push_str( - " - epiphany-hands-action `record-pass` records patch, command, and commit receipts through runtime-spine put functions.\n", - ); - context.push_str( - " - runtime_spine registers Hands receipt document types, persists/rereads them through the shared CultCache runtime-spine store, and exposes latest-chain readback.\n", - ); - context.push_str( - " - native coordinator launch context appends this packet only for Verification role launches after dynamic context assembly.\n", - ); - context.push_str( - " - coordinator treats a complete post-verification Hands chain as implementation evidence that requires a fresh Soul pass.\n", - ); - context.push_str( - " - coordinator routing requires a failed Verification result to receive an exact failure-review supersession receipt before launching a fresh Soul attempt; immutable verification-request identity remains separate from worker-attempt lifecycle.\n", - ); - context.push_str( - "Use these typed internal Verse receipts, artifacts, and source references as the concrete Hands evidence under review. Do not ask for generic receipt-path evidence without first judging this packet.\n", - ); - context.push_str(""); - Ok(context) -} - pub fn validate_frontier_plan_decision_chain( decision: &crate::RepoFrontierPlanDecisionReceipt, route: &crate::RepoFrontierRoute, @@ -485,141 +262,6 @@ fn validate_current_frontier_route<'a>( Ok(current_item) } -fn load_route_plan_authority( - runtime_store_path: &Path, - route: &crate::RepoFrontierRoute, - current_model: &EpiphanyRepoModelView, -) -> Result< - ( - Option, - Option, - ), - String, -> { - let Some(plan) = route.adopted_plan.as_ref() else { - return Ok((None, None)); - }; - let mut cache = crate::runtime_spine_cache(runtime_store_path) - .map_err(|error| format!("failed to open Mind receipt store for Soul: {error}"))?; - cache - .pull_all_backing_stores() - .map_err(|error| format!("failed to refresh Mind receipt store for Soul: {error}"))?; - let mut decisions = cache - .get_all::() - .map_err(|error| format!("failed to load frontier plan decisions for Soul: {error}"))? - .into_iter() - .filter(|decision| decision.planning_request_id == plan.planning_request_id) - .collect::>(); - if decisions.len() != 1 { - return Err( - "Soul adopted frontier route requires exactly one typed plan decision".to_string(), - ); - } - let decision = decisions.pop().expect("one decision checked above"); - validate_frontier_plan_decision_chain(&decision, route, current_model)?; - - let Some(plan_amendment) = plan.execution_amendment.as_ref() else { - return Ok((Some(decision), None)); - }; - let mut amendments = cache - .get_all::() - .map_err(|error| format!("failed to load execution amendments for Soul: {error}"))? - .into_iter() - .filter(|receipt| receipt.amendment_id == plan_amendment.amendment_id) - .collect::>(); - if amendments.len() != 1 { - return Err( - "Soul amended frontier route requires exactly one typed execution amendment" - .to_string(), - ); - } - let amendment = amendments.pop().expect("one amendment checked above"); - let previous_basis = crate::EpiphanyRepoModelBasis { - projection_digest: amendment.previous_model_projection_digest.clone(), - source_documents: amendment.previous_model_source_documents.clone(), - }; - previous_basis - .validate() - .map_err(|error| format!("execution amendment has an invalid previous basis: {error}"))?; - let admitted_basis = crate::EpiphanyRepoModelBasis { - projection_digest: amendment.admitted_model_projection_digest.clone(), - source_documents: amendment.admitted_model_source_documents.clone(), - }; - admitted_basis - .validate() - .map_err(|error| format!("execution amendment has an invalid admitted basis: {error}"))?; - let replaced_route = cache - .get::(&amendment.replaced_route_id) - .map_err(|error| format!("failed to load replaced frontier route for Soul: {error}"))? - .ok_or_else(|| "execution amendment lost its replaced frontier route".to_string())?; - if amendment.schema_version != crate::REPO_FRONTIER_EXECUTION_AMENDMENT_RECEIPT_SCHEMA_VERSION - || amendment.contract != crate::REPO_FRONTIER_EXECUTION_AMENDMENT_RECEIPT_CONTRACT - || amendment.replaced_route_id != plan_amendment.replaces_route_id - || amendment.frontier_item_id != route.frontier_item_id - || amendment.previous_frontier_item_hash != replaced_route.frontier_item_hash - || previous_basis - != (crate::EpiphanyRepoModelBasis { - projection_digest: replaced_route.model_projection_digest.clone(), - source_documents: replaced_route.model_source_documents.clone(), - }) - || admitted_basis != current_model.reasoning_basis() - || admitted_basis - != (crate::EpiphanyRepoModelBasis { - projection_digest: route.model_projection_digest.clone(), - source_documents: route.model_source_documents.clone(), - }) - || amendment.source_actor_id != plan_amendment.source_actor_id - || amendment.command_id != plan_amendment.command_id - || amendment.admission_id != plan_amendment.admission_id - || amendment.packet_sha256 != plan_amendment.packet_sha256 - || amendment.replacement_action != plan_amendment.action - || amendment.replacement_command != plan_amendment.command - || amendment.rationale != plan_amendment.rationale - || amendment.amended_at != plan_amendment.amended_at - { - return Err( - "Soul execution amendment does not exactly bind previous/current keyed routes and the effective plan" - .to_string(), - ); - } - Ok((Some(decision), Some(amendment))) -} - -fn render_repo_model_source_documents(documents: &[crate::EpiphanyMindDocumentVersion]) -> String { - documents - .iter() - .map(|document| { - format!( - "{}/{}/{}@{}#{}", - document.store_id, - document.document_type, - document.document_key, - document.schema_id.as_deref().unwrap_or(""), - document.payload_sha256, - ) - }) - .collect::>() - .join(" | ") -} - -pub fn has_complete_hands_consequence_after_latest_accepted_boundary( - runtime_store_path: &Path, - state: &EpiphanyThreadState, -) -> Result { - let Some(accepted_at) = latest_accepted_modeling_or_verification_timestamp(state) else { - return Ok(false); - }; - let chain = runtime_latest_hands_receipt_chain_after(runtime_store_path, accepted_at).map_err( - |error| format!("failed to load Hands receipt chain for coordinator routing: {error}"), - )?; - let Some(chain) = chain else { - return Ok(false); - }; - crate::runtime_hands_receipt_chain_matches_current_model(runtime_store_path, &chain).map_err( - |error| format!("failed to validate Hands receipt route against current model: {error}"), - ) -} - pub fn append_modeling_repo_model_shape_context( context: String, runtime_store_path: &Path, @@ -815,333 +457,6 @@ fn debug_variant_snake_case(value: impl std::fmt::Debug) -> String { output } -fn latest_accepted_modeling_or_verification_timestamp(state: &EpiphanyThreadState) -> Option<&str> { - state - .acceptance_receipts - .iter() - .filter(|receipt| { - matches!(receipt.role_id.as_str(), "modeling" | "verification") - && receipt.surface == "roleAccept" - && receipt.status == "accepted" - }) - .map(|receipt| receipt.accepted_at.as_str()) - .max() -} - -fn work_loop_telemetry_from_hands_chain( - chain: &RuntimeHandsReceiptChainSummary, - runtime_store_path: &Path, - state: &EpiphanyThreadState, - lower_bound_receipt_at: &str, - soul_receipt_ids: Vec, -) -> EpiphanyCultMeshWorkLoopTelemetryEntry { - EpiphanyCultMeshWorkLoopTelemetryEntry { - schema_version: EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_SCHEMA_VERSION.to_string(), - runtime_id: EPIPHANY_LOCAL_VERSE_RUNTIME_ID.to_string(), - verse_id: EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(), - telemetry_id: format!( - "work-loop-telemetry-{}-{}", - state.revision, - Uuid::new_v4() - ), - thread_id: state.last_updated_turn_id.clone().unwrap_or_default(), - produced_at_utc: Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true), - source_stage: "hands".to_string(), - target_stages: vec!["soul".to_string(), "modeling".to_string()], - lower_bound_receipt_at: lower_bound_receipt_at.to_string(), - hands_intent_id: chain.intent_id.clone(), - hands_review_id: chain.review_id.clone(), - hands_runtime_job_id: chain.runtime_job_id.clone(), - substrate_gate_grant_receipt_id: chain.substrate_gate_grant_receipt_id.clone(), - hands_patch_receipt_id: chain.patch_receipt_id.clone(), - hands_command_receipt_id: chain.command_receipt_id.clone(), - hands_commit_receipt_id: chain.commit_receipt_id.clone(), - command: chain.command.clone(), - exit_code: chain.exit_code.clone(), - stdout_artifact: chain.stdout_artifact.clone(), - stderr_artifact: chain.stderr_artifact.clone(), - commit_sha: chain.commit_sha.clone(), - branch: chain.branch.clone(), - changed_paths: chain.changed_paths.clone(), - artifact_previews: vec![ - format!("stdout: {}", artifact_preview(&chain.stdout_artifact)), - format!("stderr: {}", artifact_preview(&chain.stderr_artifact)), - ], - source_refs: vec![ - "epiphany-core/src/bin/epiphany-hands-action.rs".to_string(), - "epiphany-core/src/hands_gateway.rs".to_string(), - "epiphany-core/src/runtime_spine.rs".to_string(), - "epiphany-core/src/bin/epiphany-mvp-coordinator.rs".to_string(), - "epiphany-core/src/coordinator_launch.rs".to_string(), - "epiphany-core/src/coordinator_launch_context.rs".to_string(), - "epiphany-core/src/surfaces/coordinator.rs".to_string(), - ], - source_path_proof: vec![ - "epiphany-hands-action `record-pass` records patch, command, and commit receipts through runtime-spine put functions.".to_string(), - "runtime_spine registers Hands receipt document types, persists/rereads them through the shared CultCache runtime-spine store, and exposes latest-chain readback.".to_string(), - "native coordinator launch context writes this typed packet to the internal CultMesh Verse before Soul receives the rendered projection.".to_string(), - "coordinator treats a complete post-verification Hands chain as implementation evidence that requires a fresh Soul pass.".to_string(), - "coordinator routing requires exact failed-Verification review and supersession before a fresh Soul attempt while immutable request identity remains separate from attempt lifecycle.".to_string(), - "runtime_spine admission causally binds one verdict-incorporation result to its exact route, verification request, Soul verdict, Modeling request, and final frontier disposition; idempotent replay returns the same receipt and leaves no actionable Hands frontier.".to_string(), - ], - soul_receipt_ids, - summary: chain.summary.clone(), - receipt_payload_previews: receipt_payload_previews(runtime_store_path, chain), - commit_diff_preview: commit_diff_preview(runtime_store_path, chain), - verification_assertions: vec![ - "cultmesh_integration::tests::work_loop_telemetry_round_trips_as_internal_cultmesh_document asserts the typed internal Verse document round-trips and is registered.".to_string(), - "launch_context::tests::verification_launch_context_includes_hands_receipt_chain seeds real runtime-spine Hands receipts, asserts Soul launch renders the packet, and asserts Modeling launch enriches the typed packet with Soul receipt ids.".to_string(), - "runtime_spine::tests::repo_model_incorporates_pass_and_nonpass_soul_verdicts_causally admits the first valid result, replays it idempotently to the same receipt, verifies exact causal ids and final frontier state, and proves no actionable Hands frontier remains.".to_string(), - "epiphany-mvp-coordinator binary tests keep the Hands gate and coordinator routing surface compiling under the executable coordinator harness.".to_string(), - ], - } -} - -fn render_work_loop_telemetry(telemetry: &EpiphanyCultMeshWorkLoopTelemetryEntry) -> String { - let mut rendered = String::new(); - rendered.push_str(&format!("- schemaVersion: {}\n", telemetry.schema_version)); - rendered.push_str(&format!("- verseId: {}\n", telemetry.verse_id)); - rendered.push_str(&format!("- telemetryId: {}\n", telemetry.telemetry_id)); - rendered.push_str(&format!("- sourceStage: {}\n", telemetry.source_stage)); - rendered.push_str(&format!( - "- targetStages: {}\n", - telemetry.target_stages.join(", ") - )); - rendered.push_str(&format!( - "- lowerBoundAcceptedVerificationAt: {}\n", - telemetry.lower_bound_receipt_at - )); - rendered.push_str(&format!("- intentId: {}\n", telemetry.hands_intent_id)); - rendered.push_str(&format!("- reviewId: {}\n", telemetry.hands_review_id)); - rendered.push_str(&format!( - "- runtimeJobId: {}\n", - telemetry.hands_runtime_job_id - )); - rendered.push_str(&format!( - "- substrateGateGrantReceiptId: {}\n", - telemetry.substrate_gate_grant_receipt_id - )); - rendered.push_str(&format!( - "- receiptIds: patch={}, command={}, commit={}\n", - telemetry.hands_patch_receipt_id, - telemetry.hands_command_receipt_id, - telemetry.hands_commit_receipt_id - )); - if !telemetry.soul_receipt_ids.is_empty() { - rendered.push_str(&format!( - "- soulReceiptIds: {}\n", - telemetry.soul_receipt_ids.join(", ") - )); - } - rendered.push_str(&format!( - "- command: `{}` exitCode={} stdoutArtifact={} stderrArtifact={}\n", - telemetry.command, - telemetry.exit_code, - telemetry.stdout_artifact, - telemetry.stderr_artifact - )); - rendered.push_str(&format!( - "- commit: sha={} branch={}\n", - telemetry.commit_sha, telemetry.branch - )); - if !telemetry.changed_paths.is_empty() { - rendered.push_str("- changedPaths:\n"); - for path in &telemetry.changed_paths { - rendered.push_str(&format!(" - {path}\n")); - } - } - if !telemetry.artifact_previews.is_empty() { - rendered.push_str("- artifactPreviews:\n"); - for preview in &telemetry.artifact_previews { - rendered.push_str(&format!(" - {preview}\n")); - } - } - if !telemetry.receipt_payload_previews.is_empty() { - rendered.push_str("- resolvedReceiptPayloadPreviews:\n"); - for payload in &telemetry.receipt_payload_previews { - rendered.push_str(&format!(" - {}\n", payload.replace('\n', "\n "))); - } - } - if !telemetry.commit_diff_preview.trim().is_empty() { - rendered.push_str("- commitDiffPreview:\n"); - rendered.push_str(&format!( - " {}\n", - telemetry.commit_diff_preview.replace('\n', "\n ") - )); - } - if !telemetry.source_refs.is_empty() { - rendered.push_str("- sourceRefs:\n"); - for source_ref in &telemetry.source_refs { - rendered.push_str(&format!(" - {source_ref}\n")); - } - } - if !telemetry.source_path_proof.is_empty() { - rendered.push_str("- sourcePathProof:\n"); - for proof in &telemetry.source_path_proof { - rendered.push_str(&format!(" - {proof}\n")); - } - } - if !telemetry.verification_assertions.is_empty() { - rendered.push_str("- verificationAssertions:\n"); - for assertion in &telemetry.verification_assertions { - rendered.push_str(&format!(" - {assertion}\n")); - } - } - rendered.push_str(&format!("- summary: {}\n", telemetry.summary)); - rendered -} - -fn receipt_payload_previews( - runtime_store_path: &Path, - chain: &RuntimeHandsReceiptChainSummary, -) -> Vec { - let patch = runtime_hands_patch_receipt(runtime_store_path, chain.patch_receipt_id.as_str()) - .ok() - .flatten() - .map(|receipt| { - format!( - "patch: schemaVersion={} receiptId={} intentId={} reviewId={} runtimeJobId={} changedPaths={} emittedAt={} summary={} contract={}", - receipt.schema_version, - receipt.receipt_id, - receipt.intent_id, - receipt.review_id, - receipt.runtime_job_id, - receipt.changed_paths.join(", "), - receipt.emitted_at, - receipt.summary, - receipt.contract - ) - }) - .unwrap_or_else(|| { - format!( - "patch: receipt {} could not be resolved from runtime-spine", - chain.patch_receipt_id - ) - }); - let command = - runtime_hands_command_receipt(runtime_store_path, chain.command_receipt_id.as_str()) - .ok() - .flatten() - .map(|receipt| { - format!( - "command: schemaVersion={} receiptId={} intentId={} reviewId={} runtimeJobId={} command=`{}` exitCode={} stdoutArtifact={} stderrArtifact={} emittedAt={} summary={} contract={}", - receipt.schema_version, - receipt.receipt_id, - receipt.intent_id, - receipt.review_id, - receipt.runtime_job_id, - receipt.command, - receipt.exit_code, - receipt.stdout_artifact, - receipt.stderr_artifact, - receipt.emitted_at, - receipt.summary, - receipt.contract - ) - }) - .unwrap_or_else(|| { - format!( - "command: receipt {} could not be resolved from runtime-spine", - chain.command_receipt_id - ) - }); - let commit = runtime_hands_commit_receipt(runtime_store_path, chain.commit_receipt_id.as_str()) - .ok() - .flatten() - .map(|receipt| { - format!( - "commit: schemaVersion={} receiptId={} intentId={} reviewId={} runtimeJobId={} commitSha={} branch={} changedPaths={} emittedAt={} summary={} contract={}", - receipt.schema_version, - receipt.receipt_id, - receipt.intent_id, - receipt.review_id, - receipt.runtime_job_id, - receipt.commit_sha, - receipt.branch, - receipt.changed_paths.join(", "), - receipt.emitted_at, - receipt.summary, - receipt.contract - ) - }) - .unwrap_or_else(|| { - format!( - "commit: receipt {} could not be resolved from runtime-spine", - chain.commit_receipt_id - ) - }); - vec![patch, command, commit] -} - -fn commit_diff_preview( - runtime_store_path: &Path, - chain: &RuntimeHandsReceiptChainSummary, -) -> String { - let Some(repo_root) = runtime_store_path - .parent() - .and_then(Path::parent) - .filter(|path| path.join(".git").exists()) - else { - return "" - .to_string(); - }; - if chain.commit_sha.trim().is_empty() { - return "".to_string(); - } - let output = Command::new("git") - .current_dir(repo_root) - .arg("show") - .arg("--stat") - .arg("--patch") - .arg("--format=medium") - .arg(chain.commit_sha.as_str()) - .arg("--") - .args(chain.changed_paths.iter().map(String::as_str)) - .output(); - let Ok(output) = output else { - return "".to_string(); - }; - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - return compact_preview( - &format!( - "", - output.status.code(), - stderr - ), - 1600, - ); - } - compact_preview(&String::from_utf8_lossy(&output.stdout), 3200) -} - -fn artifact_preview(path: &str) -> String { - let path = Path::new(path); - let text = fs::read_to_string(path) - .or_else(|_| { - path.strip_prefix("./") - .map(Path::to_path_buf) - .map_err(|_| std::io::Error::from(std::io::ErrorKind::NotFound)) - .and_then(fs::read_to_string) - }) - .unwrap_or_else(|_| "".to_string()); - compact_preview(&text, 1200) -} - -fn compact_preview(text: &str, limit: usize) -> String { - let compact = text - .lines() - .map(str::trim) - .filter(|line| !line.is_empty()) - .collect::>() - .join(" | "); - if compact.len() <= limit { - compact - } else { - format!("{} ...", &compact[..limit]) - } -} - fn launch_memory_context( runtime_store_path: &Path, _state: &EpiphanyThreadState, @@ -1190,7 +505,6 @@ mod tests { use crate::open_runtime_spine_heartbeat_job; use crate::runtime_worker_launch_request; use crate::seed_epiphany_local_verse_context; - use epiphany_state_model::EpiphanyAcceptanceReceipt; use std::fs; use uuid::Uuid; @@ -1428,6 +742,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: "2026-06-02T00:00:00Z".to_string(), }, )?; @@ -1472,46 +787,4 @@ mod tests { assert!(!local_verse_store.exists()); Ok(()) } - - #[test] - fn verification_launch_refuses_to_invent_a_pre_hands_request() -> anyhow::Result<()> { - let temp = std::env::temp_dir().join(format!( - "epiphany-verification-without-hands-{}", - Uuid::new_v4() - )); - fs::create_dir(&temp)?; - let runtime_store = temp.join("runtime-spine.msgpack"); - initialize_runtime_spine( - &runtime_store, - RuntimeSpineInitOptions { - runtime_id: "epiphany-verification-without-hands".to_string(), - display_name: "Epiphany Verification Without Hands".to_string(), - created_at: "2026-08-06T00:00:00Z".to_string(), - }, - )?; - let state = EpiphanyThreadState { - acceptance_receipts: vec![EpiphanyAcceptanceReceipt { - id: "accept-modeling-without-hands".to_string(), - result_id: "result-modeling-without-hands".to_string(), - job_id: "job-modeling-without-hands".to_string(), - binding_id: "modeling-checkpoint-worker".to_string(), - surface: "roleAccept".to_string(), - role_id: "modeling".to_string(), - status: "accepted".to_string(), - accepted_at: "2026-08-06T00:00:01Z".to_string(), - ..Default::default() - }], - ..Default::default() - }; - - let error = append_verification_hands_receipt_context( - "context".to_string(), - &runtime_store, - &state, - ) - .expect_err("Soul must not launch before a complete Hands consequence chain exists"); - assert!(error.contains("complete Hands patch, command, and commit receipt chain")); - fs::remove_dir_all(temp)?; - Ok(()) - } } diff --git a/epiphany-core/src/current_work.rs b/epiphany-core/src/current_work.rs index 1519ec1b..ec39c754 100644 --- a/epiphany-core/src/current_work.rs +++ b/epiphany-core/src/current_work.rs @@ -19,17 +19,18 @@ pub const BODY_MODELING_LAUNCH_BINDING_SCHEMA_VERSION: &str = pub struct EpiphanyCurrentWorkProjection { pub mind_projection_digest: String, pub body_modeling: Option, - pub body_modeling_action: Option, + pub body_modeling_action: Option, pub research_continuation_action: Option, pub frontier_planning_stage: RepoFrontierPlanningLifecycleStage, pub proposal_modeling: Option, pub frontier_verdict_modeling: Option, + pub verification: Option, pub hands_frontier_ready: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub enum EpiphanyModelingContinuationAction { +pub enum EpiphanyAgentPassContinuationAction { Launch, Wait, Review, @@ -38,14 +39,21 @@ pub enum EpiphanyModelingContinuationAction { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct EpiphanyProposalModelingWorkProjection { pub request: RepoFrontierProposalModelingRequest, - pub action: EpiphanyModelingContinuationAction, + pub action: EpiphanyAgentPassContinuationAction, pub job_id: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct EpiphanyFrontierVerdictModelingWorkProjection { pub request: crate::RepoFrontierModelingRequest, - pub action: EpiphanyModelingContinuationAction, + pub action: EpiphanyAgentPassContinuationAction, + pub job_id: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct EpiphanyVerificationWorkProjection { + pub request: crate::RepoFrontierVerificationRequest, + pub action: EpiphanyAgentPassContinuationAction, pub job_id: Option, } @@ -307,7 +315,7 @@ pub fn project_current_work(store_path: impl AsRef) -> Result { let action = body_modeling_continuation_action(&cache, &work.work_id)?; ( - (action == EpiphanyModelingContinuationAction::Launch).then_some(work), + (action == EpiphanyAgentPassContinuationAction::Launch).then_some(work), Some(action), ) } @@ -330,10 +338,101 @@ pub fn project_current_work(store_path: impl AsRef) -> Result Result { + let prefix = format!("frontier-verification-{request_id}-attempt-"); + job_id + .strip_prefix(&prefix) + .ok_or_else(|| anyhow!("frontier Verification job identity is not canonical"))? + .parse::() + .map_err(|_| anyhow!("frontier Verification attempt ordinal is invalid")) +} + +fn current_verification_work( + cache: &CultCache, +) -> Result> { + let mut requests = cache.get_all::()?; + requests.sort_by(|left, right| left.request_id.cmp(&right.request_id)); + let launches = cache.get_all::()?; + let receipts = cache.get_all::()?; + for request in requests { + crate::runtime_spine::validate_repo_frontier_verification_request_intrinsic(&request)?; + let mut request_launches = launches + .iter() + .filter(|launch| { + launch.repo_frontier_verification_request_id.as_deref() + == Some(request.request_id.as_str()) + }) + .map(|launch| { + Ok(( + verification_attempt_ordinal(&request.request_id, &launch.job_id)?, + launch, + )) + }) + .collect::>>()?; + request_launches.sort_by_key(|(ordinal, _)| *ordinal); + if let Some((_, launch)) = request_launches.last() + && let Some(result) = + cache.get::(&launch.job_id)? + && receipts.iter().any(|receipt| { + receipt.invariant_owner == "Soul.verification" + && matches!( + &receipt.authority, + crate::EpiphanyMindCommitAuthority::ModelDecisionContext { + decision_context_id + } if decision_context_id == &result.decision_context_id + ) + }) + { + continue; + } + let Some((_, launch)) = request_launches.last() else { + if crate::runtime_spine::verification_frontier_is_current(cache, &request)? { + return Ok(Some(EpiphanyVerificationWorkProjection { + request, + action: EpiphanyAgentPassContinuationAction::Launch, + job_id: None, + })); + } + continue; + }; + let result = cache.get::(&launch.job_id)?; + if let Some(result) = result.as_ref() + && (result.verification_request_id.as_deref() != Some(request.request_id.as_str()) + || result.frontier_route_id.as_deref() != Some(request.route_id.as_str())) + { + return Err(anyhow!( + "frontier Verification result crossed request authority" + )); + } + let job = cache + .get::(&launch.job_id)? + .ok_or_else(|| anyhow!("frontier Verification launch lost its runtime job"))?; + let action = match job.status { + crate::EpiphanyRuntimeJobStatus::Failed + | crate::EpiphanyRuntimeJobStatus::Cancelled + if crate::runtime_spine::verification_frontier_is_current(cache, &request)? => + { + EpiphanyAgentPassContinuationAction::Launch + } + crate::EpiphanyRuntimeJobStatus::Completed if result.is_some() => { + EpiphanyAgentPassContinuationAction::Review + } + _ => EpiphanyAgentPassContinuationAction::Wait, + }; + return Ok(Some(EpiphanyVerificationWorkProjection { + request, + action, + job_id: Some(launch.job_id.clone()), + })); + } + Ok(None) +} + fn frontier_verdict_attempt_ordinal(request_id: &str, job_id: &str) -> Result { let prefix = format!("frontier-verdict-modeling-{request_id}-attempt-"); job_id @@ -384,7 +483,7 @@ fn current_frontier_verdict_modeling_work( let Some((_, launch)) = request_launches.last() else { return Ok(Some(EpiphanyFrontierVerdictModelingWorkProjection { request, - action: EpiphanyModelingContinuationAction::Launch, + action: EpiphanyAgentPassContinuationAction::Launch, job_id: None, })); }; @@ -403,12 +502,12 @@ fn current_frontier_verdict_modeling_work( let action = match job.status { crate::EpiphanyRuntimeJobStatus::Failed | crate::EpiphanyRuntimeJobStatus::Cancelled => { - EpiphanyModelingContinuationAction::Launch + EpiphanyAgentPassContinuationAction::Launch } crate::EpiphanyRuntimeJobStatus::Completed if result.is_some() => { - EpiphanyModelingContinuationAction::Review + EpiphanyAgentPassContinuationAction::Review } - _ => EpiphanyModelingContinuationAction::Wait, + _ => EpiphanyAgentPassContinuationAction::Wait, }; return Ok(Some(EpiphanyFrontierVerdictModelingWorkProjection { request, @@ -422,7 +521,7 @@ fn current_frontier_verdict_modeling_work( fn body_modeling_continuation_action( cache: &CultCache, work_id: &str, -) -> Result { +) -> Result { let mut bindings = cache .get_all::()? .into_iter() @@ -430,26 +529,26 @@ fn body_modeling_continuation_action( .collect::>(); bindings.sort_by_key(|binding| binding.attempt_ordinal); let Some(binding) = bindings.last() else { - return Ok(EpiphanyModelingContinuationAction::Launch); + return Ok(EpiphanyAgentPassContinuationAction::Launch); }; let job = cache .get::(&binding.job_id)? .ok_or_else(|| anyhow!("Body Modeling launch binding lost its runtime job"))?; Ok(match job.status { crate::EpiphanyRuntimeJobStatus::Failed | crate::EpiphanyRuntimeJobStatus::Cancelled => { - EpiphanyModelingContinuationAction::Launch + EpiphanyAgentPassContinuationAction::Launch } crate::EpiphanyRuntimeJobStatus::Completed => { if cache .get::(&binding.job_id)? .is_some() { - EpiphanyModelingContinuationAction::Review + EpiphanyAgentPassContinuationAction::Review } else { - EpiphanyModelingContinuationAction::Wait + EpiphanyAgentPassContinuationAction::Wait } } - _ => EpiphanyModelingContinuationAction::Wait, + _ => EpiphanyAgentPassContinuationAction::Wait, }) } @@ -478,7 +577,7 @@ fn current_proposal_modeling_work( let Some(binding) = request_bindings.last() else { return Ok(Some(EpiphanyProposalModelingWorkProjection { request, - action: EpiphanyModelingContinuationAction::Launch, + action: EpiphanyAgentPassContinuationAction::Launch, job_id: None, })); }; @@ -507,16 +606,16 @@ fn current_proposal_modeling_work( let action = match job.status { crate::EpiphanyRuntimeJobStatus::Failed | crate::EpiphanyRuntimeJobStatus::Cancelled => { - EpiphanyModelingContinuationAction::Launch + EpiphanyAgentPassContinuationAction::Launch } crate::EpiphanyRuntimeJobStatus::Completed if result.is_some() => { crate::runtime_spine::validate_proposal_modeling_worker_fulfillment( cache, result.as_ref().expect("checked terminal result"), )?; - EpiphanyModelingContinuationAction::Review + EpiphanyAgentPassContinuationAction::Review } - _ => EpiphanyModelingContinuationAction::Wait, + _ => EpiphanyAgentPassContinuationAction::Wait, }; return Ok(Some(EpiphanyProposalModelingWorkProjection { request, @@ -530,7 +629,7 @@ fn current_proposal_modeling_work( pub fn body_modeling_continuation_action_for_job( store_path: impl AsRef, job_id: &str, -) -> Result> { +) -> Result> { let mut cache = crate::runtime_spine_cache(store_path)?; cache.pull_all_backing_stores()?; let bindings = cache @@ -561,7 +660,7 @@ pub fn body_modeling_continuation_action_for_job( pub fn current_body_modeling_review_job_id(store_path: impl AsRef) -> Result> { let store_path = store_path.as_ref(); if crate::project_current_work(store_path)?.body_modeling_action - != Some(EpiphanyModelingContinuationAction::Review) + != Some(EpiphanyAgentPassContinuationAction::Review) { return Ok(None); } @@ -580,7 +679,7 @@ pub fn current_body_modeling_review_job_id(store_path: impl AsRef) -> Resu pub fn proposal_modeling_continuation_action_for_job( store_path: impl AsRef, job_id: &str, -) -> Result> { +) -> Result> { Ok(project_current_work(store_path)? .proposal_modeling .filter(|work| work.job_id.as_deref() == Some(job_id)) @@ -592,14 +691,14 @@ pub fn current_proposal_modeling_review_job_id( ) -> Result> { Ok(project_current_work(store_path)? .proposal_modeling - .filter(|work| work.action == EpiphanyModelingContinuationAction::Review) + .filter(|work| work.action == EpiphanyAgentPassContinuationAction::Review) .and_then(|work| work.job_id)) } pub fn frontier_verdict_modeling_continuation_action_for_job( store_path: impl AsRef, job_id: &str, -) -> Result> { +) -> Result> { Ok(project_current_work(store_path)? .frontier_verdict_modeling .filter(|work| work.job_id.as_deref() == Some(job_id)) @@ -611,7 +710,7 @@ pub fn current_frontier_verdict_modeling_review_job_id( ) -> Result> { Ok(project_current_work(store_path)? .frontier_verdict_modeling - .filter(|work| work.action == EpiphanyModelingContinuationAction::Review) + .filter(|work| work.action == EpiphanyAgentPassContinuationAction::Review) .and_then(|work| work.job_id)) } @@ -626,6 +725,191 @@ pub fn current_frontier_research_review_job_id( ) } +pub fn current_frontier_verification_review_job_id( + store_path: impl AsRef, +) -> Result> { + Ok(project_current_work(store_path)? + .verification + .filter(|work| work.action == EpiphanyAgentPassContinuationAction::Review) + .and_then(|work| work.job_id)) +} + +pub fn launch_current_frontier_verification_work( + store_path: impl AsRef, + created_at: &str, +) -> Result { + let store_path = store_path.as_ref(); + chrono::DateTime::parse_from_rfc3339(created_at) + .map_err(|_| anyhow!("frontier Verification launch time is invalid"))?; + let mut cache = crate::runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + let work = current_verification_work(&cache)? + .filter(|work| work.action == EpiphanyAgentPassContinuationAction::Launch) + .ok_or_else(|| anyhow!("Mind has no launchable frontier Verification work"))?; + let request = work.request; + let context = crate::runtime_spine::repo_frontier_verification_context(&cache, &request)?; + let identity = cache + .get::(crate::RUNTIME_IDENTITY_KEY)? + .ok_or_else(|| anyhow!("frontier Verification launch requires runtime identity"))?; + let attempt_ordinal = cache + .get_all::()? + .into_iter() + .filter(|launch| { + launch.repo_frontier_verification_request_id.as_deref() + == Some(request.request_id.as_str()) + }) + .count() + + cache + .get_all::()? + .into_iter() + .filter(|attempt| { + attempt.request_kind == "frontier-verification" + && attempt.request_id == request.request_id + }) + .count(); + let job_id = format!( + "frontier-verification-{}-attempt-{attempt_ordinal}", + request.request_id + ); + let launch_document = + crate::EpiphanyWorkerLaunchDocument::Role(crate::EpiphanyRoleWorkerLaunchDocument { + thread_id: job_id.clone(), + role_id: "verification".into(), + state_revision: 0, + objective: None, + dynamic_prompt_context: None, + repository_body_observation_basis: None, + proposal_modeling_context: None, + frontier_verdict_modeling_context: None, + frontier_planning_context: None, + frontier_research_context: None, + frontier_verification_context: Some(context.clone()), + frontier_plan_mind_context: None, + imagination_consideration_context: None, + admitted_model_direction_consideration_context: None, + active_subgoal_id: None, + active_subgoals: Vec::new(), + active_graph_node_ids: Vec::new(), + investigation_checkpoint: None, + scratch: None, + invariants: Vec::new(), + graphs: None, + recent_evidence: Vec::new(), + recent_observations: Vec::new(), + graph_frontier: None, + graph_checkpoint: None, + planning: None, + churn: None, + }); + let authority_scope = "epiphany.role.verification".to_string(); + let output_contract_id = launch_document.output_contract_id().to_string(); + let prepared = crate::prepare_runtime_spine_heartbeat_job( + &cache, + crate::RuntimeSpineHeartbeatJobOptions { + runtime_id: identity.runtime_id, + display_name: "Epiphany Local".into(), + session_id: crate::EPIPHANY_RUNTIME_ROOT_SESSION_ID.into(), + objective: "Audit one exact Hands consequence against its routed invariant".into(), + coordinator_note: + "Verification current-work launch transaction opened this session.".into(), + job_id: job_id.clone(), + role: crate::EPIPHANY_VERIFICATION_OWNER_ROLE.into(), + binding_id: crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID.into(), + authority_scope: authority_scope.clone(), + instruction: "Act as Epiphany Verification. Audit only the exact typed Hands consequence and route carried by this request; return a structured verdict, evidence ids, and risks.".into(), + launch_document, + output_contract_id: output_contract_id.clone(), + organ_launch_contract: crate::default_launch_organ_contract( + &authority_scope, + "role", + &output_contract_id, + ), + proposal_modeling_request_id: None, + frontier_planning_request_id: None, + frontier_plan_mind_request_id: None, + imagination_consideration_request_id: None, + admitted_model_direction_consideration_request_id: None, + repo_frontier_modeling_request_id: None, + repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: Some(request.request_id.clone()), + created_at: created_at.to_string(), + }, + )?; + let grant = crate::substrate_gate::substrate_gate_repo_access_grant_for_worker( + format!("substrate-grant-{job_id}"), + job_id.clone(), + crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID.into(), + crate::EPIPHANY_VERIFICATION_OWNER_ROLE.into(), + authority_scope, + false, + created_at.to_string(), + ); + let snapshot = cache.snapshot_envelopes(); + let mut expected = Vec::new(); + for (document_type, document_key) in [ + ( + crate::RepoFrontierVerificationRequest::TYPE, + request.request_id.as_str(), + ), + (crate::RepoFrontierRoute::TYPE, request.route_id.as_str()), + ( + crate::RepoFrontierHandsAuthority::TYPE, + context.hands_authority.authority_id.as_str(), + ), + ( + crate::HandsActionIntent::TYPE, + request.hands_intent_id.as_str(), + ), + ( + crate::HandsActionReview::TYPE, + request.hands_review_id.as_str(), + ), + ( + crate::HandsPatchReceipt::TYPE, + request.hands_patch_receipt_id.as_str(), + ), + ( + crate::HandsCommandReceipt::TYPE, + request.hands_command_receipt_id.as_str(), + ), + ( + crate::HandsCommitReceipt::TYPE, + request.hands_commit_receipt_id.as_str(), + ), + ] { + expected.push( + snapshot + .iter() + .find(|value| value.r#type == document_type && value.key == document_key) + .cloned() + .ok_or_else(|| anyhow!("frontier Verification launch lost a strong source"))?, + ); + } + for source in &request.frontier_authority_documents { + let envelope = snapshot + .iter() + .find(|value| value.r#type == source.document_type && value.key == source.document_key) + .ok_or_else(|| anyhow!("frontier Verification launch lost frontier authority"))?; + if EpiphanyMindDocumentVersion::from_envelope("epiphany-mind", envelope)? != *source { + return Err(anyhow!( + "frontier Verification launch strong source changed" + )); + } + if !expected.contains(envelope) { + expected.push(envelope.clone()); + } + } + commit_current_work_launch( + store_path, + &cache, + expected, + prepared.envelopes, + vec![cache.prepare_entry(&grant.receipt_id, &grant)?.0], + "frontier Verification", + )?; + Ok(job_id) +} + pub fn launch_current_frontier_research_work( store_path: impl AsRef, created_at: &str, @@ -678,6 +962,7 @@ pub fn launch_current_frontier_research_work( frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: Some((&request).into()), + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -725,6 +1010,7 @@ pub fn launch_current_frontier_research_work( admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: Some(request.request_id.clone()), + repo_frontier_verification_request_id: None, created_at: created_at.to_string(), }, )?; @@ -991,6 +1277,239 @@ pub fn accept_frontier_research_result( } } +pub fn accept_frontier_verification_result( + store_path: impl AsRef, + job_id: &str, + accepted_at: &str, +) -> Result { + let store_path = store_path.as_ref(); + chrono::DateTime::parse_from_rfc3339(accepted_at) + .map_err(|_| anyhow!("frontier Verification acceptance time is invalid"))?; + let mut cache = crate::runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + if let Some(result) = cache.get::(job_id)? { + let mut receipts = cache + .get_all::()? + .into_iter() + .filter(|receipt| { + receipt.invariant_owner == "Soul.verification" + && matches!( + &receipt.authority, + crate::EpiphanyMindCommitAuthority::ModelDecisionContext { + decision_context_id + } if decision_context_id == &result.decision_context_id + ) + }) + .collect::>(); + if receipts.len() > 1 { + return Err(anyhow!( + "frontier Verification result has multiple Mind commit authorities" + )); + } + if let Some(receipt) = receipts.pop() { + let audit_id = format!("verification-audit-{}", result.result_id); + let verdict_id = format!("soul-verdict-{}", result.result_id); + let audit = cache + .get::(&audit_id)? + .ok_or_else(|| anyhow!("frontier Verification replay lost its audit"))?; + let verdict = cache + .get::(&verdict_id)? + .ok_or_else(|| anyhow!("frontier Verification replay lost its Soul verdict"))?; + let modeling_requests = cache + .get_all::()? + .into_iter() + .filter(|request| request.soul_verdict_receipt_id == verdict_id) + .collect::>(); + if audit.decision_context_id != result.decision_context_id + || audit.result_id != result.result_id + || verdict.source_result_id != result.result_id + || modeling_requests.len() != 1 + || !receipt.writes.iter().any(|write| { + write.document_type == crate::EpiphanyMindVerificationAuditDocument::TYPE + && write.document_key == audit_id + }) + { + return Err(anyhow!( + "frontier Verification replay does not preserve its exact accepted decision" + )); + } + return Ok(receipt); + } + } + if current_frontier_verification_review_job_id(store_path)?.as_deref() != Some(job_id) { + return Err(anyhow!( + "frontier Verification result is not current review work" + )); + } + let job = cache + .get::(job_id)? + .ok_or_else(|| anyhow!("frontier Verification acceptance lost its runtime job"))?; + if job.status != crate::EpiphanyRuntimeJobStatus::Completed { + return Err(anyhow!( + "frontier Verification runtime job is not completed" + )); + } + let launch = cache + .get::(job_id)? + .ok_or_else(|| anyhow!("frontier Verification acceptance lost its launch"))?; + let request = crate::runtime_spine::frontier_verification_request_for_launch(&cache, &launch)? + .ok_or_else(|| anyhow!("frontier Verification acceptance lost its exact request"))?; + let fulfillment = crate::runtime_typed_request_fulfillment( + store_path, + crate::RuntimeTypedRequestRef::FrontierVerification(&request.request_id), + )? + .ok_or_else(|| anyhow!("frontier Verification result is not exact typed fulfillment"))?; + if fulfillment.job_id != job_id { + return Err(anyhow!( + "frontier Verification fulfillment belongs to another attempt" + )); + } + let result = cache + .get::(job_id)? + .ok_or_else(|| anyhow!("frontier Verification acceptance lost its typed result"))?; + if !result.role_id.eq_ignore_ascii_case("verification") + || result.verification_request_id.as_deref() != Some(request.request_id.as_str()) + || result.frontier_route_id.as_deref() != Some(request.route_id.as_str()) + || result.item_error.is_some() + { + return Err(anyhow!( + "frontier Verification result crossed request authority" + )); + } + if !crate::runtime_spine::verification_frontier_is_current(&cache, &request)? { + return Err(anyhow!( + "frontier Verification decision remains recorded but its strong frontier changed; a fresh pass is required" + )); + } + let mut evidence_ids = result.evidence_ids.clone(); + evidence_ids.sort(); + evidence_ids.dedup(); + let audit_id = format!("verification-audit-{}", result.result_id); + let audit = crate::EpiphanyMindVerificationAuditDocument { + audit_id: audit_id.clone(), + verification_request_id: request.request_id.clone(), + frontier_route_id: request.route_id.clone(), + job_id: job_id.to_string(), + result_id: result.result_id.clone(), + decision_context_id: result.decision_context_id.clone(), + verdict: result.verdict.clone(), + summary: result.summary.clone(), + evidence_ids: evidence_ids.clone(), + risks: result.risks.clone(), + audited_at: accepted_at.to_string(), + }; + audit.validate()?; + let verdict = crate::SoulVerdictReceipt { + schema_version: crate::SOUL_VERDICT_RECEIPT_SCHEMA_VERSION.into(), + receipt_id: format!("soul-verdict-{}", result.result_id), + source_result_id: result.result_id.clone(), + source_job_id: result.job_id.clone(), + verdict: result.verdict.clone(), + summary: result.summary.clone(), + evidence_ids, + risks: result.risks.clone(), + emitted_at: accepted_at.to_string(), + contract: "Soul verdict emitted by the exact Verification Mind admission owner.".into(), + verification_request_id: request.request_id.clone(), + frontier_route_id: request.route_id.clone(), + }; + let modeling_request = + crate::runtime_spine::derive_repo_frontier_modeling_request(&cache, &verdict)?; + let snapshot = cache.snapshot_envelopes(); + let mut strong_reads = Vec::new(); + for (document_type, document_key) in [ + ( + crate::RepoFrontierVerificationRequest::TYPE, + request.request_id.as_str(), + ), + (crate::EpiphanyRuntimeWorkerLaunchRequest::TYPE, job_id), + (crate::EpiphanyRuntimeJob::TYPE, job_id), + (crate::EpiphanyRuntimeRoleWorkerResult::TYPE, job_id), + ( + crate::EpiphanyDecisionContext::TYPE, + result.decision_context_id.as_str(), + ), + (crate::RepoFrontierRoute::TYPE, request.route_id.as_str()), + ( + crate::HandsActionIntent::TYPE, + request.hands_intent_id.as_str(), + ), + ( + crate::HandsActionReview::TYPE, + request.hands_review_id.as_str(), + ), + ( + crate::HandsPatchReceipt::TYPE, + request.hands_patch_receipt_id.as_str(), + ), + ( + crate::HandsCommandReceipt::TYPE, + request.hands_command_receipt_id.as_str(), + ), + ( + crate::HandsCommitReceipt::TYPE, + request.hands_commit_receipt_id.as_str(), + ), + ] { + strong_reads.push( + snapshot + .iter() + .find(|value| value.r#type == document_type && value.key == document_key) + .cloned() + .ok_or_else(|| anyhow!("frontier Verification admission lost a strong source"))?, + ); + } + for source in &request.frontier_authority_documents { + let envelope = snapshot + .iter() + .find(|value| value.r#type == source.document_type && value.key == source.document_key) + .ok_or_else(|| anyhow!("frontier Verification admission lost frontier authority"))?; + if EpiphanyMindDocumentVersion::from_envelope("epiphany-mind", envelope)? != *source { + return Err(anyhow!( + "frontier Verification output cannot be rebased onto changed frontier authority" + )); + } + if !strong_reads.contains(envelope) { + strong_reads.push(envelope.clone()); + } + } + let hands_authority = cache + .get_all::()? + .into_iter() + .find(|authority| authority.route_id == request.route_id) + .ok_or_else(|| anyhow!("frontier Verification admission lost Hands authority"))?; + strong_reads.push( + cache + .get_envelope::(&hands_authority.authority_id)? + .ok_or_else(|| anyhow!("frontier Verification admission lost Hands envelope"))?, + ); + let writes = vec![crate::mind_documents::prepare_mind_document( + &cache, &audit_id, &audit, + )?]; + let companions = vec![ + cache.prepare_entry(&verdict.receipt_id, &verdict)?.0, + cache + .prepare_entry(&modeling_request.request_id, &modeling_request)? + .0, + ]; + match crate::reasoning_context::commit_mind_mutation_with_derived_companions( + store_path, + &result.decision_context_id, + "Soul.verification", + strong_reads, + writes, + companions, + accepted_at, + )? { + crate::EpiphanyMindCommitOutcome::Committed(receipt) => Ok(receipt), + crate::EpiphanyMindCommitOutcome::Conflict { + document_identities, + } => Err(anyhow!( + "frontier Verification admission lost exact keyed reads: {document_identities:?}" + )), + } +} + pub fn accept_body_modeling_result( store_path: impl AsRef, job_id: &str, @@ -1135,7 +1654,7 @@ pub fn accept_frontier_verdict_modeling_result( cache.pull_all_backing_stores()?; let current = current_frontier_verdict_modeling_work(&cache)? .filter(|work| { - work.action == EpiphanyModelingContinuationAction::Review + work.action == EpiphanyAgentPassContinuationAction::Review && work.job_id.as_deref() == Some(job_id) }) .ok_or_else(|| anyhow!("frontier verdict Modeling result is not current review work"))?; @@ -1271,7 +1790,7 @@ pub fn launch_current_body_modeling_work( )? .is_none() || body_modeling_continuation_action(&cache, &work.work_id)? - != EpiphanyModelingContinuationAction::Launch + != EpiphanyAgentPassContinuationAction::Launch { return Err(anyhow!("Mind has no launchable Body Modeling work")); } @@ -1307,6 +1826,7 @@ pub fn launch_current_body_modeling_work( frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1355,6 +1875,7 @@ pub fn launch_current_body_modeling_work( admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: options.created_at.clone(), }, )?; @@ -1416,7 +1937,7 @@ pub fn launch_current_proposal_modeling_work( let mut cache = crate::runtime_spine_cache(store_path)?; cache.pull_all_backing_stores()?; let work = current_proposal_modeling_work(&cache)? - .filter(|work| work.action == EpiphanyModelingContinuationAction::Launch) + .filter(|work| work.action == EpiphanyAgentPassContinuationAction::Launch) .ok_or_else(|| anyhow!("Mind has no launchable proposal Modeling work"))?; let request = work.request; let proposal = cache @@ -1476,6 +1997,7 @@ pub fn launch_current_proposal_modeling_work( frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1527,6 +2049,7 @@ pub fn launch_current_proposal_modeling_work( admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: options.created_at.clone(), }, )?; @@ -1597,7 +2120,7 @@ pub fn launch_current_frontier_verdict_modeling_work( let mut cache = crate::runtime_spine_cache(store_path)?; cache.pull_all_backing_stores()?; let work = current_frontier_verdict_modeling_work(&cache)? - .filter(|work| work.action == EpiphanyModelingContinuationAction::Launch) + .filter(|work| work.action == EpiphanyAgentPassContinuationAction::Launch) .ok_or_else(|| anyhow!("Mind has no launchable frontier verdict Modeling work"))?; let request = work.request; let verdict = cache @@ -1647,6 +2170,7 @@ pub fn launch_current_frontier_verdict_modeling_work( frontier_verdict_modeling_context: Some(authority), frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1694,6 +2218,7 @@ pub fn launch_current_frontier_verdict_modeling_work( admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: Some(request.request_id.clone()), repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: created_at.to_string(), }, )?; @@ -1953,7 +2478,7 @@ mod tests { assert_eq!(current_work.body_modeling, Some(projected_work.clone())); assert_eq!( current_work.body_modeling_action, - Some(EpiphanyModelingContinuationAction::Launch) + Some(EpiphanyAgentPassContinuationAction::Launch) ); assert_eq!( current_work.mind_projection_digest, @@ -1983,7 +2508,7 @@ mod tests { assert!(scheduled_work.body_modeling.is_none()); assert_eq!( scheduled_work.body_modeling_action, - Some(EpiphanyModelingContinuationAction::Wait) + Some(EpiphanyAgentPassContinuationAction::Wait) ); assert!(crate::resident_self_body_modeling_pressure(&store, 2)?.is_none()); let mut scheduled_cache = crate::runtime_spine_cache(&store)?; @@ -2083,7 +2608,7 @@ mod tests { cache.put(&job.job_id, &job)?; assert_eq!( project_current_work(&store)?.body_modeling_action, - Some(EpiphanyModelingContinuationAction::Review) + Some(EpiphanyAgentPassContinuationAction::Review) ); let continuation = crate::EpiphanyCoordinatorRunReceipt { schema_version: crate::COORDINATOR_RUN_RECEIPT_SCHEMA_VERSION.into(), @@ -2163,7 +2688,7 @@ mod tests { assert_eq!(proposal_work.request, request); assert_eq!( proposal_work.action, - EpiphanyModelingContinuationAction::Launch + EpiphanyAgentPassContinuationAction::Launch ); let racers = (0..2) .map(|_| { @@ -2206,7 +2731,7 @@ mod tests { .proposal_modeling .expect("launched proposal remains current") .action, - EpiphanyModelingContinuationAction::Wait + EpiphanyAgentPassContinuationAction::Wait ); assert!( launch_current_proposal_modeling_work( @@ -2368,7 +2893,7 @@ mod tests { .proposal_modeling .expect("terminal proposal remains current until admission") .action, - EpiphanyModelingContinuationAction::Review + EpiphanyAgentPassContinuationAction::Review ); let proposal_commit = accept_proposal_modeling_result( &store, @@ -2416,26 +2941,202 @@ mod tests { selected_at: "2026-08-17T00:00:14Z".into(), contract: crate::REPO_FRONTIER_ROUTE_CONTRACT.into(), }; - let verification_request = crate::RepoFrontierVerificationRequest { - schema_version: crate::REPO_FRONTIER_VERIFICATION_REQUEST_SCHEMA_VERSION.into(), - request_id: "verification-proposal-frontier".into(), + accepted_cache.put(&route.route_id, &route)?; + + let hands_job_id = "hands-verification-fixture"; + let grant = crate::substrate_gate_coordinator_implementation_grant( + "substrate-grant-hands-verification-fixture".into(), + hands_job_id.into(), + route.source_scope.clone(), + "2026-08-17T00:00:14.100Z".into(), + ); + crate::put_substrate_gate_repo_access_grant_receipt(&store, &grant)?; + let intent = crate::HandsActionIntent { + schema_version: crate::HANDS_ACTION_INTENT_SCHEMA_VERSION.into(), + intent_id: "hands-intent-verification-fixture".into(), + runtime_job_id: hands_job_id.into(), + binding_id: "implementation-worker".into(), + role: "epiphany-hands".into(), + authority_scope: "epiphany.role.implementation".into(), + requested_action: "continueImplementation".into(), + requested_paths: route.source_scope.clone(), + substrate_gate_grant_receipt_id: grant.receipt_id.clone(), + requested_at: "2026-08-17T00:00:14.200Z".into(), + contract: "The exact Hands consequence under Verification.".into(), + frontier_route_id: String::new(), + plan_candidate_sha256: String::new(), + plan_action: String::new(), + }; + crate::put_hands_action_intent(&store, &intent)?; + let mut hands_review = crate::hands_action_review_for_intent( + "hands-review-verification-fixture".into(), + &intent, + "approved".into(), + vec!["patch".into(), "command".into(), "commit".into()], + vec!["Bounded fixture consequence is authorized.".into()], + "2026-08-17T00:00:14.300Z".into(), + ); + hands_review.required_receipts = vec![ + crate::HANDS_PATCH_RECEIPT_TYPE.into(), + crate::HANDS_COMMAND_RECEIPT_TYPE.into(), + crate::HANDS_COMMIT_RECEIPT_TYPE.into(), + ]; + crate::put_hands_action_review(&store, &hands_review)?; + let hands_authority = crate::RepoFrontierHandsAuthority { + schema_version: crate::REPO_FRONTIER_HANDS_AUTHORITY_SCHEMA_VERSION.into(), + authority_id: "repo-frontier-hands-authority-verification-fixture".into(), route_id: route.route_id.clone(), model_projection_digest: route.model_projection_digest.clone(), model_source_documents: route.model_source_documents.clone(), frontier_item_id: route.frontier_item_id.clone(), frontier_item_hash: route.frontier_item_hash.clone(), - hands_intent_id: "hands-intent".into(), - hands_review_id: "hands-review".into(), - hands_patch_receipt_id: "hands-patch".into(), - hands_command_receipt_id: "hands-command".into(), - hands_commit_receipt_id: "hands-commit".into(), - requested_at: "2026-08-17T00:00:15Z".into(), - contract: crate::REPO_FRONTIER_VERIFICATION_REQUEST_CONTRACT.into(), + hands_intent_id: intent.intent_id.clone(), + hands_review_id: hands_review.review_id.clone(), + substrate_grant_receipt_id: grant.receipt_id.clone(), + requested_paths: route.source_scope.clone(), + granted_at: hands_review.reviewed_at.clone(), + contract: crate::REPO_FRONTIER_HANDS_AUTHORITY_CONTRACT.into(), }; + crate::put_repo_frontier_hands_authority(&store, &hands_authority)?; + let hands_patch = crate::hands_patch_receipt_for_review( + "hands-patch-verification-fixture".into(), + &intent, + &hands_review, + route.source_scope.clone(), + "Applied the exact bounded change.".into(), + "2026-08-17T00:00:14.400Z".into(), + ); + crate::put_hands_patch_receipt(&store, &hands_patch)?; + let hands_command = crate::hands_command_receipt_for_review( + "hands-command-verification-fixture".into(), + &intent, + &hands_review, + "cargo test exact-verification-fixture".into(), + "0".into(), + "verification.stdout".into(), + "verification.stderr".into(), + "The bounded command passed.".into(), + "2026-08-17T00:00:14.500Z".into(), + ); + crate::put_hands_command_receipt(&store, &hands_command)?; + let hands_commit = crate::hands_commit_receipt_for_review( + "hands-commit-verification-fixture".into(), + &intent, + &hands_review, + "0123456789abcdef0123456789abcdef01234567".into(), + "codex/verification-fixture".into(), + route.source_scope.clone(), + "Committed the exact bounded consequence.".into(), + "2026-08-17T00:00:14.600Z".into(), + ); + crate::put_hands_commit_receipt(&store, &hands_commit)?; + + accepted_cache.pull_all_backing_stores()?; + let mut verification_requests = accepted_cache + .get_all::()? + .into_iter() + .filter(|request| request.route_id == route.route_id) + .collect::>(); + assert_eq!(verification_requests.len(), 1); + let verification_request = verification_requests.pop().unwrap(); + assert_eq!( + project_current_work(&store)? + .verification + .expect("Hands commit creates exact Verification work") + .action, + EpiphanyAgentPassContinuationAction::Launch + ); + let verification_job_id = + launch_current_frontier_verification_work(&store, "2026-08-17T00:00:14.700Z")?; + accepted_cache.pull_all_backing_stores()?; + let verification_launch = accepted_cache + .get::(&verification_job_id)? + .expect("exact Verification launch"); + assert_eq!( + verification_launch + .repo_frontier_verification_request_id + .as_deref(), + Some(verification_request.request_id.as_str()) + ); + let crate::EpiphanyWorkerLaunchDocument::Role(verification_document) = + verification_launch.launch_document()? + else { + panic!("Verification must launch as a role pass") + }; + assert!(verification_document.dynamic_prompt_context.is_none()); + assert_eq!( + verification_document + .frontier_verification_context + .as_ref() + .expect("sealed typed Verification projection") + .request, + verification_request + ); + let verification_basis = crate::worker_reasoning_basis(&store, &verification_launch)?; + crate::put_reasoning_basis(&store, &verification_basis)?; + let mut verification_native = epiphany_model_adapter::EpiphanyModelRequest::new( + "verification-model-request", + "verification-conversation", + "openai-codex", + "gpt-test", + "verify", + ); + verification_native.reasoning_basis_id = Some(verification_basis.basis_id.clone()); + verification_native.source_worker_job_id = Some(verification_job_id.clone()); + let verification_provider = + epiphany_openai_adapter::request_from_native(&verification_native); + crate::open_runtime_model_execution( + &store, + crate::RuntimeSpineSessionOptions { + session_id: "verification-model-session".into(), + objective: "Verify the exact Hands consequence.".into(), + created_at: "2026-08-17T00:00:14.750Z".into(), + coordinator_note: "Bound Verification model pass.".into(), + }, + crate::RuntimeSpineJobOptions { + job_id: "verification-model-job".into(), + session_id: "verification-model-session".into(), + role: "openai-model".into(), + created_at: "2026-08-17T00:00:14.750Z".into(), + summary: "Bound Verification inference.".into(), + artifact_refs: Vec::new(), + }, + &verification_native, + &verification_provider, + "2026-08-17T00:00:14.750Z", + )?; + let verification_context = crate::EpiphanyDecisionContext::new( + &verification_basis, + verification_native, + verification_provider, + Vec::new(), + )?; + crate::put_decision_context(&store, &verification_context)?; + let verification_process = crate::ProcessInstanceIdentity { + process_id: 42, + creation_token: 7, + created_at_rfc3339: Some("2026-08-17T00:00:14.800Z".into()), + executable_path: "verification-worker".into(), + }; + let verification_activation = "verification-activation"; + crate::claim_runtime_worker_process( + &store, + &verification_job_id, + &verification_process, + &format!("{:x}", Sha256::digest(verification_activation.as_bytes())), + "2026-08-17T00:00:14.800Z", + )?; + crate::activate_runtime_worker_process( + &store, + &verification_job_id, + &verification_process, + verification_activation, + "2026-08-17T00:00:14.900Z", + )?; let verification_result = crate::EpiphanyRuntimeRoleWorkerResult { schema_version: crate::RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.into(), result_id: "verification-result".into(), - job_id: "verification-job".into(), + job_id: verification_job_id.clone(), role_id: "verification".into(), verdict: "pass".into(), summary: "verified exact consequence".into(), @@ -2444,7 +3145,7 @@ mod tests { scratch_summary: None, files_inspected: Vec::new(), frontier_node_ids: vec!["body-node".into()], - evidence_ids: vec!["hands-commit".into()], + evidence_ids: vec![hands_commit.receipt_id.clone()], artifact_refs: Vec::new(), open_questions: Vec::new(), evidence_gaps: Vec::new(), @@ -2468,33 +3169,160 @@ mod tests { imagination_consideration_candidate_msgpack: None, admitted_model_direction_consideration_request_id: None, admitted_model_direction_consideration_result_msgpack: None, - decision_context_id: "verification-context".into(), + decision_context_id: verification_context.context_id.clone(), }; - let verdict = crate::SoulVerdictReceipt { - schema_version: crate::SOUL_VERDICT_RECEIPT_SCHEMA_VERSION.into(), - receipt_id: "soul-verdict-verification-result".into(), - source_result_id: verification_result.result_id.clone(), - source_job_id: verification_result.job_id.clone(), - verdict: verification_result.verdict.clone(), - summary: verification_result.summary.clone(), - evidence_ids: verification_result.evidence_ids.clone(), - risks: verification_result.risks.clone(), - emitted_at: "2026-08-17T00:00:16Z".into(), - contract: "test Soul verdict".into(), - verification_request_id: verification_request.request_id.clone(), - frontier_route_id: route.route_id.clone(), - }; - accepted_cache.put(&route.route_id, &route)?; - accepted_cache.put(&verification_request.request_id, &verification_request)?; - accepted_cache.put(&verification_result.job_id, &verification_result)?; - accepted_cache.put(&verdict.receipt_id, &verdict)?; - let verdict_request = crate::commit_repo_frontier_modeling_request(&store, &verdict)?; + crate::put_runtime_role_worker_result(&store, &verification_result)?; + crate::complete_runtime_job( + &store, + crate::RuntimeSpineJobResultOptions { + result_id: format!("runtime-result-{verification_job_id}"), + job_id: verification_job_id.clone(), + completed_at: "2026-08-17T00:00:15Z".into(), + verdict: verification_result.verdict.clone(), + summary: verification_result.summary.clone(), + next_safe_move: verification_result.next_safe_move.clone(), + evidence_refs: verification_result.evidence_ids.clone(), + artifact_refs: Vec::new(), + decision_context_id: Some(verification_context.context_id.clone()), + }, + )?; + assert_eq!( + project_current_work(&store)? + .verification + .expect("terminal Verification work awaits admission") + .action, + EpiphanyAgentPassContinuationAction::Review + ); + + // The same terminal decision must refuse without mutation if its exact + // frontier changes. There is no silent rebase onto newer strong state. + let verification_conflict_store = temp.path().join("verification-conflict.cc"); + std::fs::copy(&store, &verification_conflict_store)?; + let mut competing_verification_item = frontier_item.clone(); + competing_verification_item.gap = + "A competing decision changed the exact verified frontier.".into(); + competing_verification_item.updated_at = Some("2026-08-17T00:00:15.050Z".into()); + let competing_verification_proposal = crate::EpiphanyRepoModelMutationProposal::new( + "repo-model-mutation-proposal-verification-conflict", + "verification-conflict-request", + "verification-conflict-result", + vec![hands_commit.receipt_id.clone()], + body.clone(), + vec![crate::EpiphanyRepoModelMutationOperation::PutFrontier { + item: competing_verification_item, + }], + )?; + let competing_verification_plan = crate::plan_repo_model_mutation( + &verification_conflict_store, + &competing_verification_proposal, + )?; + let mut verification_conflict_cache = + crate::runtime_spine_cache(&verification_conflict_store)?; + verification_conflict_cache.pull_all_backing_stores()?; + let conflict_provenance = verification_conflict_cache + .get_envelope::(&hands_commit.receipt_id)? + .expect("typed Hands conflict provenance"); + assert!(matches!( + crate::commit_typed_organ_mind_mutation( + &verification_conflict_store, + "Modeling", + conflict_provenance, + "Modeling.verification_conflict_fixture", + competing_verification_plan.strong_reads, + competing_verification_plan.writes, + "2026-08-17T00:00:15.050Z", + )?, + crate::EpiphanyMindCommitOutcome::Committed(_) + )); + verification_conflict_cache.pull_all_backing_stores()?; + let conflict_before = verification_conflict_cache.snapshot_envelopes(); + let conflict_error = accept_frontier_verification_result( + &verification_conflict_store, + &verification_job_id, + "2026-08-17T00:00:16Z", + ) + .expect_err("stale exact frontier must block Verification admission"); + assert!(conflict_error.to_string().contains("frontier")); + verification_conflict_cache.pull_all_backing_stores()?; + assert_eq!( + verification_conflict_cache.snapshot_envelopes(), + conflict_before + ); + + // An unrelated keyed graph write after inference must not stale this + // decision. Verification owns the exact frontier and Hands chain, not + // a global RepoModel revision. + let mut concurrent_node = model + .nodes + .iter() + .find(|node| node.id == "body-node") + .cloned() + .expect("seeded Body node"); + concurrent_node.id = "verification-concurrent-node".into(); + concurrent_node.title = "Concurrent Verification neighbor".into(); + concurrent_node.claim = "Disjoint keyed Mind writes merge.".into(); + concurrent_node.updated_at = Some("2026-08-17T00:00:15.100Z".into()); + let concurrent_proposal = crate::EpiphanyRepoModelMutationProposal::new( + "repo-model-mutation-proposal-verification-concurrent", + verification_request.request_id.clone(), + verification_result.result_id.clone(), + vec![hands_commit.receipt_id.clone()], + body.clone(), + vec![crate::EpiphanyRepoModelMutationOperation::PutNode { + node: concurrent_node, + }], + )?; + let concurrent_plan = crate::plan_repo_model_mutation(&store, &concurrent_proposal)?; + accepted_cache.pull_all_backing_stores()?; + let hands_provenance = accepted_cache + .get_envelope::(&hands_commit.receipt_id)? + .expect("typed Hands provenance"); + assert!(matches!( + crate::commit_typed_organ_mind_mutation( + &store, + "Modeling", + hands_provenance, + "Modeling.verification_concurrent_fixture", + concurrent_plan.strong_reads, + concurrent_plan.writes, + "2026-08-17T00:00:15.100Z", + )?, + crate::EpiphanyMindCommitOutcome::Committed(_) + )); + + let verification_commit = accept_frontier_verification_result( + &store, + &verification_job_id, + "2026-08-17T00:00:16Z", + )?; + assert_eq!(verification_commit.invariant_owner, "Soul.verification"); + assert!(project_current_work(&store)?.verification.is_none()); + let replayed_verification_commit = accept_frontier_verification_result( + &store, + &verification_job_id, + "2026-08-17T00:00:16Z", + )?; + assert_eq!(replayed_verification_commit, verification_commit); + accepted_cache.pull_all_backing_stores()?; + let verdict = accepted_cache + .get::(&format!( + "soul-verdict-{}", + verification_result.result_id + ))? + .expect("Verification admission writes its Soul verdict"); + let verdict_request = + crate::runtime_spine::derive_repo_frontier_modeling_request(&accepted_cache, &verdict)?; + assert!( + accepted_cache + .get::(&verdict_request.request_id)? + .is_some() + ); assert_eq!( project_current_work(&store)? .frontier_verdict_modeling .expect("Soul verdict creates frontier Modeling work") .action, - EpiphanyModelingContinuationAction::Launch + EpiphanyAgentPassContinuationAction::Launch ); let verdict_racers = (0..2) .map(|_| { @@ -2707,7 +3535,7 @@ mod tests { .frontier_verdict_modeling .expect("terminal verdict work awaits admission") .action, - EpiphanyModelingContinuationAction::Review + EpiphanyAgentPassContinuationAction::Review ); // The same fixture, copied before admission, proves the other half of diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index d52e20a0..f67ccf4e 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -229,8 +229,6 @@ pub use coordinator_launch::commit_coordinator_job_launch; pub use coordinator_launch::plan_coordinator_job_launch; pub use coordinator_launch_context::EPIPHANY_LOCAL_VERSE_RUNTIME_ID; pub use coordinator_launch_context::append_modeling_repo_model_shape_context; -pub use coordinator_launch_context::append_verification_hands_receipt_context; -pub use coordinator_launch_context::has_complete_hands_consequence_after_latest_accepted_boundary; pub use coordinator_launch_context::local_verse_store_path; pub use coordinator_launch_context::memory_graph_store_path; pub use coordinator_launch_context::render_launch_dynamic_prompt_context; @@ -1320,11 +1318,14 @@ pub use surfaces::REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_CONTRACT; pub use surfaces::REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_SCHEMA_VERSION; pub use surfaces::REPO_FRONTIER_RESEARCH_CONTEXT_CONTRACT; pub use surfaces::REPO_FRONTIER_RESEARCH_CONTEXT_SCHEMA_VERSION; +pub use surfaces::REPO_FRONTIER_VERIFICATION_CONTEXT_CONTRACT; +pub use surfaces::REPO_FRONTIER_VERIFICATION_CONTEXT_SCHEMA_VERSION; pub use surfaces::ROLE_WORKER_OUTPUT_CONTRACT_ID; pub use surfaces::RepoFrontierPlanMindContextProjection; pub use surfaces::RepoFrontierPlanningContextProjection; pub use surfaces::RepoFrontierProposalModelingContextProjection; pub use surfaces::RepoFrontierResearchContextProjection; +pub use surfaces::RepoFrontierVerificationContextProjection; pub use surfaces::build_reorient_acceptance_bundle; pub use surfaces::build_reorient_job_launch_request; pub use surfaces::build_role_acceptance_bundle; diff --git a/epiphany-core/src/mind_documents.rs b/epiphany-core/src/mind_documents.rs index fba29daf..dbd06ed0 100644 --- a/epiphany-core/src/mind_documents.rs +++ b/epiphany-core/src/mind_documents.rs @@ -138,6 +138,68 @@ pub struct EpiphanyMindEvidenceDocument { pub value: EpiphanyEvidenceRecord, } +#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] +#[cultcache( + type = "epiphany.mind.verification_audit.v1", + schema = "EpiphanyMindVerificationAuditDocument" +)] +pub struct EpiphanyMindVerificationAuditDocument { + #[cultcache(key = 0)] + pub audit_id: String, + #[cultcache(key = 1)] + pub verification_request_id: String, + #[cultcache(key = 2)] + pub frontier_route_id: String, + #[cultcache(key = 3)] + pub job_id: String, + #[cultcache(key = 4)] + pub result_id: String, + #[cultcache(key = 5)] + pub decision_context_id: String, + #[cultcache(key = 6)] + pub verdict: String, + #[cultcache(key = 7)] + pub summary: String, + #[cultcache(key = 8)] + pub evidence_ids: Vec, + #[cultcache(key = 9)] + pub risks: Vec, + #[cultcache(key = 10)] + pub audited_at: String, +} + +impl EpiphanyMindVerificationAuditDocument { + pub fn validate(&self) -> Result<()> { + if [ + self.audit_id.as_str(), + self.verification_request_id.as_str(), + self.frontier_route_id.as_str(), + self.job_id.as_str(), + self.result_id.as_str(), + self.decision_context_id.as_str(), + self.verdict.as_str(), + self.summary.as_str(), + ] + .into_iter() + .any(str::is_empty) + || !matches!( + self.verdict.trim().to_ascii_lowercase().as_str(), + "pass" | "needs-review" | "needs-evidence" | "fail" + ) + || chrono::DateTime::parse_from_rfc3339(&self.audited_at).is_err() + { + return Err(anyhow!("Verification audit document is invalid")); + } + let mut evidence_ids = self.evidence_ids.clone(); + evidence_ids.sort(); + evidence_ids.dedup(); + if evidence_ids != self.evidence_ids { + return Err(anyhow!("Verification audit evidence ids are not canonical")); + } + Ok(()) + } +} + #[derive(Clone, Debug, PartialEq, DatabaseEntry)] #[cultcache( type = "epiphany.mind.investigation_checkpoint.v1", @@ -200,6 +262,7 @@ pub struct EpiphanyMindView { pub invariants: Vec, pub observations: Vec, pub evidence: Vec, + pub verification_audits: Vec, pub investigation_checkpoint: Option, pub mode: Option, pub planning: EpiphanyPlanningState, @@ -216,6 +279,7 @@ pub(crate) fn register_mind_document_types(cache: &mut CultCache) -> Result<()> cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; + cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; @@ -329,6 +393,11 @@ pub(crate) fn validate_mind_write_envelope(envelope: &CultCacheEnvelope) -> Resu rmp_serde::from_slice::(&envelope.payload)? .value .id + } else if envelope.r#type == EpiphanyMindVerificationAuditDocument::TYPE { + let value: EpiphanyMindVerificationAuditDocument = + rmp_serde::from_slice(&envelope.payload)?; + value.validate()?; + value.audit_id } else if envelope.r#type == EpiphanyMindInvestigationCheckpointDocument::TYPE { rmp_serde::from_slice::(&envelope.payload)? .value @@ -392,6 +461,7 @@ pub fn assemble_mind_view(store_path: impl AsRef) -> Result(&cache, |value| value.value)?; let mut evidence = values::(&cache, |value| value.value)?; + let mut verification_audits = cache.get_all::()?; let checkpoints = values::(&cache, |value| value.value)?; let mut captures = @@ -432,6 +502,7 @@ pub fn assemble_mind_view(store_path: impl AsRef) -> Result) -> Result, pub frontier_planning_context: Option, pub frontier_research_context: Option, + pub frontier_verification_context: Option, pub frontier_plan_mind_context: Option, pub imagination_consideration_context: Option, pub admitted_model_direction_consideration_context: @@ -51,6 +52,7 @@ impl From for EpiphanyRolePassAuthorityProject frontier_verdict_modeling_context: value.frontier_verdict_modeling_context, frontier_planning_context: value.frontier_planning_context, frontier_research_context: value.frontier_research_context, + frontier_verification_context: value.frontier_verification_context, frontier_plan_mind_context: value.frontier_plan_mind_context, imagination_consideration_context: value.imagination_consideration_context, admitted_model_direction_consideration_context: value @@ -107,6 +109,7 @@ pub struct EpiphanyMindPromptProjection { pub invariants: Vec, pub observations: Vec, pub evidence: Vec, + pub verification_audits: Vec, pub investigation_checkpoint: Option, pub mode: Option, pub planning: epiphany_state_model::EpiphanyPlanningState, @@ -126,6 +129,7 @@ impl From for EpiphanyMindPromptProjection { invariants: value.invariants, observations: value.observations, evidence: value.evidence, + verification_audits: value.verification_audits, investigation_checkpoint: value.investigation_checkpoint, mode: value.mode, planning: value.planning, @@ -1254,6 +1258,7 @@ mod tests { frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1298,6 +1303,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }) } @@ -1327,6 +1333,7 @@ mod tests { invariants: Vec::new(), observations: Vec::new(), evidence: Vec::new(), + verification_audits: Vec::new(), investigation_checkpoint: None, mode: None, planning: Default::default(), diff --git a/epiphany-core/src/resident_self.rs b/epiphany-core/src/resident_self.rs index e371747e..39bc9332 100644 --- a/epiphany-core/src/resident_self.rs +++ b/epiphany-core/src/resident_self.rs @@ -1201,7 +1201,7 @@ pub fn ingest_resident_self_domain_pressure( } if let Some(work) = crate::project_current_work(runtime_store)? .frontier_verdict_modeling - .filter(|work| work.action == crate::EpiphanyModelingContinuationAction::Launch) + .filter(|work| work.action == crate::EpiphanyAgentPassContinuationAction::Launch) { inserted += usize::from(enqueue_resident_self_pressure_idempotent( resident_store, @@ -1233,7 +1233,7 @@ pub fn ingest_resident_self_domain_pressure( // the next one on a later ingestion cycle. if let Some(selection) = crate::project_current_work(runtime_store)? .proposal_modeling - .filter(|work| work.action == crate::EpiphanyModelingContinuationAction::Launch) + .filter(|work| work.action == crate::EpiphanyAgentPassContinuationAction::Launch) .map(|work| work.request) { inserted += usize::from(enqueue_resident_self_pressure_idempotent( @@ -1348,12 +1348,6 @@ fn resident_continuation_launch_was_overtaken( crate::EpiphanyRoleResultRoleId::Modeling, crate::EPIPHANY_MODELING_ROLE_BINDING_ID, ), - "launchVerification" => role_projection_is_terminal_for_continuation( - runtime_store, - thread_id, - crate::EpiphanyRoleResultRoleId::Verification, - crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID, - ), _ => Ok(false), } } @@ -1381,11 +1375,11 @@ pub(crate) fn resident_self_safe_continuation_action( crate::body_modeling_continuation_action_for_job(runtime_store, job_id)? { return Ok(match action { - crate::EpiphanyModelingContinuationAction::Launch => { + crate::EpiphanyAgentPassContinuationAction::Launch => { Some("launchModeling".to_string()) } - crate::EpiphanyModelingContinuationAction::Wait => None, - crate::EpiphanyModelingContinuationAction::Review => { + crate::EpiphanyAgentPassContinuationAction::Wait => None, + crate::EpiphanyAgentPassContinuationAction::Review => { Some("reviewModelingResult".to_string()) } }); @@ -1394,11 +1388,11 @@ pub(crate) fn resident_self_safe_continuation_action( crate::proposal_modeling_continuation_action_for_job(runtime_store, job_id)? { return Ok(match action { - crate::EpiphanyModelingContinuationAction::Launch => { + crate::EpiphanyAgentPassContinuationAction::Launch => { Some("launchModeling".to_string()) } - crate::EpiphanyModelingContinuationAction::Wait => None, - crate::EpiphanyModelingContinuationAction::Review => { + crate::EpiphanyAgentPassContinuationAction::Wait => None, + crate::EpiphanyAgentPassContinuationAction::Review => { Some("reviewModelingResult".to_string()) } }); @@ -1407,21 +1401,38 @@ pub(crate) fn resident_self_safe_continuation_action( crate::frontier_verdict_modeling_continuation_action_for_job(runtime_store, job_id)? { return Ok(match action { - crate::EpiphanyModelingContinuationAction::Launch => { + crate::EpiphanyAgentPassContinuationAction::Launch => { Some("launchModeling".to_string()) } - crate::EpiphanyModelingContinuationAction::Wait => None, - crate::EpiphanyModelingContinuationAction::Review => { + crate::EpiphanyAgentPassContinuationAction::Wait => None, + crate::EpiphanyAgentPassContinuationAction::Review => { Some("reviewModelingResult".to_string()) } }); } } } + if matches!( + receipt.final_action.as_str(), + "launchVerification" | "waitForVerificationResult" | "reviewVerificationResult" + ) { + return Ok(crate::project_current_work(runtime_store)? + .verification + .map(|work| match work.action { + crate::EpiphanyAgentPassContinuationAction::Launch => { + "launchVerification".to_string() + } + crate::EpiphanyAgentPassContinuationAction::Wait => { + "waitForVerificationResult".to_string() + } + crate::EpiphanyAgentPassContinuationAction::Review => { + "reviewVerificationResult".to_string() + } + })); + } let direct = matches!( receipt.final_action.as_str(), "launchModeling" - | "launchVerification" | "startFrontierPlanning" | "launchImagination" | "requestMindPlanReview" @@ -1449,16 +1460,6 @@ pub(crate) fn resident_self_safe_continuation_action( { Some("reviewModelingResult") } - "reviewVerificationResult" - if role_projection_is_terminal_for_continuation( - runtime_store, - &receipt.thread_id, - crate::EpiphanyRoleResultRoleId::Verification, - crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID, - )? => - { - Some("reviewVerificationResult") - } "waitForModelingResult" if role_projection_is_terminal_for_continuation( runtime_store, @@ -1469,16 +1470,6 @@ pub(crate) fn resident_self_safe_continuation_action( { Some("reviewModelingResult") } - "waitForVerificationResult" - if role_projection_is_terminal_for_continuation( - runtime_store, - &receipt.thread_id, - crate::EpiphanyRoleResultRoleId::Verification, - crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID, - )? => - { - Some("reviewVerificationResult") - } "waitForImaginationResult" => { match crate::runtime_repo_frontier_planning_lifecycle(runtime_store)?.stage { crate::RepoFrontierPlanningLifecycleStage::ImaginationResultReady => { @@ -1568,13 +1559,13 @@ fn resident_self_failed_receipt_typed_continuation_action( _ => None, }; Ok(match action { - Some(crate::EpiphanyModelingContinuationAction::Review) => { + Some(crate::EpiphanyAgentPassContinuationAction::Review) => { Some("reviewModelingResult".to_string()) } - Some(crate::EpiphanyModelingContinuationAction::Launch) => { + Some(crate::EpiphanyAgentPassContinuationAction::Launch) => { Some("launchModeling".to_string()) } - Some(crate::EpiphanyModelingContinuationAction::Wait) | None => None, + Some(crate::EpiphanyAgentPassContinuationAction::Wait) | None => None, }) } diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index 852ff8b5..b31a0c6f 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -447,6 +447,8 @@ pub struct EpiphanyRuntimeWorkerLaunchRequest { pub organ_launch_contract: EpiphanyLaunchOrganContract, #[cultcache(key = 11, default)] pub proposal_modeling_request_id: Option, + #[cultcache(key = 12, default)] + pub repo_frontier_verification_request_id: Option, #[cultcache(key = 13, default)] pub frontier_planning_request_id: Option, #[cultcache(key = 14, default)] @@ -1012,6 +1014,7 @@ pub struct RuntimeSpineHeartbeatJobOptions { pub admitted_model_direction_consideration_request_id: Option, pub repo_frontier_modeling_request_id: Option, pub repo_frontier_research_request_id: Option, + pub repo_frontier_verification_request_id: Option, pub created_at: String, } @@ -1066,6 +1069,7 @@ pub struct EpiphanyJobLaunchRequest { pub admitted_model_direction_consideration_request_id: Option, pub repo_frontier_modeling_request_id: Option, pub repo_frontier_research_request_id: Option, + pub repo_frontier_verification_request_id: Option, } #[derive(Debug, Clone, PartialEq)] @@ -3135,6 +3139,12 @@ pub fn open_runtime_spine_heartbeat_job( options.repo_frontier_research_request_id.as_deref(), &options.launch_document, )?; + validate_frontier_verification_launch_carrier( + &options.role, + &options.binding_id, + options.repo_frontier_verification_request_id.as_deref(), + &options.launch_document, + )?; validate_imagination_consideration_launch_carrier( &options.role, &options.binding_id, @@ -3237,6 +3247,7 @@ pub fn open_runtime_spine_heartbeat_job( .admitted_model_direction_consideration_request_id, repo_frontier_modeling_request_id: options.repo_frontier_modeling_request_id, repo_frontier_research_request_id: options.repo_frontier_research_request_id, + repo_frontier_verification_request_id: options.repo_frontier_verification_request_id, }; cache.put(&job_id, &request)?; Ok(job) @@ -3271,6 +3282,12 @@ pub fn prepare_runtime_spine_heartbeat_job( options.repo_frontier_research_request_id.as_deref(), &options.launch_document, )?; + validate_frontier_verification_launch_carrier( + &options.role, + &options.binding_id, + options.repo_frontier_verification_request_id.as_deref(), + &options.launch_document, + )?; validate_imagination_consideration_launch_carrier( &options.role, &options.binding_id, @@ -3399,6 +3416,7 @@ pub fn prepare_runtime_spine_heartbeat_job( .admitted_model_direction_consideration_request_id, repo_frontier_modeling_request_id: options.repo_frontier_modeling_request_id, repo_frontier_research_request_id: options.repo_frontier_research_request_id, + repo_frontier_verification_request_id: options.repo_frontier_verification_request_id, }; let envelopes = vec![ cache.prepare_entry(RUNTIME_IDENTITY_KEY, &identity)?.0, @@ -3753,6 +3771,45 @@ fn validate_frontier_research_launch_carrier( Ok(()) } +fn validate_frontier_verification_launch_carrier( + role: &str, + binding_id: &str, + verification_request_id: Option<&str>, + launch_document: &EpiphanyWorkerLaunchDocument, +) -> Result<()> { + let projection = match launch_document { + EpiphanyWorkerLaunchDocument::Role(document) => { + document.frontier_verification_context.as_ref() + } + EpiphanyWorkerLaunchDocument::Reorient(_) => None, + }; + let Some(request_id) = verification_request_id else { + if projection.is_some() { + return Err(anyhow!( + "frontier Verification context requires its typed request id" + )); + } + return Ok(()); + }; + validate_non_empty(request_id, "frontier Verification request id")?; + if role != crate::EPIPHANY_VERIFICATION_OWNER_ROLE + || binding_id != crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID + { + return Err(anyhow!( + "frontier Verification request may only be transported by the Verification role launch" + )); + } + let projection = projection + .ok_or_else(|| anyhow!("frontier Verification request requires its typed context"))?; + if projection.request.request_id != request_id + || projection.schema_version != crate::REPO_FRONTIER_VERIFICATION_CONTEXT_SCHEMA_VERSION + || projection.contract != crate::REPO_FRONTIER_VERIFICATION_CONTEXT_CONTRACT + { + return Err(anyhow!("frontier Verification context/request mismatch")); + } + Ok(()) +} + pub fn put_runtime_requested_public_source_intents( store_path: impl AsRef, worker_job_id: &str, @@ -3894,6 +3951,48 @@ pub(crate) fn frontier_research_request_for_launch( Ok(Some(request)) } +pub(crate) fn frontier_verification_request_for_launch( + cache: &CultCache, + launch: &EpiphanyRuntimeWorkerLaunchRequest, +) -> Result> { + let document: EpiphanyWorkerLaunchDocument = + rmp_serde::from_slice(&launch.launch_document_msgpack)?; + validate_frontier_verification_launch_carrier( + &launch.role, + &launch.binding_id, + launch.repo_frontier_verification_request_id.as_deref(), + &document, + )?; + let Some(request_id) = launch.repo_frontier_verification_request_id.as_deref() else { + return Ok(None); + }; + let request = cache + .get::(request_id)? + .ok_or_else(|| anyhow!("frontier Verification launch lost its typed request"))?; + validate_repo_frontier_verification_request_intrinsic(&request)?; + let carried = match document { + EpiphanyWorkerLaunchDocument::Role(document) => document.frontier_verification_context, + EpiphanyWorkerLaunchDocument::Reorient(_) => None, + } + .ok_or_else(|| anyhow!("frontier Verification launch lost its typed context"))?; + if carried.request != request + || carried.route.route_id != request.route_id + || carried.hands_authority.route_id != request.route_id + || carried.hands_authority.hands_intent_id != request.hands_intent_id + || carried.hands_authority.hands_review_id != request.hands_review_id + || carried.hands_intent.intent_id != request.hands_intent_id + || carried.hands_review.review_id != request.hands_review_id + || carried.patch_receipt.receipt_id != request.hands_patch_receipt_id + || carried.command_receipt.receipt_id != request.hands_command_receipt_id + || carried.commit_receipt.receipt_id != request.hands_commit_receipt_id + { + return Err(anyhow!( + "frontier Verification launch context diverges from its request and Hands receipts" + )); + } + Ok(Some(request)) +} + pub fn runtime_authenticated_public_source_lookups_for_worker( store_path: impl AsRef, worker_job_id: &str, @@ -4601,6 +4700,25 @@ pub fn put_runtime_role_worker_result( return Err(anyhow!("Research result substituted its typed request")); } } + let is_frontier_verification = worker_launch + .repo_frontier_verification_request_id + .is_some(); + if is_verification != is_frontier_verification { + return Err(anyhow!( + "Verification result and launch must share one exact frontier request" + )); + } + if is_frontier_verification { + let request = frontier_verification_request_for_launch(&cache, &worker_launch)? + .ok_or_else(|| anyhow!("Verification result lost its typed request"))?; + if result.verification_request_id.as_deref() != Some(request.request_id.as_str()) + || result.frontier_route_id.as_deref() != Some(request.route_id.as_str()) + { + return Err(anyhow!( + "Verification result substituted its exact request or route" + )); + } + } if is_modeling && (result.verification_request_id.is_some() || result.frontier_route_id.is_some()) { @@ -5431,6 +5549,7 @@ pub fn runtime_typed_request_fulfillment( } RuntimeTypedRequestRef::FrontierVerdictModeling(_) => {} RuntimeTypedRequestRef::FrontierResearch(_) => {} + RuntimeTypedRequestRef::FrontierVerification(_) => {} RuntimeTypedRequestRef::ImaginationConsideration(_) => { let candidate = result .imagination_consideration_candidate()? @@ -8712,16 +8831,29 @@ fn validate_repo_frontier_hands_authority_chain( let route = cache .get::(&authority.route_id)? .ok_or_else(|| anyhow!("Hands authority requires its persisted route"))?; - let current = require_keyed_repo_model_basis( - cache, - &authority.model_projection_digest, - &authority.model_source_documents, - )?; - let current_item = current - .frontier + let frontier_source = route + .model_source_documents .iter() - .find(|item| item.id == route.frontier_item_id) + .find(|source| { + source.document_type == crate::EpiphanyRepoModelFrontierDocument::TYPE + && source.document_key == route.frontier_item_id + }) + .ok_or_else(|| anyhow!("Hands authority route lost its exact frontier version"))?; + let current_frontier_envelope = cache + .get_envelope::(&route.frontier_item_id)? .ok_or_else(|| anyhow!("Hands authority lost its model frontier"))?; + if crate::EpiphanyMindDocumentVersion::from_envelope( + "epiphany-mind", + ¤t_frontier_envelope, + )? != *frontier_source + { + return Err(anyhow!( + "Hands authority frontier version is no longer current" + )); + } + let current_item: crate::EpiphanyRepoModelFrontierDocument = + rmp_serde::from_slice(¤t_frontier_envelope.payload)?; + let current_item = current_item.value()?; let intent = cache .get::(&authority.hands_intent_id)? .ok_or_else(|| anyhow!("Hands authority requires its persisted intent"))?; @@ -8829,11 +8961,6 @@ pub fn put_repo_frontier_hands_authority( let route = cache .get::(&authority.route_id)? .ok_or_else(|| anyhow!("repo frontier Hands authority requires its persisted route"))?; - require_keyed_repo_model_basis( - &cache, - &authority.model_projection_digest, - &authority.model_source_documents, - )?; let intent = cache .get::(&authority.hands_intent_id)? .ok_or_else(|| anyhow!("repo frontier Hands authority requires its persisted intent"))?; @@ -8875,11 +9002,23 @@ pub fn put_repo_frontier_hands_authority( } let (envelope, _) = cache.prepare_entry(&authority.authority_id, authority)?; let backing = SingleFileMessagePackBackingStore::new(store_path); - let basis = crate::EpiphanyRepoModelBasis { - projection_digest: authority.model_projection_digest.clone(), - source_documents: authority.model_source_documents.clone(), - }; - let expected = keyed_repo_model_basis_envelopes(&cache, &basis)?; + let expected = vec![ + cache + .get_envelope::(&route.route_id)? + .ok_or_else(|| anyhow!("repo frontier Hands authority lost its route envelope"))?, + cache + .get_envelope::(&intent.intent_id)? + .ok_or_else(|| anyhow!("repo frontier Hands authority lost its intent envelope"))?, + cache + .get_envelope::(&review.review_id)? + .ok_or_else(|| anyhow!("repo frontier Hands authority lost its review envelope"))?, + cache + .get_envelope::(&grant.receipt_id)? + .ok_or_else(|| anyhow!("repo frontier Hands authority lost its grant envelope"))?, + cache + .get_envelope::(&authority.frontier_item_id)? + .ok_or_else(|| anyhow!("repo frontier Hands authority lost its frontier envelope"))?, + ]; let mut writes = expected.clone(); writes.push(envelope); if backing.compare_and_swap_batch(&expected, writes)? { @@ -9100,6 +9239,11 @@ fn worker_result_has_keyed_mind_commit( | crate::EpiphanyMindObservationDocument::TYPE ) }) + } else if result.role_id.eq_ignore_ascii_case("verification") { + receipt.invariant_owner == "Soul.verification" + && receipt.writes.iter().any(|write| { + write.document_type == crate::EpiphanyMindVerificationAuditDocument::TYPE + }) } else { false } @@ -9224,31 +9368,95 @@ pub fn amend_repo_frontier_execution( } } -pub fn put_repo_frontier_verification_request( - store_path: impl AsRef, +pub(crate) fn validate_repo_frontier_verification_request_intrinsic( request: &RepoFrontierVerificationRequest, -) -> Result<()> { - let store_path = store_path.as_ref(); +) -> Result { if request.schema_version != REPO_FRONTIER_VERIFICATION_REQUEST_SCHEMA_VERSION || request.contract != REPO_FRONTIER_VERIFICATION_REQUEST_CONTRACT || chrono::DateTime::parse_from_rfc3339(&request.requested_at).is_err() - || request.request_id.trim().is_empty() + || [ + request.request_id.as_str(), + request.route_id.as_str(), + request.frontier_item_id.as_str(), + request.frontier_item_hash.as_str(), + request.hands_intent_id.as_str(), + request.hands_review_id.as_str(), + request.hands_patch_receipt_id.as_str(), + request.hands_command_receipt_id.as_str(), + request.hands_commit_receipt_id.as_str(), + ] + .into_iter() + .any(str::is_empty) + || request.frontier_authority_documents.len() != 1 { return Err(anyhow!( "invalid repo frontier verification request contract" )); } + crate::EpiphanyRepoModelBasis { + projection_digest: request.model_projection_digest.clone(), + source_documents: request.model_source_documents.clone(), + } + .validate()?; + let source = &request.frontier_authority_documents[0]; + source.validate()?; + if source.store_id != "epiphany-mind" + || source.document_type != crate::EpiphanyRepoModelFrontierDocument::TYPE + || source.document_key != request.frontier_item_id + { + return Err(anyhow!( + "Verification request frontier authority has the wrong identity" + )); + } + let frontier: crate::EpiphanyRepoModelFrontierDocument = + rmp_serde::from_slice(&source.payload_msgpack)?; + let frontier = frontier.value()?; + let frontier_hash = format!("{:x}", Sha256::digest(rmp_serde::to_vec_named(&frontier)?)); + if frontier.id != request.frontier_item_id || frontier_hash != request.frontier_item_hash { + return Err(anyhow!( + "Verification request frontier authority does not bind its exact item" + )); + } + Ok(frontier) +} + +pub(crate) fn verification_frontier_is_current( + cache: &CultCache, + request: &RepoFrontierVerificationRequest, +) -> Result { + validate_repo_frontier_verification_request_intrinsic(request)?; + let source = &request.frontier_authority_documents[0]; + Ok(cache + .snapshot_envelopes() + .iter() + .find(|envelope| { + envelope.r#type == source.document_type && envelope.key == source.document_key + }) + .map(|envelope| { + crate::EpiphanyMindDocumentVersion::from_envelope("epiphany-mind", envelope) + .map(|current| current == *source) + }) + .transpose()? + .unwrap_or(false)) +} + +pub fn put_repo_frontier_verification_request( + store_path: impl AsRef, + request: &RepoFrontierVerificationRequest, +) -> Result<()> { + let store_path = store_path.as_ref(); + validate_repo_frontier_verification_request_intrinsic(request)?; let mut cache = runtime_spine_cache(store_path)?; cache.pull_all_backing_stores()?; require_identity(&cache)?; let route = cache .get::(&request.route_id)? .ok_or_else(|| anyhow!("verification request requires its exact frontier route"))?; - require_keyed_repo_model_basis( - &cache, - &request.model_projection_digest, - &request.model_source_documents, - )?; + if !verification_frontier_is_current(&cache, request)? { + return Err(anyhow!( + "verification request frontier authority is no longer current" + )); + } let authorities = cache .get_all::()? .into_iter() @@ -9331,11 +9539,41 @@ pub fn put_repo_frontier_verification_request( } let (envelope, _) = cache.prepare_entry(&request.request_id, request)?; let backing = SingleFileMessagePackBackingStore::new(store_path); - let basis = crate::EpiphanyRepoModelBasis { - projection_digest: request.model_projection_digest.clone(), - source_documents: request.model_source_documents.clone(), - }; - let expected = keyed_repo_model_basis_envelopes(&cache, &basis)?; + let mut expected = Vec::new(); + for (document_type, document_key) in [ + (RepoFrontierRoute::TYPE, request.route_id.as_str()), + ( + RepoFrontierHandsAuthority::TYPE, + authority.authority_id.as_str(), + ), + (HandsActionIntent::TYPE, request.hands_intent_id.as_str()), + (HandsActionReview::TYPE, request.hands_review_id.as_str()), + ( + HandsPatchReceipt::TYPE, + request.hands_patch_receipt_id.as_str(), + ), + ( + HandsCommandReceipt::TYPE, + request.hands_command_receipt_id.as_str(), + ), + ( + HandsCommitReceipt::TYPE, + request.hands_commit_receipt_id.as_str(), + ), + ( + crate::EpiphanyRepoModelFrontierDocument::TYPE, + request.frontier_item_id.as_str(), + ), + ] { + expected.push( + cache + .snapshot_envelopes() + .iter() + .find(|value| value.r#type == document_type && value.key == document_key) + .cloned() + .ok_or_else(|| anyhow!("verification request lost an exact authority"))?, + ); + } let mut writes = expected.clone(); writes.push(envelope); if backing.compare_and_swap_batch(&expected, writes)? { @@ -9358,6 +9596,85 @@ pub fn runtime_repo_frontier_verification_request( cache.get::(request_id) } +pub(crate) fn repo_frontier_verification_context( + cache: &CultCache, + request: &RepoFrontierVerificationRequest, +) -> Result { + repo_frontier_verification_context_with_commit(cache, request, None) +} + +fn repo_frontier_verification_context_with_commit( + cache: &CultCache, + request: &RepoFrontierVerificationRequest, + prospective_commit: Option<&HandsCommitReceipt>, +) -> Result { + validate_repo_frontier_verification_request_intrinsic(request)?; + if !verification_frontier_is_current(cache, request)? { + return Err(anyhow!( + "Verification context cannot be projected from stale frontier authority" + )); + } + let route = cache + .get::(&request.route_id)? + .ok_or_else(|| anyhow!("Verification context lost its route"))?; + let authorities = cache + .get_all::()? + .into_iter() + .filter(|authority| { + authority.route_id == request.route_id + && authority.hands_intent_id == request.hands_intent_id + && authority.hands_review_id == request.hands_review_id + }) + .collect::>(); + let [hands_authority] = authorities.as_slice() else { + return Err(anyhow!( + "Verification context requires one exact Hands authority" + )); + }; + validate_repo_frontier_hands_authority_chain(cache, hands_authority)?; + let hands_intent = cache + .get::(&request.hands_intent_id)? + .ok_or_else(|| anyhow!("Verification context lost its Hands intent"))?; + let hands_review = cache + .get::(&request.hands_review_id)? + .ok_or_else(|| anyhow!("Verification context lost its Hands review"))?; + let patch_receipt = cache + .get::(&request.hands_patch_receipt_id)? + .ok_or_else(|| anyhow!("Verification context lost its patch receipt"))?; + let command_receipt = cache + .get::(&request.hands_command_receipt_id)? + .ok_or_else(|| anyhow!("Verification context lost its command receipt"))?; + let commit_receipt = match prospective_commit { + Some(receipt) + if receipt.receipt_id == request.hands_commit_receipt_id + && receipt.intent_id == request.hands_intent_id + && receipt.review_id == request.hands_review_id => + { + receipt.clone() + } + Some(_) => { + return Err(anyhow!( + "prospective Hands commit does not exactly bind its Verification request" + )); + } + None => cache + .get::(&request.hands_commit_receipt_id)? + .ok_or_else(|| anyhow!("Verification context lost its commit receipt"))?, + }; + Ok(crate::RepoFrontierVerificationContextProjection { + schema_version: crate::REPO_FRONTIER_VERIFICATION_CONTEXT_SCHEMA_VERSION.into(), + request: request.clone(), + route, + hands_authority: hands_authority.clone(), + hands_intent, + hands_review, + patch_receipt, + command_receipt, + commit_receipt, + contract: crate::REPO_FRONTIER_VERIFICATION_CONTEXT_CONTRACT.into(), + }) +} + pub fn runtime_repo_frontier_route( store_path: impl AsRef, route_id: &str, @@ -9566,13 +9883,11 @@ fn consequence_paths_within_authority( }) } -pub fn commit_repo_frontier_verification_request_for_chain( - store_path: impl AsRef, +fn derive_repo_frontier_verification_request_for_chain( + cache: &CultCache, chain: &RuntimeHandsReceiptChainSummary, requested_at: &str, ) -> Result { - let mut cache = runtime_spine_cache(store_path.as_ref())?; - cache.pull_all_backing_stores()?; let authorities = cache .get_all::()? .into_iter() @@ -9586,6 +9901,13 @@ pub fn commit_repo_frontier_verification_request_for_chain( )); } let authority = &authorities[0]; + let frontier_envelope = cache + .get_envelope::(&authority.frontier_item_id)? + .ok_or_else(|| anyhow!("Hands authority lost its exact frontier document"))?; + let frontier_authority_documents = vec![crate::EpiphanyMindDocumentVersion::from_envelope( + "epiphany-mind", + &frontier_envelope, + )?]; let request_id = format!( "frontier-verification-{}-{}", authority.route_id, chain.commit_receipt_id @@ -9609,7 +9931,20 @@ pub fn commit_repo_frontier_verification_request_for_chain( hands_commit_receipt_id: chain.commit_receipt_id.clone(), requested_at, contract: REPO_FRONTIER_VERIFICATION_REQUEST_CONTRACT.to_string(), + frontier_authority_documents, }; + Ok(request) +} + +pub fn commit_repo_frontier_verification_request_for_chain( + store_path: impl AsRef, + chain: &RuntimeHandsReceiptChainSummary, + requested_at: &str, +) -> Result { + let store_path = store_path.as_ref(); + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + let request = derive_repo_frontier_verification_request_for_chain(&cache, chain, requested_at)?; put_repo_frontier_verification_request(store_path, &request)?; Ok(request) } @@ -9882,16 +10217,103 @@ pub fn put_hands_commit_receipt( let mut cache = runtime_spine_cache(store_path)?; cache.pull_all_backing_stores()?; require_identity(&cache)?; - let (envelope, _) = cache.prepare_entry(&receipt.receipt_id, receipt)?; + let patch = cache + .get_all::()? + .into_iter() + .filter(|patch| { + patch.intent_id == receipt.intent_id + && patch.review_id == receipt.review_id + && patch.runtime_job_id == receipt.runtime_job_id + && patch.emitted_at <= receipt.emitted_at + }) + .max_by(|left, right| left.emitted_at.cmp(&right.emitted_at)) + .ok_or_else(|| anyhow!("Hands commit requires its exact patch receipt"))?; + let command = cache + .get_all::()? + .into_iter() + .filter(|command| { + command.intent_id == receipt.intent_id + && command.review_id == receipt.review_id + && command.runtime_job_id == receipt.runtime_job_id + && command.exit_code == "0" + && command.emitted_at <= receipt.emitted_at + }) + .max_by(|left, right| left.emitted_at.cmp(&right.emitted_at)) + .ok_or_else(|| anyhow!("Hands commit requires its successful command receipt"))?; + let chain = RuntimeHandsReceiptChainSummary { + patch_schema_version: patch.schema_version.clone(), + patch_receipt_id: patch.receipt_id.clone(), + command_schema_version: command.schema_version.clone(), + command_receipt_id: command.receipt_id.clone(), + commit_schema_version: receipt.schema_version.clone(), + commit_receipt_id: receipt.receipt_id.clone(), + intent_id: receipt.intent_id.clone(), + review_id: receipt.review_id.clone(), + runtime_job_id: receipt.runtime_job_id.clone(), + substrate_gate_grant_receipt_id: command.substrate_gate_grant_receipt_id.clone(), + changed_paths: receipt.changed_paths.clone(), + command: command.command.clone(), + exit_code: command.exit_code.clone(), + stdout_artifact: command.stdout_artifact.clone(), + stderr_artifact: command.stderr_artifact.clone(), + commit_sha: receipt.commit_sha.clone(), + branch: receipt.branch.clone(), + summary: receipt.summary.clone(), + emitted_at: receipt.emitted_at.clone(), + }; + let request = + derive_repo_frontier_verification_request_for_chain(&cache, &chain, &receipt.emitted_at)?; + let context = repo_frontier_verification_context_with_commit(&cache, &request, Some(receipt))?; + let snapshot = cache.snapshot_envelopes(); + let mut expected = Vec::new(); + for (document_type, document_key) in [ + (RepoFrontierRoute::TYPE, request.route_id.as_str()), + ( + RepoFrontierHandsAuthority::TYPE, + context.hands_authority.authority_id.as_str(), + ), + (HandsActionIntent::TYPE, request.hands_intent_id.as_str()), + (HandsActionReview::TYPE, request.hands_review_id.as_str()), + ( + HandsPatchReceipt::TYPE, + request.hands_patch_receipt_id.as_str(), + ), + ( + HandsCommandReceipt::TYPE, + request.hands_command_receipt_id.as_str(), + ), + ( + crate::EpiphanyRepoModelFrontierDocument::TYPE, + request.frontier_item_id.as_str(), + ), + ] { + expected.push( + snapshot + .iter() + .find(|value| value.r#type == document_type && value.key == document_key) + .cloned() + .ok_or_else(|| anyhow!("Hands commit lost exact Verification authority"))?, + ); + } + let mut writes = expected.clone(); + writes.push(cache.prepare_entry(&receipt.receipt_id, receipt)?.0); + writes.push(cache.prepare_entry(&request.request_id, &request)?.0); if SingleFileMessagePackBackingStore::new(store_path) - .compare_and_swap_batch(&[], vec![envelope])? + .compare_and_swap_batch(&expected, writes)? { return Ok(()); } let mut reloaded = runtime_spine_cache(store_path)?; reloaded.pull_all_backing_stores()?; - match reloaded.get::(&receipt.receipt_id)? { - Some(existing) if existing == *receipt => Ok(()), + match ( + reloaded.get::(&receipt.receipt_id)?, + reloaded.get::(&request.request_id)?, + ) { + (Some(existing), Some(existing_request)) + if existing == *receipt && existing_request == request => + { + Ok(()) + } _ => Err(anyhow!("Hands commit receipt ids are immutable")), } } @@ -10066,12 +10488,7 @@ pub fn runtime_hands_receipt_chain_matches_current_model( let route = cache .get::(&authority.route_id)? .ok_or_else(|| anyhow!("Hands receipt chain lost its persisted route"))?; - let current = require_keyed_repo_model_basis( - &cache, - &route.model_projection_digest, - &route.model_source_documents, - ) - .is_ok(); + let current = validate_repo_frontier_hands_authority_chain(&cache, authority).is_ok(); Ok(authority.hands_review_id == chain.review_id && authority.substrate_grant_receipt_id == chain.substrate_gate_grant_receipt_id && authority.route_id == route.route_id @@ -10542,6 +10959,7 @@ where RuntimeTypedRequestRef::FrontierVerdictModeling(request_id) } "frontier-research" => RuntimeTypedRequestRef::FrontierResearch(request_id), + "frontier-verification" => RuntimeTypedRequestRef::FrontierVerification(request_id), "imagination-consideration" => { RuntimeTypedRequestRef::ImaginationConsideration(request_id) } @@ -10563,7 +10981,10 @@ where } if matches!( request_kind, - "proposal-modeling" | "frontier-verdict-modeling" | "frontier-research" + "proposal-modeling" + | "frontier-verdict-modeling" + | "frontier-research" + | "frontier-verification" ) && !worker_result_has_keyed_mind_commit( &cache, role_result diff --git a/epiphany-core/src/runtime_worker_attempt.rs b/epiphany-core/src/runtime_worker_attempt.rs index a83329f9..775ed181 100644 --- a/epiphany-core/src/runtime_worker_attempt.rs +++ b/epiphany-core/src/runtime_worker_attempt.rs @@ -59,6 +59,7 @@ pub enum RuntimeTypedRequestRef<'a> { ProposalModeling(&'a str), FrontierVerdictModeling(&'a str), FrontierResearch(&'a str), + FrontierVerification(&'a str), ImaginationConsideration(&'a str), AdmittedModelDirection(&'a str), } @@ -69,6 +70,7 @@ impl<'a> RuntimeTypedRequestRef<'a> { Self::ProposalModeling(id) | Self::FrontierVerdictModeling(id) | Self::FrontierResearch(id) + | Self::FrontierVerification(id) | Self::ImaginationConsideration(id) | Self::AdmittedModelDirection(id) => id, } @@ -79,6 +81,7 @@ impl<'a> RuntimeTypedRequestRef<'a> { Self::ProposalModeling(_) => "proposal-modeling", Self::FrontierVerdictModeling(_) => "frontier-verdict-modeling", Self::FrontierResearch(_) => "frontier-research", + Self::FrontierVerification(_) => "frontier-verification", Self::ImaginationConsideration(_) => "imagination-consideration", Self::AdmittedModelDirection(_) => "admitted-model-direction", } @@ -95,6 +98,9 @@ impl<'a> RuntimeTypedRequestRef<'a> { Self::FrontierResearch(id) => { launch.repo_frontier_research_request_id.as_deref() == Some(id) } + Self::FrontierVerification(id) => { + launch.repo_frontier_verification_request_id.as_deref() == Some(id) + } Self::ImaginationConsideration(id) => { launch.imagination_consideration_request_id.as_deref() == Some(id) } @@ -118,6 +124,7 @@ impl<'a> RuntimeTypedRequestRef<'a> { Self::FrontierResearch(id) => { result.repo_frontier_research_request_id.as_deref() == Some(id) } + Self::FrontierVerification(id) => result.verification_request_id.as_deref() == Some(id), Self::ImaginationConsideration(id) => { result.imagination_consideration_request_id.as_deref() == Some(id) } @@ -143,6 +150,9 @@ impl crate::EpiphanyRuntimeWorkerLaunchRequest { self.repo_frontier_research_request_id .as_deref() .map(RuntimeTypedRequestRef::FrontierResearch), + self.repo_frontier_verification_request_id + .as_deref() + .map(RuntimeTypedRequestRef::FrontierVerification), self.imagination_consideration_request_id .as_deref() .map(RuntimeTypedRequestRef::ImaginationConsideration), diff --git a/epiphany-core/src/soul_gateway.rs b/epiphany-core/src/soul_gateway.rs index 0c24d947..10eceda2 100644 --- a/epiphany-core/src/soul_gateway.rs +++ b/epiphany-core/src/soul_gateway.rs @@ -14,9 +14,9 @@ pub const SOUL_VERIFICATION_REQUEST_SCHEMA_VERSION: &str = "epiphany.soul.verifi pub const REPO_FRONTIER_VERIFICATION_REQUEST_TYPE: &str = "epiphany.soul.repo_frontier_verification_request"; pub const REPO_FRONTIER_VERIFICATION_REQUEST_SCHEMA_VERSION: &str = - "epiphany.soul.repo_frontier_verification_request.v1"; + "epiphany.soul.repo_frontier_verification_request.v2"; pub const REPO_FRONTIER_VERIFICATION_REQUEST_CONTRACT: &str = - "epiphany.repo_frontier_verification_request.v1"; + "epiphany.repo_frontier_verification_request.v2"; pub const SOUL_INVARIANT_CHECK_SCHEMA_VERSION: &str = "epiphany.soul.invariant_check.v0"; pub const SOUL_VERDICT_RECEIPT_SCHEMA_VERSION: &str = "epiphany.soul.verdict_receipt.v1"; pub const SOUL_REGRESSION_RECEIPT_SCHEMA_VERSION: &str = "epiphany.soul.regression_receipt.v0"; @@ -87,6 +87,8 @@ pub struct RepoFrontierVerificationRequest { pub requested_at: String, #[cultcache(key = 13)] pub contract: String, + #[cultcache(key = 14)] + pub frontier_authority_documents: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/epiphany-core/src/substrate_gate.rs b/epiphany-core/src/substrate_gate.rs index f0583e8f..5f2f4d56 100644 --- a/epiphany-core/src/substrate_gate.rs +++ b/epiphany-core/src/substrate_gate.rs @@ -300,6 +300,7 @@ mod tests { frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -332,6 +333,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let grant = substrate_gate_repo_access_grant_for_launch( "grant-1".to_string(), diff --git a/epiphany-core/src/surfaces/coordinator.rs b/epiphany-core/src/surfaces/coordinator.rs index 5f00062e..31838cd2 100644 --- a/epiphany-core/src/surfaces/coordinator.rs +++ b/epiphany-core/src/surfaces/coordinator.rs @@ -1,7 +1,7 @@ +use epiphany_core::EpiphanyAgentPassContinuationAction; use epiphany_core::EpiphanyCrrcAction; use epiphany_core::EpiphanyCrrcSceneAction; use epiphany_core::EpiphanyCrrcStateStatus; -use epiphany_core::EpiphanyModelingContinuationAction; use epiphany_core::EpiphanyReorientFindingInterpretation; use epiphany_core::EpiphanyRoleBoardLane; use epiphany_core::EpiphanyRoleFindingInterpretation; @@ -97,6 +97,7 @@ pub fn derive_coordinator_status( frontier_planning_stage: input.frontier_planning_stage, proposal_modeling_action: input.proposal_modeling_action, frontier_verdict_modeling_action: input.frontier_verdict_modeling_action, + verification_action: input.verification_action, body_modeling_work_ready: input.body_modeling_work_ready, body_modeling_review_ready: input.body_modeling_review_ready, }); @@ -700,7 +701,7 @@ pub fn recommend_coordinator_action( if let Some(action) = input.proposal_modeling_action { return match action { - EpiphanyModelingContinuationAction::Launch => build( + EpiphanyAgentPassContinuationAction::Launch => build( EpiphanyCoordinatorAction::LaunchModeling, Some(EpiphanyCoordinatorRoleId::Modeling), Some(EpiphanyCoordinatorSceneAction::RoleLaunch), @@ -708,7 +709,7 @@ pub fn recommend_coordinator_action( true, "The oldest unresolved proposal request is ready for its exact Modeling attempt.", ), - EpiphanyModelingContinuationAction::Wait => build( + EpiphanyAgentPassContinuationAction::Wait => build( EpiphanyCoordinatorAction::WaitForModelingResult, Some(EpiphanyCoordinatorRoleId::Modeling), Some(EpiphanyCoordinatorSceneAction::RoleResult), @@ -716,7 +717,7 @@ pub fn recommend_coordinator_action( false, "The exact proposal Modeling attempt is still running.", ), - EpiphanyModelingContinuationAction::Review => build( + EpiphanyAgentPassContinuationAction::Review => build( EpiphanyCoordinatorAction::ReviewModelingResult, Some(EpiphanyCoordinatorRoleId::Modeling), Some(EpiphanyCoordinatorSceneAction::RoleResult), @@ -751,7 +752,7 @@ pub fn recommend_coordinator_action( if let Some(action) = input.frontier_verdict_modeling_action { return match action { - EpiphanyModelingContinuationAction::Launch => build( + EpiphanyAgentPassContinuationAction::Launch => build( EpiphanyCoordinatorAction::LaunchModeling, Some(EpiphanyCoordinatorRoleId::Modeling), Some(EpiphanyCoordinatorSceneAction::RoleLaunch), @@ -759,7 +760,7 @@ pub fn recommend_coordinator_action( true, "An exact Soul verdict has unresolved keyed frontier Modeling work.", ), - EpiphanyModelingContinuationAction::Wait => build( + EpiphanyAgentPassContinuationAction::Wait => build( EpiphanyCoordinatorAction::WaitForModelingResult, Some(EpiphanyCoordinatorRoleId::Modeling), Some(EpiphanyCoordinatorSceneAction::RoleResult), @@ -767,7 +768,7 @@ pub fn recommend_coordinator_action( false, "The exact frontier verdict Modeling attempt is still running.", ), - EpiphanyModelingContinuationAction::Review => build( + EpiphanyAgentPassContinuationAction::Review => build( EpiphanyCoordinatorAction::ReviewModelingResult, Some(EpiphanyCoordinatorRoleId::Modeling), Some(EpiphanyCoordinatorSceneAction::RoleResult), @@ -778,6 +779,35 @@ pub fn recommend_coordinator_action( }; } + if let Some(action) = input.verification_action { + return match action { + EpiphanyAgentPassContinuationAction::Launch => build( + EpiphanyCoordinatorAction::LaunchVerification, + Some(EpiphanyCoordinatorRoleId::Verification), + Some(EpiphanyCoordinatorSceneAction::RoleLaunch), + false, + true, + "An exact Hands consequence has unresolved keyed Verification work.", + ), + EpiphanyAgentPassContinuationAction::Wait => build( + EpiphanyCoordinatorAction::ReviewVerificationResult, + Some(EpiphanyCoordinatorRoleId::Verification), + Some(EpiphanyCoordinatorSceneAction::RoleResult), + false, + false, + "The exact frontier Verification attempt is still running.", + ), + EpiphanyAgentPassContinuationAction::Review => build( + EpiphanyCoordinatorAction::ReviewVerificationResult, + Some(EpiphanyCoordinatorRoleId::Verification), + Some(EpiphanyCoordinatorSceneAction::RoleResult), + true, + false, + "The exact frontier Verification result awaits Soul admission.", + ), + }; + } + if let Some(action) = input.research_continuation_action { return match action { RepoFrontierResearchContinuationAction::LaunchResearch => build( @@ -799,26 +829,6 @@ pub fn recommend_coordinator_action( }; } - if input.implementation_evidence_after_verification - && matches!( - input.signals.verification_result_status, - EpiphanyCoordinatorRoleResultStatus::MissingBinding - | EpiphanyCoordinatorRoleResultStatus::BackendUnavailable - | EpiphanyCoordinatorRoleResultStatus::BackendMissing - | EpiphanyCoordinatorRoleResultStatus::Cancelled - | EpiphanyCoordinatorRoleResultStatus::Failed - ) - { - return build( - EpiphanyCoordinatorAction::LaunchVerification, - Some(EpiphanyCoordinatorRoleId::Verification), - Some(EpiphanyCoordinatorSceneAction::RoleLaunch), - false, - true, - "A complete Hands consequence chain awaits Soul independently of any Eyes lifecycle.", - ); - } - if input.signals.modeling_result_status == EpiphanyCoordinatorRoleResultStatus::Completed && !input.modeling_result_accepted && !(input.modeling_result_failure_reviewed && input.hands_frontier_ready) @@ -902,132 +912,6 @@ pub fn recommend_coordinator_action( ); } - if input.signals.verification_result_status == EpiphanyCoordinatorRoleResultStatus::Completed - && !input.verification_result_covers_current_modeling - { - return build( - EpiphanyCoordinatorAction::LaunchVerification, - Some(EpiphanyCoordinatorRoleId::Verification), - Some(EpiphanyCoordinatorSceneAction::RoleLaunch), - false, - true, - "The completed verification/review finding does not cover the currently accepted modeling evidence; relaunch verification before implementation continues.", - ); - } - - if input.signals.verification_result_status == EpiphanyCoordinatorRoleResultStatus::Completed - && !input.verification_result_accepted - { - return build( - EpiphanyCoordinatorAction::ReviewVerificationResult, - Some(EpiphanyCoordinatorRoleId::Verification), - Some(EpiphanyCoordinatorSceneAction::RoleResult), - true, - false, - "A verification/review finding is complete and must be reviewed before continuation.", - ); - } - - if input.signals.verification_result_status == EpiphanyCoordinatorRoleResultStatus::Failed - && !input.verification_result_accepted - && input.verification_result_failure_reviewed - { - return build( - EpiphanyCoordinatorAction::LaunchVerification, - Some(EpiphanyCoordinatorRoleId::Verification), - Some(EpiphanyCoordinatorSceneAction::RoleLaunch), - false, - true, - "The failed verification/review result has a supersession review receipt; relaunch Soul against the current evidence.", - ); - } - - if input.signals.verification_result_status == EpiphanyCoordinatorRoleResultStatus::Failed - && !input.verification_result_accepted - { - return build( - EpiphanyCoordinatorAction::ReviewVerificationResult, - Some(EpiphanyCoordinatorRoleId::Verification), - Some(EpiphanyCoordinatorSceneAction::RoleResult), - false, - false, - "The verification/review worker failed; inspect the failed Soul result before relaunching verification or continuing implementation.", - ); - } - - if input.verification_result_accepted && input.implementation_evidence_after_verification { - return build( - EpiphanyCoordinatorAction::LaunchVerification, - Some(EpiphanyCoordinatorRoleId::Verification), - Some(EpiphanyCoordinatorSceneAction::RoleLaunch), - false, - true, - "Implementation evidence was produced after the accepted verification/review finding; rerun verification before implementation continues.", - ); - } - - if matches!( - input.signals.verification_result_status, - EpiphanyCoordinatorRoleResultStatus::Pending | EpiphanyCoordinatorRoleResultStatus::Running - ) { - return build( - EpiphanyCoordinatorAction::ReviewVerificationResult, - Some(EpiphanyCoordinatorRoleId::Verification), - Some(EpiphanyCoordinatorSceneAction::RoleResult), - false, - false, - "A verification/review specialist is already running; wait for its result.", - ); - } - - if input.signals.verification_result_status == EpiphanyCoordinatorRoleResultStatus::Completed - && input.verification_result_accepted - && !input.verification_result_allows_implementation - && input.verification_result_needs_evidence - && input.modeling_result_accepted - && input.verification_result_covers_current_modeling - && input.hands_frontier_ready - { - return build( - EpiphanyCoordinatorAction::ContinueImplementation, - Some(EpiphanyCoordinatorRoleId::Implementation), - None, - false, - false, - "The accepted verification/review finding asks for implementation evidence from the current modeling checkpoint; continue only the bounded evidence-gathering implementation step before re-verification.", - ); - } - - if input.signals.verification_result_status == EpiphanyCoordinatorRoleResultStatus::Completed - && input.verification_result_accepted - && !input.verification_result_allows_implementation - && !input.modeling_result_accepted_after_verification - { - return build( - EpiphanyCoordinatorAction::LaunchModeling, - Some(EpiphanyCoordinatorRoleId::Modeling), - Some(EpiphanyCoordinatorSceneAction::RoleLaunch), - false, - true, - "The accepted verification/review finding did not pass; strengthen modeling/checkpoint evidence before implementation continues.", - ); - } - - if input.signals.verification_result_status == EpiphanyCoordinatorRoleResultStatus::Completed - && input.verification_result_accepted - && input.verification_result_allows_implementation - && !input.modeling_result_accepted_after_verification - { - return build( - EpiphanyCoordinatorAction::LaunchModeling, - Some(EpiphanyCoordinatorRoleId::Modeling), - Some(EpiphanyCoordinatorSceneAction::RoleLaunch), - false, - true, - "Soul has accepted the Hands consequence evidence; route Modeling to update the machine model before another implementation turn.", - ); - } - if input.hands_frontier_ready { return build( EpiphanyCoordinatorAction::ContinueImplementation, @@ -1076,34 +960,6 @@ pub fn recommend_coordinator_action( ); } - if input.implementation_evidence_after_verification - && role_status(&input.roles, EpiphanyCoordinatorRoleId::Verification).is_some_and( - |status| { - matches!( - status, - EpiphanyCoordinatorRoleStatus::Ready | EpiphanyCoordinatorRoleStatus::Needed - ) - }, - ) - && matches!( - input.signals.verification_result_status, - EpiphanyCoordinatorRoleResultStatus::MissingBinding - | EpiphanyCoordinatorRoleResultStatus::BackendUnavailable - | EpiphanyCoordinatorRoleResultStatus::BackendMissing - | EpiphanyCoordinatorRoleResultStatus::Cancelled - | EpiphanyCoordinatorRoleResultStatus::Failed - ) - { - return build( - EpiphanyCoordinatorAction::LaunchVerification, - Some(EpiphanyCoordinatorRoleId::Verification), - Some(EpiphanyCoordinatorSceneAction::RoleLaunch), - false, - true, - "A complete Hands consequence chain is newer than the last accepted Soul boundary; launch Verification against its typed request and receipts.", - ); - } - if input.recommendation.action == EpiphanyCrrcAction::RegatherManually { return build( EpiphanyCoordinatorAction::RegatherManually, @@ -1115,25 +971,6 @@ pub fn recommend_coordinator_action( ); } - if input.signals.verification_result_status == EpiphanyCoordinatorRoleResultStatus::Completed - && input.verification_result_accepted - && !input.verification_result_allows_implementation - && (input.verification_result_needs_evidence - || input.verification_result_cites_implementation_evidence) - && input.modeling_result_accepted - && input.verification_result_covers_current_modeling - && !input.hands_frontier_ready - { - return build( - EpiphanyCoordinatorAction::LaunchModeling, - Some(EpiphanyCoordinatorRoleId::Modeling), - Some(EpiphanyCoordinatorSceneAction::RoleLaunch), - false, - true, - "Soul requests another bounded consequence, but Self has no admitted actionable Hands frontier; reconcile the RepoModel before acting.", - ); - } - if input.modeling_result_accepted && !input.hands_frontier_ready { return build( EpiphanyCoordinatorAction::AwaitFrontierProposal, @@ -1318,6 +1155,7 @@ mod tests { frontier_planning_stage: RepoFrontierPlanningLifecycleStage::Unavailable, proposal_modeling_action: None, frontier_verdict_modeling_action: None, + verification_action: None, body_modeling_work_ready: false, body_modeling_review_ready: false, } @@ -1398,7 +1236,7 @@ mod tests { fn selected_user_proposal_preempts_generic_regather() { let decision = recommend_coordinator_action(EpiphanyCoordinatorInput { recommendation: recommendation(EpiphanyCrrcAction::RegatherManually), - proposal_modeling_action: Some(EpiphanyModelingContinuationAction::Launch), + proposal_modeling_action: Some(EpiphanyAgentPassContinuationAction::Launch), ..input() }); assert_eq!(decision.action, EpiphanyCoordinatorAction::LaunchModeling); @@ -1823,7 +1661,8 @@ mod tests { ); let exact_modeling_work = recommend_coordinator_action(EpiphanyCoordinatorInput { - frontier_verdict_modeling_action: Some(EpiphanyModelingContinuationAction::Launch), + frontier_verdict_modeling_action: Some(EpiphanyAgentPassContinuationAction::Launch), + verification_action: None, ..base.clone() }); assert_eq!( diff --git a/epiphany-core/src/surfaces/coordinator_contract.rs b/epiphany-core/src/surfaces/coordinator_contract.rs index 2b602948..bd5374b8 100644 --- a/epiphany-core/src/surfaces/coordinator_contract.rs +++ b/epiphany-core/src/surfaces/coordinator_contract.rs @@ -6,7 +6,7 @@ use super::EpiphanyPressure; use super::EpiphanyPressureLevel; use super::EpiphanyReorientAction; use super::EpiphanyRoleBoardLane; -use crate::EpiphanyModelingContinuationAction; +use crate::EpiphanyAgentPassContinuationAction; use crate::RepoFrontierPlanningLifecycleStage; use crate::RepoFrontierResearchContinuationAction; use serde::{Deserialize, Serialize}; @@ -167,9 +167,12 @@ pub struct EpiphanyCoordinatorInput { /// lifecycle. Self may advance it, but only Mind's result can decide adoption. pub frontier_planning_stage: RepoFrontierPlanningLifecycleStage, /// Exact current action for the oldest unresolved proposal Modeling request. - pub proposal_modeling_action: Option, + pub proposal_modeling_action: Option, /// Exact current action for one Soul-verdict-bound Modeling request. - pub frontier_verdict_modeling_action: Option, + pub frontier_verdict_modeling_action: Option, + /// Exact current action for one Hands-consequence-bound Verification request. + /// Role lanes, timestamps, and latest-result slots are display-only. + pub verification_action: Option, pub body_modeling_work_ready: bool, pub body_modeling_review_ready: bool, } @@ -222,8 +225,9 @@ pub struct EpiphanyCoordinatorStatusInput { pub hands_frontier_ready: bool, pub research_continuation_action: Option, pub frontier_planning_stage: RepoFrontierPlanningLifecycleStage, - pub proposal_modeling_action: Option, - pub frontier_verdict_modeling_action: Option, + pub proposal_modeling_action: Option, + pub frontier_verdict_modeling_action: Option, + pub verification_action: Option, pub body_modeling_work_ready: bool, pub body_modeling_review_ready: bool, } diff --git a/epiphany-core/src/surfaces/mod.rs b/epiphany-core/src/surfaces/mod.rs index e3f6e68c..b9622fd8 100644 --- a/epiphany-core/src/surfaces/mod.rs +++ b/epiphany-core/src/surfaces/mod.rs @@ -133,9 +133,12 @@ pub use worker_launch::REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_CONTRACT; pub use worker_launch::REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_SCHEMA_VERSION; pub use worker_launch::REPO_FRONTIER_RESEARCH_CONTEXT_CONTRACT; pub use worker_launch::REPO_FRONTIER_RESEARCH_CONTEXT_SCHEMA_VERSION; +pub use worker_launch::REPO_FRONTIER_VERIFICATION_CONTEXT_CONTRACT; +pub use worker_launch::REPO_FRONTIER_VERIFICATION_CONTEXT_SCHEMA_VERSION; pub use worker_launch::ROLE_WORKER_OUTPUT_CONTRACT_ID; pub use worker_launch::RepoFrontierPlanMindContextProjection; pub use worker_launch::RepoFrontierPlanningContextProjection; pub use worker_launch::RepoFrontierProposalModelingContextProjection; pub use worker_launch::RepoFrontierResearchContextProjection; +pub use worker_launch::RepoFrontierVerificationContextProjection; pub use worker_launch::build_reorient_job_launch_request; diff --git a/epiphany-core/src/surfaces/worker_launch.rs b/epiphany-core/src/surfaces/worker_launch.rs index 85f99c4a..349d6cb7 100644 --- a/epiphany-core/src/surfaces/worker_launch.rs +++ b/epiphany-core/src/surfaces/worker_launch.rs @@ -38,6 +38,10 @@ pub const REPO_FRONTIER_RESEARCH_CONTEXT_SCHEMA_VERSION: &str = "epiphany.worker.repo_frontier_research_context.v3"; pub const REPO_FRONTIER_RESEARCH_CONTEXT_CONTRACT: &str = "epiphany.repo_frontier_research_context.v3"; +pub const REPO_FRONTIER_VERIFICATION_CONTEXT_SCHEMA_VERSION: &str = + "epiphany.worker.repo_frontier_verification_context.v1"; +pub const REPO_FRONTIER_VERIFICATION_CONTEXT_CONTRACT: &str = + "epiphany.repo_frontier_verification_context.v1"; pub const REPO_FRONTIER_PLAN_MIND_CONTEXT_SCHEMA_VERSION: &str = "epiphany.worker.repo_frontier_plan_mind_context.v0"; pub const REPO_FRONTIER_PLAN_MIND_CONTEXT_CONTRACT: &str = @@ -112,6 +116,8 @@ pub struct EpiphanyRoleWorkerLaunchDocument { #[serde(default, skip_serializing_if = "Option::is_none")] pub frontier_research_context: Option, #[serde(default, skip_serializing_if = "Option::is_none")] + pub frontier_verification_context: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub frontier_plan_mind_context: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub imagination_consideration_context: Option, @@ -317,6 +323,21 @@ pub struct ImaginationConsiderationContextProjection { pub model: crate::EpiphanyRepoModelView, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RepoFrontierVerificationContextProjection { + pub schema_version: String, + pub request: crate::RepoFrontierVerificationRequest, + pub route: crate::RepoFrontierRoute, + pub hands_authority: crate::RepoFrontierHandsAuthority, + pub hands_intent: crate::HandsActionIntent, + pub hands_review: crate::HandsActionReview, + pub patch_receipt: crate::HandsPatchReceipt, + pub command_receipt: crate::HandsCommandReceipt, + pub commit_receipt: crate::HandsCommitReceipt, + pub contract: String, +} + impl ImaginationConsiderationContextProjection { pub(crate) fn new( request: &crate::ImaginationConsiderationRequest, @@ -427,6 +448,7 @@ pub fn build_reorient_job_launch_request( admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, } } diff --git a/epiphany-openai-runtime/src/bin/epiphany-openai-runtime.rs b/epiphany-openai-runtime/src/bin/epiphany-openai-runtime.rs index 7ccd3cb6..9c9cd64b 100644 --- a/epiphany-openai-runtime/src/bin/epiphany-openai-runtime.rs +++ b/epiphany-openai-runtime/src/bin/epiphany-openai-runtime.rs @@ -1563,6 +1563,9 @@ mod tests { .clone(), repo_frontier_modeling_request_id: launch.repo_frontier_modeling_request_id.clone(), repo_frontier_research_request_id: launch.repo_frontier_research_request_id.clone(), + repo_frontier_verification_request_id: launch + .repo_frontier_verification_request_id + .clone(), }; let grant = epiphany_core::substrate_gate_repo_access_grant_for_launch( format!("substrate-grant-{}", launch.job_id), @@ -1603,6 +1606,7 @@ mod tests { frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1634,6 +1638,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: now(), }, )?; @@ -1714,6 +1719,7 @@ mod tests { frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1745,6 +1751,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: now(), }, )?; @@ -1849,6 +1856,7 @@ mod tests { frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -1880,6 +1888,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: now(), }, )?; diff --git a/epiphany-openai-runtime/src/lib.rs b/epiphany-openai-runtime/src/lib.rs index feec0f82..802c32fe 100644 --- a/epiphany-openai-runtime/src/lib.rs +++ b/epiphany-openai-runtime/src/lib.rs @@ -1611,6 +1611,11 @@ fn worker_output_contract_text( { "Required proposal-Modeling fields: roleId=modeling, verdict, summary, nextSafeMove, filesInspected, frontierNodeIds, evidenceIds, proposalFrontierDraft. Emit only the semantic frontier draft. Runtime owns and composes proposal identity, Body observation basis, proposal/request provenance, active status, timestamps, and the mandatory proposal evidence binding. Do not emit repoModelOperations, proposalModelingRequestId, repositoryBodyObservationBasis, statePatch, selfPatch, commands, release, or deployment cargo." } + EpiphanyWorkerLaunchDocument::Role(document) + if document.frontier_verification_context.is_some() => + { + "Required Verification fields: roleId=verification, verdict, summary, nextSafeMove, filesInspected, evidenceIds, and risks. Audit only the exact typed request, route, and Hands receipts in the sealed projection. Runtime owns request and route identity; do not emit identity, statePatch, repoModelOperations, commands, release, or deployment cargo." + } EpiphanyWorkerLaunchDocument::Role(_) => { "Required role-result fields: roleId, verdict, summary, nextSafeMove, filesInspected. Modeling workers emit repoModelOperations, an array of semantic keyed operations; use an empty array when no RepoModel mutation is proposed. Never emit proposal ids, model revisions or hashes, timestamps, strong reads, writes, or receipts. Ordinary Imagination workers must include statePatch. Modeling statePatch is optional observations/evidence only. For ordinary Modeling, checkpoint-update-needed is a typed claim that the Body map contains a future design gap: encode exactly one PutFrontier operation with a new active, unadopted frontier, recommended_next_organ=Imagination, empty dependency_item_ids, safe non-empty source_scope, and evidence_refs grounded in top-level evidenceIds. checkpoint-ready may carry only local Atlas offer/claim transitions or an empty operation array; regather-needed requires an empty operation array. Neither may mutate frontier. nextSafeMove is display-only and never routes an organ. Use arrays for frontierNodeIds, evidenceIds, openQuestions, evidenceGaps, risks, and artifactRefs when present." } @@ -1640,8 +1645,6 @@ struct RoleWorkerResultIngress { repo_model_operations: Vec, frontier_verdict_gap: Option, self_patch: Option, - verification_request_id: Option, - frontier_route_id: Option, frontier_plan_candidate: Option, frontier_plan_mind_decision: Option, imagination_consideration_candidate: Option, @@ -1809,6 +1812,14 @@ fn role_worker_result_from_ingress( runtime_evidence_ids: Vec, artifact_refs: Vec, ) -> EpiphanyRuntimeRoleWorkerResult { + let verification_authority = rmp_serde::from_slice::( + &launch_request.launch_document_msgpack, + ) + .ok() + .and_then(|document| match document { + EpiphanyWorkerLaunchDocument::Role(role) => role.frontier_verification_context, + EpiphanyWorkerLaunchDocument::Reorient(_) => None, + }); let accepted_evidence_ids = { let mut evidence_ids = clean_string_vec(&result.evidence_ids); evidence_ids.extend(clean_string_vec(&runtime_evidence_ids)); @@ -2223,8 +2234,12 @@ fn role_worker_result_from_ingress( ), metadata: std::collections::BTreeMap::new(), repo_model_mutation_proposal_msgpack, - verification_request_id: clean_optional_string(result.verification_request_id.as_deref()), - frontier_route_id: clean_optional_string(result.frontier_route_id.as_deref()), + verification_request_id: verification_authority + .as_ref() + .map(|authority| authority.request.request_id.clone()), + frontier_route_id: verification_authority + .as_ref() + .map(|authority| authority.route.route_id.clone()), repo_frontier_modeling_request_id: launch_request.repo_frontier_modeling_request_id.clone(), proposal_modeling_request_id: launch_request.proposal_modeling_request_id.clone(), repo_frontier_research_request_id: launch_request.repo_frontier_research_request_id.clone(), @@ -2555,6 +2570,7 @@ mod tests { invariants: Vec::new(), observations: Vec::new(), evidence: Vec::new(), + verification_audits: Vec::new(), investigation_checkpoint: None, mode: None, planning: Default::default(), @@ -2727,6 +2743,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let basis = epiphany_core::RepositoryBodyObservationBasis { schema_version: "epiphany.repository_body.v2".into(), @@ -2874,6 +2891,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let atlas = RoleWorkerResultIngress { role_id: Some("modeling".into()), @@ -2996,6 +3014,7 @@ mod tests { frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -3036,6 +3055,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let request = build_worker_model_request( @@ -3067,7 +3087,7 @@ mod tests { } #[test] - fn verification_ingress_preserves_exact_request_and_route_binding() -> Result<()> { + fn verification_ingress_cannot_author_request_or_route_identity() -> Result<()> { let parsed = parse_assistant_json::( r#"{"roleId":"verification","verdict":"pass","summary":"verified","nextSafeMove":"admit","verificationRequestId":" verification-request-1 ","frontierRouteId":" frontier-route-1 "}"#, )?; @@ -3094,6 +3114,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let result = role_worker_result_from_ingress( &launch, @@ -3112,14 +3133,8 @@ mod tests { vec!["openai-request:verification-request-1".to_string()], Vec::new(), ); - assert_eq!( - result.verification_request_id.as_deref(), - Some("verification-request-1") - ); - assert_eq!( - result.frontier_route_id.as_deref(), - Some("frontier-route-1") - ); + assert!(result.verification_request_id.is_none()); + assert!(result.frontier_route_id.is_none()); assert_eq!( result.evidence_ids, vec!["openai-request:verification-request-1"] @@ -3169,6 +3184,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let planning_context = epiphany_core::RepoFrontierPlanningContextProjection { schema_version: epiphany_core::REPO_FRONTIER_PLANNING_CONTEXT_SCHEMA_VERSION.into(), @@ -3338,6 +3354,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let result = role_worker_result_from_ingress( &launch, @@ -3406,6 +3423,7 @@ mod tests { frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -3446,6 +3464,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let failed = failed_frontier_planning_role_result(&launch, "candidate mismatch")? .expect("typed planning failure"); @@ -3487,6 +3506,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, }; let context = epiphany_core::RepoFrontierPlanMindContextProjection { schema_version: epiphany_core::REPO_FRONTIER_PLAN_MIND_CONTEXT_SCHEMA_VERSION.into(), @@ -3813,6 +3833,7 @@ mod tests { hands_commit_receipt_id: "hands-commit-1".into(), requested_at: "2026-08-08T00:00:00Z".into(), contract: epiphany_core::REPO_FRONTIER_VERIFICATION_REQUEST_CONTRACT.into(), + frontier_authority_documents: route.model_source_documents.clone(), }; let verification_result = EpiphanyRuntimeRoleWorkerResult { schema_version: epiphany_core::RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.into(), @@ -3903,6 +3924,7 @@ mod tests { ), frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -3934,6 +3956,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: Some(frontier_request.request_id.clone()), repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: now(), }, )?; @@ -4188,6 +4211,7 @@ mod tests { frontier_verdict_modeling_context: None, frontier_planning_context: None, frontier_research_context: None, + frontier_verification_context: None, frontier_plan_mind_context: None, imagination_consideration_context: None, admitted_model_direction_consideration_context: None, @@ -4219,6 +4243,7 @@ mod tests { admitted_model_direction_consideration_request_id: None, repo_frontier_modeling_request_id: None, repo_frontier_research_request_id: None, + repo_frontier_verification_request_id: None, created_at: now(), }, )?;