Split native coordinator acceptance organ

This commit is contained in:
Meta Vens 2026-07-11 19:29:56 +01:00
commit 588725f5e1
6 changed files with 388 additions and 395 deletions

View file

@ -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<EpiphanyStateUpdatedField>,
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<EpiphanyStateUpdatedField>,
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<EpiphanyRoleFindingInterpretation, EpiphanyCoordinatorAdmissionError> {
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<EpiphanyReorientFindingInterpretation, EpiphanyCoordinatorAdmissionError> {
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<EpiphanyLaunchOrganContract, EpiphanyCoordinatorAdmissionError> {
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<u64>,
role_id: EpiphanyRoleResultRoleId,
binding_id: &str,
finding: &EpiphanyRoleFindingInterpretation,
accepted_evidence_id: String,
accepted_observation_id: String,
accepted_at: String,
) -> Result<EpiphanyRoleAcceptanceUpdate, String> {
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<u64>,
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<epiphany_state_model::EpiphanyInvestigationCheckpoint>,
) -> Result<EpiphanyReorientAcceptanceUpdate, String> {
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<u64>,
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<EpiphanyStateUpdatedField> {
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",
}
}

View file

@ -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<EpiphanyStateUpdatedField>,
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<EpiphanyStateUpdatedField>,
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<PathBuf>,
@ -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<EpiphanyRoleFindingInterpretation, EpiphanyCoordinatorAdmissionError> {
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<EpiphanyReorientFindingInterpretation, EpiphanyCoordinatorAdmissionError> {
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<EpiphanyLaunchOrganContract, EpiphanyCoordinatorAdmissionError> {
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<u64>,
role_id: EpiphanyRoleResultRoleId,
binding_id: &str,
finding: &EpiphanyRoleFindingInterpretation,
accepted_evidence_id: String,
accepted_observation_id: String,
accepted_at: String,
) -> Result<EpiphanyRoleAcceptanceUpdate, String> {
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<u64>,
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<epiphany_state_model::EpiphanyInvestigationCheckpoint>,
) -> Result<EpiphanyReorientAcceptanceUpdate, String> {
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<u64>,
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<EpiphanyStateUpdatedField> {
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<EpiphanyStateUpdatedField> {
pub(crate) fn changed_fields(update: &EpiphanyStateUpdate) -> Vec<EpiphanyStateUpdatedField> {
let mut fields = Vec::new();
if update.objective.is_some() {
fields.push(EpiphanyStateUpdatedField::Objective);
@ -574,6 +189,8 @@ fn changed_fields(update: &EpiphanyStateUpdate) -> Vec<EpiphanyStateUpdatedField
#[cfg(test)]
mod tests {
use super::*;
use crate::EpiphanyCoordinatorAdmissionError;
use crate::completed_role_finding;
#[test]
fn native_service_owns_revision_gated_thread_state() -> Result<()> {

View file

@ -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;

View file

@ -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

Binary file not shown.

View file

@ -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`.