diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index 9a49ecf5..8832c851 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -235,7 +235,9 @@ pub use surfaces::derive_role_board; pub use surfaces::derive_scene; pub use surfaces::imagination_role_state_patch_policy_errors; pub use surfaces::interpret_reorient_finding; +pub use surfaces::interpret_reorient_runtime_job_result; pub use surfaces::interpret_role_finding; +pub use surfaces::interpret_role_runtime_job_result; pub use surfaces::modeling_role_state_patch_policy_errors; pub use surfaces::recommend_coordinator_action; pub use surfaces::recommend_crrc_action; diff --git a/epiphany-core/src/surfaces/mod.rs b/epiphany-core/src/surfaces/mod.rs index e46bd340..f84f491a 100644 --- a/epiphany-core/src/surfaces/mod.rs +++ b/epiphany-core/src/surfaces/mod.rs @@ -103,7 +103,9 @@ pub use role_result::build_reorient_acceptance_bundle; pub use role_result::build_role_acceptance_bundle; pub use role_result::imagination_role_state_patch_policy_errors; pub use role_result::interpret_reorient_finding; +pub use role_result::interpret_reorient_runtime_job_result; pub use role_result::interpret_role_finding; +pub use role_result::interpret_role_runtime_job_result; pub use role_result::modeling_role_state_patch_policy_errors; pub use role_result::review_role_self_patch; pub use role_result::role_self_memory_target; diff --git a/epiphany-core/src/surfaces/role_result.rs b/epiphany-core/src/surfaces/role_result.rs index 32bd61dc..bf7a559c 100644 --- a/epiphany-core/src/surfaces/role_result.rs +++ b/epiphany-core/src/surfaces/role_result.rs @@ -186,6 +186,52 @@ pub fn interpret_reorient_finding( } } +pub fn interpret_role_runtime_job_result( + _role_id: EpiphanyRoleResultRoleId, + result: &EpiphanyRuntimeJobResult, +) -> EpiphanyRoleFindingInterpretation { + EpiphanyRoleFindingInterpretation { + verdict: Some(result.verdict.clone()), + summary: Some(result.summary.clone()), + next_safe_move: empty_string_as_none(&result.next_safe_move), + checkpoint_summary: None, + scratch_summary: None, + files_inspected: Vec::new(), + frontier_node_ids: Vec::new(), + evidence_ids: result.evidence_refs.clone(), + artifact_refs: result.artifact_refs.clone(), + runtime_result_id: Some(result.result_id.clone()), + runtime_job_id: Some(result.job_id.clone()), + open_questions: Vec::new(), + evidence_gaps: Vec::new(), + risks: Vec::new(), + state_patch: None, + self_patch: None, + self_persistence: None, + job_error: None, + item_error: None, + } +} + +pub fn interpret_reorient_runtime_job_result( + result: &EpiphanyRuntimeJobResult, +) -> EpiphanyReorientFindingInterpretation { + EpiphanyReorientFindingInterpretation { + mode: Some(result.role.clone()), + summary: Some(result.summary.clone()), + next_safe_move: empty_string_as_none(&result.next_safe_move), + checkpoint_still_valid: Some(result.verdict != "fail"), + files_inspected: Vec::new(), + frontier_node_ids: Vec::new(), + evidence_ids: result.evidence_refs.clone(), + artifact_refs: result.artifact_refs.clone(), + runtime_result_id: Some(result.result_id.clone()), + runtime_job_id: Some(result.job_id.clone()), + job_error: None, + item_error: None, + } +} + pub fn build_role_acceptance_bundle( binding_id: &str, role: EpiphanyRoleAcceptanceFinding, @@ -608,6 +654,10 @@ fn merge_item_error(item_error: Option, extra_error: Option) -> } } +fn empty_string_as_none(value: &str) -> Option { + (!value.trim().is_empty()).then(|| value.to_string()) +} + fn role_finding_summary(finding: &EpiphanyRoleAcceptanceFinding) -> String { let summary = finding .summary @@ -1107,6 +1157,7 @@ mod tests { use crate::agent_memory::AgentSelfPatch; use crate::agent_memory::decode_agent_self_patch; use crate::agent_memory::review_agent_self_patch_contract; +use crate::runtime_spine::EpiphanyRuntimeJobResult; use codex_protocol::protocol::EpiphanyAcceptanceReceipt; use codex_protocol::protocol::EpiphanyCodeRef; use codex_protocol::protocol::EpiphanyEvidenceRecord; diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 80b28fd9..49565bff 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -387,14 +387,18 @@ inventory and ledger now exist: launch helpers build typed documents directly; the old app-server protocol `input_json` is now hostile ingress that must parse into the typed document before core sees it. +- Third cut landed: runtime-spine job results no longer round-trip through + `runtime_job_result_to_role_json` or `runtime_job_result_to_reorient_json`. + `epiphany-core` owns typed role/reorient runtime-result interpreters and the + app-server maps those interpretations directly to protocol projections. Continue at the protocol edge. Replace `ThreadEpiphanyJobLaunchParams.input_json` / `output_schema_json` with CultNet -typed launch-intent messages, then replace runtime job result JSON projections -(`runtime_job_result_to_role_json` and `runtime_job_result_to_reorient_json`) -with typed role/reorient finding documents. Do not resume Rider, Unity, -Aquarium, Face, dogfood, planning, app, skill, marketplace, or bridge expansion -until this organ is being cut cleanly. +typed launch-intent messages, then type role `statePatch` map/planning/graph +patch documents instead of carrying `serde_json::Value` through role finding +interpretation. Do not resume Rider, Unity, Aquarium, Face, dogfood, planning, +app, skill, marketplace, or bridge expansion until this organ is being cut +cleanly. The Phase 6 freshness slice is landed. It exposes read-only `thread/epiphany/freshness` from live retrieval summaries plus graph diff --git a/notes/json-contamination-ledger.md b/notes/json-contamination-ledger.md index 2ce679aa..959522a7 100644 --- a/notes/json-contamination-ledger.md +++ b/notes/json-contamination-ledger.md @@ -60,5 +60,6 @@ Rule: JSON is allowed at external schema/wire boundaries. Inside Epiphany, live 1. Done: purge `selfPatch` as internal JSON. Role-result interpretation now stores the typed `AgentSelfPatch` document and shares the agent-memory contract; JSON remains only at the legacy app-server projection. 2. Partly done: core worker launch `input_json` and `output_schema_json` are replaced by typed launch documents and output contract ids. Remaining cut is the protocol edge. 3. Replace role `statePatch` JSON with typed map/planning/graph patch documents. -4. Seal MCP JSON behind a CultNet MCP adapter. -5. Audit heartbeat cognition values and either type them or explicitly expire them. +4. Done: replace runtime job result JSON projections. Runtime-spine results now project through typed role/reorient interpreters instead of `runtime_job_result_to_role_json` and `runtime_job_result_to_reorient_json`. +5. Seal MCP JSON behind a CultNet MCP adapter. +6. Audit heartbeat cognition values and either type them or explicitly expire them. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 128f2a71..c2f2a706 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index 2f7c31c5..6703f6bf 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -278,5 +278,5 @@ rejected_paths: reason: making Epiphany cleaner inside Codex still preserves the wrong organ; Codex must be starved down to OpenAI subscription auth/model transport, while Epiphany data and subsystem communication move to typed CultCache/CultNet. current_status: - summary: The Codex starvation pass has two receipts. `notes/codex-auth-spine-inventory.md` maps the minimal vendored OpenAI auth/model-call spine; `notes/json-contamination-ledger.md` classifies the major Epiphany JSON uses by allowed edge, quarantine, or internal contamination. Role-result `selfPatch` now uses typed `AgentSelfPatch` internally. Core worker launch now uses `EpiphanyWorkerLaunchDocument` plus `output_contract_id` instead of `input_json` / `output_schema_json`; legacy app-server JSON is restricted to hostile ingress pending CultNet replacement. - next_action: Continue the liberation pass at the protocol edge: replace `ThreadEpiphanyJobLaunchParams.input_json` / `output_schema_json` with CultNet typed launch-intent messages, then replace runtime job result JSON projections with typed role/reorient finding documents instead of `runtime_job_result_to_role_json` and `runtime_job_result_to_reorient_json`. + summary: The Codex starvation pass has three receipts. `notes/codex-auth-spine-inventory.md` maps the minimal vendored OpenAI auth/model-call spine; `notes/json-contamination-ledger.md` classifies the major Epiphany JSON uses by allowed edge, quarantine, or internal contamination. Role-result `selfPatch` now uses typed `AgentSelfPatch` internally. Core worker launch now uses `EpiphanyWorkerLaunchDocument` plus `output_contract_id` instead of `input_json` / `output_schema_json`. Runtime job results now project through typed role/reorient interpreters instead of JSON round-tripping. Legacy app-server JSON remains only at hostile ingress/protocol quarantine pending CultNet replacement. + next_action: Continue the liberation pass at the protocol edge: replace `ThreadEpiphanyJobLaunchParams.input_json` / `output_schema_json` with CultNet typed launch-intent messages, then type role `statePatch` map/planning/graph patch documents instead of carrying `serde_json::Value` through role finding interpretation. diff --git a/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs b/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs index 42c908c5..4014ff2a 100644 --- a/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs +++ b/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs @@ -564,7 +564,6 @@ use epiphany_core::EpiphanyRoleResultRoleId; use epiphany_core::EpiphanyRoleSelfPersistenceReview as CoreEpiphanyRoleSelfPersistenceReview; use epiphany_core::EpiphanyRoleSelfPersistenceStatus as CoreEpiphanyRoleSelfPersistenceStatus; use epiphany_core::EpiphanyRoleWorkerLaunchDocument; -use epiphany_core::EpiphanyRuntimeJobResult; use epiphany_core::EpiphanyRuntimeJobSnapshot; use epiphany_core::EpiphanyRuntimeJobStatus; use epiphany_core::EpiphanyScene as CoreEpiphanyScene; @@ -594,7 +593,9 @@ use epiphany_core::derive_role_board; use epiphany_core::derive_scene; use epiphany_core::imagination_role_state_patch_policy_errors; use epiphany_core::interpret_reorient_finding; +use epiphany_core::interpret_reorient_runtime_job_result; use epiphany_core::interpret_role_finding; +use epiphany_core::interpret_role_runtime_job_result; use epiphany_core::modeling_role_state_patch_policy_errors; use epiphany_core::recommend_coordinator_action; use epiphany_core::recommend_crrc_action; @@ -13638,7 +13639,11 @@ fn load_epiphany_role_result_from_runtime_spine_job( }; let status = map_runtime_role_result_status(&snapshot); let finding = snapshot.result.as_ref().map(|result| { - map_epiphany_role_finding(role_id, runtime_job_result_to_role_json(result), None, None) + map_protocol_role_finding( + role_id, + interpret_role_runtime_job_result(map_core_role_result_role_id(role_id), result), + None, + ) }); let note = render_epiphany_role_result_note(role_id, status, finding.as_ref(), None); (status, finding, note) @@ -13684,9 +13689,10 @@ fn load_epiphany_reorient_result_from_runtime_spine_job( } }; let status = map_runtime_reorient_result_status(&snapshot); - let finding = snapshot.result.as_ref().map(|result| { - map_epiphany_reorient_finding(runtime_job_result_to_reorient_json(result), None, None) - }); + let finding = snapshot + .result + .as_ref() + .map(|result| map_protocol_reorient_finding(interpret_reorient_runtime_job_result(result))); let note = render_epiphany_reorient_result_note(status, finding.as_ref(), None); (status, finding, note) } @@ -13731,31 +13737,6 @@ fn map_runtime_reorient_result_status( } } -fn runtime_job_result_to_role_json(result: &EpiphanyRuntimeJobResult) -> serde_json::Value { - serde_json::json!({ - "verdict": result.verdict, - "summary": result.summary, - "nextSafeMove": result.next_safe_move, - "evidenceIds": result.evidence_refs, - "artifactRefs": result.artifact_refs, - "runtimeResultId": result.result_id, - "runtimeJobId": result.job_id, - }) -} - -fn runtime_job_result_to_reorient_json(result: &EpiphanyRuntimeJobResult) -> serde_json::Value { - serde_json::json!({ - "mode": result.role, - "summary": result.summary, - "nextSafeMove": result.next_safe_move, - "checkpointStillValid": result.verdict != "fail", - "evidenceIds": result.evidence_refs, - "artifactRefs": result.artifact_refs, - "runtimeResultId": result.result_id, - "runtimeJobId": result.job_id, - }) -} - async fn load_epiphany_role_result_snapshot( state: &EpiphanyThreadState, runtime_store_path: Option<&Path>,