Stop runtime result JSON round trips

This commit is contained in:
Meta Vens 2026-05-12 23:11:18 +01:00
commit 65e5f8829d
8 changed files with 80 additions and 39 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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<String>, extra_error: Option<String>) ->
}
}
fn empty_string_as_none(value: &str) -> Option<String> {
(!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;

View file

@ -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

View file

@ -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.

Binary file not shown.

View file

@ -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.

View file

@ -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>,