Collapse bridge job protocol projection

This commit is contained in:
Meta Vens 2026-05-20 13:54:38 +01:00
commit 18d3f8df61
6 changed files with 35 additions and 81 deletions

View file

@ -56,7 +56,6 @@ 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;
@ -66,6 +65,7 @@ use crate::launch::EPIPHANY_VERIFICATION_ROLE_BINDING_ID;
use crate::launch::build_epiphany_reorient_launch_request;
use crate::launch::render_epiphany_coordinator_note;
use crate::pressure::derive_epiphany_pressure;
use crate::protocol_edge::protocol_job_from_surface;
use crate::reorient::EpiphanyFreshnessWatcherSnapshot;
use crate::reorient::derive_epiphany_freshness_view;
use crate::reorient::derive_epiphany_reorient;
@ -713,25 +713,29 @@ fn map_protocol_role_board_job(
.iter()
.find(|source_job| source_job.id == job.id && source_job.owner_role == job.owner_role)
.cloned()
.map(map_core_epiphany_job_view)
.map(|job| protocol_job_from_surface(job, None, None))
.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(),
})
protocol_job_from_surface(
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(),
},
None,
None,
)
})
}

View file

@ -1,6 +1,3 @@
use codex_app_server_protocol::ThreadEpiphanyJob;
use codex_app_server_protocol::ThreadEpiphanyJobKind;
use codex_app_server_protocol::ThreadEpiphanyJobStatus;
use epiphany_core::EpiphanyJobStatus as CoreEpiphanyJobStatus;
use epiphany_core::EpiphanyJobView;
use epiphany_core::EpiphanyJobsInput;
@ -26,46 +23,6 @@ pub fn map_epiphany_jobs(
derive_epiphany_jobs(state, retrieval_override)
}
pub fn map_protocol_epiphany_jobs(
state: Option<&EpiphanyThreadState>,
retrieval_override: Option<&EpiphanyRetrievalState>,
) -> Vec<ThreadEpiphanyJob> {
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),
scope: job.scope,
owner_role: job.owner_role,
launcher_job_id: None,
authority_scope: job.authority_scope,
backend_job_id: job.runtime_job_id,
status: map_core_epiphany_job_status(job.status),
items_processed: job.items_processed,
items_total: job.items_total,
progress_note: job.progress_note,
last_checkpoint_at_unix_seconds: job.last_checkpoint_at_unix_seconds,
blocking_reason: job.blocking_reason,
active_thread_ids: job.active_thread_ids,
linked_subgoal_ids: job.linked_subgoal_ids,
linked_graph_node_ids: job.linked_graph_node_ids,
}
}
pub fn map_core_epiphany_job_kind(kind: CoreEpiphanyJobKind) -> ThreadEpiphanyJobKind {
match kind {
CoreEpiphanyJobKind::Indexing => ThreadEpiphanyJobKind::Indexing,
CoreEpiphanyJobKind::Remap => ThreadEpiphanyJobKind::Remap,
CoreEpiphanyJobKind::Verification => ThreadEpiphanyJobKind::Verification,
CoreEpiphanyJobKind::Specialist => ThreadEpiphanyJobKind::Specialist,
}
}
pub fn map_launched_epiphany_job(
state: &EpiphanyThreadState,
binding_id: &str,
@ -112,20 +69,6 @@ pub fn map_interrupted_epiphany_job(
})
}
fn map_core_epiphany_job_status(status: CoreEpiphanyJobStatus) -> ThreadEpiphanyJobStatus {
match status {
CoreEpiphanyJobStatus::Idle => ThreadEpiphanyJobStatus::Idle,
CoreEpiphanyJobStatus::Needed => ThreadEpiphanyJobStatus::Needed,
CoreEpiphanyJobStatus::Pending => ThreadEpiphanyJobStatus::Pending,
CoreEpiphanyJobStatus::Running => ThreadEpiphanyJobStatus::Running,
CoreEpiphanyJobStatus::Completed => ThreadEpiphanyJobStatus::Completed,
CoreEpiphanyJobStatus::Failed => ThreadEpiphanyJobStatus::Failed,
CoreEpiphanyJobStatus::Cancelled => ThreadEpiphanyJobStatus::Cancelled,
CoreEpiphanyJobStatus::Blocked => ThreadEpiphanyJobStatus::Blocked,
CoreEpiphanyJobStatus::Unavailable => ThreadEpiphanyJobStatus::Unavailable,
}
}
pub fn epiphany_blocked_state_job(
id: &str,
kind: CoreEpiphanyJobKind,

View file

@ -31,11 +31,11 @@ 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;
use crate::pressure::map_epiphany_pressure;
use crate::protocol_edge::protocol_job_from_surface;
use crate::reorient::EpiphanyFreshnessWatcherSnapshot;
use crate::reorient::derive_epiphany_freshness_view;
use crate::reorient::derive_epiphany_reorient;
@ -307,7 +307,7 @@ pub async fn map_epiphany_view_response(
jobs: if lenses.contains(&EpiphanyViewLens::Jobs) {
jobs.iter()
.cloned()
.map(map_core_epiphany_job_view)
.map(|job| protocol_job_from_surface(job, None, None))
.collect()
} else {
Vec::new()
@ -413,7 +413,9 @@ 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().map(map_core_epiphany_job_view),
reorient_job: reorient_job
.clone()
.map(|job| protocol_job_from_surface(job, None, None)),
reorient_finding: reorient_finding.clone(),
available_actions,
note,
@ -468,7 +470,7 @@ pub async fn map_epiphany_role_result_response(
let job = map_epiphany_jobs(Some(state), None)
.into_iter()
.find(|job| job.id == binding_id)
.map(map_core_epiphany_job_view);
.map(|job| protocol_job_from_surface(job, None, None));
let result =
load_core_epiphany_role_result_snapshot(state, runtime_store_path, role_id, &binding_id)
.await;
@ -525,7 +527,7 @@ pub async fn map_epiphany_reorient_result_response(
let job = map_epiphany_jobs(Some(state), None)
.into_iter()
.find(|job| job.id == binding_id)
.map(map_core_epiphany_job_view);
.map(|job| protocol_job_from_surface(job, None, None));
let result =
load_core_epiphany_reorient_result_snapshot(Some(state), runtime_store_path, &binding_id)
.await;

View file

@ -80,6 +80,7 @@ remembers doctrine; the branch remembers the blade.
- Correction: app-server must not own Epiphany request mapping. Codex JSON-RPC request DTO conversion now lives in `epiphany-codex-bridge::protocol_edge`; vendored app-server calls those edge adapters and no longer depends directly on `epiphany-core`.
- Context, graph-query, distill, generic job-launch document, and generic update/promote patch request cargo now enters bridge as core `EpiphanyContextParams`, `EpiphanyGraphQuery`, `EpiphanyDistillInput`, `EpiphanyWorkerLaunchDocument`, and `EpiphanyRoleStatePatchDocument`.
- Freshness/job/reorient response projection also belongs to the bridge edge now. Vendored app-server no longer keeps local helpers for `EpiphanyFreshnessSurface`, `EpiphanyJobView`, `EpiphanySurfaceSource`, or reorient decision/status enum projection; it gathers host facts, invokes bridge/core services, and sends the Codex JSON-RPC response.
- Job projection has one mapper now. `epiphany-codex-bridge::jobs` derives typed `EpiphanyJobView` and no longer imports app-server protocol DTOs; `protocol_edge::protocol_job_from_surface` is the only `EpiphanyJobView` -> `ThreadEpiphanyJob` conversion path.
- 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.

Binary file not shown.

View file

@ -398,6 +398,10 @@ current_status:
invokes Epiphany bridge services, and sends Codex JSON-RPC responses, while
the bridge owns the enum/DTO conversion from typed Epiphany surfaces into
`ThreadEpiphany*` edge payloads.
The duplicate job projection authority in `epiphany-codex-bridge::jobs`
has been collapsed too: `jobs.rs` derives typed `EpiphanyJobView` only,
and `protocol_edge::protocol_job_from_surface` is the single
`EpiphanyJobView` -> `ThreadEpiphanyJob` mapper.
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 response/projection authority. Prioritize the next coordinator/view seam that still composes policy-shaped facts instead of merely loading host facts and projecting core surfaces; keep MCP/OpenAI JSON only at their true protocol/text edges. Do not add compatibility adapters unless they protect a named ownership invariant.