From 49b4b95fa90a9ef4ec0e49325ef4a744ef4fb3ea Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 9 Jun 2026 13:41:55 +0100 Subject: [PATCH] Move job launch protocol shaping into bridge --- epiphany-codex-bridge/src/protocol_edge.rs | 30 ++++++++++++++++++ notes/epiphany-current-algorithmic-map.md | 2 +- notes/fresh-workspace-handoff.md | 14 ++++---- state/ledgers.cc | Bin 185622 -> 186053 bytes state/map.yaml | 2 +- .../epiphany_mutation_routes.rs | 23 +++----------- 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/epiphany-codex-bridge/src/protocol_edge.rs b/epiphany-codex-bridge/src/protocol_edge.rs index f2269f1c..addaf6ac 100644 --- a/epiphany-codex-bridge/src/protocol_edge.rs +++ b/epiphany-codex-bridge/src/protocol_edge.rs @@ -29,6 +29,7 @@ use codex_app_server_protocol::ThreadEpiphanyJobInterruptParams; use codex_app_server_protocol::ThreadEpiphanyJobInterruptResponse; use codex_app_server_protocol::ThreadEpiphanyJobKind; use codex_app_server_protocol::ThreadEpiphanyJobLaunchParams; +use codex_app_server_protocol::ThreadEpiphanyJobLaunchResponse; use codex_app_server_protocol::ThreadEpiphanyJobStatus; use codex_app_server_protocol::ThreadEpiphanyPressure; use codex_app_server_protocol::ThreadEpiphanyPressureBasis; @@ -99,6 +100,7 @@ use epiphany_core::epiphany_view_needs_runtime_store; use epiphany_state_model::EpiphanyThreadState; use crate::mutation_service::EpiphanyJobInterruptApplied; +use crate::mutation_service::EpiphanyJobLaunchApplied; use crate::mutation_service::EpiphanyThreadPromoteApplied; use crate::mutation_service::EpiphanyThreadUpdateApplied; @@ -469,6 +471,34 @@ pub fn plan_thread_epiphany_job_launch( } } +pub struct ThreadEpiphanyJobLaunchRouteOutput { + pub response: ThreadEpiphanyJobLaunchResponse, + pub changed_fields: Vec, + pub epiphany_state: EpiphanyThreadState, +} + +pub fn thread_epiphany_job_launch_output( + applied: EpiphanyJobLaunchApplied, +) -> ThreadEpiphanyJobLaunchRouteOutput { + let changed_fields = applied.changed_fields; + let epiphany_state = applied.epiphany_state; + let response = ThreadEpiphanyJobLaunchResponse { + revision: applied.revision, + changed_fields: protocol_state_updated_fields(changed_fields.clone()), + epiphany_state: epiphany_state.clone(), + job: protocol_job_from_surface( + applied.job, + Some(applied.launcher_job_id), + Some(applied.backend_job_id), + ), + }; + ThreadEpiphanyJobLaunchRouteOutput { + response, + changed_fields, + epiphany_state, + } +} + pub struct ThreadEpiphanyUpdatePlan { pub thread_id: String, pub expected_revision: Option, diff --git a/notes/epiphany-current-algorithmic-map.md b/notes/epiphany-current-algorithmic-map.md index eb479dc4..07b698ac 100644 --- a/notes/epiphany-current-algorithmic-map.md +++ b/notes/epiphany-current-algorithmic-map.md @@ -25,7 +25,7 @@ The spine exists in more than twenty-five live paths: - scene projection: the `scene` view lens reads the same live/stored Epiphany state view used by `thread/read` and adapts the core scene projection. `epiphany-core` derives the compact client scene with objective, active subgoal, invariant status counts, graph focus/counts, retrieval status, investigation-checkpoint summary counts, observation/evidence summaries, churn, live/stored source, and available control-plane actions without mutating or persisting state in [scene.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/scene.rs:1). - job/progress reflection: the `jobs` view lens reads the same thread view as scene, optionally fills retrieval state for loaded threads with no Epiphany state, and adapts core-derived non-authoritative progress slots for retrieval indexing, graph remap, invariant verification, and bound specialist work. Durable `job_bindings` now act as thin Epiphany-owned authority slots with binding id, kind, scope, owner role, authority scope, linked ids, and optional blocking reason only. Runtime job ids project from `runtime_links` through core job view policy; old `agent_jobs` bindings are sealed instead of live-crawled in [jobs.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/jobs.rs:1). - bound-job progress notifications: app-server still declares experimental `thread/epiphany/jobsUpdated` as sealed legacy Codex evacuation telemetry, but the handler no longer emits Epiphany updates from old `agent_job_progress:{json}` background events. Heartbeat-owned activation and typed runtime-spine receipts are now the live Epiphany path. -- explicit job control: app-server declares experimental `thread/epiphany/jobLaunch` and `thread/epiphany/jobInterrupt`. `jobLaunch` route handling delegates protocol worker-launch document conversion and core `EpiphanyJobLaunchRequest` construction to `epiphany-codex-bridge::protocol_edge::plan_thread_epiphany_job_launch`; `jobInterrupt` delegates protocol param planning and legacy response projection to `plan_thread_epiphany_job_interrupt` / `thread_epiphany_job_interrupt_output`. App-server then routes the planned request through core launch/interrupt handlers that persist heartbeat-backed durable `job_bindings` revisions through the authoritative Epiphany state path. The write emits `thread/epiphany/stateUpdated` with source `jobLaunch` or `jobInterrupt` instead of creating SQLite `agent_jobs` rows or pretending jobs reflection is the scheduler. +- explicit job control: app-server declares experimental `thread/epiphany/jobLaunch` and `thread/epiphany/jobInterrupt`. `jobLaunch` route handling delegates protocol worker-launch document conversion, core `EpiphanyJobLaunchRequest` construction, and legacy launch response projection to `epiphany-codex-bridge::protocol_edge::plan_thread_epiphany_job_launch` / `thread_epiphany_job_launch_output`; `jobInterrupt` delegates protocol param planning and legacy response projection to `plan_thread_epiphany_job_interrupt` / `thread_epiphany_job_interrupt_output`. App-server then routes the planned request through core launch/interrupt handlers that persist heartbeat-backed durable `job_bindings` revisions through the authoritative Epiphany state path. The write emits `thread/epiphany/stateUpdated` with source `jobLaunch` or `jobInterrupt` instead of creating SQLite `agent_jobs` rows or pretending jobs reflection is the scheduler. - freshness reflection: app-server declares experimental read-only `thread/epiphany/freshness`, reads the same live/stored thread view used by the view lenses and context, fills live retrieval state for loaded threads, and gathers watcher snapshots because those are host lifecycle facts. `epiphany-codex-bridge::view_protocol::map_epiphany_freshness_response` owns freshness surface derivation and protocol response projection. `epiphany-core` derives the bounded retrieval/graph freshness lens plus watcher-backed invalidation inputs with watched root, changed paths, mapped graph-node hits, active-frontier hits, and revision/source identity; the read does not mutate, schedule, notify, or perform automatic semantic invalidation in [freshness.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/freshness.rs:1), [view_protocol.rs](E:/Projects/EpiphanyAgent/epiphany-codex-bridge/src/view_protocol.rs:91), [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:350), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4167), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4259), [epiphany_invalidation.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/epiphany_invalidation.rs:19), and [epiphany_read_routes.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_read_routes.rs:221). - targeted context reflection: app-server declares experimental read-only `thread/epiphany/context`, reads the same live/stored Epiphany state view used by the view lenses, and derives a bounded state shard containing selected graph nodes/edges, links, active frontier, checkpoint, the full durable investigation-checkpoint packet, observations, direct evidence, linked evidence, and missing requested ids without retrieval, proposal, promotion, notification, mutation, or persistence in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:355), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4253), [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4261), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:11152). - graph traversal reflection: app-server declares experimental read-only `thread/epiphany/graphQuery`, reads the same live Epiphany state view used by context, and derives explicit node/edge lookup, path/symbol matches, bounded neighbors, edge-kind matches, and frontier neighborhoods with architecture/dataflow links plus frontier/checkpoint identity, matched selectors, and missing ids without retrieval, proposal, promotion, indexing, notification, mutation, or scheduling in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:379), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4720), [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:5060), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:15201). diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 09f5fabc..f4d3b160 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -902,12 +902,14 @@ loads the thread, calls the mutation service, builds the legacy accept response DTO, and emits the state-updated notification. `epiphany_mutation_routes.rs` is about 744 lines after this cut. -Job-launch write-route planning has moved to the bridge protocol edge. -`plan_thread_epiphany_job_launch` owns protocol worker-launch document -conversion and core `EpiphanyJobLaunchRequest` construction. App-server still -loads the thread, invokes the mutation service, builds the legacy launch -response DTO, and emits the state-updated notification. -`epiphany_mutation_routes.rs` is about 718 lines after this cut. +Job-launch write-route planning and response shaping moved to the bridge +protocol edge. `plan_thread_epiphany_job_launch` owns protocol worker-launch +document conversion and core `EpiphanyJobLaunchRequest` construction; +`thread_epiphany_job_launch_output` owns the legacy response DTO plus +notification payload handoff from `EpiphanyJobLaunchApplied`. App-server still +loads the thread, invokes the mutation service, sends the response, and emits +the state-updated notification. `epiphany_mutation_routes.rs` is about 659 +lines after this cut. Role-launch write-route planning has moved to the bridge protocol edge. `plan_thread_epiphany_role_launch` owns protocol role conversion while diff --git a/state/ledgers.cc b/state/ledgers.cc index a698de1ccf29d6dfb4d38cf6600ff8aaab27e4e4..750baa5f5eddaf54d121f88cbca26017f07cf12a 100644 GIT binary patch delta 271 zcmbQXiu>qV?hO)*+$M%rMrKjR7?`du+N{i2$jB(&Je8?^Dih=OsZ30bm!=DxVB%#p zG_o==oUXWvNqTxs6eFi#dTL&3QD(A2R(_ICVrgD-#`L`xnOvsxUtkiQZg-A}OF^Ny zBr_*RA-_DYSfMDjur#%}L?JmpuPn8wI5R&_M`60)6(*7T9I)Q_{L+$w(h`Nd{Bnqf zkc^_##1z+p%z})>yh<;a9YLwZ1^Ic!sR{)}`B|ySB|w7|67y1k`bsj>GLsX*0tJbc oIr)hx3P7DH`DtkiX+`z>% delta 41 xcmX@QmV4SN?hO)*+{UI>2BuNR7?>`9-mJ`6$jB(wJe8?^Dih=OsZ30bmjER_4a@)l diff --git a/state/map.yaml b/state/map.yaml index 4ff57f97..3b6461f2 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -139,7 +139,7 @@ accepted_design: - `epiphany-core` and `epiphany-codex-bridge` must not depend directly on vendored `codex-core`. Host-specific Codex concepts belong in vendored app-server host adapters or the thin OpenAI subscription auth/model-transport spine, while native retrieval/state/runtime/mutation policy stays in Epiphany crates. - The prompt reads typed state through a bounded `` developer fragment. - Clients read typed state through `Thread.epiphanyState`, read-only `thread/epiphany/view` lenses for scene/jobs/roles/planning/pressure/reorient/CRRC/coordinator, freshness through `thread/epiphany/freshness`, targeted state shards through `thread/epiphany/context`, bounded graph traversal through `thread/epiphany/graphQuery`, and reviewable role output through `thread/epiphany/roleResult`. The scene, jobs, roles, planning, pressure, reorient, CRRC, and coordinator read projections now live only as view lenses, not standalone protocol verbs. - - Durable typed state writes route through `thread/epiphany/update`, accepted `thread/epiphany/promote`, `thread/epiphany/reorientAccept`, `thread/epiphany/roleAccept`, or the bounded `thread/epiphany/jobLaunch`, `thread/epiphany/jobInterrupt`, `thread/epiphany/reorientLaunch`, and `thread/epiphany/roleLaunch` authority surfaces when they mutate state or `job_bindings`; update/promote and jobInterrupt response shaping, jobLaunch request construction, plus roleLaunch/roleAccept/reorientAccept protocol/default-binding planning now live in the bridge protocol edge rather than app-server mutation routes. + - Durable typed state writes route through `thread/epiphany/update`, accepted `thread/epiphany/promote`, `thread/epiphany/reorientAccept`, `thread/epiphany/roleAccept`, or the bounded `thread/epiphany/jobLaunch`, `thread/epiphany/jobInterrupt`, `thread/epiphany/reorientLaunch`, and `thread/epiphany/roleLaunch` authority surfaces when they mutate state or `job_bindings`; update/promote, jobLaunch, and jobInterrupt response shaping, jobLaunch request construction, plus roleLaunch/roleAccept/reorientAccept protocol/default-binding planning now live in the bridge protocol edge rather than app-server mutation routes. - `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. Qdrant's HTTP API and acknowledgements remain JSON wire edges, but Epiphany-owned retrieval point payloads are typed at the adapter boundary before serialization/deserialization. diff --git a/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_mutation_routes.rs b/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_mutation_routes.rs index 685922dc..b0098683 100644 --- a/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_mutation_routes.rs +++ b/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_mutation_routes.rs @@ -31,6 +31,7 @@ use epiphany_codex_bridge::protocol_edge::protocol_role_finding; use epiphany_codex_bridge::protocol_edge::protocol_state_updated_fields; use epiphany_codex_bridge::protocol_edge::protocol_state_updated_notification; use epiphany_codex_bridge::protocol_edge::thread_epiphany_job_interrupt_output; +use epiphany_codex_bridge::protocol_edge::thread_epiphany_job_launch_output; use epiphany_codex_bridge::protocol_edge::thread_epiphany_promote_output; use epiphany_codex_bridge::protocol_edge::thread_epiphany_update_output; use epiphany_codex_bridge::retrieve::epiphany_retrieval_state_for_paths; @@ -586,30 +587,16 @@ impl CodexMessageProcessor { return; } }; - let changed_fields = applied.changed_fields; - let protocol_changed_fields = protocol_state_updated_fields(changed_fields.clone()); - let epiphany_state = applied.epiphany_state; + let output = thread_epiphany_job_launch_output(applied); self.outgoing - .send_response( - request_id, - ThreadEpiphanyJobLaunchResponse { - revision: applied.revision, - changed_fields: protocol_changed_fields, - epiphany_state: epiphany_state.clone(), - job: protocol_job_from_surface( - applied.job, - Some(applied.launcher_job_id), - Some(applied.backend_job_id), - ), - }, - ) + .send_response(request_id, output.response) .await; self.send_epiphany_state_updated( thread_uuid, ThreadEpiphanyStateUpdatedSource::JobLaunch, - changed_fields, - epiphany_state, + output.changed_fields, + output.epiphany_state, ) .await; }