From bc57becdba5562a1d2d21365bbd5b40e231200e0 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 19 May 2026 17:05:26 +0100 Subject: [PATCH] Build reorient launches from typed decisions --- epiphany-codex-bridge/src/coordinator.rs | 10 +- epiphany-codex-bridge/src/launch.rs | 108 ++++++++---------- epiphany-codex-bridge/src/mutation_service.rs | 10 +- epiphany-codex-bridge/src/reorient.rs | 27 ++++- 4 files changed, 88 insertions(+), 67 deletions(-) diff --git a/epiphany-codex-bridge/src/coordinator.rs b/epiphany-codex-bridge/src/coordinator.rs index 03bb0c9e..cf21ae90 100644 --- a/epiphany-codex-bridge/src/coordinator.rs +++ b/epiphany-codex-bridge/src/coordinator.rs @@ -69,6 +69,7 @@ use crate::pressure::derive_epiphany_pressure; use crate::pressure::map_epiphany_pressure; use crate::reorient::EpiphanyFreshnessWatcherSnapshot; use crate::reorient::derive_epiphany_freshness_view; +use crate::reorient::derive_epiphany_reorient; use crate::reorient::map_epiphany_reorient; use crate::runtime_results::load_epiphany_reorient_result_snapshot; use crate::runtime_results::load_epiphany_role_result_snapshot; @@ -418,6 +419,13 @@ pub async fn select_epiphany_coordinator_automation( &core_freshness.graph, &core_freshness.watcher, ); + let (_core_state_status, core_reorient_decision) = derive_epiphany_reorient( + Some(input.state), + &core_pressure, + &core_freshness.retrieval, + &core_freshness.graph, + &core_freshness.watcher, + ); if state_status != ThreadEpiphanyReorientStateStatus::Ready { return EpiphanyCoordinatorAutomationVerdict { action: EpiphanyCoordinatorAutomationAction::None, @@ -488,7 +496,7 @@ pub async fn select_epiphany_coordinator_automation( None, input.state, checkpoint, - &reorient_decision, + &core_reorient_decision, ) }), EpiphanyCoordinatorAutomationAction::None diff --git a/epiphany-codex-bridge/src/launch.rs b/epiphany-codex-bridge/src/launch.rs index ac6a8f10..c42f7a34 100644 --- a/epiphany-codex-bridge/src/launch.rs +++ b/epiphany-codex-bridge/src/launch.rs @@ -1,18 +1,17 @@ use std::path::Path; use std::sync::OnceLock; -use codex_app_server_protocol::ThreadEpiphanyGraphFreshnessStatus; -use codex_app_server_protocol::ThreadEpiphanyInvalidationStatus; use codex_app_server_protocol::ThreadEpiphanyPressureLevel; -use codex_app_server_protocol::ThreadEpiphanyReorientAction; -use codex_app_server_protocol::ThreadEpiphanyReorientDecision; -use codex_app_server_protocol::ThreadEpiphanyReorientReason; use codex_app_server_protocol::ThreadEpiphanyReorientWorkerLaunchDocument; -use codex_app_server_protocol::ThreadEpiphanyRetrievalFreshnessStatus; use codex_app_server_protocol::ThreadEpiphanyRoleId; use codex_app_server_protocol::ThreadEpiphanyRoleWorkerLaunchDocument; use codex_app_server_protocol::ThreadEpiphanyWorkerLaunchDocument; use epiphany_core::EpiphanyJobLaunchRequest; +use epiphany_core::EpiphanyReorientAction as CoreEpiphanyReorientAction; +use epiphany_core::EpiphanyReorientDecision as CoreEpiphanyReorientDecision; +use epiphany_core::EpiphanyReorientFreshnessStatus as CoreEpiphanyReorientFreshnessStatus; +use epiphany_core::EpiphanyReorientPressureLevel as CoreEpiphanyReorientPressureLevel; +use epiphany_core::EpiphanyReorientReason as CoreEpiphanyReorientReason; use epiphany_core::EpiphanyReorientWorkerLaunchDocument; use epiphany_core::EpiphanyRoleWorkerLaunchDocument; use epiphany_core::EpiphanyWorkerLaunchDocument; @@ -480,7 +479,7 @@ pub fn build_epiphany_reorient_launch_request( max_runtime_seconds: Option, state: &EpiphanyThreadState, checkpoint: &EpiphanyInvestigationCheckpoint, - decision: &ThreadEpiphanyReorientDecision, + decision: &CoreEpiphanyReorientDecision, ) -> EpiphanyJobLaunchRequest { let linked_subgoal_ids = epiphany_active_subgoal_ids(Some(state)); let linked_graph_node_ids = unique_strings( @@ -493,12 +492,12 @@ pub fn build_epiphany_reorient_launch_request( .clone() .unwrap_or_else(|| decision.next_action.clone()); let authority_scope = match decision.action { - ThreadEpiphanyReorientAction::Resume => "epiphany.reorient.resume", - ThreadEpiphanyReorientAction::Regather => "epiphany.reorient.regather", + CoreEpiphanyReorientAction::Resume => "epiphany.reorient.resume", + CoreEpiphanyReorientAction::Regather => "epiphany.reorient.regather", }; let scope = match decision.action { - ThreadEpiphanyReorientAction::Resume => "reorient-guided checkpoint resume", - ThreadEpiphanyReorientAction::Regather => "reorient-guided checkpoint regather", + CoreEpiphanyReorientAction::Resume => "reorient-guided checkpoint resume", + CoreEpiphanyReorientAction::Regather => "reorient-guided checkpoint regather", }; let instruction = build_epiphany_reorient_launch_instruction(decision.action); let launch_document = @@ -521,11 +520,10 @@ pub fn build_epiphany_reorient_launch_request( .map(|reason| reorient_reason_label(*reason).to_string()) .collect(), decision_note: decision.note.clone(), - pressure_level: pressure_level_label(decision.pressure_level).to_string(), - retrieval_status: retrieval_freshness_status_label(decision.retrieval_status) - .to_string(), - graph_status: graph_freshness_status_label(decision.graph_status).to_string(), - watcher_status: invalidation_status_label(decision.watcher_status).to_string(), + pressure_level: reorient_pressure_level_label(decision.pressure_level).to_string(), + retrieval_status: reorient_freshness_status_label(decision.retrieval_status).to_string(), + graph_status: reorient_freshness_status_label(decision.graph_status).to_string(), + watcher_status: reorient_freshness_status_label(decision.watcher_status).to_string(), checkpoint_dirty_paths: decision .checkpoint_dirty_paths .iter() @@ -566,11 +564,11 @@ pub fn build_epiphany_reorient_launch_request( } } -pub fn build_epiphany_reorient_launch_instruction(action: ThreadEpiphanyReorientAction) -> String { +pub fn build_epiphany_reorient_launch_instruction(action: CoreEpiphanyReorientAction) -> String { let prompts = &epiphany_specialist_prompt_config().reorientation; let body = match action { - ThreadEpiphanyReorientAction::Resume => prompts.resume.as_str(), - ThreadEpiphanyReorientAction::Regather => prompts.regather.as_str(), + CoreEpiphanyReorientAction::Resume => prompts.resume.as_str(), + CoreEpiphanyReorientAction::Regather => prompts.regather.as_str(), }; epiphany_agent_prompt_with_memory(body) } @@ -741,10 +739,10 @@ fn path_to_display_string(path: impl AsRef) -> String { path.as_ref().to_string_lossy().to_string() } -fn reorient_action_label(action: ThreadEpiphanyReorientAction) -> &'static str { +fn reorient_action_label(action: CoreEpiphanyReorientAction) -> &'static str { match action { - ThreadEpiphanyReorientAction::Resume => "resume", - ThreadEpiphanyReorientAction::Regather => "regather", + CoreEpiphanyReorientAction::Resume => "resume", + CoreEpiphanyReorientAction::Regather => "regather", } } @@ -755,18 +753,38 @@ fn investigation_disposition_label(disposition: EpiphanyInvestigationDisposition } } -fn reorient_reason_label(reason: ThreadEpiphanyReorientReason) -> &'static str { +fn reorient_reason_label(reason: CoreEpiphanyReorientReason) -> &'static str { match reason { - ThreadEpiphanyReorientReason::MissingState => "missingState", - ThreadEpiphanyReorientReason::MissingCheckpoint => "missingCheckpoint", - ThreadEpiphanyReorientReason::CheckpointRequestedRegather => "checkpointRequestedRegather", - ThreadEpiphanyReorientReason::CheckpointPathsDirty => "checkpointPathsDirty", - ThreadEpiphanyReorientReason::CheckpointPathsChanged => "checkpointPathsChanged", - ThreadEpiphanyReorientReason::FrontierChanged => "frontierChanged", - ThreadEpiphanyReorientReason::UnanchoredCheckpointWhileStateStale => { + CoreEpiphanyReorientReason::MissingState => "missingState", + CoreEpiphanyReorientReason::MissingCheckpoint => "missingCheckpoint", + CoreEpiphanyReorientReason::CheckpointRequestedRegather => "checkpointRequestedRegather", + CoreEpiphanyReorientReason::CheckpointPathsDirty => "checkpointPathsDirty", + CoreEpiphanyReorientReason::CheckpointPathsChanged => "checkpointPathsChanged", + CoreEpiphanyReorientReason::FrontierChanged => "frontierChanged", + CoreEpiphanyReorientReason::UnanchoredCheckpointWhileStateStale => { "unanchoredCheckpointWhileStateStale" } - ThreadEpiphanyReorientReason::CheckpointReady => "checkpointReady", + CoreEpiphanyReorientReason::CheckpointReady => "checkpointReady", + } +} + +fn reorient_pressure_level_label(level: CoreEpiphanyReorientPressureLevel) -> &'static str { + match level { + CoreEpiphanyReorientPressureLevel::Unknown => "unknown", + CoreEpiphanyReorientPressureLevel::Low => "low", + CoreEpiphanyReorientPressureLevel::Medium => "medium", + CoreEpiphanyReorientPressureLevel::High => "high", + CoreEpiphanyReorientPressureLevel::Critical => "critical", + } +} + +fn reorient_freshness_status_label(status: CoreEpiphanyReorientFreshnessStatus) -> &'static str { + match status { + CoreEpiphanyReorientFreshnessStatus::Unknown => "unknown", + CoreEpiphanyReorientFreshnessStatus::Clean => "clean", + CoreEpiphanyReorientFreshnessStatus::Dirty => "dirty", + CoreEpiphanyReorientFreshnessStatus::Stale => "stale", + CoreEpiphanyReorientFreshnessStatus::Changed => "changed", } } @@ -779,31 +797,3 @@ pub fn pressure_level_label(level: ThreadEpiphanyPressureLevel) -> &'static str ThreadEpiphanyPressureLevel::Critical => "critical", } } - -fn retrieval_freshness_status_label( - status: ThreadEpiphanyRetrievalFreshnessStatus, -) -> &'static str { - match status { - ThreadEpiphanyRetrievalFreshnessStatus::Missing => "missing", - ThreadEpiphanyRetrievalFreshnessStatus::Ready => "ready", - ThreadEpiphanyRetrievalFreshnessStatus::Stale => "stale", - ThreadEpiphanyRetrievalFreshnessStatus::Indexing => "indexing", - ThreadEpiphanyRetrievalFreshnessStatus::Unavailable => "unavailable", - } -} - -fn graph_freshness_status_label(status: ThreadEpiphanyGraphFreshnessStatus) -> &'static str { - match status { - ThreadEpiphanyGraphFreshnessStatus::Missing => "missing", - ThreadEpiphanyGraphFreshnessStatus::Ready => "ready", - ThreadEpiphanyGraphFreshnessStatus::Stale => "stale", - } -} - -fn invalidation_status_label(status: ThreadEpiphanyInvalidationStatus) -> &'static str { - match status { - ThreadEpiphanyInvalidationStatus::Unavailable => "unavailable", - ThreadEpiphanyInvalidationStatus::Clean => "clean", - ThreadEpiphanyInvalidationStatus::Changed => "changed", - } -} diff --git a/epiphany-codex-bridge/src/mutation_service.rs b/epiphany-codex-bridge/src/mutation_service.rs index b98f8656..80f33a40 100644 --- a/epiphany-codex-bridge/src/mutation_service.rs +++ b/epiphany-codex-bridge/src/mutation_service.rs @@ -53,6 +53,7 @@ use crate::mutation::thread_epiphany_patch_has_state_replacements; use crate::pressure::derive_epiphany_pressure; use crate::reorient::EpiphanyFreshnessWatcherSnapshot; use crate::reorient::derive_epiphany_freshness_view; +use crate::reorient::derive_epiphany_reorient; use crate::reorient::map_epiphany_reorient; use crate::runtime_results::load_completed_epiphany_reorient_finding; use crate::runtime_results::load_completed_epiphany_role_finding; @@ -590,6 +591,13 @@ pub async fn launch_thread_epiphany_reorient( &freshness.graph, &freshness.watcher, ); + let (_core_state_status, launch_decision) = derive_epiphany_reorient( + state, + &pressure, + &freshness.retrieval, + &freshness.graph, + &freshness.watcher, + ); let state = state.ok_or_else(|| { EpiphanyBridgeError::InvalidRequest(format!( @@ -610,7 +618,7 @@ pub async fn launch_thread_epiphany_reorient( max_runtime_seconds, state, checkpoint, - &decision, + &launch_decision, ); let changed_fields = epiphany_job_launch_changed_fields(); let launched = launch_epiphany_job_on_thread(thread, launch_request).await?; diff --git a/epiphany-codex-bridge/src/reorient.rs b/epiphany-codex-bridge/src/reorient.rs index 1c2cd2ba..fb835e94 100644 --- a/epiphany-codex-bridge/src/reorient.rs +++ b/epiphany-codex-bridge/src/reorient.rs @@ -21,6 +21,7 @@ use epiphany_core::EpiphanyPressure; use epiphany_core::EpiphanyPressureLevel; use epiphany_core::EpiphanyReorientAction as CoreEpiphanyReorientAction; use epiphany_core::EpiphanyReorientCheckpointStatus as CoreEpiphanyReorientCheckpointStatus; +use epiphany_core::EpiphanyReorientDecision as CoreEpiphanyReorientDecision; use epiphany_core::EpiphanyReorientFreshnessStatus as CoreEpiphanyReorientFreshnessStatus; use epiphany_core::EpiphanyReorientInput; use epiphany_core::EpiphanyReorientPressureLevel as CoreEpiphanyReorientPressureLevel; @@ -172,7 +173,25 @@ pub fn map_epiphany_reorient( ThreadEpiphanyReorientStateStatus, ThreadEpiphanyReorientDecision, ) { - let (state_status, decision) = recommend_reorientation(EpiphanyReorientInput { + let (state_status, decision) = + derive_epiphany_reorient(state, pressure, retrieval, graph, watcher); + ( + map_protocol_reorient_state_status(state_status), + map_protocol_reorient_decision(decision), + ) +} + +pub fn derive_epiphany_reorient( + state: Option<&EpiphanyThreadState>, + pressure: &EpiphanyPressure, + retrieval: &CoreEpiphanyRetrievalFreshness, + graph: &CoreEpiphanyGraphFreshness, + watcher: &CoreEpiphanyInvalidationInput, +) -> ( + CoreEpiphanyReorientStateStatus, + CoreEpiphanyReorientDecision, +) { + recommend_reorientation(EpiphanyReorientInput { checkpoint: state.and_then(|state| state.investigation_checkpoint.as_ref()), state_present: state.is_some(), pressure_level: map_core_reorient_pressure_level(pressure.level), @@ -185,11 +204,7 @@ pub fn map_epiphany_reorient( watcher_graph_node_ids: watcher.graph_node_ids.clone(), active_frontier_node_ids: watcher.active_frontier_node_ids.clone(), watched_root: watcher.watched_root.clone(), - }); - ( - map_protocol_reorient_state_status(state_status), - map_protocol_reorient_decision(decision), - ) + }) } fn map_core_reorient_pressure_level(