Seal immutable frontier planning results
This commit is contained in:
parent
6cbe47313c
commit
05300c8add
12 changed files with 645 additions and 14 deletions
|
|
@ -412,6 +412,61 @@ pub fn epiphany_role_launch_output_schema(role_id: EpiphanyRoleResultRoleId) ->
|
|||
schema
|
||||
}
|
||||
|
||||
pub fn epiphany_frontier_planning_output_schema() -> serde_json::Value {
|
||||
let mut schema = epiphany_role_launch_output_schema(EpiphanyRoleResultRoleId::Imagination);
|
||||
let properties = schema["properties"]
|
||||
.as_object_mut()
|
||||
.expect("role output schema properties");
|
||||
properties.remove("statePatch");
|
||||
properties.remove("selfPatch");
|
||||
properties.insert(
|
||||
"frontierPlanningRequestId".to_string(),
|
||||
serde_json::json!({
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Exact echo of the coordinator-bound repo frontier planning request."
|
||||
}),
|
||||
);
|
||||
properties.insert(
|
||||
"frontierPlanCandidate".to_string(),
|
||||
serde_json::json!({
|
||||
"type": "object",
|
||||
"required": [
|
||||
"planning_request_id", "model_revision", "model_hash",
|
||||
"frontier_item_id", "frontier_item_hash", "safe_paths", "action", "command",
|
||||
"checks", "stop_conditions", "rollback_steps", "commit_message", "proposed_at"
|
||||
],
|
||||
"properties": {
|
||||
"planning_request_id": {"type": "string", "minLength": 1},
|
||||
"model_revision": {"type": "integer", "minimum": 0},
|
||||
"model_hash": {"type": "string", "minLength": 1},
|
||||
"frontier_item_id": {"type": "string", "minLength": 1},
|
||||
"frontier_item_hash": {"type": "string", "minLength": 1},
|
||||
"safe_paths": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
||||
"action": {"type": "string", "minLength": 1},
|
||||
"command": {"type": "string", "minLength": 1},
|
||||
"checks": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
||||
"stop_conditions": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
||||
"rollback_steps": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
|
||||
"commit_message": {"type": "string", "minLength": 1},
|
||||
"proposed_at": {"type": "string", "minLength": 1}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}),
|
||||
);
|
||||
schema["required"] = serde_json::json!([
|
||||
"roleId",
|
||||
"verdict",
|
||||
"summary",
|
||||
"nextSafeMove",
|
||||
"filesInspected",
|
||||
"frontierPlanningRequestId",
|
||||
"frontierPlanCandidate"
|
||||
]);
|
||||
schema["additionalProperties"] = serde_json::Value::Bool(false);
|
||||
schema
|
||||
}
|
||||
|
||||
pub fn epiphany_reorient_launch_output_schema() -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "object",
|
||||
|
|
@ -956,6 +1011,21 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frontier_planning_schema_exposes_candidate_without_generic_patch_mouths() {
|
||||
let schema = epiphany_frontier_planning_output_schema();
|
||||
assert!(
|
||||
schema["properties"]
|
||||
.get("frontierPlanningRequestId")
|
||||
.is_some()
|
||||
);
|
||||
assert!(schema["properties"].get("frontierPlanCandidate").is_some());
|
||||
assert!(schema["properties"].get("statePatch").is_none());
|
||||
assert!(schema["properties"].get("selfPatch").is_none());
|
||||
assert!(schema["properties"].get("repoModelPatch").is_none());
|
||||
assert_eq!(schema["additionalProperties"], false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reorient_worker_prompt_is_bounded_not_full_persistent_memory() {
|
||||
let prompt = build_epiphany_reorient_launch_instruction(CoreEpiphanyReorientAction::Resume);
|
||||
|
|
|
|||
|
|
@ -838,6 +838,62 @@ pub(crate) mod tests {
|
|||
Ok((store, state, launch, planning))
|
||||
}
|
||||
|
||||
fn frontier_planning_result(
|
||||
planning: &RepoFrontierPlanningRequest,
|
||||
job_id: &str,
|
||||
proposed_at: &str,
|
||||
) -> Result<EpiphanyRuntimeRoleWorkerResult> {
|
||||
let mut candidate = RepoFrontierPlanCandidate {
|
||||
schema_version: REPO_FRONTIER_PLAN_CANDIDATE_SCHEMA_VERSION.into(),
|
||||
candidate_id: String::new(),
|
||||
planning_request_id: planning.request_id.clone(),
|
||||
model_revision: planning.model_revision,
|
||||
model_hash: planning.model_hash.clone(),
|
||||
frontier_item_id: planning.frontier_item_id.clone(),
|
||||
frontier_item_hash: planning.frontier_item_hash.clone(),
|
||||
safe_paths: vec![planning.source_scope[0].clone()],
|
||||
action: "Implement the bounded frontier plan.".into(),
|
||||
command: "cargo test --lib".into(),
|
||||
checks: vec!["Focused tests pass.".into()],
|
||||
stop_conditions: vec!["Authority scope changes.".into()],
|
||||
rollback_steps: vec!["Revert the bounded commit.".into()],
|
||||
commit_message: "Implement bounded frontier plan".into(),
|
||||
proposed_at: proposed_at.into(),
|
||||
contract: REPO_FRONTIER_PLANNING_CONTRACT.into(),
|
||||
};
|
||||
candidate.candidate_id = canonical_repo_frontier_plan_candidate_id(&candidate)?;
|
||||
Ok(EpiphanyRuntimeRoleWorkerResult {
|
||||
schema_version: RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.into(),
|
||||
result_id: format!("frontier-planning-result-{job_id}"),
|
||||
job_id: job_id.into(),
|
||||
role_id: "imagination".into(),
|
||||
verdict: "draft-ready".into(),
|
||||
summary: "Produced one bounded candidate.".into(),
|
||||
next_safe_move: "Mind admission.".into(),
|
||||
checkpoint_summary: None,
|
||||
scratch_summary: None,
|
||||
files_inspected: planning.source_scope.clone(),
|
||||
frontier_node_ids: vec![planning.frontier_item_id.clone()],
|
||||
evidence_ids: vec![planning.admission_receipt_id.clone()],
|
||||
artifact_refs: Vec::new(),
|
||||
open_questions: Vec::new(),
|
||||
evidence_gaps: Vec::new(),
|
||||
risks: Vec::new(),
|
||||
state_patch_msgpack: None,
|
||||
self_patch_msgpack: None,
|
||||
item_error: None,
|
||||
metadata: std::collections::BTreeMap::new(),
|
||||
repo_model_patch_msgpack: None,
|
||||
verification_request_id: None,
|
||||
frontier_route_id: None,
|
||||
repo_frontier_modeling_request_id: None,
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: Some(planning.request_id.clone()),
|
||||
frontier_plan_candidate_msgpack: Some(rmp_serde::to_vec_named(&candidate)?),
|
||||
})
|
||||
}
|
||||
|
||||
fn research_launch(state: &EpiphanyThreadState) -> EpiphanyJobLaunchRequest {
|
||||
build_epiphany_role_launch_request(
|
||||
"thread-1",
|
||||
|
|
@ -1321,6 +1377,107 @@ pub(crate) mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frontier_planning_result_is_exact_immutable_and_patch_sealed() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
let (store, state, launch, planning) =
|
||||
frontier_planning_launch_fixture(root.path(), "result-exact")?;
|
||||
let plan = plan_coordinator_job_launch(
|
||||
&state,
|
||||
&launch,
|
||||
&store,
|
||||
"launcher-result".into(),
|
||||
"backend-result".into(),
|
||||
)?;
|
||||
commit_coordinator_job_launch(
|
||||
&store,
|
||||
&planning.thread_id,
|
||||
&state,
|
||||
&launch,
|
||||
&plan,
|
||||
"2026-07-15T09:00:03Z".into(),
|
||||
)?;
|
||||
let result = frontier_planning_result(&planning, "backend-result", "2026-07-15T09:00:04Z")?;
|
||||
put_runtime_role_worker_result(&store, &result)?;
|
||||
put_runtime_role_worker_result(&store, &result)?;
|
||||
let stored = runtime_role_worker_result(&store, "backend-result")?
|
||||
.expect("immutable planning result");
|
||||
assert_eq!(stored, result);
|
||||
let candidate = stored
|
||||
.frontier_plan_candidate()?
|
||||
.expect("typed candidate payload");
|
||||
assert_eq!(candidate.planning_request_id, planning.request_id);
|
||||
assert_eq!(
|
||||
candidate.candidate_id,
|
||||
canonical_repo_frontier_plan_candidate_id(&candidate)?
|
||||
);
|
||||
assert!(
|
||||
coordinator_acceptance_cache(&store)?
|
||||
.get_all::<RepoFrontierPlanCandidate>()?
|
||||
.is_empty(),
|
||||
"candidate must remain inside immutable result until Mind admission"
|
||||
);
|
||||
let mut counterfeit = result;
|
||||
counterfeit.summary = "different result bytes".into();
|
||||
let before = std::fs::read(&store)?;
|
||||
assert!(put_runtime_role_worker_result(&store, &counterfeit).is_err());
|
||||
assert_eq!(std::fs::read(&store)?, before);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frontier_planning_result_refuses_hostile_substitution_before_persistence() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
for mutation in 0..9 {
|
||||
let (store, state, launch, planning) =
|
||||
frontier_planning_launch_fixture(root.path(), &format!("result-{mutation}"))?;
|
||||
let job_id = format!("backend-result-{mutation}");
|
||||
let plan = plan_coordinator_job_launch(
|
||||
&state,
|
||||
&launch,
|
||||
&store,
|
||||
format!("launcher-result-{mutation}"),
|
||||
job_id.clone(),
|
||||
)?;
|
||||
commit_coordinator_job_launch(
|
||||
&store,
|
||||
&planning.thread_id,
|
||||
&state,
|
||||
&launch,
|
||||
&plan,
|
||||
"2026-07-15T09:00:03Z".into(),
|
||||
)?;
|
||||
let mut result = frontier_planning_result(&planning, &job_id, "2026-07-15T09:00:04Z")?;
|
||||
match mutation {
|
||||
0 => result.frontier_planning_request_id = Some("swapped-request".into()),
|
||||
1 => result.frontier_planning_request_id = None,
|
||||
2 => result.frontier_plan_candidate_msgpack = None,
|
||||
3 => result.role_id = "modeling".into(),
|
||||
4 => result.state_patch_msgpack = Some(vec![0]),
|
||||
5 => result.self_patch_msgpack = Some(vec![0]),
|
||||
6 => result.repo_model_patch_msgpack = Some(vec![0]),
|
||||
_ => {
|
||||
let mut candidate = result.frontier_plan_candidate()?.unwrap();
|
||||
if mutation == 7 {
|
||||
candidate.frontier_item_hash = "adjacent-frontier".into();
|
||||
} else {
|
||||
candidate.safe_paths = vec!["outside/authority".into()];
|
||||
}
|
||||
result.frontier_plan_candidate_msgpack =
|
||||
Some(rmp_serde::to_vec_named(&candidate)?);
|
||||
}
|
||||
}
|
||||
let before = std::fs::read(&store)?;
|
||||
assert!(
|
||||
put_runtime_role_worker_result(&store, &result).is_err(),
|
||||
"hostile planning result mutation {mutation} must fail"
|
||||
);
|
||||
assert_eq!(std::fs::read(&store)?, before);
|
||||
assert!(runtime_role_worker_result(&store, &job_id)?.is_none());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concurrent_frontier_planning_launches_leave_only_winner_artifacts() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
|
|
|
|||
|
|
@ -938,6 +938,8 @@ mod tests {
|
|||
repo_frontier_modeling_request_id: None,
|
||||
proposal_modeling_request_id: Some(selection.request_id),
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
};
|
||||
crate::put_runtime_role_worker_result(store, &result)?;
|
||||
crate::commit_repo_model_admission(
|
||||
|
|
@ -1419,6 +1421,8 @@ mod tests {
|
|||
repo_frontier_modeling_request_id: None,
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
};
|
||||
crate::put_runtime_role_worker_result(&runtime_store, &verification_result)?;
|
||||
crate::put_soul_verdict_receipt(
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ pub use agent_launch::build_epiphany_reorient_launch_request_with_dynamic_contex
|
|||
pub use agent_launch::build_epiphany_role_launch_request;
|
||||
pub use agent_launch::build_epiphany_role_launch_request_with_dynamic_context;
|
||||
pub use agent_launch::epiphany_agent_prompt_with_memory;
|
||||
pub use agent_launch::epiphany_frontier_planning_output_schema;
|
||||
pub use agent_launch::epiphany_reorient_launch_output_schema;
|
||||
pub use agent_launch::epiphany_role_binding_id;
|
||||
pub use agent_launch::epiphany_role_label;
|
||||
|
|
@ -776,6 +777,7 @@ pub use runtime_spine::RuntimeSpineJobOptions;
|
|||
pub use runtime_spine::RuntimeSpineJobResultOptions;
|
||||
pub use runtime_spine::RuntimeSpineSessionOptions;
|
||||
pub use runtime_spine::append_runtime_event;
|
||||
pub use runtime_spine::canonical_repo_frontier_plan_candidate_id;
|
||||
pub use runtime_spine::clear_epiphany_job_binding_backend;
|
||||
pub use runtime_spine::commit_repo_frontier_modeling_request;
|
||||
pub use runtime_spine::commit_repo_frontier_verification_request_for_chain;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ use crate::repo_model_gateway::{
|
|||
REPO_FRONTIER_HANDS_AUTHORITY_CONTRACT, REPO_FRONTIER_HANDS_AUTHORITY_SCHEMA_VERSION,
|
||||
REPO_FRONTIER_MODELING_REQUEST_CONTRACT, REPO_FRONTIER_MODELING_REQUEST_SCHEMA_VERSION,
|
||||
REPO_FRONTIER_PLAN_ADOPTION_SCHEMA_VERSION, REPO_FRONTIER_PLAN_CANDIDATE_SCHEMA_VERSION,
|
||||
REPO_FRONTIER_PLANNING_CONTRACT, REPO_FRONTIER_PLANNING_REQUEST_SCHEMA_VERSION,
|
||||
REPO_FRONTIER_PLANNING_CONTRACT, REPO_FRONTIER_PLANNING_LAUNCH_BINDING_CONTRACT,
|
||||
REPO_FRONTIER_PLANNING_LAUNCH_BINDING_SCHEMA_VERSION,
|
||||
REPO_FRONTIER_PLANNING_REQUEST_SCHEMA_VERSION,
|
||||
REPO_FRONTIER_PROPOSAL_MODELING_LAUNCH_BINDING_CONTRACT,
|
||||
REPO_FRONTIER_PROPOSAL_MODELING_LAUNCH_BINDING_SCHEMA_VERSION,
|
||||
REPO_FRONTIER_PROPOSAL_MODELING_REQUEST_CONTRACT,
|
||||
|
|
@ -174,7 +176,7 @@ pub const RUNTIME_SPINE_SCHEMA_VERSION: &str = "epiphany.runtime_spine.v0";
|
|||
pub const RUNTIME_WORKER_LAUNCH_REQUEST_SCHEMA_VERSION: &str =
|
||||
"epiphany.runtime.worker_launch_request.v1";
|
||||
pub const RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION: &str =
|
||||
"epiphany.runtime.role_worker_result.v1";
|
||||
"epiphany.runtime.role_worker_result.v2";
|
||||
pub const RUNTIME_REORIENT_WORKER_RESULT_SCHEMA_VERSION: &str =
|
||||
"epiphany.runtime.reorient_worker_result.v0";
|
||||
pub const COORDINATOR_RUN_RECEIPT_SCHEMA_VERSION: &str = "epiphany.coordinator_run_receipt.v0";
|
||||
|
|
@ -410,6 +412,10 @@ pub struct EpiphanyRuntimeRoleWorkerResult {
|
|||
pub proposal_modeling_request_id: Option<String>,
|
||||
#[cultcache(key = 25, default)]
|
||||
pub claim_repair_request_id: Option<String>,
|
||||
#[cultcache(key = 26, default)]
|
||||
pub frontier_planning_request_id: Option<String>,
|
||||
#[cultcache(key = 27, default)]
|
||||
pub frontier_plan_candidate_msgpack: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl EpiphanyRuntimeRoleWorkerResult {
|
||||
|
|
@ -430,6 +436,13 @@ impl EpiphanyRuntimeRoleWorkerResult {
|
|||
"role worker repoModelPatch",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn frontier_plan_candidate(&self) -> Result<Option<RepoFrontierPlanCandidate>> {
|
||||
decode_optional_msgpack(
|
||||
self.frontier_plan_candidate_msgpack.as_deref(),
|
||||
"role worker frontierPlanCandidate",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DatabaseEntry)]
|
||||
|
|
@ -1457,6 +1470,11 @@ pub fn put_runtime_role_worker_result(
|
|||
validate_non_empty(&result.job_id, "role worker result job id")?;
|
||||
validate_non_empty(&result.result_id, "role worker result id")?;
|
||||
validate_non_empty(&result.role_id, "role worker result role id")?;
|
||||
if result.schema_version != RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION {
|
||||
return Err(anyhow!("role worker result schema version mismatch"));
|
||||
}
|
||||
let mut cache = runtime_spine_cache(store_path)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
let is_verification = result.role_id == "verification";
|
||||
if is_verification
|
||||
!= (result
|
||||
|
|
@ -1553,8 +1571,94 @@ pub fn put_runtime_role_worker_result(
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut cache = runtime_spine_cache(store_path)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
let has_planning_echo = result.frontier_planning_request_id.is_some();
|
||||
let has_planning_candidate = result.frontier_plan_candidate_msgpack.is_some();
|
||||
if has_planning_echo != has_planning_candidate {
|
||||
return Err(anyhow!(
|
||||
"frontier planning result requires both its request echo and typed candidate"
|
||||
));
|
||||
}
|
||||
if has_planning_echo {
|
||||
if !result.role_id.eq_ignore_ascii_case("imagination") {
|
||||
return Err(anyhow!(
|
||||
"only Imagination results may echo a frontier planning request"
|
||||
));
|
||||
}
|
||||
if result.state_patch_msgpack.is_some()
|
||||
|| result.self_patch_msgpack.is_some()
|
||||
|| result.repo_model_patch_msgpack.is_some()
|
||||
|| result.verification_request_id.is_some()
|
||||
|| result.frontier_route_id.is_some()
|
||||
|| result.repo_frontier_modeling_request_id.is_some()
|
||||
|| result.proposal_modeling_request_id.is_some()
|
||||
|| result.claim_repair_request_id.is_some()
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"frontier planning result may carry only its request echo and typed candidate authority"
|
||||
));
|
||||
}
|
||||
let request_id = result
|
||||
.frontier_planning_request_id
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow!("frontier planning request echo disappeared"))?;
|
||||
let request = cache
|
||||
.get::<RepoFrontierPlanningRequest>(request_id)?
|
||||
.ok_or_else(|| anyhow!("frontier planning result requires persisted request"))?;
|
||||
validate_current_repo_frontier_planning_request(&cache, &request)?;
|
||||
let candidate = result
|
||||
.frontier_plan_candidate()?
|
||||
.ok_or_else(|| anyhow!("frontier planning candidate disappeared"))?;
|
||||
validate_repo_frontier_plan_candidate_against_request(&cache, &candidate, &request)?;
|
||||
let bindings = cache
|
||||
.get_all::<RepoFrontierPlanningLaunchBinding>()?
|
||||
.into_iter()
|
||||
.filter(|binding| binding.planning_request_id == request.request_id)
|
||||
.collect::<Vec<_>>();
|
||||
if bindings.len() != 1 {
|
||||
return Err(anyhow!(
|
||||
"frontier planning result requires exactly one coordinator launch binding"
|
||||
));
|
||||
}
|
||||
let binding = &bindings[0];
|
||||
let worker_launch = cache
|
||||
.get::<EpiphanyRuntimeWorkerLaunchRequest>(&result.job_id)?
|
||||
.ok_or_else(|| anyhow!("frontier planning result requires its worker launch"))?;
|
||||
let launch_document = worker_launch.launch_document()?;
|
||||
let projection = match &launch_document {
|
||||
EpiphanyWorkerLaunchDocument::Role(document) => {
|
||||
document.frontier_planning_context.as_ref()
|
||||
}
|
||||
EpiphanyWorkerLaunchDocument::Reorient(_) => None,
|
||||
};
|
||||
let expected_projection =
|
||||
crate::RepoFrontierPlanningContextProjection::from_request(&request);
|
||||
let launch_hash = format!(
|
||||
"{:x}",
|
||||
Sha256::digest(&worker_launch.launch_document_msgpack)
|
||||
);
|
||||
if binding.schema_version != REPO_FRONTIER_PLANNING_LAUNCH_BINDING_SCHEMA_VERSION
|
||||
|| binding.contract != REPO_FRONTIER_PLANNING_LAUNCH_BINDING_CONTRACT
|
||||
|| binding.binding_record_id
|
||||
!= format!("repo-frontier-planning-launch-{}", request.request_id)
|
||||
|| binding.job_id != result.job_id
|
||||
|| binding.binding_id != EPIPHANY_IMAGINATION_ROLE_BINDING_ID
|
||||
|| binding.runtime_id != request.runtime_id
|
||||
|| binding.thread_id != request.thread_id
|
||||
|| binding.worker_launch_document_sha256 != launch_hash
|
||||
|| worker_launch.job_id != result.job_id
|
||||
|| worker_launch.role != EPIPHANY_IMAGINATION_OWNER_ROLE
|
||||
|| worker_launch.binding_id != EPIPHANY_IMAGINATION_ROLE_BINDING_ID
|
||||
|| worker_launch.frontier_planning_request_id.as_deref()
|
||||
!= Some(request.request_id.as_str())
|
||||
|| worker_launch.proposal_modeling_request_id.is_some()
|
||||
|| worker_launch.claim_repair_request_id.is_some()
|
||||
|| projection != Some(&expected_projection)
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"frontier planning result does not exactly bind request, launch, runtime, thread, and candidate"
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(existing) = cache.get::<EpiphanyRuntimeRoleWorkerResult>(&result.job_id)? {
|
||||
return if existing == *result {
|
||||
Ok(())
|
||||
|
|
@ -3540,6 +3644,94 @@ pub(crate) fn put_repo_frontier_plan_candidate(
|
|||
put_immutable_planning_entry(store_path, &candidate.candidate_id, candidate)
|
||||
}
|
||||
|
||||
fn validate_repo_frontier_plan_candidate_against_request(
|
||||
cache: &CultCache,
|
||||
candidate: &RepoFrontierPlanCandidate,
|
||||
request: &RepoFrontierPlanningRequest,
|
||||
) -> Result<()> {
|
||||
if candidate.schema_version != REPO_FRONTIER_PLAN_CANDIDATE_SCHEMA_VERSION
|
||||
|| candidate.contract != REPO_FRONTIER_PLANNING_CONTRACT
|
||||
|| candidate.selected_fields_invalid()
|
||||
|| candidate.planning_request_id != request.request_id
|
||||
|| candidate.model_revision != request.model_revision
|
||||
|| candidate.model_hash != request.model_hash
|
||||
|| candidate.frontier_item_id != request.frontier_item_id
|
||||
|| candidate.frontier_item_hash != request.frontier_item_hash
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"frontier planning candidate substituted request identity or required cargo"
|
||||
));
|
||||
}
|
||||
let expected_candidate_id = canonical_repo_frontier_plan_candidate_id(candidate)?;
|
||||
if candidate.candidate_id != expected_candidate_id {
|
||||
return Err(anyhow!("frontier planning candidate id is not canonical"));
|
||||
}
|
||||
let admission = cache
|
||||
.get::<RepoModelAdmissionReceipt>(&request.admission_receipt_id)?
|
||||
.ok_or_else(|| anyhow!("frontier planning candidate requires exact admission receipt"))?;
|
||||
if admission.schema_version != REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION
|
||||
|| admission.contract != REPO_MODEL_ADMISSION_CONTRACT
|
||||
|| admission.admitted_revision != request.model_revision
|
||||
|| admission.admitted_hash != request.model_hash
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"frontier planning candidate admission binding mismatch"
|
||||
));
|
||||
}
|
||||
let entry = cache
|
||||
.get::<crate::EpiphanyMemoryGraphEntry>(crate::MEMORY_GRAPH_KEY)?
|
||||
.ok_or_else(|| anyhow!("frontier planning candidate requires canonical model"))?;
|
||||
let model = entry.snapshot()?;
|
||||
if model.model_revision != request.model_revision
|
||||
|| crate::memory_graph_model_hash(&model)? != request.model_hash
|
||||
{
|
||||
return Err(anyhow!("frontier planning candidate model is stale"));
|
||||
}
|
||||
let item = model
|
||||
.frontier
|
||||
.iter()
|
||||
.find(|item| item.id == request.frontier_item_id)
|
||||
.ok_or_else(|| anyhow!("frontier planning candidate frontier disappeared"))?;
|
||||
if format!("{:x}", Sha256::digest(rmp_serde::to_vec_named(item)?)) != request.frontier_item_hash
|
||||
|| item.source_scope != request.source_scope
|
||||
|| !candidate.safe_paths.iter().all(|path| {
|
||||
request.source_scope.iter().any(|scope| {
|
||||
path == scope
|
||||
|| path.starts_with(&format!("{}/", scope.trim_end_matches(['/', '\\'])))
|
||||
})
|
||||
})
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"frontier planning candidate exceeds exact frontier authority"
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn canonical_repo_frontier_plan_candidate_id(
|
||||
candidate: &RepoFrontierPlanCandidate,
|
||||
) -> Result<String> {
|
||||
let semantic_bytes = rmp_serde::to_vec_named(&(
|
||||
&candidate.planning_request_id,
|
||||
candidate.model_revision,
|
||||
&candidate.model_hash,
|
||||
&candidate.frontier_item_id,
|
||||
&candidate.frontier_item_hash,
|
||||
&candidate.safe_paths,
|
||||
&candidate.action,
|
||||
&candidate.command,
|
||||
&candidate.checks,
|
||||
&candidate.stop_conditions,
|
||||
&candidate.rollback_steps,
|
||||
&candidate.commit_message,
|
||||
&candidate.proposed_at,
|
||||
))?;
|
||||
Ok(format!(
|
||||
"repo-frontier-plan-candidate-{:x}",
|
||||
Sha256::digest(semantic_bytes)
|
||||
))
|
||||
}
|
||||
|
||||
impl RepoFrontierPlanCandidate {
|
||||
fn selected_fields_invalid(&self) -> bool {
|
||||
self.candidate_id.trim().is_empty()
|
||||
|
|
@ -6980,6 +7172,8 @@ pub(crate) mod tests {
|
|||
repo_frontier_modeling_request_id: None,
|
||||
proposal_modeling_request_id: Some(selection.request_id),
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
};
|
||||
let review = RepoModelAdmissionReview {
|
||||
schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.to_string(),
|
||||
|
|
@ -7109,6 +7303,8 @@ pub(crate) mod tests {
|
|||
repo_frontier_modeling_request_id: None,
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: Some(repair.request_id.clone()),
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
};
|
||||
let review = RepoModelAdmissionReview {
|
||||
schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.into(),
|
||||
|
|
@ -8382,6 +8578,8 @@ pub(crate) mod tests {
|
|||
repo_frontier_modeling_request_id: None,
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
};
|
||||
put_runtime_role_worker_result(&store, &verification_result)?;
|
||||
let verdict = SoulVerdictReceipt {
|
||||
|
|
@ -8501,6 +8699,8 @@ pub(crate) mod tests {
|
|||
repo_frontier_modeling_request_id: Some(fixture.modeling_request.request_id.clone()),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
};
|
||||
let review = RepoModelAdmissionReview {
|
||||
schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.to_string(),
|
||||
|
|
@ -8777,6 +8977,8 @@ pub(crate) mod tests {
|
|||
repo_frontier_modeling_request_id: None,
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
};
|
||||
put_runtime_role_worker_result(&fixture.store, &adjacent)?;
|
||||
put_soul_verdict_receipt(
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use crate::EpiphanyReorientFreshnessStatus;
|
|||
use crate::EpiphanyReorientPressureLevel;
|
||||
use crate::EpiphanyReorientReason;
|
||||
|
||||
pub const ROLE_WORKER_OUTPUT_CONTRACT_ID: &str = "epiphany.worker.role_result.v1";
|
||||
pub const ROLE_WORKER_OUTPUT_CONTRACT_ID: &str = "epiphany.worker.role_result.v2";
|
||||
pub const REORIENT_WORKER_OUTPUT_CONTRACT_ID: &str = "epiphany.worker.reorient_result.v0";
|
||||
pub const REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_SCHEMA_VERSION: &str =
|
||||
"epiphany.worker.repo_frontier_proposal_modeling_context.v0";
|
||||
|
|
|
|||
|
|
@ -971,6 +971,9 @@ mod tests {
|
|||
state_revision: 1,
|
||||
objective: Some("Verify the worker loop.".to_string()),
|
||||
dynamic_prompt_context: None,
|
||||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -992,6 +995,9 @@ mod tests {
|
|||
"role",
|
||||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -1051,6 +1057,9 @@ mod tests {
|
|||
state_revision: 1,
|
||||
objective: Some("Verify runtime error sealing.".to_string()),
|
||||
dynamic_prompt_context: None,
|
||||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -1072,6 +1081,9 @@ mod tests {
|
|||
"role",
|
||||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -1119,6 +1131,9 @@ mod tests {
|
|||
state_revision: 1,
|
||||
objective: Some("Verify the worker loop ceiling.".to_string()),
|
||||
dynamic_prompt_context: None,
|
||||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -1140,6 +1155,9 @@ mod tests {
|
|||
"role",
|
||||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -1298,7 +1298,11 @@ fn worker_output_schema_json(document: &EpiphanyWorkerLaunchDocument) -> Result<
|
|||
EpiphanyWorkerLaunchDocument::Role(document) => {
|
||||
let role_id = role_result_id_for_launch_role(&document.role_id)
|
||||
.with_context(|| format!("unknown role launch id {:?}", document.role_id))?;
|
||||
epiphany_core::epiphany_role_launch_output_schema(role_id)
|
||||
if document.frontier_planning_context.is_some() {
|
||||
epiphany_core::epiphany_frontier_planning_output_schema()
|
||||
} else {
|
||||
epiphany_core::epiphany_role_launch_output_schema(role_id)
|
||||
}
|
||||
}
|
||||
EpiphanyWorkerLaunchDocument::Reorient(_) => {
|
||||
epiphany_core::epiphany_reorient_launch_output_schema()
|
||||
|
|
@ -1323,8 +1327,13 @@ fn role_result_id_for_launch_role(
|
|||
|
||||
fn worker_output_contract_text(document: &EpiphanyWorkerLaunchDocument) -> &'static str {
|
||||
match document {
|
||||
EpiphanyWorkerLaunchDocument::Role(document)
|
||||
if document.frontier_planning_context.is_some() =>
|
||||
{
|
||||
"Required frontier-planning result fields: roleId, verdict, summary, nextSafeMove, filesInspected, frontierPlanningRequestId, frontierPlanCandidate. Echo the exact request and candidate identity from the typed launch context. Do not emit statePatch, selfPatch, or repoModelPatch."
|
||||
}
|
||||
EpiphanyWorkerLaunchDocument::Role(_) => {
|
||||
"Required role-result fields: roleId, verdict, summary, nextSafeMove, filesInspected. Modeling workers must include repoModelPatch; Imagination workers must include statePatch. Modeling statePatch is optional observations/evidence only. Use arrays for frontierNodeIds, evidenceIds, openQuestions, evidenceGaps, risks, and artifactRefs when present."
|
||||
"Required role-result fields: roleId, verdict, summary, nextSafeMove, filesInspected. Modeling workers must include repoModelPatch; ordinary Imagination workers must include statePatch. Modeling statePatch is optional observations/evidence only. Use arrays for frontierNodeIds, evidenceIds, openQuestions, evidenceGaps, risks, and artifactRefs when present."
|
||||
}
|
||||
EpiphanyWorkerLaunchDocument::Reorient(_) => {
|
||||
"Required reorient-result fields: mode, summary, nextSafeMove. Include checkpointStillValid, filesInspected, frontierNodeIds, evidenceIds, openQuestions, and continuityRisks when present."
|
||||
|
|
@ -1355,6 +1364,27 @@ struct RoleWorkerResultIngress {
|
|||
frontier_route_id: Option<String>,
|
||||
repo_frontier_modeling_request_id: Option<String>,
|
||||
proposal_modeling_request_id: Option<String>,
|
||||
claim_repair_request_id: Option<String>,
|
||||
frontier_planning_request_id: Option<String>,
|
||||
frontier_plan_candidate: Option<RepoFrontierPlanCandidateIngress>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
struct RepoFrontierPlanCandidateIngress {
|
||||
planning_request_id: String,
|
||||
model_revision: u64,
|
||||
model_hash: String,
|
||||
frontier_item_id: String,
|
||||
frontier_item_hash: String,
|
||||
safe_paths: Vec<String>,
|
||||
action: String,
|
||||
command: String,
|
||||
checks: Vec<String>,
|
||||
stop_conditions: Vec<String>,
|
||||
rollback_steps: Vec<String>,
|
||||
commit_message: String,
|
||||
proposed_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Deserialize)]
|
||||
|
|
@ -1450,6 +1480,37 @@ fn role_worker_result_from_ingress(
|
|||
encode_optional_document(&result.repo_model_patch, "repoModelPatch");
|
||||
let (self_patch_msgpack, self_patch_error) =
|
||||
encode_optional_document(&result.self_patch, "selfPatch");
|
||||
let (frontier_plan_candidate_msgpack, frontier_plan_candidate_error) = if let Some(ingress) =
|
||||
result.frontier_plan_candidate.as_ref()
|
||||
{
|
||||
let mut candidate = epiphany_core::RepoFrontierPlanCandidate {
|
||||
schema_version: epiphany_core::REPO_FRONTIER_PLAN_CANDIDATE_SCHEMA_VERSION.to_string(),
|
||||
candidate_id: String::new(),
|
||||
planning_request_id: ingress.planning_request_id.clone(),
|
||||
model_revision: ingress.model_revision,
|
||||
model_hash: ingress.model_hash.clone(),
|
||||
frontier_item_id: ingress.frontier_item_id.clone(),
|
||||
frontier_item_hash: ingress.frontier_item_hash.clone(),
|
||||
safe_paths: clean_string_vec(&ingress.safe_paths),
|
||||
action: ingress.action.trim().to_string(),
|
||||
command: ingress.command.trim().to_string(),
|
||||
checks: clean_string_vec(&ingress.checks),
|
||||
stop_conditions: clean_string_vec(&ingress.stop_conditions),
|
||||
rollback_steps: clean_string_vec(&ingress.rollback_steps),
|
||||
commit_message: ingress.commit_message.trim().to_string(),
|
||||
proposed_at: ingress.proposed_at.trim().to_string(),
|
||||
contract: epiphany_core::REPO_FRONTIER_PLANNING_CONTRACT.to_string(),
|
||||
};
|
||||
match epiphany_core::canonical_repo_frontier_plan_candidate_id(&candidate) {
|
||||
Ok(candidate_id) => {
|
||||
candidate.candidate_id = candidate_id;
|
||||
encode_optional_document(&Some(candidate), "frontierPlanCandidate")
|
||||
}
|
||||
Err(error) => (None, Some(format!("frontierPlanCandidate: {error}"))),
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
EpiphanyRuntimeRoleWorkerResult {
|
||||
schema_version: epiphany_core::RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.to_string(),
|
||||
result_id: result_id.to_string(),
|
||||
|
|
@ -1475,8 +1536,11 @@ fn role_worker_result_from_ingress(
|
|||
state_patch_msgpack,
|
||||
self_patch_msgpack,
|
||||
item_error: merge_optional_errors(
|
||||
merge_optional_errors(state_patch_error, self_patch_error),
|
||||
repo_model_patch_error,
|
||||
merge_optional_errors(
|
||||
merge_optional_errors(state_patch_error, self_patch_error),
|
||||
repo_model_patch_error,
|
||||
),
|
||||
frontier_plan_candidate_error,
|
||||
),
|
||||
metadata: std::collections::BTreeMap::new(),
|
||||
repo_model_patch_msgpack,
|
||||
|
|
@ -1488,6 +1552,11 @@ fn role_worker_result_from_ingress(
|
|||
proposal_modeling_request_id: clean_optional_string(
|
||||
result.proposal_modeling_request_id.as_deref(),
|
||||
),
|
||||
claim_repair_request_id: clean_optional_string(result.claim_repair_request_id.as_deref()),
|
||||
frontier_planning_request_id: clean_optional_string(
|
||||
result.frontier_planning_request_id.as_deref(),
|
||||
),
|
||||
frontier_plan_candidate_msgpack,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1626,6 +1695,8 @@ mod tests {
|
|||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
};
|
||||
let result = role_worker_result_from_ingress(
|
||||
&launch,
|
||||
|
|
@ -1645,6 +1716,80 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frontier_planning_ingress_derives_typed_candidate_identity() -> Result<()> {
|
||||
let parsed = parse_assistant_json::<RoleWorkerResultIngress>(
|
||||
r#"{
|
||||
"roleId":"imagination",
|
||||
"verdict":"draft-ready",
|
||||
"summary":"bounded plan",
|
||||
"nextSafeMove":"Mind admission",
|
||||
"frontierPlanningRequestId":"planning-request-1",
|
||||
"frontierPlanCandidate":{
|
||||
"planning_request_id":"planning-request-1",
|
||||
"model_revision":7,
|
||||
"model_hash":"model-hash",
|
||||
"frontier_item_id":"frontier-1",
|
||||
"frontier_item_hash":"frontier-hash",
|
||||
"safe_paths":["src"],
|
||||
"action":"Implement the bounded cut.",
|
||||
"command":"cargo test --lib",
|
||||
"checks":["focused test passes"],
|
||||
"stop_conditions":["scope changes"],
|
||||
"rollback_steps":["revert commit"],
|
||||
"commit_message":"Implement bounded cut",
|
||||
"proposed_at":"2026-07-15T10:00:00Z"
|
||||
}
|
||||
}"#,
|
||||
)?;
|
||||
let launch = EpiphanyRuntimeWorkerLaunchRequest {
|
||||
schema_version: epiphany_core::RUNTIME_WORKER_LAUNCH_REQUEST_SCHEMA_VERSION.to_string(),
|
||||
job_id: "planning-job-1".into(),
|
||||
binding_id: epiphany_core::EPIPHANY_IMAGINATION_ROLE_BINDING_ID.into(),
|
||||
role: epiphany_core::EPIPHANY_IMAGINATION_OWNER_ROLE.into(),
|
||||
authority_scope: "epiphany.role.imagination".into(),
|
||||
instruction: "plan".into(),
|
||||
output_contract_id: epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID.into(),
|
||||
document_kind: "role".into(),
|
||||
launch_document_msgpack: Vec::new(),
|
||||
metadata: std::collections::BTreeMap::new(),
|
||||
organ_launch_contract: epiphany_core::default_launch_organ_contract(
|
||||
"epiphany.role.imagination",
|
||||
"role",
|
||||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: Some("planning-request-1".into()),
|
||||
};
|
||||
let result = role_worker_result_from_ingress(
|
||||
&launch,
|
||||
"imagination",
|
||||
"planning-result-1",
|
||||
&parsed,
|
||||
Vec::new(),
|
||||
);
|
||||
assert_eq!(
|
||||
result.frontier_planning_request_id.as_deref(),
|
||||
Some("planning-request-1")
|
||||
);
|
||||
assert!(result.state_patch_msgpack.is_none());
|
||||
assert!(result.self_patch_msgpack.is_none());
|
||||
let candidate = result
|
||||
.frontier_plan_candidate()?
|
||||
.expect("typed frontier candidate");
|
||||
assert_eq!(candidate.planning_request_id, "planning-request-1");
|
||||
assert_eq!(
|
||||
candidate.candidate_id,
|
||||
epiphany_core::canonical_repo_frontier_plan_candidate_id(&candidate)?
|
||||
);
|
||||
assert_eq!(
|
||||
candidate.schema_version,
|
||||
epiphany_core::REPO_FRONTIER_PLAN_CANDIDATE_SCHEMA_VERSION
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_openai_event(
|
||||
request_id: &str,
|
||||
sequence: u64,
|
||||
|
|
@ -1818,6 +1963,8 @@ mod tests {
|
|||
.to_string(),
|
||||
),
|
||||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -1840,6 +1987,8 @@ mod tests {
|
|||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -1945,6 +2094,8 @@ mod tests {
|
|||
.to_string(),
|
||||
),
|
||||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -1967,6 +2118,8 @@ mod tests {
|
|||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -965,3 +965,20 @@ and the loser leaves no job, worker launch, or opened event. Full core proof:
|
|||
322 passed, 1 ignored. Next build the exclusive immutable Imagination result
|
||||
echo carrying the typed candidate, then Mind-only Adopt/Refuse/Hold admission
|
||||
and the narrow Adopt model transition that makes Hands routing true.
|
||||
|
||||
The immutable Imagination result slice is now complete. Runtime role result v2
|
||||
adds an exclusive `frontier_planning_request_id` plus dedicated canonical
|
||||
MessagePack `RepoFrontierPlanCandidate`. Persistence replays the exact planning
|
||||
request, request-keyed launch binding, stored launch-document hash/projection,
|
||||
runtime/thread, current model/admission/frontier, deterministic candidate
|
||||
identity, and bounded safe paths. Planning results reject generic state, Self,
|
||||
RepoModel, Verification, and Modeling authority cargo before writing. The
|
||||
OpenAI ingress uses a specialized planning schema, supplies candidate
|
||||
schema/contract itself, derives candidate identity from semantic cargo, and
|
||||
does not expose generic patch mouths. The candidate remains embedded in the
|
||||
immutable result; no standalone candidate document is written. Proof: core
|
||||
325 passed/1 ignored; OpenAI runtime 9 passed; hostile missing/swapped request,
|
||||
missing/adjacent/escaped candidate, wrong role, generic patch smuggling, exact
|
||||
retry, and immutable collision checks pass. Next implement Mind-only terminal
|
||||
decision plus the dedicated Adopt RepoModel transition from Imagination to
|
||||
Hands, then delete the old internal candidate/adoption fixture writers.
|
||||
|
|
|
|||
|
|
@ -11,10 +11,18 @@ worker launch document. Caller-authored projections, mixed authority carriers,
|
|||
stale requests, causal substitutions, and duplicate/racing launches are
|
||||
refused without loser artifacts.
|
||||
|
||||
The downstream nerve is still incomplete. Imagination has no exclusive
|
||||
immutable result carrying the candidate; Mind has no correlated admission; the
|
||||
old internal candidate/adoption fixture writers still exist; and Adopt still
|
||||
changes no RepoModel bytes or Hands truth.
|
||||
Imagination's output is now a v2 immutable runtime result with an exclusive
|
||||
planning-request echo and one dedicated canonical MessagePack candidate. Result
|
||||
persistence replays the current request, launch binding, stored launch bytes,
|
||||
projection, runtime/thread, model, admission, frontier, candidate identity, and
|
||||
safe-path scope before its absent-only write. Generic state, Self, RepoModel,
|
||||
Verification, and Modeling authority cargo are forbidden on this path. The
|
||||
candidate remains inside the immutable result until Mind admission; no
|
||||
independent candidate writer participates in production.
|
||||
|
||||
The downstream nerve is still incomplete. Mind has no correlated admission;
|
||||
the old internal candidate/adoption fixture writers still exist for the legacy
|
||||
test chain; and Adopt still changes no RepoModel bytes or Hands truth.
|
||||
|
||||
The public candidate/adoption writers remain removed. The existing internal
|
||||
functions are test scaffolding only and die when result correlation and Mind
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -714,4 +714,4 @@ current_status:
|
|||
- Service lifecycle receipt history is now event-only: `load_epiphany_cultmesh_daemon_service_lifecycle_receipts` filters out the `epiphany-local/daemon-service-lifecycle-receipt/latest` mirror, while `load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt` remains the latest-state reader. Focused unit test `service_lifecycle_receipt_history_excludes_latest_mirror` proves two per-receipt writes return exactly two history records and latest points at the second. Fresh smoke `verse-query-lifecycle-history-filter-smoke` and live overview/triage `local-20260618-145632-497-efac6f0d` / `local-20260618-145632-321-867947af` prove compact swarm sights still expose service lifecycle attention plus the present elevated runbook artifact without daemon pokes or service mutation. Do not use a broad unscoped cargo test filter for this check on Windows; it fans out across bin harnesses and hit a broken pipe during this pass.
|
||||
- `epiphany-verse-query seed-compact` is the compact seed receipt for wrappers and read-only rites. The older `seed` command still prints the full local Verse context for explicit debugging, but compact wrapper modes now avoid full `seed`/final `query` readback after the combined overview proof exposed Windows debug stack overflow from full-context serialization pressure.
|
||||
last_completed_repo_swarm_cut: Huginn live-fire has crossed the first real repo Body from Persona pressure to branch-local Hands commit and Soul/Mind closure, and now has repeatable closure and redacted-public-proof mouths. On `E:\Projects\Huginn` branch `epiphany/huginn/first-awakening`, `tools/epiphany_local_run.ps1 -Mode repo-persona-intake` accepted item `huginn-inspection-readback-livefire`, Weksa/Persona intake remained sealed, and `epiphany-work derive-plan` lowered it to model-authored safe family `repo.status_section` for `README.md` only. `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` advanced four bounded pulses: run-from-plan, adopt-from-plan, execute-from-plan, and close-from-execute. Huginn commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2` updated only `README.md`; closure receipt `.epiphany\work\work-close-huginn-inspection-readback-livefire.json` records status `closed`, Soul verdict `passed`, Mind state commit `repo-work-close-huginn-inspection-readback-livefire-mind-commit`, safe family `repo.status_section`, path scope `README.md`, Bifrost publication gate, and `privateStateExposed=false`. Verification also ran Huginn's TypeScript compiler through concrete Node path: `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json` passed. Native verifier `epiphany-repo-livefire-closure-smoke --workspace E:\Projects\Huginn --item huginn-inspection-readback-livefire --expected-path README.md --expected-family repo.status_section --expected-gate awaiting-publication --expected-blocker bifrost-publication-missing` checks closure receipt, Mind map entry, overview gate/blocker, authority seals, and `git show` changed paths, returning `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, and `privateStateExposed=false`. Wrapper `tools/epiphany_local_run.ps1 -Mode repo-livefire-closure -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exposes the same read-only proof through the local operator mouth; artifact `local-20260621-190156-803-2f85ba15` printed commit, path, gate, blocker, Soul verdict, Mind commit receipt, and `privateStateExposed=False` without push, merge, publication, or target mutation. New wrapper `tools/epiphany_local_run.ps1 -Mode repo-work-public-proof -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exports the redacted proof bundle and repo-local Verse index without publishing; artifact `local-20260621-190645-345-4555b1dc` wrote `.epiphany\public\proof-bundles\repo-work-public-proof-huginn-inspection-readback-livefire.json`, proof id `repo-work-public-proof-huginn-inspection-readback-livefire`, SHA-256 `857545b199f3e37eb06c2e757e5cfc201958e46667bb1f8403fd333a22620f63`, nine artifact rows, zero publication rows, and `privateStateExposed=False`. swarm overview readback `local-20260621-190701-140-b9519ec6` sees the `REPO-WORK-PUBLIC-PROOF` row and action row from Huginn's repo-local Verse with the same SHA-256 while Bifrost publication remains missing. Launcher compact modes are now named compact operator-context modes instead of read-only modes, and `bifrost-public-proof` summaries print item, target public Verse, room, proof SHA-256, review receipts, and credit receipts so publication receipts are auditable without claiming upstream sync.
|
||||
next_action: Continue the repo-frontier planning nerve from notes/repo-frontier-planning-authority-map.md. The Self-to-Imagination launch artery is now real: planning request v1 binds runtime/thread, coordinator commit replays current model/admission/frontier/challenges/state, injects the typed projection, and atomically persists one request-keyed launch binding with the exact stored launch-document hash; hostile substitutions and a two-launch race leave no loser artifacts. Next add one exclusive planning-request echo on immutable worker result v1 containing the typed candidate, then replace the internal candidate/adoption fixture writers with Mind-only correlated Adopt/Refuse/Hold admission. Adopt must cause a narrow RepoModel transition that makes downstream Hands truth real; Refuse/Hold remain inert immutable receipts. After the nerve lands, add the production semantic index over typed graph claims so Modeling maintains a live searchable body map instead of forcing Hands to rediscover anatomy. Do not revive Proprioception, epiphany-work, metadata adapters, generic work-order routers, or inference from namespaces/prompt prose/evidence coincidence. Keep closure's zero-substring invariant.
|
||||
next_action: Continue the repo-frontier planning nerve from notes/repo-frontier-planning-authority-map.md. Self-to-Imagination launch and result arteries are real: request v1 binds model/frontier/runtime/thread; coordinator atomically injects and binds the exact launch; immutable worker result v2 exclusively echoes that request and embeds one canonical typed candidate after full request/launch/model/frontier replay, with generic patch mouths sealed. Next replace the internal candidate/adoption fixture writers with one Mind-only correlated Adopt/Refuse/Hold admission. Adopt must atomically install the exact candidate cargo and provenance into the canonical frontier, change only Imagination to Hands through a dedicated patch purpose/operation, and admit the new RepoModel; routes must copy execution cargo from that model, never consult a candidate/adoption shadow store. Refuse/Hold remain inert immutable receipts. After the nerve lands, add the production semantic index over typed graph claims so Modeling maintains a live searchable body map instead of forcing Hands to rediscover anatomy. Do not revive Proprioception, epiphany-work, metadata adapters, generic work-order routers, or inference from namespaces/prompt prose/evidence coincidence. Keep closure's zero-substring invariant.
|
||||
|
|
|
|||
Reference in a new issue