From 588725f5e12207fef866800984bf8fb9bee51271 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 11 Jul 2026 19:29:56 +0100 Subject: [PATCH] Split native coordinator acceptance organ --- epiphany-core/src/coordinator_acceptance.rs | 367 ++++++++++++++++++ epiphany-core/src/coordinator_service.rs | 389 +------------------- epiphany-core/src/lib.rs | 17 +- notes/fresh-workspace-handoff.md | 8 + state/ledgers.msgpack | Bin 400344 -> 400640 bytes state/map.yaml | 2 +- 6 files changed, 388 insertions(+), 395 deletions(-) create mode 100644 epiphany-core/src/coordinator_acceptance.rs diff --git a/epiphany-core/src/coordinator_acceptance.rs b/epiphany-core/src/coordinator_acceptance.rs new file mode 100644 index 00000000..0008780e --- /dev/null +++ b/epiphany-core/src/coordinator_acceptance.rs @@ -0,0 +1,367 @@ +use crate::coordinator_results::latest_runtime_link; +use crate::coordinator_service::changed_fields; +use crate::*; +use epiphany_state_model::EpiphanyThreadState; +use std::path::Path; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EpiphanyCoordinatorAdmissionError { + InvalidRequest(String), + Store(String), +} + +impl std::fmt::Display for EpiphanyCoordinatorAdmissionError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::InvalidRequest(message) | Self::Store(message) => formatter.write_str(message), + } + } +} + +impl std::error::Error for EpiphanyCoordinatorAdmissionError {} + +/// The Epiphany-native owner of coordinator state. +/// +/// Codex compatibility surfaces may translate requests into calls on this +/// service, but they do not receive a persistence hook or retain an independent +/// state opinion. +pub struct EpiphanyRoleAcceptanceUpdate { + pub state_update: EpiphanyStateUpdate, + pub applied_patch: EpiphanyRoleStatePatchDocument, + pub changed_fields: Vec, + pub accepted_receipt_id: String, + pub accepted_observation_id: String, + pub accepted_evidence_id: String, + pub mind_review: MindGatewayReview, +} + +pub struct EpiphanyReorientAcceptanceUpdate { + pub state_update: EpiphanyStateUpdate, + pub changed_fields: Vec, + pub accepted_receipt_id: String, + pub accepted_observation_id: String, + pub accepted_evidence_id: String, + pub mind_review: MindGatewayReview, +} + +pub fn completed_role_finding( + runtime_store_path: Option<&Path>, + state: &EpiphanyThreadState, + role_id: EpiphanyRoleResultRoleId, + binding_id: &str, +) -> Result { + let Some(link) = latest_runtime_link(state, binding_id) else { + return if state + .job_bindings + .iter() + .any(|binding| binding.id == binding_id) + { + Err(EpiphanyCoordinatorAdmissionError::InvalidRequest( + "role findings without runtime-spine results are unsupported; accept only typed runtime-spine results".to_string(), + )) + } else { + Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "epiphany role binding {binding_id:?} was not found" + ))) + }; + }; + let snapshot = read_runtime_role_result(runtime_store_path, &link.runtime_job_id, role_id); + if snapshot.status != EpiphanyCoordinatorRoleResultStatus::Completed { + return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "cannot accept role result while worker status is {:?}", + snapshot.status + ))); + } + let store = runtime_store_path.ok_or_else(|| { + EpiphanyCoordinatorAdmissionError::InvalidRequest( + "cannot accept completed role worker without a loaded runtime-spine store".to_string(), + ) + })?; + load_launch_organ_contract(store, &link.runtime_job_id, "role")?; + snapshot.finding.ok_or_else(|| { + EpiphanyCoordinatorAdmissionError::InvalidRequest( + "cannot accept completed role worker because no typed runtime-spine result was recorded" + .to_string(), + ) + }) +} + +pub fn completed_reorient_finding( + runtime_store_path: Option<&Path>, + state: &EpiphanyThreadState, + binding_id: &str, +) -> Result { + let Some(link) = latest_runtime_link(state, binding_id) else { + return if state + .job_bindings + .iter() + .any(|binding| binding.id == binding_id) + { + Err(EpiphanyCoordinatorAdmissionError::InvalidRequest( + "reorientation findings without runtime-spine results are unsupported; accept only typed runtime-spine results".to_string(), + )) + } else { + Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "epiphany reorientation binding {binding_id:?} was not found" + ))) + }; + }; + let snapshot = read_runtime_reorient_result(runtime_store_path, &link.runtime_job_id); + if snapshot.status != EpiphanyCrrcResultStatus::Completed { + return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "cannot accept reorientation result while worker status is {:?}", + snapshot.status + ))); + } + let store = runtime_store_path.ok_or_else(|| { + EpiphanyCoordinatorAdmissionError::InvalidRequest( + "cannot accept completed reorientation worker without a loaded runtime-spine store" + .to_string(), + ) + })?; + load_launch_organ_contract(store, &link.runtime_job_id, "reorient")?; + snapshot.finding.ok_or_else(|| { + EpiphanyCoordinatorAdmissionError::InvalidRequest( + "cannot accept completed reorientation worker because no typed runtime-spine result was recorded" + .to_string(), + ) + }) +} + +pub fn load_launch_organ_contract( + runtime_store_path: &Path, + job_id: &str, + expected_document_kind: &str, +) -> Result { + let request = runtime_worker_launch_request(runtime_store_path, job_id).map_err(|error| { + EpiphanyCoordinatorAdmissionError::Store(format!( + "failed to read worker launch request for runtime job {job_id:?}: {error}" + )) + })?; + let request = request.ok_or_else(|| { + EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "cannot accept runtime job {job_id:?} without its typed worker launch request" + )) + })?; + if request.document_kind != expected_document_kind { + return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "cannot accept runtime job {job_id:?}: launch document kind {:?} does not match expected {expected_document_kind:?}", + request.document_kind + ))); + } + if request.organ_launch_contract.dependencies.is_empty() + || request + .organ_launch_contract + .receipt_proof_profiles + .is_empty() + { + return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "cannot accept runtime job {job_id:?}: worker launch request has no organ dependency/proof-profile contract" + ))); + } + if !request + .organ_launch_contract + .receipt_proof_profiles + .iter() + .any(|profile| { + profile.effect_kind == EpiphanyReceiptEffectKind::StateAdmission + && profile + .required_before_promotion_document_types + .iter() + .any(|document_type| document_type == MIND_GATEWAY_REVIEW_TYPE) + }) + { + return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( + "cannot accept runtime job {job_id:?}: worker launch contract has no state-admission proof profile requiring Mind review" + ))); + } + Ok(request.organ_launch_contract) +} + +pub fn build_native_role_acceptance_update( + expected_revision: Option, + role_id: EpiphanyRoleResultRoleId, + binding_id: &str, + finding: &EpiphanyRoleFindingInterpretation, + accepted_evidence_id: String, + accepted_observation_id: String, + accepted_at: String, +) -> Result { + let mut patch = finding.state_patch.clone().unwrap_or_default(); + let errors = match role_id { + EpiphanyRoleResultRoleId::Imagination => imagination_role_state_patch_policy_errors(&patch), + EpiphanyRoleResultRoleId::Modeling => modeling_role_state_patch_policy_errors(&patch), + EpiphanyRoleResultRoleId::Research => research_role_state_patch_policy_errors(&patch), + EpiphanyRoleResultRoleId::Verification => { + patch = EpiphanyRoleStatePatchDocument::default(); + Vec::new() + } + EpiphanyRoleResultRoleId::Implementation | EpiphanyRoleResultRoleId::Reorientation => { + return Err(format!( + "role {role_id:?} cannot be accepted through roleAccept" + )); + } + }; + if role_id != EpiphanyRoleResultRoleId::Verification && finding.state_patch.is_none() { + return Err("completed role finding did not include a reviewable statePatch".to_string()); + } + if !errors.is_empty() { + return Err(format!( + "{} role state patch is not acceptable: {}", + role_label_lower(role_id), + errors.join("; ") + )); + } + + let projected_fields = changed_fields_from_patch(&patch) + .iter() + .map(|field| format!("{field:?}")) + .collect(); + let mind_review = mind_review_role_acceptance(binding_id, role_id, finding, &patch); + mind_review_allows_state(&mind_review)?; + let bundle = build_role_acceptance_bundle( + binding_id, + EpiphanyRoleAcceptanceFinding { + role_id, + verdict: finding.verdict.clone(), + summary: finding.summary.clone(), + next_safe_move: finding.next_safe_move.clone(), + files_inspected: finding.files_inspected.clone(), + runtime_result_id: finding.runtime_result_id.clone(), + runtime_job_id: finding.runtime_job_id.clone(), + projected_fields, + }, + accepted_evidence_id, + accepted_observation_id, + accepted_at, + )?; + let accepted_receipt_id = bundle.accepted_receipt_id.clone(); + let accepted_observation_id = bundle.accepted_observation_id.clone(); + let accepted_evidence_id = bundle.accepted_evidence_id.clone(); + patch.evidence.push(bundle.evidence); + patch.observations.push(bundle.observation); + patch.acceptance_receipts.push(bundle.receipt); + let changed_fields = changed_fields_from_patch(&patch); + Ok(EpiphanyRoleAcceptanceUpdate { + state_update: state_update_from_patch(expected_revision, patch.clone()), + applied_patch: patch, + changed_fields, + accepted_receipt_id, + accepted_observation_id, + accepted_evidence_id, + mind_review, + }) +} + +pub fn build_native_reorient_acceptance_update( + expected_revision: Option, + binding_id: &str, + finding: &EpiphanyReorientFindingInterpretation, + accepted_evidence_id: String, + accepted_observation_id: String, + accepted_at: String, + update_scratch: bool, + update_investigation_checkpoint: bool, + checkpoint: Option, +) -> Result { + let mind_finding = EpiphanyReorientAcceptanceFinding { + mode: finding.mode.clone(), + summary: finding.summary.clone(), + next_safe_move: finding.next_safe_move.clone(), + checkpoint_still_valid: finding.checkpoint_still_valid, + files_inspected: finding.files_inspected.clone(), + runtime_result_id: finding.runtime_result_id.clone(), + runtime_job_id: finding.runtime_job_id.clone(), + }; + let mind_review = mind_review_reorient_acceptance( + binding_id, + &mind_finding, + update_scratch, + update_investigation_checkpoint, + ); + mind_review_allows_state(&mind_review)?; + let bundle = build_reorient_acceptance_bundle( + binding_id, + mind_finding, + accepted_evidence_id, + accepted_observation_id, + accepted_at, + update_scratch, + update_investigation_checkpoint + .then_some(checkpoint) + .flatten(), + )?; + let accepted_receipt_id = bundle.accepted_receipt_id.clone(); + let accepted_observation_id = bundle.accepted_observation_id.clone(); + let accepted_evidence_id = bundle.accepted_evidence_id.clone(); + let mut changed_fields = vec![ + EpiphanyStateUpdatedField::AcceptanceReceipts, + EpiphanyStateUpdatedField::Observations, + EpiphanyStateUpdatedField::Evidence, + ]; + if bundle.scratch.is_some() { + changed_fields.push(EpiphanyStateUpdatedField::Scratch); + } + if bundle.investigation_checkpoint.is_some() { + changed_fields.push(EpiphanyStateUpdatedField::InvestigationCheckpoint); + } + Ok(EpiphanyReorientAcceptanceUpdate { + state_update: EpiphanyStateUpdate { + expected_revision, + scratch: bundle.scratch, + investigation_checkpoint: bundle.investigation_checkpoint, + acceptance_receipts: vec![bundle.receipt], + observations: vec![bundle.observation], + evidence: vec![bundle.evidence], + ..Default::default() + }, + changed_fields, + accepted_receipt_id, + accepted_observation_id, + accepted_evidence_id, + mind_review, + }) +} + +fn state_update_from_patch( + expected_revision: Option, + patch: EpiphanyRoleStatePatchDocument, +) -> EpiphanyStateUpdate { + EpiphanyStateUpdate { + expected_revision, + objective: patch.objective, + active_subgoal_id: patch.active_subgoal_id, + subgoals: patch.subgoals, + invariants: patch.invariants, + graphs: patch.graphs, + graph_frontier: patch.graph_frontier, + graph_checkpoint: patch.graph_checkpoint, + scratch: patch.scratch, + investigation_checkpoint: patch.investigation_checkpoint, + job_bindings: patch.job_bindings, + acceptance_receipts: patch.acceptance_receipts, + runtime_links: patch.runtime_links, + observations: patch.observations, + evidence: patch.evidence, + churn: patch.churn, + mode: patch.mode, + planning: patch.planning, + } +} + +fn changed_fields_from_patch( + patch: &EpiphanyRoleStatePatchDocument, +) -> Vec { + changed_fields(&state_update_from_patch(None, patch.clone())) +} + +fn role_label_lower(role_id: EpiphanyRoleResultRoleId) -> &'static str { + match role_id { + EpiphanyRoleResultRoleId::Imagination => "imagination", + EpiphanyRoleResultRoleId::Modeling => "modeling", + EpiphanyRoleResultRoleId::Research => "research", + EpiphanyRoleResultRoleId::Verification => "verification", + EpiphanyRoleResultRoleId::Implementation => "implementation", + EpiphanyRoleResultRoleId::Reorientation => "reorientation", + } +} diff --git a/epiphany-core/src/coordinator_service.rs b/epiphany-core/src/coordinator_service.rs index eaf7fe6d..94f47af5 100644 --- a/epiphany-core/src/coordinator_service.rs +++ b/epiphany-core/src/coordinator_service.rs @@ -1,36 +1,13 @@ use crate::EpiphanyCoordinatorReorientResultSnapshot; use crate::EpiphanyCoordinatorRoleResultSnapshot; -use crate::EpiphanyCoordinatorRoleResultStatus; -use crate::EpiphanyCrrcResultStatus; -use crate::EpiphanyLaunchOrganContract; -use crate::EpiphanyReceiptEffectKind; -use crate::EpiphanyReorientAcceptanceFinding; -use crate::EpiphanyReorientFindingInterpretation; -use crate::EpiphanyRoleAcceptanceFinding; -use crate::EpiphanyRoleFindingInterpretation; use crate::EpiphanyRoleResultRoleId; -use crate::EpiphanyRoleStatePatchDocument; use crate::EpiphanyStateUpdate; use crate::EpiphanyStateUpdatedField; -use crate::MIND_GATEWAY_REVIEW_TYPE; -use crate::MindGatewayReview; use crate::apply_epiphany_state_update; -use crate::build_reorient_acceptance_bundle; -use crate::build_role_acceptance_bundle; -use crate::coordinator_results::latest_runtime_link; use crate::epiphany_state_update_validation_errors; -use crate::imagination_role_state_patch_policy_errors; use crate::load_thread_state; -use crate::mind_review_allows_state; -use crate::mind_review_reorient_acceptance; -use crate::mind_review_role_acceptance; -use crate::modeling_role_state_patch_policy_errors; use crate::read_reorient_result_snapshot; use crate::read_role_result_snapshot; -use crate::read_runtime_reorient_result; -use crate::read_runtime_role_result; -use crate::research_role_state_patch_policy_errors; -use crate::runtime_worker_launch_request; use crate::write_thread_state; use anyhow::Result; use anyhow::anyhow; @@ -38,27 +15,6 @@ use epiphany_state_model::EpiphanyThreadState; use std::path::Path; use std::path::PathBuf; -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum EpiphanyCoordinatorAdmissionError { - InvalidRequest(String), - Store(String), -} - -impl std::fmt::Display for EpiphanyCoordinatorAdmissionError { - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::InvalidRequest(message) | Self::Store(message) => formatter.write_str(message), - } - } -} - -impl std::error::Error for EpiphanyCoordinatorAdmissionError {} - -/// The Epiphany-native owner of coordinator state. -/// -/// Codex compatibility surfaces may translate requests into calls on this -/// service, but they do not receive a persistence hook or retain an independent -/// state opinion. #[derive(Debug, Clone)] pub struct EpiphanyCoordinatorService { thread_state_store: PathBuf, @@ -72,25 +28,6 @@ pub struct EpiphanyCoordinatorStateApplied { pub state: EpiphanyThreadState, } -pub struct EpiphanyRoleAcceptanceUpdate { - pub state_update: EpiphanyStateUpdate, - pub applied_patch: EpiphanyRoleStatePatchDocument, - pub changed_fields: Vec, - pub accepted_receipt_id: String, - pub accepted_observation_id: String, - pub accepted_evidence_id: String, - pub mind_review: MindGatewayReview, -} - -pub struct EpiphanyReorientAcceptanceUpdate { - pub state_update: EpiphanyStateUpdate, - pub changed_fields: Vec, - pub accepted_receipt_id: String, - pub accepted_observation_id: String, - pub accepted_evidence_id: String, - pub mind_review: MindGatewayReview, -} - impl EpiphanyCoordinatorService { pub fn new( thread_state_store: impl Into, @@ -161,328 +98,6 @@ impl EpiphanyCoordinatorService { } } -pub fn completed_role_finding( - runtime_store_path: Option<&Path>, - state: &EpiphanyThreadState, - role_id: EpiphanyRoleResultRoleId, - binding_id: &str, -) -> Result { - let Some(link) = latest_runtime_link(state, binding_id) else { - return if state - .job_bindings - .iter() - .any(|binding| binding.id == binding_id) - { - Err(EpiphanyCoordinatorAdmissionError::InvalidRequest( - "role findings without runtime-spine results are unsupported; accept only typed runtime-spine results".to_string(), - )) - } else { - Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( - "epiphany role binding {binding_id:?} was not found" - ))) - }; - }; - let snapshot = read_runtime_role_result(runtime_store_path, &link.runtime_job_id, role_id); - if snapshot.status != EpiphanyCoordinatorRoleResultStatus::Completed { - return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( - "cannot accept role result while worker status is {:?}", - snapshot.status - ))); - } - let store = runtime_store_path.ok_or_else(|| { - EpiphanyCoordinatorAdmissionError::InvalidRequest( - "cannot accept completed role worker without a loaded runtime-spine store".to_string(), - ) - })?; - load_launch_organ_contract(store, &link.runtime_job_id, "role")?; - snapshot.finding.ok_or_else(|| { - EpiphanyCoordinatorAdmissionError::InvalidRequest( - "cannot accept completed role worker because no typed runtime-spine result was recorded" - .to_string(), - ) - }) -} - -pub fn completed_reorient_finding( - runtime_store_path: Option<&Path>, - state: &EpiphanyThreadState, - binding_id: &str, -) -> Result { - let Some(link) = latest_runtime_link(state, binding_id) else { - return if state - .job_bindings - .iter() - .any(|binding| binding.id == binding_id) - { - Err(EpiphanyCoordinatorAdmissionError::InvalidRequest( - "reorientation findings without runtime-spine results are unsupported; accept only typed runtime-spine results".to_string(), - )) - } else { - Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( - "epiphany reorientation binding {binding_id:?} was not found" - ))) - }; - }; - let snapshot = read_runtime_reorient_result(runtime_store_path, &link.runtime_job_id); - if snapshot.status != EpiphanyCrrcResultStatus::Completed { - return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( - "cannot accept reorientation result while worker status is {:?}", - snapshot.status - ))); - } - let store = runtime_store_path.ok_or_else(|| { - EpiphanyCoordinatorAdmissionError::InvalidRequest( - "cannot accept completed reorientation worker without a loaded runtime-spine store" - .to_string(), - ) - })?; - load_launch_organ_contract(store, &link.runtime_job_id, "reorient")?; - snapshot.finding.ok_or_else(|| { - EpiphanyCoordinatorAdmissionError::InvalidRequest( - "cannot accept completed reorientation worker because no typed runtime-spine result was recorded" - .to_string(), - ) - }) -} - -pub fn load_launch_organ_contract( - runtime_store_path: &Path, - job_id: &str, - expected_document_kind: &str, -) -> Result { - let request = runtime_worker_launch_request(runtime_store_path, job_id).map_err(|error| { - EpiphanyCoordinatorAdmissionError::Store(format!( - "failed to read worker launch request for runtime job {job_id:?}: {error}" - )) - })?; - let request = request.ok_or_else(|| { - EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( - "cannot accept runtime job {job_id:?} without its typed worker launch request" - )) - })?; - if request.document_kind != expected_document_kind { - return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( - "cannot accept runtime job {job_id:?}: launch document kind {:?} does not match expected {expected_document_kind:?}", - request.document_kind - ))); - } - if request.organ_launch_contract.dependencies.is_empty() - || request - .organ_launch_contract - .receipt_proof_profiles - .is_empty() - { - return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( - "cannot accept runtime job {job_id:?}: worker launch request has no organ dependency/proof-profile contract" - ))); - } - if !request - .organ_launch_contract - .receipt_proof_profiles - .iter() - .any(|profile| { - profile.effect_kind == EpiphanyReceiptEffectKind::StateAdmission - && profile - .required_before_promotion_document_types - .iter() - .any(|document_type| document_type == MIND_GATEWAY_REVIEW_TYPE) - }) - { - return Err(EpiphanyCoordinatorAdmissionError::InvalidRequest(format!( - "cannot accept runtime job {job_id:?}: worker launch contract has no state-admission proof profile requiring Mind review" - ))); - } - Ok(request.organ_launch_contract) -} - -pub fn build_native_role_acceptance_update( - expected_revision: Option, - role_id: EpiphanyRoleResultRoleId, - binding_id: &str, - finding: &EpiphanyRoleFindingInterpretation, - accepted_evidence_id: String, - accepted_observation_id: String, - accepted_at: String, -) -> Result { - let mut patch = finding.state_patch.clone().unwrap_or_default(); - let errors = match role_id { - EpiphanyRoleResultRoleId::Imagination => imagination_role_state_patch_policy_errors(&patch), - EpiphanyRoleResultRoleId::Modeling => modeling_role_state_patch_policy_errors(&patch), - EpiphanyRoleResultRoleId::Research => research_role_state_patch_policy_errors(&patch), - EpiphanyRoleResultRoleId::Verification => { - patch = EpiphanyRoleStatePatchDocument::default(); - Vec::new() - } - EpiphanyRoleResultRoleId::Implementation | EpiphanyRoleResultRoleId::Reorientation => { - return Err(format!( - "role {role_id:?} cannot be accepted through roleAccept" - )); - } - }; - if role_id != EpiphanyRoleResultRoleId::Verification && finding.state_patch.is_none() { - return Err("completed role finding did not include a reviewable statePatch".to_string()); - } - if !errors.is_empty() { - return Err(format!( - "{} role state patch is not acceptable: {}", - role_label_lower(role_id), - errors.join("; ") - )); - } - - let projected_fields = changed_fields_from_patch(&patch) - .iter() - .map(|field| format!("{field:?}")) - .collect(); - let mind_review = mind_review_role_acceptance(binding_id, role_id, finding, &patch); - mind_review_allows_state(&mind_review)?; - let bundle = build_role_acceptance_bundle( - binding_id, - EpiphanyRoleAcceptanceFinding { - role_id, - verdict: finding.verdict.clone(), - summary: finding.summary.clone(), - next_safe_move: finding.next_safe_move.clone(), - files_inspected: finding.files_inspected.clone(), - runtime_result_id: finding.runtime_result_id.clone(), - runtime_job_id: finding.runtime_job_id.clone(), - projected_fields, - }, - accepted_evidence_id, - accepted_observation_id, - accepted_at, - )?; - let accepted_receipt_id = bundle.accepted_receipt_id.clone(); - let accepted_observation_id = bundle.accepted_observation_id.clone(); - let accepted_evidence_id = bundle.accepted_evidence_id.clone(); - patch.evidence.push(bundle.evidence); - patch.observations.push(bundle.observation); - patch.acceptance_receipts.push(bundle.receipt); - let changed_fields = changed_fields_from_patch(&patch); - Ok(EpiphanyRoleAcceptanceUpdate { - state_update: state_update_from_patch(expected_revision, patch.clone()), - applied_patch: patch, - changed_fields, - accepted_receipt_id, - accepted_observation_id, - accepted_evidence_id, - mind_review, - }) -} - -pub fn build_native_reorient_acceptance_update( - expected_revision: Option, - binding_id: &str, - finding: &EpiphanyReorientFindingInterpretation, - accepted_evidence_id: String, - accepted_observation_id: String, - accepted_at: String, - update_scratch: bool, - update_investigation_checkpoint: bool, - checkpoint: Option, -) -> Result { - let mind_finding = EpiphanyReorientAcceptanceFinding { - mode: finding.mode.clone(), - summary: finding.summary.clone(), - next_safe_move: finding.next_safe_move.clone(), - checkpoint_still_valid: finding.checkpoint_still_valid, - files_inspected: finding.files_inspected.clone(), - runtime_result_id: finding.runtime_result_id.clone(), - runtime_job_id: finding.runtime_job_id.clone(), - }; - let mind_review = mind_review_reorient_acceptance( - binding_id, - &mind_finding, - update_scratch, - update_investigation_checkpoint, - ); - mind_review_allows_state(&mind_review)?; - let bundle = build_reorient_acceptance_bundle( - binding_id, - mind_finding, - accepted_evidence_id, - accepted_observation_id, - accepted_at, - update_scratch, - update_investigation_checkpoint - .then_some(checkpoint) - .flatten(), - )?; - let accepted_receipt_id = bundle.accepted_receipt_id.clone(); - let accepted_observation_id = bundle.accepted_observation_id.clone(); - let accepted_evidence_id = bundle.accepted_evidence_id.clone(); - let mut changed_fields = vec![ - EpiphanyStateUpdatedField::AcceptanceReceipts, - EpiphanyStateUpdatedField::Observations, - EpiphanyStateUpdatedField::Evidence, - ]; - if bundle.scratch.is_some() { - changed_fields.push(EpiphanyStateUpdatedField::Scratch); - } - if bundle.investigation_checkpoint.is_some() { - changed_fields.push(EpiphanyStateUpdatedField::InvestigationCheckpoint); - } - Ok(EpiphanyReorientAcceptanceUpdate { - state_update: EpiphanyStateUpdate { - expected_revision, - scratch: bundle.scratch, - investigation_checkpoint: bundle.investigation_checkpoint, - acceptance_receipts: vec![bundle.receipt], - observations: vec![bundle.observation], - evidence: vec![bundle.evidence], - ..Default::default() - }, - changed_fields, - accepted_receipt_id, - accepted_observation_id, - accepted_evidence_id, - mind_review, - }) -} - -fn state_update_from_patch( - expected_revision: Option, - patch: EpiphanyRoleStatePatchDocument, -) -> EpiphanyStateUpdate { - EpiphanyStateUpdate { - expected_revision, - objective: patch.objective, - active_subgoal_id: patch.active_subgoal_id, - subgoals: patch.subgoals, - invariants: patch.invariants, - graphs: patch.graphs, - graph_frontier: patch.graph_frontier, - graph_checkpoint: patch.graph_checkpoint, - scratch: patch.scratch, - investigation_checkpoint: patch.investigation_checkpoint, - job_bindings: patch.job_bindings, - acceptance_receipts: patch.acceptance_receipts, - runtime_links: patch.runtime_links, - observations: patch.observations, - evidence: patch.evidence, - churn: patch.churn, - mode: patch.mode, - planning: patch.planning, - } -} - -fn changed_fields_from_patch( - patch: &EpiphanyRoleStatePatchDocument, -) -> Vec { - changed_fields(&state_update_from_patch(None, patch.clone())) -} - -fn role_label_lower(role_id: EpiphanyRoleResultRoleId) -> &'static str { - match role_id { - EpiphanyRoleResultRoleId::Imagination => "imagination", - EpiphanyRoleResultRoleId::Modeling => "modeling", - EpiphanyRoleResultRoleId::Research => "research", - EpiphanyRoleResultRoleId::Verification => "verification", - EpiphanyRoleResultRoleId::Implementation => "implementation", - EpiphanyRoleResultRoleId::Reorientation => "reorientation", - } -} - pub fn apply_coordinator_state_update_to_state( current_state: &EpiphanyThreadState, update: EpiphanyStateUpdate, @@ -515,7 +130,7 @@ pub fn apply_coordinator_state_update_to_state( Ok(next_state) } -fn changed_fields(update: &EpiphanyStateUpdate) -> Vec { +pub(crate) fn changed_fields(update: &EpiphanyStateUpdate) -> Vec { let mut fields = Vec::new(); if update.objective.is_some() { fields.push(EpiphanyStateUpdatedField::Objective); @@ -574,6 +189,8 @@ fn changed_fields(update: &EpiphanyStateUpdate) -> Vec Result<()> { diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index b720c940..47ab1321 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -2,6 +2,7 @@ mod agent_launch; mod agent_memory; mod agent_utterance_state; mod continuity_gateway; +mod coordinator_acceptance; mod coordinator_results; mod coordinator_service; mod cultmesh_integration; @@ -120,23 +121,23 @@ pub use agent_utterance_state::AgentUtteranceValue; pub use agent_utterance_state::WEKSA_UTTERANCE_HANDOFF_SCHEMA_VERSION; pub use agent_utterance_state::derive_agent_utterance_state; pub use continuity_gateway::*; +pub use coordinator_acceptance::EpiphanyCoordinatorAdmissionError; +pub use coordinator_acceptance::EpiphanyReorientAcceptanceUpdate; +pub use coordinator_acceptance::EpiphanyRoleAcceptanceUpdate; +pub use coordinator_acceptance::build_native_reorient_acceptance_update; +pub use coordinator_acceptance::build_native_role_acceptance_update; +pub use coordinator_acceptance::completed_reorient_finding; +pub use coordinator_acceptance::completed_role_finding; +pub use coordinator_acceptance::load_launch_organ_contract; pub use coordinator_results::EpiphanyCoordinatorReorientResultSnapshot; pub use coordinator_results::EpiphanyCoordinatorRoleResultSnapshot; pub use coordinator_results::read_reorient_result_snapshot; pub use coordinator_results::read_role_result_snapshot; pub use coordinator_results::read_runtime_reorient_result; pub use coordinator_results::read_runtime_role_result; -pub use coordinator_service::EpiphanyCoordinatorAdmissionError; pub use coordinator_service::EpiphanyCoordinatorService; pub use coordinator_service::EpiphanyCoordinatorStateApplied; -pub use coordinator_service::EpiphanyReorientAcceptanceUpdate; -pub use coordinator_service::EpiphanyRoleAcceptanceUpdate; pub use coordinator_service::apply_coordinator_state_update_to_state; -pub use coordinator_service::build_native_reorient_acceptance_update; -pub use coordinator_service::build_native_role_acceptance_update; -pub use coordinator_service::completed_reorient_finding; -pub use coordinator_service::completed_role_finding; -pub use coordinator_service::load_launch_organ_contract; pub use cultcache_rs::CacheBackingStore; pub use cultcache_rs::CultCache; pub use cultcache_rs::CultCacheEnvelope; diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index a7081079..fb9c7643 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -692,6 +692,14 @@ service fell from 1,122 to 661 lines; the result organ is 461 lines and remains read-only. Sixteen coordinator-related core tests and all eight bridge tests pass. Split acceptance and state next before resuming authority extraction. +Acceptance is now split into +`epiphany-core/src/coordinator_acceptance.rs`: admission errors, +completed-finding checks, launch-contract proof validation, Mind review, and +role/reorientation acceptance mutation construction. The remaining mixed +service fell from 661 to 278 lines; acceptance is 367 lines and results remain +461 lines. The same sixteen coordinator-related core tests and eight bridge +tests pass. Extract state ownership next, leaving a policy-free facade. + Bridge identity correction from 2026-06-22: Epiphany owns swarm execution, repo Body work, branch-local lanes, memory, evidence, and Persona cognition. Bifrost owns GameCult identity, governed crossings into GitHub/Discord/Reddit diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 0bd1fd77eb5d49113d05cb37c8483d5d2df1da2d..f8c16f66250247f5e5970c13914f95a14d8eb13a 100644 GIT binary patch delta 206 zcmcbyU7}%^#0E)5E=wy@v#4VXY|?i(D>D`{GHz?0%G5rUi4llFG&2yh05R+KsZ4Cn zlG7W+*aTRNfSRT=HZw{w{+#|(icQ`;F(o&%xHvOEPe&mqu{19^LpM1;ucRn3xkRBX zF()%6u>>sVo0*rQP?TDhnOd%+keHV;eYpgi()9CzjI7gtm^1NkB_=1Q7L+9BC8thL wlV;Q4GBYz&D9KkaGPjsMOPWna0;om7LLsRrGbKG$p(M4qq*$RKvA9?d0Nad0zyJUM delta 56 zcmZqJC2?cB#0E)5E)y$5qo`vHY*Je{D>D`{GHz|2%G5rUi4llFG&2yh05R+KsZ4Cn Fk^ly#5dr`J diff --git a/state/map.yaml b/state/map.yaml index 535c26ff..1fc309aa 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -659,4 +659,4 @@ current_status: - Service lifecycle receipt history is now event-only: `load_epiphany_cultmesh_daemon_service_lifecycle_receipts` filters out the `epiphany-local/daemon-service-lifecycle-receipt/latest` mirror, while `load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt` remains the latest-state reader. Focused unit test `service_lifecycle_receipt_history_excludes_latest_mirror` proves two per-receipt writes return exactly two history records and latest points at the second. Fresh smoke `verse-query-lifecycle-history-filter-smoke` and live overview/triage `local-20260618-145632-497-efac6f0d` / `local-20260618-145632-321-867947af` prove compact swarm sights still expose service lifecycle attention plus the present elevated runbook artifact without daemon pokes or service mutation. Do not use a broad unscoped cargo test filter for this check on Windows; it fans out across bin harnesses and hit a broken pipe during this pass. - `epiphany-verse-query seed-compact` is the compact seed receipt for wrappers and read-only rites. The older `seed` command still prints the full local Verse context for explicit debugging, but compact wrapper modes now avoid full `seed`/final `query` readback after the combined overview proof exposed Windows debug stack overflow from full-context serialization pressure. last_completed_repo_swarm_cut: Huginn live-fire has crossed the first real repo Body from Persona pressure to branch-local Hands commit and Soul/Mind closure, and now has repeatable closure and redacted-public-proof mouths. On `E:\Projects\Huginn` branch `epiphany/huginn/first-awakening`, `tools/epiphany_local_run.ps1 -Mode repo-persona-intake` accepted item `huginn-inspection-readback-livefire`, Weksa/Persona intake remained sealed, and `epiphany-work derive-plan` lowered it to model-authored safe family `repo.status_section` for `README.md` only. `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` advanced four bounded pulses: run-from-plan, adopt-from-plan, execute-from-plan, and close-from-execute. Huginn commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2` updated only `README.md`; closure receipt `.epiphany\work\work-close-huginn-inspection-readback-livefire.json` records status `closed`, Soul verdict `passed`, Mind state commit `repo-work-close-huginn-inspection-readback-livefire-mind-commit`, safe family `repo.status_section`, path scope `README.md`, Bifrost publication gate, and `privateStateExposed=false`. Verification also ran Huginn's TypeScript compiler through concrete Node path: `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json` passed. Native verifier `epiphany-repo-livefire-closure-smoke --workspace E:\Projects\Huginn --item huginn-inspection-readback-livefire --expected-path README.md --expected-family repo.status_section --expected-gate awaiting-publication --expected-blocker bifrost-publication-missing` checks closure receipt, Mind map entry, overview gate/blocker, authority seals, and `git show` changed paths, returning `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, and `privateStateExposed=false`. Wrapper `tools/epiphany_local_run.ps1 -Mode repo-livefire-closure -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exposes the same read-only proof through the local operator mouth; artifact `local-20260621-190156-803-2f85ba15` printed commit, path, gate, blocker, Soul verdict, Mind commit receipt, and `privateStateExposed=False` without push, merge, publication, or target mutation. New wrapper `tools/epiphany_local_run.ps1 -Mode repo-work-public-proof -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exports the redacted proof bundle and repo-local Verse index without publishing; artifact `local-20260621-190645-345-4555b1dc` wrote `.epiphany\public\proof-bundles\repo-work-public-proof-huginn-inspection-readback-livefire.json`, proof id `repo-work-public-proof-huginn-inspection-readback-livefire`, SHA-256 `857545b199f3e37eb06c2e757e5cfc201958e46667bb1f8403fd333a22620f63`, nine artifact rows, zero publication rows, and `privateStateExposed=False`. Gjallar readback `local-20260621-190701-140-b9519ec6` sees the `REPO-WORK-PUBLIC-PROOF` row and action row from Huginn's repo-local Verse with the same SHA-256 while Bifrost publication remains missing. Launcher compact modes are now named compact operator-context modes instead of read-only modes, and `bifrost-public-proof` summaries print item, target public Verse, room, proof SHA-256, review receipts, and credit receipts so publication receipts are auditable without claiming upstream sync. - next_action: Continue the native coordinator anatomy split defined in `notes/native-coordinator-liberation-map.md`. Result readback now lives in `coordinator_results.rs` and the mixed service fell from 1,122 to 661 lines. Extract acceptance and state into narrow organs, leave a policy-free facade, then resume receipt persistence/proof enforcement extraction and remove `thread/epiphany/*` JSON-RPC from `epiphany-mvp-coordinator`. + next_action: Finish the native coordinator anatomy split defined in `notes/native-coordinator-liberation-map.md`. Results and acceptance now have separate organs; `coordinator_service.rs` fell from 1,122 to 278 lines. Extract state ownership and leave a policy-free facade, then resume receipt persistence/proof enforcement extraction and remove `thread/epiphany/*` JSON-RPC from `epiphany-mvp-coordinator`.