From e9c0b2aa78f67acc7b4ef3b8deb3f1c7ec1c2df3 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 20 May 2026 02:19:06 +0100 Subject: [PATCH] Keep jobs core-shaped in bridge --- epiphany-codex-bridge/src/coordinator.rs | 96 ++++++++++++----------- epiphany-codex-bridge/src/jobs.rs | 17 ++-- epiphany-codex-bridge/src/view.rs | 14 +++- notes/fresh-workspace-handoff.md | 1 + state/ledgers.msgpack | Bin 136630 -> 136882 bytes state/map.yaml | 9 ++- 6 files changed, 79 insertions(+), 58 deletions(-) diff --git a/epiphany-codex-bridge/src/coordinator.rs b/epiphany-codex-bridge/src/coordinator.rs index 3aef0a58..abfa8fa8 100644 --- a/epiphany-codex-bridge/src/coordinator.rs +++ b/epiphany-codex-bridge/src/coordinator.rs @@ -3,8 +3,6 @@ use codex_app_server_protocol::ThreadEpiphanyCoordinatorSignals; use codex_app_server_protocol::ThreadEpiphanyCrrcAction; use codex_app_server_protocol::ThreadEpiphanyCrrcRecommendation; use codex_app_server_protocol::ThreadEpiphanyJob; -use codex_app_server_protocol::ThreadEpiphanyJobKind; -use codex_app_server_protocol::ThreadEpiphanyJobStatus; use codex_app_server_protocol::ThreadEpiphanyReorientAction; use codex_app_server_protocol::ThreadEpiphanyReorientResultStatus; use codex_app_server_protocol::ThreadEpiphanyReorientStateStatus; @@ -34,6 +32,8 @@ use epiphany_core::EpiphanyCrrcResultStatus as CoreEpiphanyCrrcResultStatus; use epiphany_core::EpiphanyCrrcSceneAction as CoreEpiphanyCrrcSceneAction; use epiphany_core::EpiphanyCrrcStateStatus as CoreEpiphanyCrrcStateStatus; use epiphany_core::EpiphanyJobLaunchRequest; +use epiphany_core::EpiphanyJobStatus as CoreEpiphanyJobStatus; +use epiphany_core::EpiphanyJobView; use epiphany_core::EpiphanyReorientAction as CoreEpiphanyReorientAction; use epiphany_core::EpiphanyReorientDecision as CoreEpiphanyReorientDecision; use epiphany_core::EpiphanyReorientFindingInterpretation as CoreEpiphanyReorientFinding; @@ -54,9 +54,11 @@ use epiphany_core::recommend_crrc_action; use epiphany_core::render_role_board_note; use epiphany_core::select_coordinator_automation_action; use epiphany_state_model::EpiphanyAcceptanceReceipt; +use epiphany_state_model::EpiphanyJobKind as CoreEpiphanyJobKind; use epiphany_state_model::EpiphanyRetrievalState; use epiphany_state_model::EpiphanyThreadState; +use crate::jobs::map_core_epiphany_job_view; use crate::launch::EPIPHANY_IMAGINATION_OWNER_ROLE; use crate::launch::EPIPHANY_IMAGINATION_ROLE_BINDING_ID; use crate::launch::EPIPHANY_MODELING_ROLE_BINDING_ID; @@ -997,17 +999,17 @@ fn reorient_finding_runtime_result_id(finding: &CoreEpiphanyReorientFinding) -> #[derive(Debug, Clone)] pub struct EpiphanyRoleBoardStatus { pub roles: Vec, - source_jobs: Vec, + source_jobs: Vec, } pub fn map_epiphany_roles( state: Option<&EpiphanyThreadState>, - jobs: &[ThreadEpiphanyJob], + jobs: &[EpiphanyJobView], decision: &CoreEpiphanyReorientDecision, pressure: &epiphany_core::EpiphanyPressure, recommendation: &CoreEpiphanyCrrcRecommendation, result_status: CoreEpiphanyCrrcResultStatus, - reorient_job: Option<&ThreadEpiphanyJob>, + reorient_job: Option<&EpiphanyJobView>, ) -> EpiphanyRoleBoardStatus { let planning = state.map(|state| &state.planning); let checkpoint = state.and_then(|state| state.investigation_checkpoint.as_ref()); @@ -1060,7 +1062,7 @@ pub fn map_epiphany_roles( EpiphanyRoleBoardStatus { roles, source_jobs } } -fn map_core_role_board_job(job: &ThreadEpiphanyJob) -> EpiphanyRoleBoardJob { +fn map_core_role_board_job(job: &EpiphanyJobView) -> EpiphanyRoleBoardJob { EpiphanyRoleBoardJob { id: job.id.clone(), owner_role: job.owner_role.clone(), @@ -1070,17 +1072,17 @@ fn map_core_role_board_job(job: &ThreadEpiphanyJob) -> EpiphanyRoleBoardJob { } } -fn map_core_role_board_job_status(status: ThreadEpiphanyJobStatus) -> EpiphanyRoleBoardJobStatus { +fn map_core_role_board_job_status(status: CoreEpiphanyJobStatus) -> EpiphanyRoleBoardJobStatus { match status { - ThreadEpiphanyJobStatus::Idle => EpiphanyRoleBoardJobStatus::Idle, - ThreadEpiphanyJobStatus::Needed => EpiphanyRoleBoardJobStatus::Needed, - ThreadEpiphanyJobStatus::Pending => EpiphanyRoleBoardJobStatus::Pending, - ThreadEpiphanyJobStatus::Running => EpiphanyRoleBoardJobStatus::Running, - ThreadEpiphanyJobStatus::Completed => EpiphanyRoleBoardJobStatus::Completed, - ThreadEpiphanyJobStatus::Failed => EpiphanyRoleBoardJobStatus::Failed, - ThreadEpiphanyJobStatus::Cancelled => EpiphanyRoleBoardJobStatus::Cancelled, - ThreadEpiphanyJobStatus::Blocked => EpiphanyRoleBoardJobStatus::Blocked, - ThreadEpiphanyJobStatus::Unavailable => EpiphanyRoleBoardJobStatus::Unavailable, + CoreEpiphanyJobStatus::Idle => EpiphanyRoleBoardJobStatus::Idle, + CoreEpiphanyJobStatus::Needed => EpiphanyRoleBoardJobStatus::Needed, + CoreEpiphanyJobStatus::Pending => EpiphanyRoleBoardJobStatus::Pending, + CoreEpiphanyJobStatus::Running => EpiphanyRoleBoardJobStatus::Running, + CoreEpiphanyJobStatus::Completed => EpiphanyRoleBoardJobStatus::Completed, + CoreEpiphanyJobStatus::Failed => EpiphanyRoleBoardJobStatus::Failed, + CoreEpiphanyJobStatus::Cancelled => EpiphanyRoleBoardJobStatus::Cancelled, + CoreEpiphanyJobStatus::Blocked => EpiphanyRoleBoardJobStatus::Blocked, + CoreEpiphanyJobStatus::Unavailable => EpiphanyRoleBoardJobStatus::Unavailable, } } @@ -1097,7 +1099,7 @@ pub fn map_protocol_role_board_lanes( fn map_protocol_role_board_lane( lane: EpiphanyRoleBoardLane, - source_jobs: &[ThreadEpiphanyJob], + source_jobs: &[EpiphanyJobView], ) -> ThreadEpiphanyRoleLane { ThreadEpiphanyRoleLane { id: map_protocol_coordinator_role_id(lane.id), @@ -1119,45 +1121,47 @@ fn map_protocol_role_board_lane( fn map_protocol_role_board_job( job: &EpiphanyRoleBoardJob, - source_jobs: &[ThreadEpiphanyJob], + source_jobs: &[EpiphanyJobView], ) -> ThreadEpiphanyJob { source_jobs .iter() .find(|source_job| source_job.id == job.id && source_job.owner_role == job.owner_role) .cloned() - .unwrap_or_else(|| ThreadEpiphanyJob { - id: job.id.clone(), - kind: ThreadEpiphanyJobKind::Specialist, - scope: job.id.clone(), - owner_role: job.owner_role.clone(), - launcher_job_id: None, - authority_scope: None, - backend_job_id: None, - status: map_protocol_role_board_job_status(job.status), - items_processed: None, - items_total: None, - progress_note: job.progress_note.clone(), - last_checkpoint_at_unix_seconds: None, - blocking_reason: job.blocking_reason.clone(), - active_thread_ids: Vec::new(), - linked_subgoal_ids: Vec::new(), - linked_graph_node_ids: Vec::new(), + .map(map_core_epiphany_job_view) + .unwrap_or_else(|| { + map_core_epiphany_job_view(EpiphanyJobView { + id: job.id.clone(), + kind: CoreEpiphanyJobKind::Specialist, + scope: job.id.clone(), + owner_role: job.owner_role.clone(), + authority_scope: None, + runtime_job_id: None, + status: map_core_epiphany_job_status_from_role_board(job.status), + items_processed: None, + items_total: None, + progress_note: job.progress_note.clone(), + last_checkpoint_at_unix_seconds: None, + blocking_reason: job.blocking_reason.clone(), + active_thread_ids: Vec::new(), + linked_subgoal_ids: Vec::new(), + linked_graph_node_ids: Vec::new(), + }) }) } -fn map_protocol_role_board_job_status( +fn map_core_epiphany_job_status_from_role_board( status: EpiphanyRoleBoardJobStatus, -) -> ThreadEpiphanyJobStatus { +) -> CoreEpiphanyJobStatus { match status { - EpiphanyRoleBoardJobStatus::Idle => ThreadEpiphanyJobStatus::Idle, - EpiphanyRoleBoardJobStatus::Needed => ThreadEpiphanyJobStatus::Needed, - EpiphanyRoleBoardJobStatus::Pending => ThreadEpiphanyJobStatus::Pending, - EpiphanyRoleBoardJobStatus::Running => ThreadEpiphanyJobStatus::Running, - EpiphanyRoleBoardJobStatus::Completed => ThreadEpiphanyJobStatus::Completed, - EpiphanyRoleBoardJobStatus::Failed => ThreadEpiphanyJobStatus::Failed, - EpiphanyRoleBoardJobStatus::Cancelled => ThreadEpiphanyJobStatus::Cancelled, - EpiphanyRoleBoardJobStatus::Blocked => ThreadEpiphanyJobStatus::Blocked, - EpiphanyRoleBoardJobStatus::Unavailable => ThreadEpiphanyJobStatus::Unavailable, + EpiphanyRoleBoardJobStatus::Idle => CoreEpiphanyJobStatus::Idle, + EpiphanyRoleBoardJobStatus::Needed => CoreEpiphanyJobStatus::Needed, + EpiphanyRoleBoardJobStatus::Pending => CoreEpiphanyJobStatus::Pending, + EpiphanyRoleBoardJobStatus::Running => CoreEpiphanyJobStatus::Running, + EpiphanyRoleBoardJobStatus::Completed => CoreEpiphanyJobStatus::Completed, + EpiphanyRoleBoardJobStatus::Failed => CoreEpiphanyJobStatus::Failed, + EpiphanyRoleBoardJobStatus::Cancelled => CoreEpiphanyJobStatus::Cancelled, + EpiphanyRoleBoardJobStatus::Blocked => CoreEpiphanyJobStatus::Blocked, + EpiphanyRoleBoardJobStatus::Unavailable => CoreEpiphanyJobStatus::Unavailable, } } diff --git a/epiphany-codex-bridge/src/jobs.rs b/epiphany-codex-bridge/src/jobs.rs index 7a1c6de0..a1700e3f 100644 --- a/epiphany-codex-bridge/src/jobs.rs +++ b/epiphany-codex-bridge/src/jobs.rs @@ -22,14 +22,21 @@ pub fn derive_epiphany_jobs( pub fn map_epiphany_jobs( state: Option<&EpiphanyThreadState>, retrieval_override: Option<&EpiphanyRetrievalState>, -) -> Vec { +) -> Vec { derive_epiphany_jobs(state, retrieval_override) - .into_iter() - .map(map_core_epiphany_job_view) - .collect() } -fn map_core_epiphany_job_view(job: EpiphanyJobView) -> ThreadEpiphanyJob { +pub fn map_protocol_epiphany_jobs( + state: Option<&EpiphanyThreadState>, + retrieval_override: Option<&EpiphanyRetrievalState>, +) -> Vec { + derive_epiphany_jobs(state, retrieval_override) + .into_iter() + .map(map_core_epiphany_job_view) + .collect() +} + +pub fn map_core_epiphany_job_view(job: EpiphanyJobView) -> ThreadEpiphanyJob { ThreadEpiphanyJob { id: job.id, kind: map_core_epiphany_job_kind(job.kind), diff --git a/epiphany-codex-bridge/src/view.rs b/epiphany-codex-bridge/src/view.rs index ce8cb7ca..cc895274 100644 --- a/epiphany-codex-bridge/src/view.rs +++ b/epiphany-codex-bridge/src/view.rs @@ -24,6 +24,7 @@ use crate::coordinator::map_protocol_role_board_lanes; use crate::coordinator::render_epiphany_roles_note; use crate::cultnet::EpiphanyFreshnessSurface; use crate::cultnet::EpiphanySurfaceSource; +use crate::jobs::map_core_epiphany_job_view; use crate::jobs::map_epiphany_jobs; use crate::launch::EPIPHANY_REORIENT_LAUNCH_BINDING_ID; use crate::pressure::derive_epiphany_pressure; @@ -322,7 +323,10 @@ pub async fn map_epiphany_view_response( .contains(&ThreadEpiphanyViewLens::Scene) .then(|| map_epiphany_scene(state, loaded, EPIPHANY_REORIENT_LAUNCH_BINDING_ID)), jobs: if lenses.contains(&ThreadEpiphanyViewLens::Jobs) { - jobs.clone() + jobs.iter() + .cloned() + .map(map_core_epiphany_job_view) + .collect() } else { Vec::new() }, @@ -427,7 +431,7 @@ pub async fn map_epiphany_view_response( recommendation: protocol_recommendation, reorient_binding_id: EPIPHANY_REORIENT_LAUNCH_BINDING_ID.to_string(), reorient_result_status, - reorient_job: reorient_job.clone(), + reorient_job: reorient_job.clone().map(map_core_epiphany_job_view), reorient_finding: reorient_finding.clone(), available_actions, note, @@ -476,7 +480,8 @@ pub async fn map_epiphany_role_result_response( let job = map_epiphany_jobs(Some(state), None) .into_iter() - .find(|job| job.id == binding_id); + .find(|job| job.id == binding_id) + .map(map_core_epiphany_job_view); let result = load_core_epiphany_role_result_snapshot(state, runtime_store_path, role_id, &binding_id) .await; @@ -531,7 +536,8 @@ pub async fn map_epiphany_reorient_result_response( let job = map_epiphany_jobs(Some(state), None) .into_iter() - .find(|job| job.id == binding_id); + .find(|job| job.id == binding_id) + .map(map_core_epiphany_job_view); let result = load_core_epiphany_reorient_result_snapshot(Some(state), runtime_store_path, &binding_id) .await; diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index bebdd671..d98da21f 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -57,6 +57,7 @@ remembers doctrine; the branch remembers the blade. - Runtime result view responses now project directly from typed result snapshots; the old protocol tuple wrappers in `runtime_results.rs` are gone, and runtime-result tests assert core typed statuses. - Role acceptance now applies core `EpiphanyStateUpdate` from typed findings; `ThreadEpiphanyUpdatePatch` survives in that path only as legacy accept-response projection. - Role-board derivation consumes core `EpiphanyPressure` directly now; `ThreadEpiphanyPressure` is only response projection. +- Job reflection is now core-shaped inside the bridge. `map_epiphany_jobs` returns typed `EpiphanyJobView`, role-board/view/result assembly consumes that typed job surface, and `ThreadEpiphanyJob` is projected only when building legacy JSON-RPC response structs. - 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 cba319cfe26db94d0c82f36a015c7255207044dd..01650bd201caa72f3a9b215afc01257996477ac4 100644 GIT binary patch delta 278 zcmYk1u}T9$6h)a-OKP#Pc_5Z6n}q~fYa8oeVK+PT+)OgOx4xMqZe?L1c7nxzgjgt| zmE;Tj06Tv~a9s#k-^020zC9){Ps#oMVE5|z;8Ll(n^xVnO1-ygFx+Xsf7CF^vuQRe zMn?~ukG<=!&r>d;7M`PtjtR^%7Ep?_6~erWEQ(v{J|bR&U|b3XFf0fswX0{Enoe=% za1D+k3Tf|~W0=kawf#qc3)>#Fz~mdaU=;9JY14G#P%{M#pvi);$FyWE7Kp(!RUK)V jRw%L8L^kaI1_mg#SwQ4|(&1~K<&!iUr`wmsI6MCZsjy@l delta 52 zcmdnAmSfv$jtR<4qBR>;3mF+Znx`|iPiJJ@KAn+?OGI>wk%5t!u7RnpkwJ)op_QSL Im5EUl0Jj+qk^lez diff --git a/state/map.yaml b/state/map.yaml index 54af920a..594e058d 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -328,7 +328,10 @@ current_status: wrappers themselves are gone. Role acceptance now produces a core `EpiphanyStateUpdate`; `ThreadEpiphanyUpdatePatch` is retained there only as legacy accept-response projection. Role-board derivation now consumes core - pressure directly instead of `ThreadEpiphanyPressure`. The bridge still - depends on vendored Codex for app-server protocol DTOs and absolute path - validation, so the purification is not done. + pressure directly instead of `ThreadEpiphanyPressure`. Job reflection now + stays core-shaped inside the bridge as `EpiphanyJobView`; role-board and + view assembly consume those typed job documents, and `ThreadEpiphanyJob` is + projected only at legacy response edges. 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 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.