diff --git a/epiphany-codex-bridge/src/mutation.rs b/epiphany-codex-bridge/src/mutation.rs index a84df5ea..1081f1a0 100644 --- a/epiphany-codex-bridge/src/mutation.rs +++ b/epiphany-codex-bridge/src/mutation.rs @@ -90,13 +90,6 @@ pub fn protocol_patch_from_core( } } -pub fn state_update_from_thread_patch( - expected_revision: Option, - patch: ThreadEpiphanyUpdatePatch, -) -> EpiphanyStateUpdate { - state_update_from_core_patch(expected_revision, core_state_patch_from_protocol(&patch)) -} - pub fn state_update_from_core_patch( expected_revision: Option, patch: EpiphanyRoleStatePatchDocument, @@ -180,14 +173,6 @@ pub fn map_protocol_state_updated_field( } } -pub fn imagination_role_accept_patch_errors(patch: &ThreadEpiphanyUpdatePatch) -> Vec { - imagination_role_state_patch_policy_errors(&core_state_patch_from_protocol(patch)) -} - -pub fn modeling_role_accept_patch_errors(patch: &ThreadEpiphanyUpdatePatch) -> Vec { - modeling_role_state_patch_policy_errors(&core_state_patch_from_protocol(patch)) -} - pub fn build_role_acceptance_update( expected_revision: Option, role_id: EpiphanyRoleResultRoleId, @@ -229,8 +214,7 @@ pub fn build_role_acceptance_update( } }; - let patch_for_projection = protocol_patch_from_core(core_patch.clone()); - let projected_fields = epiphany_update_patch_changed_fields(&patch_for_projection) + let projected_fields = epiphany_update_patch_changed_fields(&core_patch) .into_iter() .map(|field| format!("{field:?}")) .collect(); @@ -259,8 +243,7 @@ pub fn build_role_acceptance_update( .acceptance_receipts .push(acceptance_bundle.receipt); let applied_patch = core_patch.clone(); - let changed_fields = - epiphany_update_patch_changed_fields(&protocol_patch_from_core(applied_patch.clone())); + let changed_fields = epiphany_update_patch_changed_fields(&applied_patch); let state_update = state_update_from_core_patch(expected_revision, core_patch); Ok(RoleAcceptanceUpdate { @@ -366,7 +349,7 @@ pub fn thread_epiphany_patch_has_state_replacements( } pub fn epiphany_update_patch_changed_fields( - patch: &ThreadEpiphanyUpdatePatch, + patch: &EpiphanyRoleStatePatchDocument, ) -> Vec { let mut fields = Vec::new(); if patch.objective.is_some() { @@ -426,7 +409,7 @@ pub fn epiphany_update_patch_changed_fields( pub fn epiphany_promote_changed_fields( patch: &EpiphanyRoleStatePatchDocument, ) -> Vec { - let mut fields = epiphany_update_patch_changed_fields(&protocol_patch_from_core(patch.clone())); + let mut fields = epiphany_update_patch_changed_fields(patch); if !fields.contains(&EpiphanyStateUpdatedField::Evidence) { fields.push(EpiphanyStateUpdatedField::Evidence); } diff --git a/epiphany-codex-bridge/src/mutation_service.rs b/epiphany-codex-bridge/src/mutation_service.rs index 3b97a9f9..b5d429c6 100644 --- a/epiphany-codex-bridge/src/mutation_service.rs +++ b/epiphany-codex-bridge/src/mutation_service.rs @@ -313,9 +313,7 @@ pub async fn apply_thread_epiphany_update( expected_revision: Option, patch: EpiphanyRoleStatePatchDocument, ) -> BridgeResult { - let changed_fields = epiphany_update_patch_changed_fields( - &crate::mutation::protocol_patch_from_core(patch.clone()), - ); + let changed_fields = epiphany_update_patch_changed_fields(&patch); let update = crate::mutation::state_update_from_core_patch(expected_revision, patch); let epiphany_state = apply_epiphany_state_update_to_thread(thread, update).await?; let epiphany_state = thread.client_visible_epiphany_state(epiphany_state).await; diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 8f2a50fb..2992a4b7 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -70,6 +70,7 @@ remembers doctrine; the branch remembers the blade. - Role-result response assembly now takes core `EpiphanyRoleResultRoleId`; `ThreadEpiphanyRoleId` is reconstructed only for the legacy response payload and finding projection. - Distill response assembly now consumes core `EpiphanyDistillInput`; `ThreadEpiphanyDistillParams` is converted before bridge policy invocation. - Role/reorient result response inputs now use core `EpiphanySurfaceSource`; `ThreadEpiphanyRolesSource` / `ThreadEpiphanyReorientSource` are response projection only. +- Changed-field derivation now inspects typed `EpiphanyRoleStatePatchDocument` directly. The bridge no longer converts typed patches back into `ThreadEpiphanyUpdatePatch` merely to calculate changed fields. - 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 c559f691..19748e15 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index de8155df..f2b2cfe7 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -361,7 +361,9 @@ current_status: consumes core `EpiphanyDistillInput`; the JSON-RPC distill params are converted before policy invocation. Role/reorient result response inputs now use core `EpiphanySurfaceSource`; role-specific legacy source enums are - created only during response projection. + created only during response projection. Changed-field derivation now + inspects typed `EpiphanyRoleStatePatchDocument` directly instead of + converting patches back through `ThreadEpiphanyUpdatePatch` projection. 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.