diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index 263e6af8..0bb6c5cb 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -131,6 +131,17 @@ pub use state_ledger::append_state_evidence; pub use state_ledger::close_state_branch; pub use state_ledger::load_state_ledger; pub use state_ledger::state_ledger_status; +pub use surfaces::EpiphanyCoordinatorAction; +pub use surfaces::EpiphanyCoordinatorAutomationAction; +pub use surfaces::EpiphanyCoordinatorCrrcRecommendation; +pub use surfaces::EpiphanyCoordinatorDecision; +pub use surfaces::EpiphanyCoordinatorInput; +pub use surfaces::EpiphanyCoordinatorRoleId; +pub use surfaces::EpiphanyCoordinatorRoleLane; +pub use surfaces::EpiphanyCoordinatorRoleResultStatus; +pub use surfaces::EpiphanyCoordinatorRoleStatus; +pub use surfaces::EpiphanyCoordinatorSceneAction; +pub use surfaces::EpiphanyCoordinatorSignals; pub use surfaces::EpiphanyCrrcAction; pub use surfaces::EpiphanyCrrcInput; pub use surfaces::EpiphanyCrrcRecommendation; @@ -145,5 +156,9 @@ pub use surfaces::EpiphanyPressureStatus; pub use surfaces::EpiphanyViewInput; pub use surfaces::EpiphanyViewLens; pub use surfaces::EpiphanyViewOutput; +pub use surfaces::coordinator_automation_action; +pub use surfaces::crrc_scene_action_to_coordinator_scene_action; pub use surfaces::derive_pressure_view; +pub use surfaces::recommend_coordinator_action; pub use surfaces::recommend_crrc_action; +pub use surfaces::select_coordinator_automation_action; diff --git a/epiphany-core/src/surfaces/coordinator.rs b/epiphany-core/src/surfaces/coordinator.rs new file mode 100644 index 00000000..b0479823 --- /dev/null +++ b/epiphany-core/src/surfaces/coordinator.rs @@ -0,0 +1,873 @@ +use super::EpiphanyCrrcAction; +use super::EpiphanyCrrcSceneAction; +use super::EpiphanyCrrcStateStatus; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EpiphanyCoordinatorRoleId { + Implementation, + Imagination, + Modeling, + Verification, + Reorientation, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EpiphanyCoordinatorRoleStatus { + Ready, + Needed, + Running, + Waiting, + Review, + Blocked, + Unavailable, + Completed, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EpiphanyCoordinatorRoleResultStatus { + MissingState, + MissingBinding, + BackendUnavailable, + BackendMissing, + Pending, + Running, + Completed, + Failed, + Cancelled, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EpiphanyCoordinatorAction { + PrepareCheckpoint, + CompactRehydrateReorient, + LaunchReorientWorker, + WaitForReorientWorker, + ReviewReorientResult, + RegatherManually, + LaunchModeling, + ReviewModelingResult, + LaunchVerification, + ReviewVerificationResult, + ContinueImplementation, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EpiphanyCoordinatorSceneAction { + Update, + Reorient, + ReorientLaunch, + ReorientResult, + ReorientAccept, + RoleLaunch, + RoleResult, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EpiphanyCoordinatorAutomationAction { + None, + CompactRehydrateReorient, + LaunchReorientWorker, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EpiphanyCoordinatorCrrcRecommendation { + pub action: EpiphanyCrrcAction, + pub recommended_scene_action: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EpiphanyCoordinatorSignals { + pub modeling_result_status: EpiphanyCoordinatorRoleResultStatus, + pub verification_result_status: EpiphanyCoordinatorRoleResultStatus, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EpiphanyCoordinatorRoleLane { + pub id: EpiphanyCoordinatorRoleId, + pub status: EpiphanyCoordinatorRoleStatus, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EpiphanyCoordinatorInput { + pub state_status: EpiphanyCrrcStateStatus, + pub checkpoint_present: bool, + pub should_prepare_compaction: bool, + pub recommendation: EpiphanyCoordinatorCrrcRecommendation, + pub roles: Vec, + pub signals: EpiphanyCoordinatorSignals, + pub modeling_result_accepted: bool, + pub modeling_result_reviewable: bool, + pub modeling_result_accepted_after_verification: bool, + pub implementation_evidence_after_verification: bool, + pub verification_result_cites_implementation_evidence: bool, + pub verification_result_covers_current_modeling: bool, + pub verification_result_accepted: bool, + pub verification_result_allows_implementation: bool, + pub verification_result_needs_evidence: bool, + pub reorient_finding_accepted: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EpiphanyCoordinatorDecision { + pub action: EpiphanyCoordinatorAction, + pub target_role: Option, + pub recommended_scene_action: Option, + pub requires_review: bool, + pub can_auto_run: bool, + pub reason: String, +} + +pub fn crrc_scene_action_to_coordinator_scene_action( + action: EpiphanyCrrcSceneAction, +) -> EpiphanyCoordinatorSceneAction { + match action { + EpiphanyCrrcSceneAction::Update => EpiphanyCoordinatorSceneAction::Update, + EpiphanyCrrcSceneAction::Reorient => EpiphanyCoordinatorSceneAction::Reorient, + EpiphanyCrrcSceneAction::ReorientLaunch => EpiphanyCoordinatorSceneAction::ReorientLaunch, + EpiphanyCrrcSceneAction::ReorientResult => EpiphanyCoordinatorSceneAction::ReorientResult, + EpiphanyCrrcSceneAction::ReorientAccept => EpiphanyCoordinatorSceneAction::ReorientAccept, + } +} + +pub fn recommend_coordinator_action( + input: EpiphanyCoordinatorInput, +) -> EpiphanyCoordinatorDecision { + let build = |action, + target_role, + recommended_scene_action, + requires_review, + can_auto_run, + reason: &str| EpiphanyCoordinatorDecision { + action, + target_role, + recommended_scene_action, + requires_review, + can_auto_run, + reason: reason.to_string(), + }; + + if input.state_status == EpiphanyCrrcStateStatus::Missing || !input.checkpoint_present { + return build( + EpiphanyCoordinatorAction::PrepareCheckpoint, + Some(EpiphanyCoordinatorRoleId::Modeling), + Some(EpiphanyCoordinatorSceneAction::Update), + false, + false, + "Authoritative state or investigation checkpoint is missing; prepare a checkpoint before coordination can continue.", + ); + } + + match input.recommendation.action { + EpiphanyCrrcAction::WaitForReorientWorker => { + return build( + EpiphanyCoordinatorAction::WaitForReorientWorker, + Some(EpiphanyCoordinatorRoleId::Reorientation), + Some(EpiphanyCoordinatorSceneAction::ReorientResult), + false, + false, + "A CRRC reorient worker is already in flight; wait for the bounded result.", + ); + } + EpiphanyCrrcAction::AcceptReorientResult | EpiphanyCrrcAction::ReviewReorientResult => { + return build( + EpiphanyCoordinatorAction::ReviewReorientResult, + Some(EpiphanyCoordinatorRoleId::Reorientation), + input.recommendation.recommended_scene_action, + true, + false, + "A CRRC reorientation finding needs human review before continuation.", + ); + } + EpiphanyCrrcAction::PrepareCheckpoint + | EpiphanyCrrcAction::LaunchReorientWorker + | EpiphanyCrrcAction::RegatherManually + | EpiphanyCrrcAction::Continue => {} + } + + if input.should_prepare_compaction + && !(input.reorient_finding_accepted + && input.recommendation.action == EpiphanyCrrcAction::Continue) + { + return build( + EpiphanyCoordinatorAction::CompactRehydrateReorient, + Some(EpiphanyCoordinatorRoleId::Reorientation), + Some(EpiphanyCoordinatorSceneAction::Reorient), + false, + true, + "Context pressure crossed the preparation threshold; compact, rehydrate, and reorient before more implementation work.", + ); + } + + if input.recommendation.action == EpiphanyCrrcAction::LaunchReorientWorker { + return build( + EpiphanyCoordinatorAction::LaunchReorientWorker, + Some(EpiphanyCoordinatorRoleId::Reorientation), + Some(EpiphanyCoordinatorSceneAction::ReorientLaunch), + false, + true, + "CRRC says continuity needs a bounded reorient worker before safe continuation.", + ); + } + + if input.signals.modeling_result_status == EpiphanyCoordinatorRoleResultStatus::Completed + && !input.modeling_result_accepted + { + if !input.modeling_result_reviewable { + return build( + EpiphanyCoordinatorAction::LaunchModeling, + Some(EpiphanyCoordinatorRoleId::Modeling), + Some(EpiphanyCoordinatorSceneAction::RoleLaunch), + false, + true, + "The completed modeling/checkpoint finding is not reviewable because it has no acceptable statePatch; relaunch modeling with the typed patch contract before verification or implementation continues.", + ); + } + return build( + EpiphanyCoordinatorAction::ReviewModelingResult, + Some(EpiphanyCoordinatorRoleId::Modeling), + Some(EpiphanyCoordinatorSceneAction::RoleResult), + true, + false, + "A modeling/checkpoint finding is complete and must be reviewed before verification or implementation continues.", + ); + } + + if matches!( + input.signals.modeling_result_status, + EpiphanyCoordinatorRoleResultStatus::Pending | EpiphanyCoordinatorRoleResultStatus::Running + ) { + return build( + EpiphanyCoordinatorAction::ReviewModelingResult, + Some(EpiphanyCoordinatorRoleId::Modeling), + Some(EpiphanyCoordinatorSceneAction::RoleResult), + false, + false, + "A modeling/checkpoint specialist is already running; wait for its result before reviewing stale verification output.", + ); + } + + 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.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 input.recommendation.action == EpiphanyCrrcAction::RegatherManually + && role_status(&input.roles, EpiphanyCoordinatorRoleId::Implementation) + == Some(EpiphanyCoordinatorRoleStatus::Blocked) + { + return build( + EpiphanyCoordinatorAction::RegatherManually, + Some(EpiphanyCoordinatorRoleId::Reorientation), + input.recommendation.recommended_scene_action, + true, + false, + "CRRC says regather is required and the implementation lane is blocked; repair continuity before another coding turn.", + ); + } + + 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 + { + 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.verification_result_cites_implementation_evidence + && input.modeling_result_accepted + && input.verification_result_covers_current_modeling + { + return build( + EpiphanyCoordinatorAction::ContinueImplementation, + Some(EpiphanyCoordinatorRoleId::Implementation), + None, + false, + false, + "The accepted verification/review finding failed against concrete implementation evidence from the current model; continue only the bounded repair 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.modeling_result_status == EpiphanyCoordinatorRoleResultStatus::Completed + && input.modeling_result_accepted + && !input.verification_result_allows_implementation + { + return build( + EpiphanyCoordinatorAction::LaunchVerification, + Some(EpiphanyCoordinatorRoleId::Verification), + Some(EpiphanyCoordinatorSceneAction::RoleLaunch), + false, + true, + "Modeling/checkpoint guidance is available; run verification before implementation continues.", + ); + } + + if role_status(&input.roles, EpiphanyCoordinatorRoleId::Modeling).is_some_and(|status| { + matches!( + status, + EpiphanyCoordinatorRoleStatus::Ready | EpiphanyCoordinatorRoleStatus::Needed + ) + }) && matches!( + input.signals.modeling_result_status, + EpiphanyCoordinatorRoleResultStatus::MissingBinding + | EpiphanyCoordinatorRoleResultStatus::BackendUnavailable + | EpiphanyCoordinatorRoleResultStatus::BackendMissing + | EpiphanyCoordinatorRoleResultStatus::Cancelled + | EpiphanyCoordinatorRoleResultStatus::Failed + ) { + return build( + EpiphanyCoordinatorAction::LaunchModeling, + Some(EpiphanyCoordinatorRoleId::Modeling), + Some(EpiphanyCoordinatorSceneAction::RoleLaunch), + false, + true, + "The modeling/checkpoint lane is ready and no current modeling finding is available.", + ); + } + + if 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, + "The verification/review lane is ready and no current verification finding is available.", + ); + } + + 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.recommendation.action == EpiphanyCrrcAction::RegatherManually { + return build( + EpiphanyCoordinatorAction::RegatherManually, + Some(EpiphanyCoordinatorRoleId::Reorientation), + input.recommendation.recommended_scene_action, + true, + false, + "CRRC cannot safely continue automatically and no fixed specialist lane is currently able to advance the regather.", + ); + } + + build( + EpiphanyCoordinatorAction::ContinueImplementation, + Some(EpiphanyCoordinatorRoleId::Implementation), + None, + false, + false, + "CRRC is clear and no specialist lane is currently blocking implementation.", + ) +} + +pub fn coordinator_automation_action( + decision: &EpiphanyCoordinatorDecision, +) -> EpiphanyCoordinatorAutomationAction { + if !decision.can_auto_run { + return EpiphanyCoordinatorAutomationAction::None; + } + match decision.action { + EpiphanyCoordinatorAction::CompactRehydrateReorient => { + EpiphanyCoordinatorAutomationAction::CompactRehydrateReorient + } + EpiphanyCoordinatorAction::LaunchReorientWorker => { + EpiphanyCoordinatorAutomationAction::LaunchReorientWorker + } + EpiphanyCoordinatorAction::PrepareCheckpoint + | EpiphanyCoordinatorAction::WaitForReorientWorker + | EpiphanyCoordinatorAction::ReviewReorientResult + | EpiphanyCoordinatorAction::RegatherManually + | EpiphanyCoordinatorAction::LaunchModeling + | EpiphanyCoordinatorAction::ReviewModelingResult + | EpiphanyCoordinatorAction::LaunchVerification + | EpiphanyCoordinatorAction::ReviewVerificationResult + | EpiphanyCoordinatorAction::ContinueImplementation => { + EpiphanyCoordinatorAutomationAction::None + } + } +} + +pub fn select_coordinator_automation_action( + decision: &EpiphanyCoordinatorDecision, + force_checkpoint_compaction: bool, +) -> EpiphanyCoordinatorAutomationAction { + if force_checkpoint_compaction { + return EpiphanyCoordinatorAutomationAction::CompactRehydrateReorient; + } + coordinator_automation_action(decision) +} + +fn role_status( + roles: &[EpiphanyCoordinatorRoleLane], + role_id: EpiphanyCoordinatorRoleId, +) -> Option { + roles + .iter() + .find(|role| role.id == role_id) + .map(|role| role.status) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn base_roles() -> Vec { + vec![ + role( + EpiphanyCoordinatorRoleId::Implementation, + EpiphanyCoordinatorRoleStatus::Ready, + ), + role( + EpiphanyCoordinatorRoleId::Imagination, + EpiphanyCoordinatorRoleStatus::Ready, + ), + role( + EpiphanyCoordinatorRoleId::Modeling, + EpiphanyCoordinatorRoleStatus::Ready, + ), + role( + EpiphanyCoordinatorRoleId::Verification, + EpiphanyCoordinatorRoleStatus::Ready, + ), + role( + EpiphanyCoordinatorRoleId::Reorientation, + EpiphanyCoordinatorRoleStatus::Ready, + ), + ] + } + + fn role( + id: EpiphanyCoordinatorRoleId, + status: EpiphanyCoordinatorRoleStatus, + ) -> EpiphanyCoordinatorRoleLane { + EpiphanyCoordinatorRoleLane { id, status } + } + + fn recommendation(action: EpiphanyCrrcAction) -> EpiphanyCoordinatorCrrcRecommendation { + EpiphanyCoordinatorCrrcRecommendation { + action, + recommended_scene_action: Some(EpiphanyCoordinatorSceneAction::Reorient), + } + } + + fn input() -> EpiphanyCoordinatorInput { + EpiphanyCoordinatorInput { + state_status: EpiphanyCrrcStateStatus::Ready, + checkpoint_present: true, + should_prepare_compaction: false, + recommendation: recommendation(EpiphanyCrrcAction::Continue), + roles: base_roles(), + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::MissingBinding, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::MissingBinding, + }, + modeling_result_accepted: false, + modeling_result_reviewable: false, + modeling_result_accepted_after_verification: false, + implementation_evidence_after_verification: false, + verification_result_cites_implementation_evidence: false, + verification_result_covers_current_modeling: true, + verification_result_accepted: false, + verification_result_allows_implementation: false, + verification_result_needs_evidence: false, + reorient_finding_accepted: false, + } + } + + #[test] + fn prepares_missing_checkpoint() { + let decision = recommend_coordinator_action(EpiphanyCoordinatorInput { + checkpoint_present: false, + recommendation: recommendation(EpiphanyCrrcAction::PrepareCheckpoint), + ..input() + }); + + assert_eq!( + decision.action, + EpiphanyCoordinatorAction::PrepareCheckpoint + ); + assert_eq!( + decision.target_role, + Some(EpiphanyCoordinatorRoleId::Modeling) + ); + assert!(!decision.can_auto_run); + } + + #[test] + fn compacts_at_pressure_threshold() { + let decision = recommend_coordinator_action(EpiphanyCoordinatorInput { + should_prepare_compaction: true, + ..input() + }); + + assert_eq!( + decision.action, + EpiphanyCoordinatorAction::CompactRehydrateReorient + ); + assert_eq!( + coordinator_automation_action(&decision), + EpiphanyCoordinatorAutomationAction::CompactRehydrateReorient + ); + } + + #[test] + fn does_not_recompact_after_accepted_resume_reorient() { + let decision = recommend_coordinator_action(EpiphanyCoordinatorInput { + should_prepare_compaction: true, + roles: Vec::new(), + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::BackendMissing, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::BackendMissing, + }, + reorient_finding_accepted: true, + ..input() + }); + + assert_eq!( + decision.action, + EpiphanyCoordinatorAction::ContinueImplementation + ); + } + + #[test] + fn launches_reorient_worker_from_crrc() { + let decision = recommend_coordinator_action(EpiphanyCoordinatorInput { + recommendation: recommendation(EpiphanyCrrcAction::LaunchReorientWorker), + ..input() + }); + + assert_eq!( + decision.action, + EpiphanyCoordinatorAction::LaunchReorientWorker + ); + assert_eq!( + coordinator_automation_action(&decision), + EpiphanyCoordinatorAutomationAction::LaunchReorientWorker + ); + } + + #[test] + fn reviews_reorient_result() { + let decision = recommend_coordinator_action(EpiphanyCoordinatorInput { + recommendation: EpiphanyCoordinatorCrrcRecommendation { + action: EpiphanyCrrcAction::AcceptReorientResult, + recommended_scene_action: Some(EpiphanyCoordinatorSceneAction::ReorientAccept), + }, + ..input() + }); + + assert_eq!( + decision.action, + EpiphanyCoordinatorAction::ReviewReorientResult + ); + assert!(decision.requires_review); + assert_eq!( + coordinator_automation_action(&decision), + EpiphanyCoordinatorAutomationAction::None + ); + } + + #[test] + fn uses_fixed_lanes_before_manual_regather() { + let launch_modeling = recommend_coordinator_action(EpiphanyCoordinatorInput { + recommendation: recommendation(EpiphanyCrrcAction::RegatherManually), + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::MissingBinding, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::MissingBinding, + }, + reorient_finding_accepted: true, + ..input() + }); + assert_eq!( + launch_modeling.action, + EpiphanyCoordinatorAction::LaunchModeling + ); + + let review_modeling = recommend_coordinator_action(EpiphanyCoordinatorInput { + recommendation: recommendation(EpiphanyCrrcAction::RegatherManually), + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::MissingBinding, + }, + modeling_result_reviewable: true, + reorient_finding_accepted: true, + ..input() + }); + assert_eq!( + review_modeling.action, + EpiphanyCoordinatorAction::ReviewModelingResult + ); + + let blocked_regather = recommend_coordinator_action(EpiphanyCoordinatorInput { + recommendation: recommendation(EpiphanyCrrcAction::RegatherManually), + roles: vec![role( + EpiphanyCoordinatorRoleId::Implementation, + EpiphanyCoordinatorRoleStatus::Blocked, + )], + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + }, + modeling_result_accepted: true, + modeling_result_accepted_after_verification: true, + verification_result_accepted: true, + verification_result_needs_evidence: true, + reorient_finding_accepted: true, + ..input() + }); + assert_eq!( + blocked_regather.action, + EpiphanyCoordinatorAction::RegatherManually + ); + } + + #[test] + fn runs_modeling_then_verification_then_continue() { + let launch_modeling = recommend_coordinator_action(input()); + assert_eq!( + launch_modeling.action, + EpiphanyCoordinatorAction::LaunchModeling + ); + assert_eq!( + coordinator_automation_action(&launch_modeling), + EpiphanyCoordinatorAutomationAction::None + ); + + let review_modeling = recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::MissingBinding, + }, + modeling_result_reviewable: true, + ..input() + }); + assert_eq!( + review_modeling.action, + EpiphanyCoordinatorAction::ReviewModelingResult + ); + assert!(review_modeling.requires_review); + + let wait_for_modeling = recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::Running, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + }, + ..input() + }); + assert_eq!( + wait_for_modeling.action, + EpiphanyCoordinatorAction::ReviewModelingResult + ); + assert!(wait_for_modeling.reason.contains("stale verification")); + + let relaunch_unreviewable_modeling = + recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::MissingBinding, + }, + ..input() + }); + assert_eq!( + relaunch_unreviewable_modeling.action, + EpiphanyCoordinatorAction::LaunchModeling + ); + + let launch_verification = recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::MissingBinding, + }, + modeling_result_accepted: true, + modeling_result_reviewable: true, + ..input() + }); + assert_eq!( + launch_verification.action, + EpiphanyCoordinatorAction::LaunchVerification + ); + + let verification_done = EpiphanyCoordinatorSignals { + modeling_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + verification_result_status: EpiphanyCoordinatorRoleResultStatus::Completed, + }; + + let stale_verification = recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: verification_done, + modeling_result_accepted: true, + modeling_result_reviewable: true, + verification_result_covers_current_modeling: false, + ..input() + }); + assert_eq!( + stale_verification.action, + EpiphanyCoordinatorAction::LaunchVerification + ); + + let review_verification = recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: verification_done, + modeling_result_accepted: true, + modeling_result_reviewable: true, + ..input() + }); + assert_eq!( + review_verification.action, + EpiphanyCoordinatorAction::ReviewVerificationResult + ); + + let accepted_non_pass = recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: verification_done, + modeling_result_accepted: true, + modeling_result_reviewable: true, + verification_result_accepted: true, + ..input() + }); + assert_eq!( + accepted_non_pass.action, + EpiphanyCoordinatorAction::LaunchModeling + ); + + let needs_evidence = recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: verification_done, + modeling_result_accepted: true, + modeling_result_reviewable: true, + verification_result_accepted: true, + verification_result_needs_evidence: true, + ..input() + }); + assert_eq!( + needs_evidence.action, + EpiphanyCoordinatorAction::ContinueImplementation + ); + + let implementation_after_verification = + recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: verification_done, + modeling_result_accepted: true, + modeling_result_reviewable: true, + implementation_evidence_after_verification: true, + verification_result_accepted: true, + verification_result_needs_evidence: true, + ..input() + }); + assert_eq!( + implementation_after_verification.action, + EpiphanyCoordinatorAction::LaunchVerification + ); + + let accepted_pass = recommend_coordinator_action(EpiphanyCoordinatorInput { + signals: verification_done, + modeling_result_accepted: true, + modeling_result_reviewable: true, + verification_result_accepted: true, + verification_result_allows_implementation: true, + ..input() + }); + assert_eq!( + accepted_pass.action, + EpiphanyCoordinatorAction::ContinueImplementation + ); + } + + #[test] + fn forced_checkpoint_compaction_overrides_decision() { + let decision = EpiphanyCoordinatorDecision { + action: EpiphanyCoordinatorAction::ContinueImplementation, + target_role: Some(EpiphanyCoordinatorRoleId::Implementation), + recommended_scene_action: None, + requires_review: false, + can_auto_run: false, + reason: "ordinary coordinator would continue".to_string(), + }; + + assert_eq!( + coordinator_automation_action(&decision), + EpiphanyCoordinatorAutomationAction::None + ); + assert_eq!( + select_coordinator_automation_action(&decision, true), + EpiphanyCoordinatorAutomationAction::CompactRehydrateReorient + ); + } +} diff --git a/epiphany-core/src/surfaces/mod.rs b/epiphany-core/src/surfaces/mod.rs index 37599946..b1e8fab5 100644 --- a/epiphany-core/src/surfaces/mod.rs +++ b/epiphany-core/src/surfaces/mod.rs @@ -1,7 +1,22 @@ +mod coordinator; mod crrc; mod pressure; -pub use crrc::recommend_crrc_action; +pub use coordinator::EpiphanyCoordinatorAction; +pub use coordinator::EpiphanyCoordinatorAutomationAction; +pub use coordinator::EpiphanyCoordinatorCrrcRecommendation; +pub use coordinator::EpiphanyCoordinatorDecision; +pub use coordinator::EpiphanyCoordinatorInput; +pub use coordinator::EpiphanyCoordinatorRoleId; +pub use coordinator::EpiphanyCoordinatorRoleLane; +pub use coordinator::EpiphanyCoordinatorRoleResultStatus; +pub use coordinator::EpiphanyCoordinatorRoleStatus; +pub use coordinator::EpiphanyCoordinatorSceneAction; +pub use coordinator::EpiphanyCoordinatorSignals; +pub use coordinator::coordinator_automation_action; +pub use coordinator::crrc_scene_action_to_coordinator_scene_action; +pub use coordinator::recommend_coordinator_action; +pub use coordinator::select_coordinator_automation_action; pub use crrc::EpiphanyCrrcAction; pub use crrc::EpiphanyCrrcInput; pub use crrc::EpiphanyCrrcRecommendation; @@ -9,11 +24,12 @@ pub use crrc::EpiphanyCrrcReorientAction; pub use crrc::EpiphanyCrrcResultStatus; pub use crrc::EpiphanyCrrcSceneAction; pub use crrc::EpiphanyCrrcStateStatus; -pub use pressure::derive_pressure_view; +pub use crrc::recommend_crrc_action; pub use pressure::EpiphanyPressure; pub use pressure::EpiphanyPressureBasis; pub use pressure::EpiphanyPressureLevel; pub use pressure::EpiphanyPressureStatus; +pub use pressure::derive_pressure_view; #[derive(Debug, Clone, PartialEq, Eq)] pub struct EpiphanyViewInput { diff --git a/notes/epiphany-current-algorithmic-map.md b/notes/epiphany-current-algorithmic-map.md index d0b42601..f95d113a 100644 --- a/notes/epiphany-current-algorithmic-map.md +++ b/notes/epiphany-current-algorithmic-map.md @@ -38,7 +38,7 @@ The spine exists in more than twenty-five live paths: - role ownership projection: the `roles` view lens reuses the same state/jobs/pressure/reorient/result/CRRC signals as the coordinator loop, and projects implementation, Imagination/planning, modeling/checkpoint, verification/review, and reorientation lanes with owner roles, status, linked jobs, authority scopes, and recommended scene actions without launching, scheduling, accepting, compacting, notifying, or mutating state. - role specialist launch/read-back/acceptance: app-server declares experimental `thread/epiphany/roleLaunch`, read-only `thread/epiphany/roleResult`, and write-gated `thread/epiphany/roleAccept`. `roleLaunch` accepts fixed Imagination/planning, modeling/checkpoint, and verification/review role ids, builds bounded specialist payloads from authoritative Epiphany state, routes them through the heartbeat-backed `epiphany_launch_job` seam, opens a typed runtime-spine job receipt, and emits the normal `jobLaunch` state update for `jobBindings` only. `roleResult` resolves heartbeat bindings through typed runtime-spine job results and seals legacy `agent_jobs` bindings, projecting structured output as a reviewable finding with verdict, summary, next safe move, inspected files, frontier/evidence ids, optional `statePatch`, optional `selfPatch`, coordinator `selfPersistence` review, raw result, and errors without mutation, promotion, scheduling, or hidden continuation. `selfPatch` is a Ghostlight-shaped lane-memory request, not project truth: the coordinator accepts only role-matched, bounded, evidence-grounded memory/goal/value/private-note mutations and refuses objectives, graphs, checkpoints, scratch, planning, jobs, authority, code edits, or raw transcripts with reasons. `roleAccept` accepts completed heartbeat findings from typed runtime-spine results, accepts completed Imagination findings only when they contain a valid planning-only patch with draft Objective Draft review gates, accepts completed modeling/checkpoint findings only with a modeling-safe `statePatch`, applies accepted project patches through the existing Epiphany state validator, appends audit observation/evidence, and carries the same `selfPersistence` review. GUI/coordinator accept paths then apply accepted `selfPatch` requests to typed CultCache role dossiers in `state/agents.msgpack` through the native `epiphany-agent-memory-store` binary. Implementation stays owned by the main coding agent, verification output remains review-only, and reorientation stays on the CRRC-specific `reorientLaunch` / `reorientResult` / `reorientAccept` path. - shared prompt memory projection and role dossiers: the prompt config now carries `[shared].persistent_memory` in [epiphany_specialists.toml](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/prompts/epiphany_specialists.toml:2). App-server parses that block, prepends it through `epiphany_agent_prompt_with_memory`, and therefore projects the same Ghostlight-derived memory into fixed role specialist instructions, reorientation-worker instructions, coordinator notes, and CRRC checkpoint steering. The base `` doctrine reinforces the Perfect Machine rule through [prompt.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/prompt.rs:31) and [epiphany_doctrine.md](E:/Projects/EpiphanyAgent/epiphany-core/src/prompts/epiphany_doctrine.md:1). Individual Ghostlight-shaped role dossiers now live in typed CultCache `state/agents.msgpack`, and the native `epiphany-agent-memory-store` binary validates, reviews, and applies bounded self-memory patches through the Rust store. -- fixed-lane coordinator: the `coordinator` view lens composes role, pressure, reorient, CRRC, role result, and reorient result signals, and returns one current action without mutation. Review acceptance and implementation clearance are intentionally different: an accepted verification finding clears implementation only when the verifier verdict is `pass`; accepted `needs-evidence`, `needs-review`, or `fail` findings route back toward modeling/checkpoint strengthening before code work resumes. `regatherManually` is now fallback, not a supervisor-puppeteering excuse: completed unaccepted modeling findings stop at `reviewModelingResult`, modeling acceptance precedes verification, and fixed lane actions are considered before manual regather. +- fixed-lane coordinator: the `coordinator` view lens composes role, pressure, reorient, CRRC, role result, and reorient result signals, and returns one current action without mutation. App-server assembles host/protocol inputs, but the pure fixed-lane decision ladder now lives in `epiphany-core` in [coordinator.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/coordinator.rs:132). Review acceptance and implementation clearance are intentionally different: an accepted verification finding clears implementation only when the verifier verdict is `pass`; accepted `needs-evidence`, `needs-review`, or `fail` findings route back toward modeling/checkpoint strengthening before code work resumes. `regatherManually` is now fallback, not a supervisor-puppeteering excuse: completed unaccepted modeling findings stop at `reviewModelingResult`, modeling acceptance precedes verification, and fixed lane actions are considered before manual regather. - MVP operator view: native `epiphany-mvp-status` launches app-server over stdio, starts or reads one thread, gathers scene, planning, pressure, reorient, jobs, roles, Imagination/modeling/verification role results, reorient result, CRRC responses, heartbeat, and Face bubble artifacts, and renders them as text or JSON so the loop can be dogfooded without Python owning the product surface. - MVP dogfood runner: the old Python runner was cut with the `agent_jobs` path. The replacement must be native Rust/CultCache/CultNet and complete heartbeat-owned runtime-spine job results with operator-safe artifacts. - Agent function/API telemetry: native `epiphany-agent-telemetry` mechanically parses sealed AppServerClient JSONL transcripts into operator-safe telemetry with method counts, request shape, job/status/path fields, and visible function/tool names while sealing text, turn items, raw results, and direct agent messages. @@ -55,7 +55,7 @@ The thick Epiphany-owned implementation is mostly outside vendored Codex now: - deterministic observation distillation lives in [epiphany-core/src/distillation.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/distillation.rs:28). - deterministic map/churn proposal from verified, evidence-backed observations lives in [epiphany-core/src/proposal.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/proposal.rs:115). - verifier-backed promotion policy lives in [epiphany-core/src/promotion.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/promotion.rs:33). -- pressure and pure CRRC recommendation policy live in [pressure.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/pressure.rs:15) and [crrc.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/crrc.rs:60). +- pressure, pure CRRC recommendation policy, and fixed-lane coordinator decision policy live in [pressure.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/pressure.rs:15), [crrc.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/crrc.rs:60), and [coordinator.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/coordinator.rs:132). ## Whole Control Flow diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 7b3e7766..4353aa9c 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -32,7 +32,7 @@ remembers doctrine; the branch remembers the blade. ## Current Orientation - Do not copy exact branch or HEAD from this note. Run `git status --short --branch` and `git log --oneline -5`. -- Top priority is still foundation cleanup, not outward feature work. Read `notes/epiphany-architectural-teardown.md` before touching Epiphany control-plane code. The May 2026 suspicion pass found that the app-server Epiphany control plane had become architectural Jenga: too much policy, projection, lifecycle glue, prompt packet assembly, coordinator sequencing, and test authority lives in `vendor/codex/codex-rs/app-server/src/codex_message_processor.rs`. The rebuild has begun: pressure policy and pure CRRC recommendation policy moved into `epiphany-core`, typed acceptance receipts replaced summary-string identity for live accept paths, runtime links are dual-written on launch, and result read-back now prefers runtime links. +- Top priority is still foundation cleanup, not outward feature work. Read `notes/epiphany-architectural-teardown.md` before touching Epiphany control-plane code. The May 2026 suspicion pass found that the app-server Epiphany control plane had become architectural Jenga: too much policy, projection, lifecycle glue, prompt packet assembly, coordinator sequencing, and test authority lives in `vendor/codex/codex-rs/app-server/src/codex_message_processor.rs`. The rebuild has begun: pressure policy, pure CRRC recommendation policy, and fixed-lane coordinator decision policy moved into `epiphany-core`, typed acceptance receipts replaced summary-string identity for live accept paths, runtime links are dual-written on launch, and result read-back now prefers runtime links. - Read `notes/epiphany-ideal-architecture-rebuild-plan.md` immediately after the teardown. It defines the smallest coherent replacement architecture: durable `EpiphanyState`, runtime-spine-owned `RuntimeState`, separate role memory/heartbeat state, `epiphany-core` as policy owner, app-server as adapter, Aquarium as reflector, typed acceptance receipts, and derived view lenses instead of a protocol verb zoo. - Do not continue Aquarium UI, bridge, Face, or dogfood expansion until the teardown has a source-grounded cleanup slice plan. Epiphany is the foundation; patches on patches are not a purification rite, they are how the altar becomes load-bearing garbage. - Phase 1 through Phase 5 are complete enough. @@ -366,15 +366,15 @@ The next real move is still foundation cleanup. Start from `notes/epiphany-architectural-teardown.md` and `notes/epiphany-ideal-architecture-rebuild-plan.md`, not from the older outward Phase 6 bridge/UI trail. Landed rebuild cuts: `epiphany-core/src/surfaces/` -exists, pressure view derivation/tests and pure CRRC recommendation policy/tests -live in core, live acceptance writes typed receipts keyed by runtime result id, +exists, pressure view derivation/tests, pure CRRC recommendation policy/tests, +and fixed-lane coordinator decision policy/tests live in core, live acceptance writes typed receipts keyed by runtime result id, summary-string acceptance identity is gone for live paths, launches dual-write runtime links, result read-back prefers those runtime links, normal public `raw_result` is sealed, MVP consumers use `thread/epiphany/view`, heartbeat cognition is quarantined, and the old scene/jobs/roles/planning/pressure/reorient/ -CRRC/coordinator read wrappers are deleted. The next cut is fixed-lane -coordinator or role-board policy extraction into `epiphany-core`. No Agile -confetti. No new balcony on the swaying tower. +CRRC/coordinator read wrappers are deleted. The next cut is role-board +projection policy or role-result interpretation extraction into `epiphany-core`. +No Agile confetti. No new balcony on the swaying tower. The Phase 6 freshness slice is landed. It exposes read-only `thread/epiphany/freshness` from live retrieval summaries plus graph diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index f8ccd6a4..96ea08e9 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index 3bd8439f..fdd5a443 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -100,7 +100,7 @@ accepted_design: - `thread/epiphany/retrieve`, `thread/epiphany/distill`, `thread/epiphany/propose`, `thread/epiphany/freshness`, `thread/epiphany/context`, `thread/epiphany/graphQuery`, and `thread/epiphany/view` stay read-only. - `thread/epiphany/index` writes the retrieval catalog, not durable Epiphany understanding; catalog is auspex output, not doctrine. - Qdrant is the preferred persistent semantic backend; BM25 remains the bootstrap/fallback/control path. - - `epiphany-core` owns prompt rendering, rollout replay helpers, retrieval/indexing, distillation, proposal, promotion policy, pressure derivation, and the pure CRRC recommendation policy where possible. + - `epiphany-core` owns prompt rendering, rollout replay helpers, retrieval/indexing, distillation, proposal, promotion policy, pressure derivation, pure CRRC recommendation policy, and fixed-lane coordinator decision policy where possible. - Vendored Codex keeps the thin typed host seam: protocol types, thread bridges, app-server protocol, request routing, and rollout integration. - The current algorithmic map is the source-grounded description of live control flow. - Compact-Rehydrate-Reorient-Continue is an architecture primitive; automatic runtime coordination is not landed yet. @@ -273,4 +273,4 @@ rejected_paths: current_status: summary: Phase 1 durable state through Phase 6 coordinator/specialist/CRRC/backend organs are landed, and the May 2026 rebuild has started cutting the app-server control-plane tumor. Pressure policy now lives in `epiphany-core`; role/reorient acceptance writes typed `acceptance_receipts`; live duplicate-acceptance checks use runtime result receipt identity instead of summary strings; launch writes durable `runtime_links`; and role/reorient result read-back follows runtime links before falling back to legacy bindings. - next_action: Continue the teardown/rebuild migration from `notes/epiphany-ideal-architecture-rebuild-plan.md`. Landed cuts now shrink `jobBindings`, remove normal public `raw_result`, add the canonical `thread/epiphany/view` lens wrapper, migrate MVP consumers, quarantine heartbeat cognition blobs, delete the old scene/jobs/roles/planning/pressure/reorient/CRRC/coordinator read wrappers, and move pure CRRC recommendation policy into `epiphany-core`. The next bounded cut is to extract the fixed-lane coordinator decision policy or role-board policy from app-server into `epiphany-core`. Do not resume outward Aquarium/bridge/Face/dogfood expansion until this foundation cleanup is materially farther along. + next_action: Continue the teardown/rebuild migration from `notes/epiphany-ideal-architecture-rebuild-plan.md`. Landed cuts now shrink `jobBindings`, remove normal public `raw_result`, add the canonical `thread/epiphany/view` lens wrapper, migrate MVP consumers, quarantine heartbeat cognition blobs, delete the old scene/jobs/roles/planning/pressure/reorient/CRRC/coordinator read wrappers, and move pure CRRC plus fixed-lane coordinator decision policy into `epiphany-core`. The next bounded cut is to extract role-board projection policy or role-result interpretation from app-server into `epiphany-core`. Do not resume outward Aquarium/bridge/Face/dogfood expansion until this foundation cleanup is materially farther along. diff --git a/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs b/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs index 66392d5a..dfbd0e13 100644 --- a/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs +++ b/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs @@ -495,6 +495,17 @@ use codex_thread_store::ThreadStoreError; use codex_thread_store::UpdateThreadMetadataParams as StoreUpdateThreadMetadataParams; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_pty::DEFAULT_OUTPUT_BYTES_CAP; +use epiphany_core::EpiphanyCoordinatorAction as CoreEpiphanyCoordinatorAction; +use epiphany_core::EpiphanyCoordinatorAutomationAction as CoreEpiphanyCoordinatorAutomationAction; +use epiphany_core::EpiphanyCoordinatorCrrcRecommendation; +use epiphany_core::EpiphanyCoordinatorDecision as CoreEpiphanyCoordinatorDecision; +use epiphany_core::EpiphanyCoordinatorInput; +use epiphany_core::EpiphanyCoordinatorRoleId as CoreEpiphanyCoordinatorRoleId; +use epiphany_core::EpiphanyCoordinatorRoleLane as CoreEpiphanyCoordinatorRoleLane; +use epiphany_core::EpiphanyCoordinatorRoleResultStatus as CoreEpiphanyCoordinatorRoleResultStatus; +use epiphany_core::EpiphanyCoordinatorRoleStatus as CoreEpiphanyCoordinatorRoleStatus; +use epiphany_core::EpiphanyCoordinatorSceneAction as CoreEpiphanyCoordinatorSceneAction; +use epiphany_core::EpiphanyCoordinatorSignals; use epiphany_core::EpiphanyCrrcAction as CoreEpiphanyCrrcAction; use epiphany_core::EpiphanyCrrcInput; use epiphany_core::EpiphanyCrrcRecommendation as CoreEpiphanyCrrcRecommendation; @@ -509,9 +520,12 @@ use epiphany_core::EpiphanyPressureStatus as CoreEpiphanyPressureStatus; use epiphany_core::EpiphanyRuntimeJobResult; use epiphany_core::EpiphanyRuntimeJobSnapshot; use epiphany_core::EpiphanyRuntimeJobStatus; +use epiphany_core::coordinator_automation_action; use epiphany_core::derive_pressure_view; +use epiphany_core::recommend_coordinator_action; use epiphany_core::recommend_crrc_action; use epiphany_core::runtime_job_snapshot; +use epiphany_core::select_coordinator_automation_action; use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; @@ -14348,38 +14362,19 @@ enum EpiphanyCoordinatorAutomationAction { fn map_epiphany_coordinator_automation_action( decision: &EpiphanyCoordinatorDecision, ) -> EpiphanyCoordinatorAutomationAction { - if !decision.can_auto_run { - return EpiphanyCoordinatorAutomationAction::None; - } - match decision.action { - ThreadEpiphanyCoordinatorAction::CompactRehydrateReorient => { - EpiphanyCoordinatorAutomationAction::CompactRehydrateReorient - } - ThreadEpiphanyCoordinatorAction::LaunchReorientWorker => { - EpiphanyCoordinatorAutomationAction::LaunchReorientWorker - } - ThreadEpiphanyCoordinatorAction::PrepareCheckpoint - | ThreadEpiphanyCoordinatorAction::WaitForReorientWorker - | ThreadEpiphanyCoordinatorAction::ReviewReorientResult - | ThreadEpiphanyCoordinatorAction::RegatherManually - | ThreadEpiphanyCoordinatorAction::LaunchModeling - | ThreadEpiphanyCoordinatorAction::ReviewModelingResult - | ThreadEpiphanyCoordinatorAction::LaunchVerification - | ThreadEpiphanyCoordinatorAction::ReviewVerificationResult - | ThreadEpiphanyCoordinatorAction::ContinueImplementation => { - EpiphanyCoordinatorAutomationAction::None - } - } + map_protocol_coordinator_automation_action(coordinator_automation_action( + &map_core_coordinator_decision_from_protocol(decision), + )) } fn select_epiphany_coordinator_automation_action( decision: &EpiphanyCoordinatorDecision, force_checkpoint_compaction: bool, ) -> EpiphanyCoordinatorAutomationAction { - if force_checkpoint_compaction { - return EpiphanyCoordinatorAutomationAction::CompactRehydrateReorient; - } - map_epiphany_coordinator_automation_action(decision) + map_protocol_coordinator_automation_action(select_coordinator_automation_action( + &map_core_coordinator_decision_from_protocol(decision), + force_checkpoint_compaction, + )) } fn map_epiphany_coordinator( @@ -14400,324 +14395,327 @@ fn map_epiphany_coordinator( verification_result_needs_evidence: bool, reorient_finding_accepted: bool, ) -> EpiphanyCoordinatorDecision { - let build = |action, - target_role, - recommended_scene_action, - requires_review, - can_auto_run, - reason: &str| EpiphanyCoordinatorDecision { - action, - target_role, - recommended_scene_action, - requires_review, - can_auto_run, - reason: reason.to_string(), - }; - - if state_status == ThreadEpiphanyReorientStateStatus::Missing || !checkpoint_present { - return build( - ThreadEpiphanyCoordinatorAction::PrepareCheckpoint, - Some(ThreadEpiphanyRoleId::Modeling), - Some(ThreadEpiphanySceneAction::Update), - false, - false, - "Authoritative state or investigation checkpoint is missing; prepare a checkpoint before coordination can continue.", - ); - } - - match recommendation.action { - ThreadEpiphanyCrrcAction::WaitForReorientWorker => { - return build( - ThreadEpiphanyCoordinatorAction::WaitForReorientWorker, - Some(ThreadEpiphanyRoleId::Reorientation), - Some(ThreadEpiphanySceneAction::ReorientResult), - false, - false, - "A CRRC reorient worker is already in flight; wait for the bounded result.", - ); - } - ThreadEpiphanyCrrcAction::AcceptReorientResult - | ThreadEpiphanyCrrcAction::ReviewReorientResult => { - return build( - ThreadEpiphanyCoordinatorAction::ReviewReorientResult, - Some(ThreadEpiphanyRoleId::Reorientation), - recommendation.recommended_scene_action, - true, - false, - "A CRRC reorientation finding needs human review before continuation.", - ); - } - ThreadEpiphanyCrrcAction::PrepareCheckpoint - | ThreadEpiphanyCrrcAction::LaunchReorientWorker - | ThreadEpiphanyCrrcAction::RegatherManually - | ThreadEpiphanyCrrcAction::Continue => {} - } - - if pressure.should_prepare_compaction - && !(reorient_finding_accepted - && recommendation.action == ThreadEpiphanyCrrcAction::Continue) - { - return build( - ThreadEpiphanyCoordinatorAction::CompactRehydrateReorient, - Some(ThreadEpiphanyRoleId::Reorientation), - Some(ThreadEpiphanySceneAction::Reorient), - false, - true, - "Context pressure crossed the preparation threshold; compact, rehydrate, and reorient before more implementation work.", - ); - } - - if recommendation.action == ThreadEpiphanyCrrcAction::LaunchReorientWorker { - return build( - ThreadEpiphanyCoordinatorAction::LaunchReorientWorker, - Some(ThreadEpiphanyRoleId::Reorientation), - Some(ThreadEpiphanySceneAction::ReorientLaunch), - false, - true, - "CRRC says continuity needs a bounded reorient worker before safe continuation.", - ); - } - - if signals.modeling_result_status == ThreadEpiphanyRoleResultStatus::Completed - && !modeling_result_accepted - { - if !modeling_result_reviewable { - return build( - ThreadEpiphanyCoordinatorAction::LaunchModeling, - Some(ThreadEpiphanyRoleId::Modeling), - Some(ThreadEpiphanySceneAction::RoleLaunch), - false, - true, - "The completed modeling/checkpoint finding is not reviewable because it has no acceptable statePatch; relaunch modeling with the typed patch contract before verification or implementation continues.", - ); - } - return build( - ThreadEpiphanyCoordinatorAction::ReviewModelingResult, - Some(ThreadEpiphanyRoleId::Modeling), - Some(ThreadEpiphanySceneAction::RoleResult), - true, - false, - "A modeling/checkpoint finding is complete and must be reviewed before verification or implementation continues.", - ); - } - - if matches!( - signals.modeling_result_status, - ThreadEpiphanyRoleResultStatus::Pending | ThreadEpiphanyRoleResultStatus::Running - ) { - return build( - ThreadEpiphanyCoordinatorAction::ReviewModelingResult, - Some(ThreadEpiphanyRoleId::Modeling), - Some(ThreadEpiphanySceneAction::RoleResult), - false, - false, - "A modeling/checkpoint specialist is already running; wait for its result before reviewing stale verification output.", - ); - } - - if signals.verification_result_status == ThreadEpiphanyRoleResultStatus::Completed - && !verification_result_covers_current_modeling - { - return build( - ThreadEpiphanyCoordinatorAction::LaunchVerification, - Some(ThreadEpiphanyRoleId::Verification), - Some(ThreadEpiphanySceneAction::RoleLaunch), - false, - true, - "The completed verification/review finding does not cover the currently accepted modeling evidence; relaunch verification before implementation continues.", - ); - } - - if signals.verification_result_status == ThreadEpiphanyRoleResultStatus::Completed - && !verification_result_accepted - { - return build( - ThreadEpiphanyCoordinatorAction::ReviewVerificationResult, - Some(ThreadEpiphanyRoleId::Verification), - Some(ThreadEpiphanySceneAction::RoleResult), - true, - false, - "A verification/review finding is complete and must be reviewed before continuation.", - ); - } - - if verification_result_accepted && implementation_evidence_after_verification { - return build( - ThreadEpiphanyCoordinatorAction::LaunchVerification, - Some(ThreadEpiphanyRoleId::Verification), - Some(ThreadEpiphanySceneAction::RoleLaunch), - false, - true, - "Implementation evidence was produced after the accepted verification/review finding; rerun verification before implementation continues.", - ); - } - - if recommendation.action == ThreadEpiphanyCrrcAction::RegatherManually - && role_status(roles, ThreadEpiphanyRoleId::Implementation) - == Some(ThreadEpiphanyRoleStatus::Blocked) - { - return build( - ThreadEpiphanyCoordinatorAction::RegatherManually, - Some(ThreadEpiphanyRoleId::Reorientation), - recommendation.recommended_scene_action, - true, - false, - "CRRC says regather is required and the implementation lane is blocked; repair continuity before another coding turn.", - ); - } - - if signals.verification_result_status == ThreadEpiphanyRoleResultStatus::Completed - && verification_result_accepted - && !verification_result_allows_implementation - && verification_result_needs_evidence - && modeling_result_accepted - && verification_result_covers_current_modeling - { - return build( - ThreadEpiphanyCoordinatorAction::ContinueImplementation, - Some(ThreadEpiphanyRoleId::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 signals.verification_result_status == ThreadEpiphanyRoleResultStatus::Completed - && verification_result_accepted - && !verification_result_allows_implementation - && verification_result_cites_implementation_evidence - && modeling_result_accepted - && verification_result_covers_current_modeling - { - return build( - ThreadEpiphanyCoordinatorAction::ContinueImplementation, - Some(ThreadEpiphanyRoleId::Implementation), - None, - false, - false, - "The accepted verification/review finding failed against concrete implementation evidence from the current model; continue only the bounded repair step before re-verification.", - ); - } - - if signals.verification_result_status == ThreadEpiphanyRoleResultStatus::Completed - && verification_result_accepted - && !verification_result_allows_implementation - && !modeling_result_accepted_after_verification - { - return build( - ThreadEpiphanyCoordinatorAction::LaunchModeling, - Some(ThreadEpiphanyRoleId::Modeling), - Some(ThreadEpiphanySceneAction::RoleLaunch), - false, - true, - "The accepted verification/review finding did not pass; strengthen modeling/checkpoint evidence before implementation continues.", - ); - } - - if signals.modeling_result_status == ThreadEpiphanyRoleResultStatus::Completed - && modeling_result_accepted - && !verification_result_allows_implementation - { - return build( - ThreadEpiphanyCoordinatorAction::LaunchVerification, - Some(ThreadEpiphanyRoleId::Verification), - Some(ThreadEpiphanySceneAction::RoleLaunch), - false, - true, - "Modeling/checkpoint guidance is available; run verification before implementation continues.", - ); - } - - if role_status(roles, ThreadEpiphanyRoleId::Modeling).is_some_and(|status| { - matches!( - status, - ThreadEpiphanyRoleStatus::Ready | ThreadEpiphanyRoleStatus::Needed - ) - }) && matches!( - signals.modeling_result_status, - ThreadEpiphanyRoleResultStatus::MissingBinding - | ThreadEpiphanyRoleResultStatus::BackendUnavailable - | ThreadEpiphanyRoleResultStatus::BackendMissing - | ThreadEpiphanyRoleResultStatus::Cancelled - | ThreadEpiphanyRoleResultStatus::Failed - ) { - return build( - ThreadEpiphanyCoordinatorAction::LaunchModeling, - Some(ThreadEpiphanyRoleId::Modeling), - Some(ThreadEpiphanySceneAction::RoleLaunch), - false, - true, - "The modeling/checkpoint lane is ready and no current modeling finding is available.", - ); - } - - if role_status(roles, ThreadEpiphanyRoleId::Verification).is_some_and(|status| { - matches!( - status, - ThreadEpiphanyRoleStatus::Ready | ThreadEpiphanyRoleStatus::Needed - ) - }) && matches!( - signals.verification_result_status, - ThreadEpiphanyRoleResultStatus::MissingBinding - | ThreadEpiphanyRoleResultStatus::BackendUnavailable - | ThreadEpiphanyRoleResultStatus::BackendMissing - | ThreadEpiphanyRoleResultStatus::Cancelled - | ThreadEpiphanyRoleResultStatus::Failed - ) { - return build( - ThreadEpiphanyCoordinatorAction::LaunchVerification, - Some(ThreadEpiphanyRoleId::Verification), - Some(ThreadEpiphanySceneAction::RoleLaunch), - false, - true, - "The verification/review lane is ready and no current verification finding is available.", - ); - } - - if matches!( - signals.verification_result_status, - ThreadEpiphanyRoleResultStatus::Pending | ThreadEpiphanyRoleResultStatus::Running - ) { - return build( - ThreadEpiphanyCoordinatorAction::ReviewVerificationResult, - Some(ThreadEpiphanyRoleId::Verification), - Some(ThreadEpiphanySceneAction::RoleResult), - false, - false, - "A verification/review specialist is already running; wait for its result.", - ); - } - - if recommendation.action == ThreadEpiphanyCrrcAction::RegatherManually { - return build( - ThreadEpiphanyCoordinatorAction::RegatherManually, - Some(ThreadEpiphanyRoleId::Reorientation), - recommendation.recommended_scene_action, - true, - false, - "CRRC cannot safely continue automatically and no fixed specialist lane is currently able to advance the regather.", - ); - } - - build( - ThreadEpiphanyCoordinatorAction::ContinueImplementation, - Some(ThreadEpiphanyRoleId::Implementation), - None, - false, - false, - "CRRC is clear and no specialist lane is currently blocking implementation.", - ) + map_core_coordinator_decision(recommend_coordinator_action(EpiphanyCoordinatorInput { + state_status: map_core_crrc_state_status(state_status), + checkpoint_present, + should_prepare_compaction: pressure.should_prepare_compaction, + recommendation: map_core_coordinator_crrc_recommendation(recommendation), + roles: roles.iter().map(map_core_coordinator_role_lane).collect(), + signals: EpiphanyCoordinatorSignals { + modeling_result_status: map_core_coordinator_role_result_status( + signals.modeling_result_status, + ), + verification_result_status: map_core_coordinator_role_result_status( + signals.verification_result_status, + ), + }, + modeling_result_accepted, + modeling_result_reviewable, + modeling_result_accepted_after_verification, + implementation_evidence_after_verification, + verification_result_cites_implementation_evidence, + verification_result_covers_current_modeling, + verification_result_accepted, + verification_result_allows_implementation, + verification_result_needs_evidence, + reorient_finding_accepted, + })) } -fn role_status( - roles: &[ThreadEpiphanyRoleLane], +fn map_core_coordinator_crrc_recommendation( + recommendation: &ThreadEpiphanyCrrcRecommendation, +) -> EpiphanyCoordinatorCrrcRecommendation { + EpiphanyCoordinatorCrrcRecommendation { + action: map_core_crrc_action_from_protocol(recommendation.action), + recommended_scene_action: recommendation + .recommended_scene_action + .map(map_core_coordinator_scene_action_from_protocol), + } +} + +fn map_core_crrc_action_from_protocol(action: ThreadEpiphanyCrrcAction) -> CoreEpiphanyCrrcAction { + match action { + ThreadEpiphanyCrrcAction::Continue => CoreEpiphanyCrrcAction::Continue, + ThreadEpiphanyCrrcAction::PrepareCheckpoint => CoreEpiphanyCrrcAction::PrepareCheckpoint, + ThreadEpiphanyCrrcAction::LaunchReorientWorker => { + CoreEpiphanyCrrcAction::LaunchReorientWorker + } + ThreadEpiphanyCrrcAction::WaitForReorientWorker => { + CoreEpiphanyCrrcAction::WaitForReorientWorker + } + ThreadEpiphanyCrrcAction::ReviewReorientResult => { + CoreEpiphanyCrrcAction::ReviewReorientResult + } + ThreadEpiphanyCrrcAction::AcceptReorientResult => { + CoreEpiphanyCrrcAction::AcceptReorientResult + } + ThreadEpiphanyCrrcAction::RegatherManually => CoreEpiphanyCrrcAction::RegatherManually, + } +} + +fn map_core_coordinator_role_lane( + role: &ThreadEpiphanyRoleLane, +) -> CoreEpiphanyCoordinatorRoleLane { + CoreEpiphanyCoordinatorRoleLane { + id: map_core_coordinator_role_id(role.id), + status: map_core_coordinator_role_status(role.status), + } +} + +fn map_core_coordinator_role_id(role_id: ThreadEpiphanyRoleId) -> CoreEpiphanyCoordinatorRoleId { + match role_id { + ThreadEpiphanyRoleId::Implementation => CoreEpiphanyCoordinatorRoleId::Implementation, + ThreadEpiphanyRoleId::Imagination => CoreEpiphanyCoordinatorRoleId::Imagination, + ThreadEpiphanyRoleId::Modeling => CoreEpiphanyCoordinatorRoleId::Modeling, + ThreadEpiphanyRoleId::Verification => CoreEpiphanyCoordinatorRoleId::Verification, + ThreadEpiphanyRoleId::Reorientation => CoreEpiphanyCoordinatorRoleId::Reorientation, + } +} + +fn map_protocol_coordinator_role_id( + role_id: CoreEpiphanyCoordinatorRoleId, +) -> ThreadEpiphanyRoleId { + match role_id { + CoreEpiphanyCoordinatorRoleId::Implementation => ThreadEpiphanyRoleId::Implementation, + CoreEpiphanyCoordinatorRoleId::Imagination => ThreadEpiphanyRoleId::Imagination, + CoreEpiphanyCoordinatorRoleId::Modeling => ThreadEpiphanyRoleId::Modeling, + CoreEpiphanyCoordinatorRoleId::Verification => ThreadEpiphanyRoleId::Verification, + CoreEpiphanyCoordinatorRoleId::Reorientation => ThreadEpiphanyRoleId::Reorientation, + } +} + +fn map_core_coordinator_role_id_from_protocol( role_id: ThreadEpiphanyRoleId, -) -> Option { - roles - .iter() - .find(|role| role.id == role_id) - .map(|role| role.status) +) -> CoreEpiphanyCoordinatorRoleId { + map_core_coordinator_role_id(role_id) +} + +fn map_core_coordinator_role_status( + status: ThreadEpiphanyRoleStatus, +) -> CoreEpiphanyCoordinatorRoleStatus { + match status { + ThreadEpiphanyRoleStatus::Ready => CoreEpiphanyCoordinatorRoleStatus::Ready, + ThreadEpiphanyRoleStatus::Needed => CoreEpiphanyCoordinatorRoleStatus::Needed, + ThreadEpiphanyRoleStatus::Running => CoreEpiphanyCoordinatorRoleStatus::Running, + ThreadEpiphanyRoleStatus::Waiting => CoreEpiphanyCoordinatorRoleStatus::Waiting, + ThreadEpiphanyRoleStatus::Review => CoreEpiphanyCoordinatorRoleStatus::Review, + ThreadEpiphanyRoleStatus::Blocked => CoreEpiphanyCoordinatorRoleStatus::Blocked, + ThreadEpiphanyRoleStatus::Unavailable => CoreEpiphanyCoordinatorRoleStatus::Unavailable, + ThreadEpiphanyRoleStatus::Completed => CoreEpiphanyCoordinatorRoleStatus::Completed, + } +} + +fn map_core_coordinator_role_result_status( + status: ThreadEpiphanyRoleResultStatus, +) -> CoreEpiphanyCoordinatorRoleResultStatus { + match status { + ThreadEpiphanyRoleResultStatus::MissingState => { + CoreEpiphanyCoordinatorRoleResultStatus::MissingState + } + ThreadEpiphanyRoleResultStatus::MissingBinding => { + CoreEpiphanyCoordinatorRoleResultStatus::MissingBinding + } + ThreadEpiphanyRoleResultStatus::BackendUnavailable => { + CoreEpiphanyCoordinatorRoleResultStatus::BackendUnavailable + } + ThreadEpiphanyRoleResultStatus::BackendMissing => { + CoreEpiphanyCoordinatorRoleResultStatus::BackendMissing + } + ThreadEpiphanyRoleResultStatus::Pending => CoreEpiphanyCoordinatorRoleResultStatus::Pending, + ThreadEpiphanyRoleResultStatus::Running => CoreEpiphanyCoordinatorRoleResultStatus::Running, + ThreadEpiphanyRoleResultStatus::Completed => { + CoreEpiphanyCoordinatorRoleResultStatus::Completed + } + ThreadEpiphanyRoleResultStatus::Failed => CoreEpiphanyCoordinatorRoleResultStatus::Failed, + ThreadEpiphanyRoleResultStatus::Cancelled => { + CoreEpiphanyCoordinatorRoleResultStatus::Cancelled + } + } +} + +fn map_core_coordinator_scene_action_from_protocol( + action: ThreadEpiphanySceneAction, +) -> CoreEpiphanyCoordinatorSceneAction { + match action { + ThreadEpiphanySceneAction::Update => CoreEpiphanyCoordinatorSceneAction::Update, + ThreadEpiphanySceneAction::Reorient => CoreEpiphanyCoordinatorSceneAction::Reorient, + ThreadEpiphanySceneAction::ReorientLaunch => { + CoreEpiphanyCoordinatorSceneAction::ReorientLaunch + } + ThreadEpiphanySceneAction::ReorientResult => { + CoreEpiphanyCoordinatorSceneAction::ReorientResult + } + ThreadEpiphanySceneAction::ReorientAccept => { + CoreEpiphanyCoordinatorSceneAction::ReorientAccept + } + ThreadEpiphanySceneAction::RoleLaunch => CoreEpiphanyCoordinatorSceneAction::RoleLaunch, + ThreadEpiphanySceneAction::RoleResult => CoreEpiphanyCoordinatorSceneAction::RoleResult, + ThreadEpiphanySceneAction::Index + | ThreadEpiphanySceneAction::Retrieve + | ThreadEpiphanySceneAction::Distill + | ThreadEpiphanySceneAction::Context + | ThreadEpiphanySceneAction::Planning + | ThreadEpiphanySceneAction::GraphQuery + | ThreadEpiphanySceneAction::Jobs + | ThreadEpiphanySceneAction::Roles + | ThreadEpiphanySceneAction::Coordinator + | ThreadEpiphanySceneAction::RoleAccept + | ThreadEpiphanySceneAction::JobLaunch + | ThreadEpiphanySceneAction::JobInterrupt + | ThreadEpiphanySceneAction::Freshness + | ThreadEpiphanySceneAction::Pressure + | ThreadEpiphanySceneAction::Crrc + | ThreadEpiphanySceneAction::Propose + | ThreadEpiphanySceneAction::Promote => { + unreachable!("unsupported CRRC coordinator scene action: {action:?}") + } + } +} + +fn map_protocol_coordinator_scene_action( + action: CoreEpiphanyCoordinatorSceneAction, +) -> ThreadEpiphanySceneAction { + match action { + CoreEpiphanyCoordinatorSceneAction::Update => ThreadEpiphanySceneAction::Update, + CoreEpiphanyCoordinatorSceneAction::Reorient => ThreadEpiphanySceneAction::Reorient, + CoreEpiphanyCoordinatorSceneAction::ReorientLaunch => { + ThreadEpiphanySceneAction::ReorientLaunch + } + CoreEpiphanyCoordinatorSceneAction::ReorientResult => { + ThreadEpiphanySceneAction::ReorientResult + } + CoreEpiphanyCoordinatorSceneAction::ReorientAccept => { + ThreadEpiphanySceneAction::ReorientAccept + } + CoreEpiphanyCoordinatorSceneAction::RoleLaunch => ThreadEpiphanySceneAction::RoleLaunch, + CoreEpiphanyCoordinatorSceneAction::RoleResult => ThreadEpiphanySceneAction::RoleResult, + } +} + +fn map_core_coordinator_action_from_protocol( + action: ThreadEpiphanyCoordinatorAction, +) -> CoreEpiphanyCoordinatorAction { + match action { + ThreadEpiphanyCoordinatorAction::PrepareCheckpoint => { + CoreEpiphanyCoordinatorAction::PrepareCheckpoint + } + ThreadEpiphanyCoordinatorAction::CompactRehydrateReorient => { + CoreEpiphanyCoordinatorAction::CompactRehydrateReorient + } + ThreadEpiphanyCoordinatorAction::LaunchReorientWorker => { + CoreEpiphanyCoordinatorAction::LaunchReorientWorker + } + ThreadEpiphanyCoordinatorAction::WaitForReorientWorker => { + CoreEpiphanyCoordinatorAction::WaitForReorientWorker + } + ThreadEpiphanyCoordinatorAction::ReviewReorientResult => { + CoreEpiphanyCoordinatorAction::ReviewReorientResult + } + ThreadEpiphanyCoordinatorAction::RegatherManually => { + CoreEpiphanyCoordinatorAction::RegatherManually + } + ThreadEpiphanyCoordinatorAction::LaunchModeling => { + CoreEpiphanyCoordinatorAction::LaunchModeling + } + ThreadEpiphanyCoordinatorAction::ReviewModelingResult => { + CoreEpiphanyCoordinatorAction::ReviewModelingResult + } + ThreadEpiphanyCoordinatorAction::LaunchVerification => { + CoreEpiphanyCoordinatorAction::LaunchVerification + } + ThreadEpiphanyCoordinatorAction::ReviewVerificationResult => { + CoreEpiphanyCoordinatorAction::ReviewVerificationResult + } + ThreadEpiphanyCoordinatorAction::ContinueImplementation => { + CoreEpiphanyCoordinatorAction::ContinueImplementation + } + } +} + +fn map_protocol_coordinator_action( + action: CoreEpiphanyCoordinatorAction, +) -> ThreadEpiphanyCoordinatorAction { + match action { + CoreEpiphanyCoordinatorAction::PrepareCheckpoint => { + ThreadEpiphanyCoordinatorAction::PrepareCheckpoint + } + CoreEpiphanyCoordinatorAction::CompactRehydrateReorient => { + ThreadEpiphanyCoordinatorAction::CompactRehydrateReorient + } + CoreEpiphanyCoordinatorAction::LaunchReorientWorker => { + ThreadEpiphanyCoordinatorAction::LaunchReorientWorker + } + CoreEpiphanyCoordinatorAction::WaitForReorientWorker => { + ThreadEpiphanyCoordinatorAction::WaitForReorientWorker + } + CoreEpiphanyCoordinatorAction::ReviewReorientResult => { + ThreadEpiphanyCoordinatorAction::ReviewReorientResult + } + CoreEpiphanyCoordinatorAction::RegatherManually => { + ThreadEpiphanyCoordinatorAction::RegatherManually + } + CoreEpiphanyCoordinatorAction::LaunchModeling => { + ThreadEpiphanyCoordinatorAction::LaunchModeling + } + CoreEpiphanyCoordinatorAction::ReviewModelingResult => { + ThreadEpiphanyCoordinatorAction::ReviewModelingResult + } + CoreEpiphanyCoordinatorAction::LaunchVerification => { + ThreadEpiphanyCoordinatorAction::LaunchVerification + } + CoreEpiphanyCoordinatorAction::ReviewVerificationResult => { + ThreadEpiphanyCoordinatorAction::ReviewVerificationResult + } + CoreEpiphanyCoordinatorAction::ContinueImplementation => { + ThreadEpiphanyCoordinatorAction::ContinueImplementation + } + } +} + +fn map_core_coordinator_decision_from_protocol( + decision: &EpiphanyCoordinatorDecision, +) -> CoreEpiphanyCoordinatorDecision { + CoreEpiphanyCoordinatorDecision { + action: map_core_coordinator_action_from_protocol(decision.action), + target_role: decision + .target_role + .map(map_core_coordinator_role_id_from_protocol), + recommended_scene_action: decision + .recommended_scene_action + .map(map_core_coordinator_scene_action_from_protocol), + requires_review: decision.requires_review, + can_auto_run: decision.can_auto_run, + reason: decision.reason.clone(), + } +} + +fn map_core_coordinator_decision( + decision: CoreEpiphanyCoordinatorDecision, +) -> EpiphanyCoordinatorDecision { + EpiphanyCoordinatorDecision { + action: map_protocol_coordinator_action(decision.action), + target_role: decision.target_role.map(map_protocol_coordinator_role_id), + recommended_scene_action: decision + .recommended_scene_action + .map(map_protocol_coordinator_scene_action), + requires_review: decision.requires_review, + can_auto_run: decision.can_auto_run, + reason: decision.reason, + } +} + +fn map_protocol_coordinator_automation_action( + action: CoreEpiphanyCoordinatorAutomationAction, +) -> EpiphanyCoordinatorAutomationAction { + match action { + CoreEpiphanyCoordinatorAutomationAction::None => EpiphanyCoordinatorAutomationAction::None, + CoreEpiphanyCoordinatorAutomationAction::CompactRehydrateReorient => { + EpiphanyCoordinatorAutomationAction::CompactRehydrateReorient + } + CoreEpiphanyCoordinatorAutomationAction::LaunchReorientWorker => { + EpiphanyCoordinatorAutomationAction::LaunchReorientWorker + } + } } fn epiphany_reorient_finding_already_accepted(