diff --git a/epiphany-codex-bridge/src/coordinator.rs b/epiphany-codex-bridge/src/coordinator.rs index e1a0d3a9..86bb4f90 100644 --- a/epiphany-codex-bridge/src/coordinator.rs +++ b/epiphany-codex-bridge/src/coordinator.rs @@ -7,7 +7,6 @@ use codex_app_server_protocol::ThreadEpiphanyJobKind; use codex_app_server_protocol::ThreadEpiphanyJobStatus; use codex_app_server_protocol::ThreadEpiphanyPressure; use codex_app_server_protocol::ThreadEpiphanyReorientAction; -use codex_app_server_protocol::ThreadEpiphanyReorientDecision; use codex_app_server_protocol::ThreadEpiphanyReorientFinding; use codex_app_server_protocol::ThreadEpiphanyReorientResultStatus; use codex_app_server_protocol::ThreadEpiphanyReorientStateStatus; @@ -39,6 +38,8 @@ use epiphany_core::EpiphanyCrrcSceneAction as CoreEpiphanyCrrcSceneAction; use epiphany_core::EpiphanyCrrcStateStatus as CoreEpiphanyCrrcStateStatus; use epiphany_core::EpiphanyJobLaunchRequest; use epiphany_core::EpiphanyReorientAction as CoreEpiphanyReorientAction; +use epiphany_core::EpiphanyReorientDecision as CoreEpiphanyReorientDecision; +use epiphany_core::EpiphanyReorientStateStatus as CoreEpiphanyReorientStateStatus; use epiphany_core::EpiphanyRoleBoardCheckpointSummary; use epiphany_core::EpiphanyRoleBoardInput; use epiphany_core::EpiphanyRoleBoardJob; @@ -71,7 +72,6 @@ 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; @@ -80,54 +80,45 @@ use std::path::Path; pub fn map_epiphany_crrc_recommendation( loaded: bool, - state_status: ThreadEpiphanyReorientStateStatus, + state_status: CoreEpiphanyReorientStateStatus, pressure: &epiphany_core::EpiphanyPressure, - decision: &ThreadEpiphanyReorientDecision, - result_status: ThreadEpiphanyReorientResultStatus, + decision: &CoreEpiphanyReorientDecision, + result_status: CoreEpiphanyCrrcResultStatus, checkpoint_present: bool, finding_present: bool, finding_accepted: bool, ) -> CoreEpiphanyCrrcRecommendation { recommend_crrc_action(EpiphanyCrrcInput { loaded, - state_status: map_core_crrc_state_status(state_status), + state_status: map_core_crrc_state_status_from_reorient(state_status), should_prepare_compaction: pressure.should_prepare_compaction, reorient_action: map_core_crrc_reorient_action(decision.action), - result_status: map_core_crrc_result_status(result_status), + result_status, checkpoint_present, finding_present, finding_accepted, }) } -fn map_core_crrc_state_status( - status: ThreadEpiphanyReorientStateStatus, +fn map_core_crrc_state_status_from_reorient( + status: CoreEpiphanyReorientStateStatus, ) -> CoreEpiphanyCrrcStateStatus { match status { - ThreadEpiphanyReorientStateStatus::Missing => CoreEpiphanyCrrcStateStatus::Missing, - ThreadEpiphanyReorientStateStatus::Ready => CoreEpiphanyCrrcStateStatus::Ready, + CoreEpiphanyReorientStateStatus::Missing => CoreEpiphanyCrrcStateStatus::Missing, + CoreEpiphanyReorientStateStatus::Ready => CoreEpiphanyCrrcStateStatus::Ready, } } fn map_core_crrc_reorient_action( - action: ThreadEpiphanyReorientAction, + action: CoreEpiphanyReorientAction, ) -> CoreEpiphanyCrrcReorientAction { match action { - ThreadEpiphanyReorientAction::Resume => CoreEpiphanyCrrcReorientAction::Resume, - ThreadEpiphanyReorientAction::Regather => CoreEpiphanyCrrcReorientAction::Regather, + CoreEpiphanyReorientAction::Resume => CoreEpiphanyCrrcReorientAction::Resume, + CoreEpiphanyReorientAction::Regather => CoreEpiphanyCrrcReorientAction::Regather, } } -fn map_core_reorient_action_from_protocol( - action: ThreadEpiphanyReorientAction, -) -> CoreEpiphanyReorientAction { - match action { - ThreadEpiphanyReorientAction::Resume => CoreEpiphanyReorientAction::Resume, - ThreadEpiphanyReorientAction::Regather => CoreEpiphanyReorientAction::Regather, - } -} - -fn map_core_crrc_result_status( +pub fn map_core_crrc_result_status( status: ThreadEpiphanyReorientResultStatus, ) -> CoreEpiphanyCrrcResultStatus { match status { @@ -291,11 +282,11 @@ pub struct EpiphanyCoordinatorStatus { pub async fn derive_epiphany_coordinator_status( state: Option<&EpiphanyThreadState>, runtime_store_path: Option<&Path>, - state_status: ThreadEpiphanyReorientStateStatus, + state_status: CoreEpiphanyReorientStateStatus, pressure: &epiphany_core::EpiphanyPressure, recommendation: &CoreEpiphanyCrrcRecommendation, roles: Vec, - reorient_decision: Option<&ThreadEpiphanyReorientDecision>, + reorient_decision: Option<&CoreEpiphanyReorientDecision>, reorient_result_status: ThreadEpiphanyReorientResultStatus, reorient_finding: Option<&ThreadEpiphanyReorientFinding>, checkpoint_present: bool, @@ -376,7 +367,6 @@ pub async fn derive_epiphany_coordinator_status( should_prepare_compaction: pressure.should_prepare_compaction, reorient_action: reorient_decision .map(|decision| decision.action) - .map(map_core_reorient_action_from_protocol) .unwrap_or(CoreEpiphanyReorientAction::Resume), crrc_action: recommendation.action, modeling_result_status: map_core_coordinator_role_result_status(modeling_result_status), @@ -506,21 +496,14 @@ pub async fn select_epiphany_coordinator_automation( ); let core_pressure = derive_epiphany_pressure(input.token_usage_info); let pressure = map_epiphany_pressure(input.token_usage_info); - let (state_status, reorient_decision) = map_epiphany_reorient( + let (state_status, reorient_decision) = derive_epiphany_reorient( Some(input.state), &core_pressure, &core_freshness.retrieval, &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 { + if state_status != CoreEpiphanyReorientStateStatus::Ready { return EpiphanyCoordinatorAutomationVerdict { action: EpiphanyCoordinatorAutomationAction::None, launch_request: None, @@ -538,6 +521,7 @@ pub async fn select_epiphany_coordinator_automation( EPIPHANY_REORIENT_LAUNCH_BINDING_ID, ) .await; + let core_reorient_result_status = map_core_crrc_result_status(reorient_result_status); let reorient_finding_accepted = reorient_finding .as_ref() .is_some_and(|finding| epiphany_reorient_finding_already_accepted(input.state, finding)); @@ -546,7 +530,7 @@ pub async fn select_epiphany_coordinator_automation( state_status, &core_pressure, &reorient_decision, - reorient_result_status, + core_reorient_result_status, input.state.investigation_checkpoint.as_ref().is_some(), reorient_finding.is_some(), reorient_finding_accepted, @@ -557,7 +541,7 @@ pub async fn select_epiphany_coordinator_automation( &reorient_decision, &pressure, &crrc_recommendation, - reorient_result_status, + core_reorient_result_status, reorient_job.as_ref(), ); let coordinator = derive_epiphany_coordinator_status( @@ -590,7 +574,7 @@ pub async fn select_epiphany_coordinator_automation( None, input.state, checkpoint, - &core_reorient_decision, + &reorient_decision, ) }), EpiphanyCoordinatorAutomationAction::None @@ -613,7 +597,7 @@ pub async fn select_epiphany_coordinator_automation( } pub fn map_epiphany_coordinator( - state_status: ThreadEpiphanyReorientStateStatus, + state_status: CoreEpiphanyReorientStateStatus, checkpoint_present: bool, pressure: &epiphany_core::EpiphanyPressure, recommendation: &CoreEpiphanyCrrcRecommendation, @@ -631,7 +615,7 @@ pub fn map_epiphany_coordinator( reorient_finding_accepted: bool, ) -> EpiphanyCoordinatorDecision { recommend_coordinator_action(EpiphanyCoordinatorInput { - state_status: map_core_crrc_state_status(state_status), + state_status: map_core_crrc_state_status_from_reorient(state_status), checkpoint_present, should_prepare_compaction: pressure.should_prepare_compaction, recommendation: EpiphanyCoordinatorCrrcRecommendation { @@ -1012,10 +996,10 @@ pub struct EpiphanyRoleBoardStatus { pub fn map_epiphany_roles( state: Option<&EpiphanyThreadState>, jobs: &[ThreadEpiphanyJob], - decision: &ThreadEpiphanyReorientDecision, + decision: &CoreEpiphanyReorientDecision, pressure: &ThreadEpiphanyPressure, recommendation: &CoreEpiphanyCrrcRecommendation, - result_status: ThreadEpiphanyReorientResultStatus, + result_status: CoreEpiphanyCrrcResultStatus, reorient_job: Option<&ThreadEpiphanyJob>, ) -> EpiphanyRoleBoardStatus { let planning = state.map(|state| &state.planning); @@ -1057,7 +1041,7 @@ pub fn map_epiphany_roles( crrc_reason: recommendation.reason.clone(), reorient_decision_action: format!("{:?}", decision.action), pressure_level: format!("{:?}", pressure.level), - reorient_result_status: map_core_crrc_result_status(result_status), + reorient_result_status: result_status, reorient_job: reorient_job.map(map_core_role_board_job), imagination_binding_id: EPIPHANY_IMAGINATION_ROLE_BINDING_ID.to_string(), modeling_binding_id: EPIPHANY_MODELING_ROLE_BINDING_ID.to_string(), diff --git a/epiphany-codex-bridge/src/reorient.rs b/epiphany-codex-bridge/src/reorient.rs index fb835e94..19b5b10b 100644 --- a/epiphany-codex-bridge/src/reorient.rs +++ b/epiphany-codex-bridge/src/reorient.rs @@ -163,24 +163,6 @@ fn map_core_epiphany_invalidation_input( } } -pub fn map_epiphany_reorient( - state: Option<&EpiphanyThreadState>, - pressure: &EpiphanyPressure, - retrieval: &CoreEpiphanyRetrievalFreshness, - graph: &CoreEpiphanyGraphFreshness, - watcher: &CoreEpiphanyInvalidationInput, -) -> ( - ThreadEpiphanyReorientStateStatus, - ThreadEpiphanyReorientDecision, -) { - 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, @@ -249,15 +231,13 @@ fn map_core_reorient_watcher_status( status: CoreEpiphanyInvalidationStatus, ) -> CoreEpiphanyReorientFreshnessStatus { match status { - CoreEpiphanyInvalidationStatus::Unavailable => { - CoreEpiphanyReorientFreshnessStatus::Unknown - } + CoreEpiphanyInvalidationStatus::Unavailable => CoreEpiphanyReorientFreshnessStatus::Unknown, CoreEpiphanyInvalidationStatus::Clean => CoreEpiphanyReorientFreshnessStatus::Clean, CoreEpiphanyInvalidationStatus::Changed => CoreEpiphanyReorientFreshnessStatus::Changed, } } -fn map_protocol_reorient_state_status( +pub fn map_protocol_reorient_state_status( status: CoreEpiphanyReorientStateStatus, ) -> ThreadEpiphanyReorientStateStatus { match status { @@ -266,7 +246,7 @@ fn map_protocol_reorient_state_status( } } -fn map_protocol_reorient_decision( +pub fn map_protocol_reorient_decision( decision: epiphany_core::EpiphanyReorientDecision, ) -> ThreadEpiphanyReorientDecision { ThreadEpiphanyReorientDecision { diff --git a/epiphany-codex-bridge/src/view.rs b/epiphany-codex-bridge/src/view.rs index a9c5f092..637b5c9b 100644 --- a/epiphany-codex-bridge/src/view.rs +++ b/epiphany-codex-bridge/src/view.rs @@ -16,6 +16,7 @@ use crate::context::map_epiphany_graph_query; use crate::context::map_epiphany_planning; use crate::coordinator::derive_epiphany_coordinator_status; use crate::coordinator::epiphany_reorient_finding_already_accepted; +use crate::coordinator::map_core_crrc_result_status; use crate::coordinator::map_epiphany_coordinator_view; use crate::coordinator::map_epiphany_crrc_recommendation; use crate::coordinator::map_epiphany_roles; @@ -30,7 +31,9 @@ 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::map_epiphany_reorient; +use crate::reorient::derive_epiphany_reorient; +use crate::reorient::map_protocol_reorient_decision; +use crate::reorient::map_protocol_reorient_state_status; use crate::runtime_results::load_epiphany_reorient_result_snapshot; use crate::runtime_results::load_epiphany_role_result_snapshot; use crate::scene::map_core_epiphany_scene_action; @@ -182,11 +185,11 @@ pub async fn map_epiphany_view_response( let pressure = needs_pressure.then(|| map_epiphany_pressure(token_usage_info)); let core_freshness = needs_reorientation_inputs .then(|| derive_epiphany_freshness_view(state, retrieval_override, watcher_snapshot)); - let (state_revision, reorient_state_status, reorient_decision) = + let (state_revision, core_reorient_state_status, core_reorient_decision) = if let (Some(freshness), Some(core_pressure)) = (core_freshness.as_ref(), core_pressure.as_ref()) { - let (state_status, decision) = map_epiphany_reorient( + let (state_status, decision) = derive_epiphany_reorient( state, core_pressure, &freshness.retrieval, @@ -195,8 +198,16 @@ pub async fn map_epiphany_view_response( ); (freshness.state_revision, state_status, Some(decision)) } else { - (None, ThreadEpiphanyReorientStateStatus::Missing, None) + ( + None, + epiphany_core::EpiphanyReorientStateStatus::Missing, + None, + ) }; + let reorient_state_status = map_protocol_reorient_state_status(core_reorient_state_status); + let reorient_decision = core_reorient_decision + .clone() + .map(map_protocol_reorient_decision); let jobs = if needs_jobs { map_epiphany_jobs(state, retrieval_override) } else { @@ -230,15 +241,16 @@ pub async fn map_epiphany_view_response( let reorient_finding_accepted = reorient_finding.as_ref().is_some_and(|finding| { state.is_some_and(|state| epiphany_reorient_finding_already_accepted(state, finding)) }); + let core_reorient_result_status = map_core_crrc_result_status(reorient_result_status); let recommendation = if let (Some(core_pressure), Some(decision)) = - (core_pressure.as_ref(), reorient_decision.as_ref()) + (core_pressure.as_ref(), core_reorient_decision.as_ref()) { Some(map_epiphany_crrc_recommendation( loaded, - reorient_state_status, + core_reorient_state_status, core_pressure, decision, - reorient_result_status, + core_reorient_result_status, checkpoint_present, reorient_finding.is_some(), reorient_finding_accepted, @@ -248,7 +260,7 @@ pub async fn map_epiphany_view_response( }; let roles = if let (Some(pressure), Some(decision), Some(recommendation)) = ( pressure.as_ref(), - reorient_decision.as_ref(), + core_reorient_decision.as_ref(), recommendation.as_ref(), ) { Some(map_epiphany_roles( @@ -257,7 +269,7 @@ pub async fn map_epiphany_view_response( decision, pressure, recommendation, - reorient_result_status, + core_reorient_result_status, reorient_job.as_ref(), )) } else { @@ -273,11 +285,11 @@ pub async fn map_epiphany_view_response( let status = derive_epiphany_coordinator_status( state, runtime_store_path, - reorient_state_status, + core_reorient_state_status, core_pressure, recommendation, roles.roles.clone(), - reorient_decision.as_ref(), + core_reorient_decision.as_ref(), reorient_result_status, reorient_finding.as_ref(), checkpoint_present, diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index cac85e11..85da1c25 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -48,7 +48,7 @@ remembers doctrine; the branch remembers the blade. - `epiphany-codex-bridge` no longer depends on vendored `codex-core` at all. Retrieval takes workspace/Codex-home paths, invalidation owns a native `notify` watcher, runtime-result acceptance takes typed state plus runtime-store path, live rollout/thread reads moved into app-server `epiphany_thread_host`, and mutation policy depends on the small `EpiphanyMutationHost` trait implemented by app-server's `EpiphanyCodexThreadHost` wrapper. The bridge owns Epiphany law; vendored app-server owns CodexThread access. - `epiphany-codex-bridge` also no longer depends on `codex-protocol`. Bridge durable-state imports point at `epiphany-state-model`, token pressure takes native `EpiphanyTokenUsageSnapshot`, app-server maps Codex token telemetry at the host wall, Codex token usage rollout replay lives in `codex_message_processor/token_usage_replay.rs`, and bridge errors are `EpiphanyBridgeError`. The only remaining vendored bridge dependencies are app-server protocol DTOs and absolute path validation. - The first bridge JSON-RPC starvation cuts are landed. Freshness now returns a serializable `EpiphanyFreshnessSurface`, job launch/interrupt mutation results carry typed `EpiphanyJobView` plus separate host-edge launcher/backend ids, reorient/pre-compaction decisions consume typed `EpiphanyPressure`, reorient policy consumes typed freshness directly, and reorient launch documents/results use typed core reorient decisions/status before app-server projects them into legacy JSON-RPC DTOs. Continue by cutting the full view/coordinator board into typed surfaces, not by inventing adapters with nicer uniforms. -- Coordinator decision/status is less contaminated now: `epiphany-codex-bridge` stores coordinator decisions, source signals, CRRC recommendations, and role-board lanes as core-shaped types, core CRRC/coordinator/role-board surfaces serialize natively, and `ThreadEpiphany*` projection happens at the legacy view emission boundary. Keep cutting remaining result/reorient inputs until Aquarium/CultNet can read coordinator surfaces without JSON-RPC DTO authority. +- Coordinator/reorient decision/status is less contaminated now: `epiphany-codex-bridge` stores reorient decisions, coordinator decisions, source signals, CRRC recommendations, and role-board lanes as core-shaped types, core CRRC/coordinator/role-board surfaces serialize natively, and `ThreadEpiphany*` projection happens at the legacy view emission boundary. Keep cutting remaining result inputs until Aquarium/CultNet can read coordinator surfaces without JSON-RPC DTO authority. - MCP remains JSON because MCP is a JSON protocol. The latest processor cut only moved MCP refresh/OAuth/status/resource/tool-call route handling into `codex_message_processor/mcp_routes.rs`, reducing the root processor to about 8,977 lines; it did not claim MCP payloads are CultNet or make MCP an Epiphany-owned runtime contract. - Codex account auth route handling remains in vendored Codex but now lives in `codex_message_processor/auth_routes.rs`: API-key login, ChatGPT browser/device-code/external-token login, logout, auth status, account, rate limits, and add-credits nudge handling. This preserves Codex ownership of subscription auth while cutting the root processor to about 8,094 lines. - One-off command execution and command stream write/resize/terminate route handling now live in `codex_message_processor/command_routes.rs`; `CommandExecManager` remains the lifecycle owner, while the root processor is about 7,800 lines. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 0311dedd..27775faa 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index f94f186f..8bb1b65a 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -311,10 +311,10 @@ current_status: reorient launch documents are built from typed core decisions, reorient launch mutation results return typed decision/state-status values from the bridge, and core reorient decisions are serializable CultNet-ready state. - Coordinator decisions, coordinator source signals, CRRC recommendations, - and role-board lanes now remain core-shaped inside the bridge; app-server - still projects those typed surfaces into `ThreadEpiphany*` responses at the - legacy JSON-RPC edge. The bridge still + Reorient decisions, coordinator decisions, coordinator source signals, CRRC + recommendations, and role-board lanes now remain core-shaped inside the + bridge; app-server still projects those typed surfaces into + `ThreadEpiphany*` responses at the legacy JSON-RPC edge. The bridge still depends on vendored Codex for app-server protocol DTOs and absolute path validation, so the purification is not done. - next_action: Continue Codex starvation and CultNet liberation by cutting bridge-internal `ThreadEpiphany*` authority. Prioritize role-result/reorient-result/reorient DTO coupling next: derive typed CultNet surfaces first, push JSON-RPC projection into app-server, and keep MCP/OpenAI JSON only at their true protocol/text edges. Do not add compatibility adapters unless they protect a named ownership invariant. + next_action: Continue Codex starvation and CultNet liberation by cutting bridge-internal `ThreadEpiphany*` authority. Prioritize role-result/reorient-result DTO coupling next: derive typed CultNet surfaces first, push JSON-RPC projection into app-server, and keep MCP/OpenAI JSON only at their true protocol/text edges. Do not add compatibility adapters unless they protect a named ownership invariant.