Make frontier plan adoption canonical
This commit is contained in:
parent
05300c8add
commit
f72228fe61
26 changed files with 2272 additions and 505 deletions
|
|
@ -21,6 +21,8 @@ use epiphany_state_model::EpiphanyThreadState;
|
|||
|
||||
pub const EPIPHANY_IMAGINATION_ROLE_BINDING_ID: &str = "planning-synthesis-worker";
|
||||
pub const EPIPHANY_IMAGINATION_OWNER_ROLE: &str = "epiphany-imagination";
|
||||
pub const EPIPHANY_MIND_ROLE_BINDING_ID: &str = "mind-admission-reviewer";
|
||||
pub const EPIPHANY_MIND_OWNER_ROLE: &str = "epiphany-mind-admission-review";
|
||||
pub const EPIPHANY_RESEARCH_ROLE_BINDING_ID: &str = "research-source-gather-worker";
|
||||
pub const EPIPHANY_RESEARCH_OWNER_ROLE: &str = "epiphany-eyes";
|
||||
pub const EPIPHANY_MODELING_ROLE_BINDING_ID: &str = "modeling-checkpoint-worker";
|
||||
|
|
@ -467,6 +469,32 @@ pub fn epiphany_frontier_planning_output_schema() -> serde_json::Value {
|
|||
schema
|
||||
}
|
||||
|
||||
pub fn epiphany_frontier_plan_mind_output_schema() -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roleId": {"type": "string", "const": "mindAdmissionReview"},
|
||||
"verdict": {"type": "string", "minLength": 1},
|
||||
"summary": {"type": "string", "minLength": 1},
|
||||
"nextSafeMove": {"type": "string", "minLength": 1},
|
||||
"filesInspected": {"type": "array", "items": {"type": "string"}},
|
||||
"frontierPlanMindRequestId": {"type": "string", "minLength": 1},
|
||||
"frontierPlanMindDecision": {
|
||||
"type": "object",
|
||||
"required": ["mindRequestId", "planningRequestId", "imaginationResultId", "candidateId", "candidateSha256", "decision", "rationale", "decidedAt"],
|
||||
"properties": {
|
||||
"mindRequestId": {"type": "string", "minLength": 1}, "planningRequestId": {"type": "string", "minLength": 1},
|
||||
"imaginationResultId": {"type": "string", "minLength": 1}, "candidateId": {"type": "string", "minLength": 1},
|
||||
"candidateSha256": {"type": "string", "minLength": 1}, "decision": {"type": "string", "enum": ["adopt", "refuse", "hold"]},
|
||||
"rationale": {"type": "string", "minLength": 1}, "decidedAt": {"type": "string", "minLength": 1}
|
||||
}, "additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["roleId", "verdict", "summary", "nextSafeMove", "filesInspected", "frontierPlanMindRequestId", "frontierPlanMindDecision"],
|
||||
"additionalProperties": false
|
||||
})
|
||||
}
|
||||
|
||||
pub fn epiphany_reorient_launch_output_schema() -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "object",
|
||||
|
|
@ -530,6 +558,7 @@ pub struct EpiphanySharedPromptConfig {
|
|||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct EpiphanyRolePromptConfig {
|
||||
pub imagination: String,
|
||||
pub mind: String,
|
||||
pub modeling: String,
|
||||
pub verification: String,
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -613,6 +642,74 @@ pub fn build_epiphany_role_launch_request(
|
|||
)
|
||||
}
|
||||
|
||||
pub fn build_epiphany_frontier_plan_mind_launch_request(
|
||||
thread_id: &str,
|
||||
expected_revision: Option<u64>,
|
||||
max_runtime_seconds: Option<u64>,
|
||||
state: &EpiphanyThreadState,
|
||||
mind_request_id: String,
|
||||
) -> Result<EpiphanyJobLaunchRequest, String> {
|
||||
let linked_subgoal_ids = epiphany_active_subgoal_ids(Some(state));
|
||||
let linked_graph_node_ids = epiphany_active_graph_node_ids(Some(state));
|
||||
let authority_scope = "epiphany.procedure.mind_admission_review";
|
||||
let launch_document = EpiphanyWorkerLaunchDocument::Role(EpiphanyRoleWorkerLaunchDocument {
|
||||
thread_id: thread_id.to_string(),
|
||||
role_id: "mindAdmissionReview".to_string(),
|
||||
state_revision: state.revision,
|
||||
objective: state.objective.clone(),
|
||||
dynamic_prompt_context: None,
|
||||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
frontier_plan_mind_context: None,
|
||||
active_subgoal_id: state.active_subgoal_id.clone(),
|
||||
active_subgoals: state
|
||||
.subgoals
|
||||
.iter()
|
||||
.filter(|subgoal| Some(subgoal.id.as_str()) == state.active_subgoal_id.as_deref())
|
||||
.cloned()
|
||||
.collect(),
|
||||
active_graph_node_ids: linked_graph_node_ids.clone(),
|
||||
investigation_checkpoint: state.investigation_checkpoint.clone(),
|
||||
scratch: state.scratch.clone(),
|
||||
invariants: state.invariants.clone(),
|
||||
graphs: Some(state.graphs.clone()),
|
||||
recent_evidence: state.recent_evidence.iter().take(8).cloned().collect(),
|
||||
recent_observations: state.observations.iter().take(8).cloned().collect(),
|
||||
graph_frontier: state.graph_frontier.clone(),
|
||||
graph_checkpoint: state.graph_checkpoint.clone(),
|
||||
planning: Some(state.planning.clone()),
|
||||
churn: state.churn.clone(),
|
||||
});
|
||||
let output_contract_id = launch_document.output_contract_id().to_string();
|
||||
let organ_launch_contract = default_launch_organ_contract(
|
||||
authority_scope,
|
||||
launch_document.document_kind(),
|
||||
&output_contract_id,
|
||||
);
|
||||
Ok(EpiphanyJobLaunchRequest {
|
||||
expected_revision,
|
||||
binding_id: EPIPHANY_MIND_ROLE_BINDING_ID.to_string(),
|
||||
kind: CoreEpiphanyJobKind::Specialist,
|
||||
scope: "role-scoped frontier plan judgment".to_string(),
|
||||
owner_role: EPIPHANY_MIND_OWNER_ROLE.to_string(),
|
||||
authority_scope: authority_scope.to_string(),
|
||||
linked_subgoal_ids,
|
||||
linked_graph_node_ids,
|
||||
instruction: epiphany_worker_prompt(
|
||||
epiphany_specialist_prompt_config().roles.mind.as_str(),
|
||||
),
|
||||
launch_document,
|
||||
output_contract_id,
|
||||
organ_launch_contract,
|
||||
max_runtime_seconds,
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: Some(mind_request_id),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build_epiphany_role_launch_request_with_dynamic_context(
|
||||
thread_id: &str,
|
||||
role_id: EpiphanyRoleResultRoleId,
|
||||
|
|
@ -659,6 +756,7 @@ pub fn build_epiphany_role_launch_request_with_dynamic_context(
|
|||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
frontier_plan_mind_context: None,
|
||||
active_subgoal_id: state.active_subgoal_id.clone(),
|
||||
active_subgoals: state
|
||||
.subgoals
|
||||
|
|
@ -702,6 +800,7 @@ pub fn build_epiphany_role_launch_request_with_dynamic_context(
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -807,6 +906,7 @@ pub fn build_epiphany_job_launch_request(
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -932,6 +1032,7 @@ mod tests {
|
|||
prompts.shared.persistent_memory.as_str(),
|
||||
),
|
||||
("roles.imagination", prompts.roles.imagination.as_str()),
|
||||
("roles.mind", prompts.roles.mind.as_str()),
|
||||
("roles.modeling", prompts.roles.modeling.as_str()),
|
||||
("roles.verification", prompts.roles.verification.as_str()),
|
||||
("roles.research", prompts.roles.research.as_str()),
|
||||
|
|
@ -981,6 +1082,42 @@ mod tests {
|
|||
assert!(!prompt.contains("Heartbeat: every lane"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frontier_plan_mind_launch_is_a_real_prompted_role_with_strict_schema() {
|
||||
let request = build_epiphany_frontier_plan_mind_launch_request(
|
||||
"thread-mind",
|
||||
Some(0),
|
||||
Some(60),
|
||||
&EpiphanyThreadState::default(),
|
||||
"mind-request-1".into(),
|
||||
)
|
||||
.expect("Mind launch request");
|
||||
assert_eq!(request.owner_role, EPIPHANY_MIND_OWNER_ROLE);
|
||||
assert_eq!(request.binding_id, EPIPHANY_MIND_ROLE_BINDING_ID);
|
||||
assert!(
|
||||
request
|
||||
.instruction
|
||||
.contains("admission-review procedure serving Epiphany Mind")
|
||||
);
|
||||
assert_eq!(
|
||||
request.frontier_plan_mind_request_id.as_deref(),
|
||||
Some("mind-request-1")
|
||||
);
|
||||
let EpiphanyWorkerLaunchDocument::Role(document) = request.launch_document else {
|
||||
panic!("Mind must use a role launch document")
|
||||
};
|
||||
assert_eq!(document.role_id, "mindAdmissionReview");
|
||||
let schema = epiphany_frontier_plan_mind_output_schema();
|
||||
assert_eq!(
|
||||
schema["properties"]["roleId"]["const"],
|
||||
"mindAdmissionReview"
|
||||
);
|
||||
assert_eq!(
|
||||
schema["properties"]["frontierPlanMindDecision"]["properties"]["decision"]["enum"],
|
||||
serde_json::json!(["adopt", "refuse", "hold"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modeling_schema_exposes_only_typed_authority_purposes() {
|
||||
let schema = epiphany_role_launch_output_schema(EpiphanyRoleResultRoleId::Modeling);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ fn main() -> Result<()> {
|
|||
requested_at: "2026-06-02T00:00:10Z".to_string(),
|
||||
contract: "Smoke intent proves Hands action starts as typed runtime-spine state."
|
||||
.to_string(),
|
||||
frontier_route_id: String::new(),
|
||||
plan_candidate_sha256: String::new(),
|
||||
plan_action: String::new(),
|
||||
};
|
||||
put_hands_action_intent(&store, &intent)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -801,6 +801,9 @@ mod tests {
|
|||
substrate_gate_grant_receipt_id: "substrate-grant-test".to_string(),
|
||||
requested_at: "2026-06-02T00:00:01Z".to_string(),
|
||||
contract: "Test Hands intent.".to_string(),
|
||||
frontier_route_id: String::new(),
|
||||
plan_candidate_sha256: String::new(),
|
||||
plan_action: String::new(),
|
||||
};
|
||||
put_hands_action_intent(store, &intent)?;
|
||||
let review = hands_action_review_for_intent(
|
||||
|
|
|
|||
|
|
@ -853,6 +853,9 @@ fn record_hands_implementation_gate(
|
|||
requested_at: requested_at.clone(),
|
||||
contract: "Coordinator continuation becomes a typed Hands action intent before any file edit, command, or commit may count as implementation evidence."
|
||||
.to_string(),
|
||||
frontier_route_id: String::new(),
|
||||
plan_candidate_sha256: String::new(),
|
||||
plan_action: String::new(),
|
||||
};
|
||||
put_hands_action_intent(runtime_store, &intent)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -1175,6 +1175,7 @@ mod tests {
|
|||
frontier_modeling_request_id: String::new(),
|
||||
proposal_modeling_request_id: String::new(),
|
||||
claim_repair_request_id: String::new(),
|
||||
frontier_plan_decision_id: String::new(),
|
||||
},
|
||||
)?;
|
||||
let mut fresh = existing.clone();
|
||||
|
|
|
|||
|
|
@ -138,6 +138,37 @@ fn commit_coordinator_job_launch_in_cache(
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let frontier_plan_mind_launch =
|
||||
if let Some(request_id) = request.frontier_plan_mind_request_id.as_deref() {
|
||||
let (mind_request, planning, candidate, identity) =
|
||||
validate_frontier_plan_mind_launch(cache, current_state, request, request_id)?;
|
||||
let projection =
|
||||
RepoFrontierPlanMindContextProjection::new(&mind_request, &planning, &candidate);
|
||||
match &mut effective_launch_document {
|
||||
EpiphanyWorkerLaunchDocument::Role(document) => {
|
||||
if document.proposal_modeling_context.is_some()
|
||||
|| document.claim_repair_context.is_some()
|
||||
|| document.frontier_planning_context.is_some()
|
||||
|| document.frontier_plan_mind_context.is_some()
|
||||
{
|
||||
return Err(anyhow!("Mind frontier decision context is exclusive"));
|
||||
}
|
||||
document.frontier_plan_mind_context = Some(projection);
|
||||
}
|
||||
EpiphanyWorkerLaunchDocument::Reorient(_) => {
|
||||
return Err(anyhow!(
|
||||
"reorient launch cannot carry Mind decision context"
|
||||
));
|
||||
}
|
||||
}
|
||||
let hash = format!(
|
||||
"{:x}",
|
||||
Sha256::digest(rmp_serde::to_vec_named(&effective_launch_document)?)
|
||||
);
|
||||
Some((mind_request, identity, hash))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let prepared = prepare_runtime_spine_heartbeat_job(
|
||||
&cache,
|
||||
RuntimeSpineHeartbeatJobOptions {
|
||||
|
|
@ -163,6 +194,7 @@ fn commit_coordinator_job_launch_in_cache(
|
|||
proposal_modeling_request_id: request.proposal_modeling_request_id.clone(),
|
||||
claim_repair_request_id: request.claim_repair_request_id.clone(),
|
||||
frontier_planning_request_id: request.frontier_planning_request_id.clone(),
|
||||
frontier_plan_mind_request_id: request.frontier_plan_mind_request_id.clone(),
|
||||
created_at: created_at.clone(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -263,6 +295,31 @@ fn commit_coordinator_job_launch_in_cache(
|
|||
.0,
|
||||
);
|
||||
}
|
||||
if let Some((mind_request, identity, worker_launch_document_sha256)) = frontier_plan_mind_launch
|
||||
{
|
||||
let binding = RepoFrontierPlanMindLaunchBinding {
|
||||
schema_version: REPO_FRONTIER_PLAN_MIND_LAUNCH_BINDING_SCHEMA_VERSION.into(),
|
||||
binding_record_id: format!(
|
||||
"repo-frontier-plan-mind-launch-{}",
|
||||
mind_request.request_id
|
||||
),
|
||||
mind_request_id: mind_request.request_id,
|
||||
job_id: plan.backend_job_id.clone(),
|
||||
binding_id: request.binding_id.clone(),
|
||||
runtime_id: identity.runtime_id,
|
||||
thread_id: mind_request.thread_id,
|
||||
launched_at: created_at.clone(),
|
||||
worker_launch_document_sha256,
|
||||
contract: REPO_FRONTIER_PLAN_MIND_LAUNCH_BINDING_CONTRACT.into(),
|
||||
};
|
||||
if cache
|
||||
.get::<RepoFrontierPlanMindLaunchBinding>(&binding.binding_record_id)?
|
||||
.is_some()
|
||||
{
|
||||
return Err(anyhow!("Mind request is already bound to a launch"));
|
||||
}
|
||||
batch.push(cache.prepare_entry(&binding.binding_record_id, &binding)?.0);
|
||||
}
|
||||
if request.binding_id == EPIPHANY_RESEARCH_ROLE_BINDING_ID {
|
||||
let grant = substrate_gate_repo_access_grant_for_launch(
|
||||
format!("substrate-grant-{}", plan.backend_job_id),
|
||||
|
|
@ -295,15 +352,20 @@ pub fn plan_coordinator_job_launch(
|
|||
launcher_job_id: String,
|
||||
backend_job_id: String,
|
||||
) -> Result<EpiphanyCoordinatorJobLaunchPlan> {
|
||||
let (caller_proposal_projection, caller_repair_projection, caller_planning_projection) =
|
||||
match &request.launch_document {
|
||||
EpiphanyWorkerLaunchDocument::Role(document) => (
|
||||
document.proposal_modeling_context.as_ref(),
|
||||
document.claim_repair_context.as_ref(),
|
||||
document.frontier_planning_context.as_ref(),
|
||||
),
|
||||
EpiphanyWorkerLaunchDocument::Reorient(_) => (None, None, None),
|
||||
};
|
||||
let (
|
||||
caller_proposal_projection,
|
||||
caller_repair_projection,
|
||||
caller_planning_projection,
|
||||
caller_mind_projection,
|
||||
) = match &request.launch_document {
|
||||
EpiphanyWorkerLaunchDocument::Role(document) => (
|
||||
document.proposal_modeling_context.as_ref(),
|
||||
document.claim_repair_context.as_ref(),
|
||||
document.frontier_planning_context.as_ref(),
|
||||
document.frontier_plan_mind_context.as_ref(),
|
||||
),
|
||||
EpiphanyWorkerLaunchDocument::Reorient(_) => (None, None, None, None),
|
||||
};
|
||||
if caller_proposal_projection.is_some() {
|
||||
return Err(anyhow!(
|
||||
"caller-prepopulated proposal Modeling context is forbidden; coordinator commit owns projection"
|
||||
|
|
@ -319,10 +381,16 @@ pub fn plan_coordinator_job_launch(
|
|||
"caller-prepopulated frontier planning context is forbidden; coordinator commit owns projection"
|
||||
));
|
||||
}
|
||||
if caller_mind_projection.is_some() {
|
||||
return Err(anyhow!(
|
||||
"caller-prepopulated Mind decision context is forbidden; coordinator commit owns projection"
|
||||
));
|
||||
}
|
||||
if [
|
||||
request.proposal_modeling_request_id.is_some(),
|
||||
request.claim_repair_request_id.is_some(),
|
||||
request.frontier_planning_request_id.is_some(),
|
||||
request.frontier_plan_mind_request_id.is_some(),
|
||||
]
|
||||
.into_iter()
|
||||
.filter(|present| *present)
|
||||
|
|
@ -330,7 +398,7 @@ pub fn plan_coordinator_job_launch(
|
|||
> 1
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"proposal Modeling, claim repair, and frontier planning launches are mutually exclusive"
|
||||
"specialized authority launches are mutually exclusive"
|
||||
));
|
||||
}
|
||||
if let Some(expected) = request.expected_revision
|
||||
|
|
@ -353,6 +421,10 @@ pub fn plan_coordinator_job_launch(
|
|||
let mut cache = runtime_spine_cache(runtime_store)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
validate_frontier_planning_launch(&cache, state, request, request_id)?;
|
||||
} else if let Some(request_id) = request.frontier_plan_mind_request_id.as_deref() {
|
||||
let mut cache = runtime_spine_cache(runtime_store)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
validate_frontier_plan_mind_launch(&cache, state, request, request_id)?;
|
||||
} else if request.owner_role == EPIPHANY_MODELING_OWNER_ROLE {
|
||||
// Ordinary Modeling launches remain valid, but carry no proposal authority.
|
||||
}
|
||||
|
|
@ -410,6 +482,52 @@ pub fn plan_coordinator_job_launch(
|
|||
})
|
||||
}
|
||||
|
||||
fn validate_frontier_plan_mind_launch(
|
||||
cache: &CultCache,
|
||||
state: &EpiphanyThreadState,
|
||||
launch: &EpiphanyJobLaunchRequest,
|
||||
request_id: &str,
|
||||
) -> Result<(
|
||||
RepoFrontierPlanMindRequest,
|
||||
RepoFrontierPlanningRequest,
|
||||
RepoFrontierPlanCandidate,
|
||||
EpiphanyRuntimeIdentity,
|
||||
)> {
|
||||
if launch.owner_role != EPIPHANY_MIND_OWNER_ROLE
|
||||
|| launch.binding_id != EPIPHANY_MIND_ROLE_BINDING_ID
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"frontier plan decision may only be carried by the Mind role launch"
|
||||
));
|
||||
}
|
||||
let request = cache
|
||||
.get::<RepoFrontierPlanMindRequest>(request_id)?
|
||||
.ok_or_else(|| anyhow!("Mind request does not exist"))?;
|
||||
let (planning, candidate) =
|
||||
crate::runtime_spine::validate_repo_frontier_plan_mind_request(cache, &request)?;
|
||||
let identity = cache
|
||||
.get::<EpiphanyRuntimeIdentity>(RUNTIME_IDENTITY_KEY)?
|
||||
.ok_or_else(|| anyhow!("Mind launch requires runtime identity"))?;
|
||||
let persisted = cache
|
||||
.get::<crate::EpiphanyThreadStateEntry>(crate::THREAD_STATE_KEY)?
|
||||
.ok_or_else(|| anyhow!("Mind launch requires thread state"))?;
|
||||
if persisted.state()? != *state
|
||||
|| request.runtime_id != identity.runtime_id
|
||||
|| request.thread_id != persisted.thread_id
|
||||
|| launch.launch_document.thread_id() != request.thread_id
|
||||
{
|
||||
return Err(anyhow!("Mind launch provenance mismatch"));
|
||||
}
|
||||
if cache
|
||||
.get_all::<RepoFrontierPlanMindLaunchBinding>()?
|
||||
.iter()
|
||||
.any(|b| b.mind_request_id == request_id)
|
||||
{
|
||||
return Err(anyhow!("Mind request already bound"));
|
||||
}
|
||||
Ok((request, planning, candidate, identity))
|
||||
}
|
||||
|
||||
fn validate_frontier_planning_launch(
|
||||
cache: &CultCache,
|
||||
state: &EpiphanyThreadState,
|
||||
|
|
@ -891,9 +1009,97 @@ pub(crate) mod tests {
|
|||
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)?),
|
||||
frontier_plan_mind_request_id: None,
|
||||
frontier_plan_mind_decision_msgpack: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn launch_frontier_mind_result(
|
||||
store: &Path,
|
||||
imagination_result: &EpiphanyRuntimeRoleWorkerResult,
|
||||
decision: RepoFrontierPlanDecision,
|
||||
suffix: &str,
|
||||
) -> Result<EpiphanyRuntimeRoleWorkerResult> {
|
||||
let mind_request = crate::commit_repo_frontier_plan_mind_request(
|
||||
store,
|
||||
&imagination_result.result_id,
|
||||
"2026-07-15T09:00:05Z",
|
||||
)?;
|
||||
let mut cache = coordinator_acceptance_cache(store)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
let state = cache
|
||||
.get_required::<EpiphanyThreadStateEntry>(THREAD_STATE_KEY)?
|
||||
.state()?;
|
||||
let launch = crate::build_epiphany_frontier_plan_mind_launch_request(
|
||||
&mind_request.thread_id,
|
||||
Some(state.revision),
|
||||
Some(60),
|
||||
&state,
|
||||
mind_request.request_id.clone(),
|
||||
)
|
||||
.map_err(|error| anyhow!(error))?;
|
||||
let job_id = format!("backend-mind-decision-{suffix}");
|
||||
let plan = plan_coordinator_job_launch(
|
||||
&state,
|
||||
&launch,
|
||||
store,
|
||||
format!("launcher-mind-{suffix}"),
|
||||
job_id.clone(),
|
||||
)?;
|
||||
commit_coordinator_job_launch(
|
||||
store,
|
||||
&mind_request.thread_id,
|
||||
&state,
|
||||
&launch,
|
||||
&plan,
|
||||
"2026-07-15T09:00:06Z".into(),
|
||||
)?;
|
||||
let payload = RepoFrontierPlanMindDecision {
|
||||
mind_request_id: mind_request.request_id.clone(),
|
||||
planning_request_id: mind_request.planning_request_id.clone(),
|
||||
imagination_result_id: mind_request.imagination_result_id.clone(),
|
||||
candidate_id: mind_request.candidate_id.clone(),
|
||||
candidate_sha256: mind_request.candidate_sha256.clone(),
|
||||
decision,
|
||||
rationale: format!("Mind judged the exact typed candidate as {decision:?}."),
|
||||
decided_at: "2026-07-15T09:00:07Z".into(),
|
||||
};
|
||||
let result = EpiphanyRuntimeRoleWorkerResult {
|
||||
schema_version: RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.into(),
|
||||
result_id: format!("frontier-mind-result-{suffix}"),
|
||||
job_id,
|
||||
role_id: "mindAdmissionReview".into(),
|
||||
verdict: format!("{decision:?}").to_lowercase(),
|
||||
summary: "Judged one exact frontier plan candidate.".into(),
|
||||
next_safe_move: "Coordinator may admit this immutable judgment.".into(),
|
||||
checkpoint_summary: None,
|
||||
scratch_summary: None,
|
||||
files_inspected: Vec::new(),
|
||||
frontier_node_ids: Vec::new(),
|
||||
evidence_ids: vec![mind_request.request_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: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
frontier_plan_mind_request_id: Some(mind_request.request_id),
|
||||
frontier_plan_mind_decision_msgpack: Some(rmp_serde::to_vec_named(&payload)?),
|
||||
};
|
||||
put_runtime_role_worker_result(store, &result)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn research_launch(state: &EpiphanyThreadState) -> EpiphanyJobLaunchRequest {
|
||||
build_epiphany_role_launch_request(
|
||||
"thread-1",
|
||||
|
|
@ -1428,7 +1634,7 @@ pub(crate) mod tests {
|
|||
#[test]
|
||||
fn frontier_planning_result_refuses_hostile_substitution_before_persistence() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
for mutation in 0..9 {
|
||||
for mutation in 0..10 {
|
||||
let (store, state, launch, planning) =
|
||||
frontier_planning_launch_fixture(root.path(), &format!("result-{mutation}"))?;
|
||||
let job_id = format!("backend-result-{mutation}");
|
||||
|
|
@ -1456,7 +1662,7 @@ pub(crate) mod tests {
|
|||
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]),
|
||||
_ => {
|
||||
7 | 8 => {
|
||||
let mut candidate = result.frontier_plan_candidate()?.unwrap();
|
||||
if mutation == 7 {
|
||||
candidate.frontier_item_hash = "adjacent-frontier".into();
|
||||
|
|
@ -1466,6 +1672,7 @@ pub(crate) mod tests {
|
|||
result.frontier_plan_candidate_msgpack =
|
||||
Some(rmp_serde::to_vec_named(&candidate)?);
|
||||
}
|
||||
_ => result.item_error = Some("planner failed after emitting cargo".into()),
|
||||
}
|
||||
let before = std::fs::read(&store)?;
|
||||
assert!(
|
||||
|
|
@ -1478,6 +1685,573 @@ pub(crate) mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mind_adopt_installs_plan_in_model_and_hands_route_copies_it() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
let (store, state, launch, planning) =
|
||||
frontier_planning_launch_fixture(root.path(), "mind-adopt")?;
|
||||
let plan = plan_coordinator_job_launch(
|
||||
&state,
|
||||
&launch,
|
||||
&store,
|
||||
"launcher-mind-adopt".into(),
|
||||
"backend-mind-adopt".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-mind-adopt", "2026-07-15T09:00:04Z")?;
|
||||
put_runtime_role_worker_result(&store, &result)?;
|
||||
let mind_result =
|
||||
launch_frontier_mind_result(&store, &result, RepoFrontierPlanDecision::Adopt, "adopt")?;
|
||||
|
||||
let before = runtime_current_repo_model(&store)?.expect("pre-Adopt model");
|
||||
let mut illicit_item = before
|
||||
.frontier
|
||||
.iter()
|
||||
.find(|item| item.id == planning.frontier_item_id)
|
||||
.unwrap()
|
||||
.clone();
|
||||
illicit_item.recommended_next_organ = "Hands".into();
|
||||
let illicit = RepoModelPatch {
|
||||
patch_id: "generic-adopt-bypass".into(),
|
||||
base_revision: before.model_revision,
|
||||
base_hash: memory_graph_model_hash(&before)?,
|
||||
applied_at: "2026-07-15T09:00:05Z".into(),
|
||||
purpose: RepoModelPatchPurpose::Evolution,
|
||||
operations: vec![RepoModelPatchOperation::ReviseFrontier { item: illicit_item }],
|
||||
};
|
||||
assert!(derive_repo_model_patch(&before, &illicit).is_err());
|
||||
|
||||
let decision = commit_repo_frontier_plan_decision(&store, &mind_result.result_id)?;
|
||||
let retry = commit_repo_frontier_plan_decision(&store, &mind_result.result_id)?;
|
||||
assert_eq!(retry, decision);
|
||||
assert!(!decision.model_admission_receipt_id.is_empty());
|
||||
let current = runtime_current_repo_model(&store)?.expect("admitted Adopt model");
|
||||
assert_eq!(current.model_revision, before.model_revision + 1);
|
||||
let item = current
|
||||
.frontier
|
||||
.iter()
|
||||
.find(|item| item.id == planning.frontier_item_id)
|
||||
.expect("adopted frontier");
|
||||
assert_eq!(item.recommended_next_organ, "Hands");
|
||||
let adopted = item.adopted_plan.as_ref().expect("model-owned plan");
|
||||
assert_eq!(adopted.planning_request_id, planning.request_id);
|
||||
assert_eq!(adopted.result_id, result.result_id);
|
||||
assert_eq!(adopted.job_id, result.job_id);
|
||||
assert_eq!(adopted.candidate_id, decision.candidate_id);
|
||||
assert_eq!(adopted.candidate_sha256, decision.candidate_sha256);
|
||||
let mut illicit_adopted_item = item.clone();
|
||||
illicit_adopted_item.source_scope = vec!["different/execution-scope".into()];
|
||||
let illicit_adopted_revision = RepoModelPatch {
|
||||
patch_id: "generic-adopted-anatomy-mutation".into(),
|
||||
base_revision: current.model_revision,
|
||||
base_hash: memory_graph_model_hash(¤t)?,
|
||||
applied_at: "2026-07-15T09:00:06Z".into(),
|
||||
purpose: RepoModelPatchPurpose::Evolution,
|
||||
operations: vec![RepoModelPatchOperation::ReviseFrontier {
|
||||
item: illicit_adopted_item,
|
||||
}],
|
||||
};
|
||||
assert!(derive_repo_model_patch(¤t, &illicit_adopted_revision).is_err());
|
||||
let route = select_and_commit_repo_frontier_route(&store, "2026-07-15T09:00:06Z")?;
|
||||
assert_eq!(route.adopted_plan.as_ref(), Some(adopted));
|
||||
assert_eq!(route.source_scope, adopted.safe_paths);
|
||||
let grant = crate::substrate_gate_coordinator_implementation_grant(
|
||||
"adopt-hands-grant".into(),
|
||||
"adopt-hands-job".into(),
|
||||
adopted.safe_paths.clone(),
|
||||
"2026-07-15T09:00:08Z".into(),
|
||||
);
|
||||
crate::put_substrate_gate_repo_access_grant_receipt(&store, &grant)?;
|
||||
let intent = crate::HandsActionIntent {
|
||||
schema_version: crate::HANDS_ACTION_INTENT_SCHEMA_VERSION.into(),
|
||||
intent_id: "adopt-hands-intent".into(),
|
||||
runtime_job_id: grant.runtime_job_id.clone(),
|
||||
binding_id: grant.binding_id.clone(),
|
||||
role: grant.role.clone(),
|
||||
authority_scope: grant.authority_scope.clone(),
|
||||
requested_action: "continueImplementation".into(),
|
||||
requested_paths: adopted.safe_paths.clone(),
|
||||
substrate_gate_grant_receipt_id: grant.receipt_id.clone(),
|
||||
requested_at: "2026-07-15T09:00:09Z".into(),
|
||||
contract: "Hands intent executes only the admitted plan.".into(),
|
||||
frontier_route_id: route.route_id.clone(),
|
||||
plan_candidate_sha256: adopted.candidate_sha256.clone(),
|
||||
plan_action: adopted.action.clone(),
|
||||
};
|
||||
crate::put_hands_action_intent(&store, &intent)?;
|
||||
let review = crate::hands_action_review_for_intent(
|
||||
"adopt-hands-review".into(),
|
||||
&intent,
|
||||
"approved".into(),
|
||||
vec!["patch".into(), "command".into(), "commit".into()],
|
||||
vec!["Exact admitted plan binding is present.".into()],
|
||||
"2026-07-15T09:00:10Z".into(),
|
||||
);
|
||||
crate::put_hands_action_review(&store, &review)?;
|
||||
let authority = crate::RepoFrontierHandsAuthority {
|
||||
schema_version: crate::REPO_FRONTIER_HANDS_AUTHORITY_SCHEMA_VERSION.into(),
|
||||
authority_id: "adopt-hands-authority".into(),
|
||||
route_id: route.route_id.clone(),
|
||||
model_revision: route.model_revision,
|
||||
model_hash: route.model_hash.clone(),
|
||||
frontier_item_id: route.frontier_item_id.clone(),
|
||||
frontier_item_hash: route.frontier_item_hash.clone(),
|
||||
hands_intent_id: intent.intent_id.clone(),
|
||||
hands_review_id: review.review_id.clone(),
|
||||
substrate_grant_receipt_id: grant.receipt_id.clone(),
|
||||
requested_paths: adopted.safe_paths.clone(),
|
||||
granted_at: "2026-07-15T09:00:11Z".into(),
|
||||
contract: crate::REPO_FRONTIER_HANDS_AUTHORITY_CONTRACT.into(),
|
||||
};
|
||||
crate::put_repo_frontier_hands_authority(&store, &authority)?;
|
||||
let patch_receipt = crate::hands_patch_receipt_for_review(
|
||||
"adopt-hands-patch".into(),
|
||||
&intent,
|
||||
&review,
|
||||
adopted.safe_paths.clone(),
|
||||
"bounded patch".into(),
|
||||
"2026-07-15T09:00:12Z".into(),
|
||||
);
|
||||
crate::put_hands_patch_receipt(&store, &patch_receipt)?;
|
||||
let wrong_command = crate::hands_command_receipt_for_review(
|
||||
"adopt-hands-command-wrong".into(),
|
||||
&intent,
|
||||
&review,
|
||||
"cargo test unrelated".into(),
|
||||
"0".into(),
|
||||
"stdout".into(),
|
||||
"stderr".into(),
|
||||
"wrong command".into(),
|
||||
"2026-07-15T09:00:13Z".into(),
|
||||
);
|
||||
let commit_receipt = crate::hands_commit_receipt_for_review(
|
||||
"adopt-hands-commit".into(),
|
||||
&intent,
|
||||
&review,
|
||||
"abc123".into(),
|
||||
"main".into(),
|
||||
adopted.safe_paths.clone(),
|
||||
adopted.commit_message.clone(),
|
||||
"2026-07-15T09:00:14Z".into(),
|
||||
);
|
||||
crate::put_hands_commit_receipt(&store, &commit_receipt)?;
|
||||
let hostile_request = crate::RepoFrontierVerificationRequest {
|
||||
schema_version: crate::REPO_FRONTIER_VERIFICATION_REQUEST_SCHEMA_VERSION.into(),
|
||||
request_id: "adopt-verification-hostile".into(),
|
||||
route_id: route.route_id.clone(),
|
||||
model_revision: route.model_revision,
|
||||
model_hash: route.model_hash.clone(),
|
||||
frontier_item_id: route.frontier_item_id.clone(),
|
||||
frontier_item_hash: route.frontier_item_hash.clone(),
|
||||
hands_intent_id: intent.intent_id.clone(),
|
||||
hands_review_id: review.review_id.clone(),
|
||||
hands_patch_receipt_id: patch_receipt.receipt_id.clone(),
|
||||
hands_command_receipt_id: wrong_command.receipt_id.clone(),
|
||||
hands_commit_receipt_id: commit_receipt.receipt_id.clone(),
|
||||
requested_at: "2026-07-15T09:00:15Z".into(),
|
||||
contract: crate::REPO_FRONTIER_VERIFICATION_REQUEST_CONTRACT.into(),
|
||||
};
|
||||
assert!(crate::put_repo_frontier_verification_request(&store, &hostile_request).is_err());
|
||||
|
||||
// Prove the whole admitted-plan nerve, not merely its neighboring joints. The
|
||||
// rejected command above remains immutable evidence, while the exact planned
|
||||
// command becomes the only chain Soul is allowed to verify.
|
||||
let exact_command = crate::hands_command_receipt_for_review(
|
||||
"adopt-hands-command-exact".into(),
|
||||
&intent,
|
||||
&review,
|
||||
adopted.command.clone(),
|
||||
"0".into(),
|
||||
"stdout-exact".into(),
|
||||
"stderr-exact".into(),
|
||||
"exact admitted command passed".into(),
|
||||
"2026-07-15T09:00:13Z".into(),
|
||||
);
|
||||
crate::put_hands_command_receipt(&store, &exact_command)?;
|
||||
let chain =
|
||||
crate::runtime_latest_hands_receipt_chain_after(&store, "2026-07-15T09:00:11Z")?
|
||||
.expect("exact admitted Hands chain");
|
||||
assert_eq!(chain.patch_receipt_id, patch_receipt.receipt_id);
|
||||
assert_eq!(chain.command_receipt_id, exact_command.receipt_id);
|
||||
assert_eq!(chain.commit_receipt_id, commit_receipt.receipt_id);
|
||||
let verification_request = crate::commit_repo_frontier_verification_request_for_chain(
|
||||
&store,
|
||||
&chain,
|
||||
"2026-07-15T09:00:17Z",
|
||||
)?;
|
||||
assert_eq!(verification_request.route_id, route.route_id);
|
||||
|
||||
let verification_result = crate::EpiphanyRuntimeRoleWorkerResult {
|
||||
schema_version: crate::RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.into(),
|
||||
result_id: "adopt-verification-result".into(),
|
||||
job_id: "adopt-verification-job".into(),
|
||||
role_id: "verification".into(),
|
||||
verdict: "pass".into(),
|
||||
summary: "Exact adopted plan passed Soul verification.".into(),
|
||||
next_safe_move: "Modeling incorporates the verdict.".into(),
|
||||
checkpoint_summary: None,
|
||||
scratch_summary: None,
|
||||
files_inspected: adopted.safe_paths.clone(),
|
||||
frontier_node_ids: vec![route.frontier_item_id.clone()],
|
||||
evidence_ids: vec![verification_request.request_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: Default::default(),
|
||||
repo_model_patch_msgpack: None,
|
||||
verification_request_id: Some(verification_request.request_id.clone()),
|
||||
frontier_route_id: Some(route.route_id.clone()),
|
||||
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,
|
||||
frontier_plan_mind_request_id: None,
|
||||
frontier_plan_mind_decision_msgpack: None,
|
||||
};
|
||||
crate::put_runtime_role_worker_result(&store, &verification_result)?;
|
||||
let soul_verdict = crate::SoulVerdictReceipt {
|
||||
schema_version: crate::SOUL_VERDICT_RECEIPT_SCHEMA_VERSION.into(),
|
||||
receipt_id: "adopt-soul-verdict".into(),
|
||||
source_result_id: verification_result.result_id.clone(),
|
||||
source_job_id: verification_result.job_id.clone(),
|
||||
verdict: "pass".into(),
|
||||
summary: verification_result.summary.clone(),
|
||||
evidence_ids: verification_result.evidence_ids.clone(),
|
||||
risks: Vec::new(),
|
||||
emitted_at: "2026-07-15T09:00:18Z".into(),
|
||||
contract: "Soul judges the exact routed consequence chain.".into(),
|
||||
verification_request_id: verification_request.request_id.clone(),
|
||||
frontier_route_id: route.route_id.clone(),
|
||||
};
|
||||
crate::put_soul_verdict_receipt(&store, &soul_verdict)?;
|
||||
let acceptance = epiphany_state_model::EpiphanyAcceptanceReceipt {
|
||||
id: "adopt-verification-acceptance".into(),
|
||||
result_id: verification_result.result_id.clone(),
|
||||
job_id: verification_result.job_id.clone(),
|
||||
binding_id: "verification-worker".into(),
|
||||
surface: "roleAccept".into(),
|
||||
role_id: "verification".into(),
|
||||
status: "accepted".into(),
|
||||
accepted_at: "2026-07-15T09:00:18Z".into(),
|
||||
..Default::default()
|
||||
};
|
||||
let accepted_state = epiphany_state_model::EpiphanyThreadState {
|
||||
revision: 1,
|
||||
acceptance_receipts: vec![acceptance.clone()],
|
||||
..Default::default()
|
||||
};
|
||||
let mut state_cache = crate::runtime_spine_cache(&store)?;
|
||||
state_cache.put(
|
||||
crate::THREAD_STATE_KEY,
|
||||
&crate::EpiphanyThreadStateEntry::from_state("mind-adopt-thread", &accepted_state)?,
|
||||
)?;
|
||||
let modeling_request = crate::commit_repo_frontier_modeling_request(&store, &acceptance)?;
|
||||
|
||||
let pre_incorporation =
|
||||
crate::runtime_current_repo_model(&store)?.expect("model before verdict incorporation");
|
||||
let mut resolved_item = pre_incorporation
|
||||
.frontier
|
||||
.iter()
|
||||
.find(|candidate| candidate.id == route.frontier_item_id)
|
||||
.expect("routed adopted item before incorporation")
|
||||
.clone();
|
||||
let adopted_before = rmp_serde::to_vec_named(
|
||||
resolved_item
|
||||
.adopted_plan
|
||||
.as_ref()
|
||||
.expect("adopted plan before closure"),
|
||||
)?;
|
||||
resolved_item.status = crate::RepoFrontierStatus::Resolved;
|
||||
resolved_item.updated_at = Some("2026-07-15T09:00:19Z".into());
|
||||
resolved_item
|
||||
.evidence_refs
|
||||
.push(verification_request.request_id.clone());
|
||||
resolved_item
|
||||
.evidence_refs
|
||||
.push(soul_verdict.receipt_id.clone());
|
||||
resolved_item.evidence_refs.sort();
|
||||
resolved_item.evidence_refs.dedup();
|
||||
let incorporation_patch = crate::RepoModelPatch {
|
||||
patch_id: "adopt-verdict-incorporation-patch".into(),
|
||||
base_revision: pre_incorporation.model_revision,
|
||||
base_hash: crate::memory_graph_model_hash(&pre_incorporation)?,
|
||||
applied_at: "2026-07-15T09:00:19Z".into(),
|
||||
purpose: crate::RepoModelPatchPurpose::IncorporateFrontierVerdict {
|
||||
route_id: route.route_id.clone(),
|
||||
soul_verdict_receipt_id: soul_verdict.receipt_id.clone(),
|
||||
},
|
||||
operations: vec![crate::RepoModelPatchOperation::ReviseFrontier {
|
||||
item: resolved_item,
|
||||
}],
|
||||
};
|
||||
let incorporation_bytes = rmp_serde::to_vec_named(&incorporation_patch)?;
|
||||
let modeling_result = crate::EpiphanyRuntimeRoleWorkerResult {
|
||||
schema_version: crate::RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.into(),
|
||||
result_id: "adopt-incorporation-result".into(),
|
||||
job_id: "adopt-incorporation-job".into(),
|
||||
role_id: "modeling".into(),
|
||||
verdict: "checkpoint-ready".into(),
|
||||
summary: "Close the verified adopted frontier.".into(),
|
||||
next_safe_move: "Mind admits the exact Modeling transition.".into(),
|
||||
checkpoint_summary: None,
|
||||
scratch_summary: None,
|
||||
files_inspected: Vec::new(),
|
||||
frontier_node_ids: vec![route.frontier_item_id.clone()],
|
||||
evidence_ids: vec![soul_verdict.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: Default::default(),
|
||||
repo_model_patch_msgpack: Some(incorporation_bytes.clone()),
|
||||
verification_request_id: None,
|
||||
frontier_route_id: None,
|
||||
repo_frontier_modeling_request_id: Some(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,
|
||||
frontier_plan_mind_request_id: None,
|
||||
frontier_plan_mind_decision_msgpack: None,
|
||||
};
|
||||
crate::put_runtime_role_worker_result(&store, &modeling_result)?;
|
||||
let incorporation_review = crate::RepoModelAdmissionReview {
|
||||
schema_version: crate::REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.into(),
|
||||
review_id: "adopt-incorporation-review".into(),
|
||||
result_id: modeling_result.result_id.clone(),
|
||||
job_id: modeling_result.job_id.clone(),
|
||||
patch_id: incorporation_patch.patch_id.clone(),
|
||||
patch_sha256: format!("{:x}", Sha256::digest(&incorporation_bytes)),
|
||||
base_revision: incorporation_patch.base_revision,
|
||||
base_hash: incorporation_patch.base_hash.clone(),
|
||||
decision: crate::MindGatewayDecision::Accept,
|
||||
evidence_ids: modeling_result.evidence_ids.clone(),
|
||||
reviewed_at: "2026-07-15T09:00:20Z".into(),
|
||||
contract: crate::REPO_MODEL_ADMISSION_CONTRACT.into(),
|
||||
};
|
||||
let incorporation = crate::commit_repo_model_admission(
|
||||
&store,
|
||||
&modeling_result.result_id,
|
||||
&incorporation_review,
|
||||
)?;
|
||||
assert_eq!(incorporation.frontier_route_id, route.route_id);
|
||||
assert_eq!(
|
||||
incorporation.frontier_modeling_request_id,
|
||||
modeling_request.request_id
|
||||
);
|
||||
let closed = crate::runtime_current_repo_model(&store)?.expect("closed model");
|
||||
let closed_item = closed
|
||||
.frontier
|
||||
.iter()
|
||||
.find(|candidate| candidate.id == route.frontier_item_id)
|
||||
.expect("closed adopted frontier");
|
||||
assert_eq!(closed_item.status, crate::RepoFrontierStatus::Resolved);
|
||||
assert_eq!(
|
||||
rmp_serde::to_vec_named(
|
||||
closed_item
|
||||
.adopted_plan
|
||||
.as_ref()
|
||||
.expect("plan survives closure")
|
||||
)?,
|
||||
adopted_before,
|
||||
"verdict incorporation must preserve the adopted plan byte-for-byte"
|
||||
);
|
||||
let cache = coordinator_acceptance_cache(&store)?;
|
||||
let admission = cache
|
||||
.get::<RepoModelAdmissionReceipt>(&decision.model_admission_receipt_id)?
|
||||
.expect("Adopt admission receipt");
|
||||
assert_eq!(admission.frontier_plan_decision_id, decision.decision_id);
|
||||
assert_eq!(admission.admitted_hash, memory_graph_model_hash(¤t)?);
|
||||
assert_eq!(
|
||||
commit_repo_frontier_plan_decision(&store, &mind_result.result_id)?,
|
||||
decision
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mind_refuse_and_hold_are_terminal_inert_receipts() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
for (suffix, terminal) in [
|
||||
("refuse", RepoFrontierPlanDecision::Refuse),
|
||||
("hold", RepoFrontierPlanDecision::Hold),
|
||||
] {
|
||||
let (store, state, launch, planning) =
|
||||
frontier_planning_launch_fixture(root.path(), suffix)?;
|
||||
let job_id = format!("backend-{suffix}");
|
||||
let plan = plan_coordinator_job_launch(
|
||||
&state,
|
||||
&launch,
|
||||
&store,
|
||||
format!("launcher-{suffix}"),
|
||||
job_id.clone(),
|
||||
)?;
|
||||
commit_coordinator_job_launch(
|
||||
&store,
|
||||
&planning.thread_id,
|
||||
&state,
|
||||
&launch,
|
||||
&plan,
|
||||
"2026-07-15T09:00:03Z".into(),
|
||||
)?;
|
||||
let result = frontier_planning_result(&planning, &job_id, "2026-07-15T09:00:04Z")?;
|
||||
put_runtime_role_worker_result(&store, &result)?;
|
||||
let mind_result = launch_frontier_mind_result(&store, &result, terminal, suffix)?;
|
||||
let before = std::fs::read(&store)?;
|
||||
let before_model = runtime_current_repo_model(&store)?.unwrap();
|
||||
let receipt = commit_repo_frontier_plan_decision(&store, &mind_result.result_id)?;
|
||||
assert!(receipt.model_admission_receipt_id.is_empty());
|
||||
assert_eq!(runtime_current_repo_model(&store)?.unwrap(), before_model);
|
||||
assert!(select_and_commit_repo_frontier_route(&store, "2026-07-15T09:00:06Z").is_err());
|
||||
assert!(std::fs::read(&store)?.len() > before.len());
|
||||
assert!(
|
||||
select_and_commit_repo_frontier_planning_request(&store, "2026-07-15T09:00:07Z")
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concurrent_mind_admission_retries_converge_on_one_terminal_receipt() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
let (store, state, launch, planning) =
|
||||
frontier_planning_launch_fixture(root.path(), "mind-race")?;
|
||||
let plan = plan_coordinator_job_launch(
|
||||
&state,
|
||||
&launch,
|
||||
&store,
|
||||
"launcher-mind-race".into(),
|
||||
"backend-mind-race".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-mind-race", "2026-07-15T09:00:04Z")?;
|
||||
put_runtime_role_worker_result(&store, &result)?;
|
||||
let mind_result =
|
||||
launch_frontier_mind_result(&store, &result, RepoFrontierPlanDecision::Adopt, "race")?;
|
||||
let left_store = store.clone();
|
||||
let right_store = store.clone();
|
||||
let left_id = mind_result.result_id.clone();
|
||||
let right_id = mind_result.result_id.clone();
|
||||
let barrier = std::sync::Arc::new(std::sync::Barrier::new(2));
|
||||
let left_barrier = barrier.clone();
|
||||
let right_barrier = barrier.clone();
|
||||
let left = std::thread::spawn(move || {
|
||||
crate::runtime_spine::commit_repo_frontier_plan_decision_with_pre_cas(
|
||||
left_store,
|
||||
&left_id,
|
||||
&move || {
|
||||
left_barrier.wait();
|
||||
},
|
||||
)
|
||||
});
|
||||
let right = std::thread::spawn(move || {
|
||||
crate::runtime_spine::commit_repo_frontier_plan_decision_with_pre_cas(
|
||||
right_store,
|
||||
&right_id,
|
||||
&move || {
|
||||
right_barrier.wait();
|
||||
},
|
||||
)
|
||||
});
|
||||
let outcomes = [left.join().unwrap(), right.join().unwrap()];
|
||||
assert_eq!(outcomes.iter().filter(|outcome| outcome.is_ok()).count(), 2);
|
||||
let mut cache = coordinator_acceptance_cache(&store)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
let decisions = cache
|
||||
.get_all::<RepoFrontierPlanDecisionReceipt>()?
|
||||
.into_iter()
|
||||
.filter(|receipt| receipt.planning_request_id == planning.request_id)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(decisions.len(), 1);
|
||||
let current = runtime_current_repo_model(&store)?.unwrap();
|
||||
if decisions[0].decision == RepoFrontierPlanDecision::Adopt {
|
||||
assert_eq!(current.model_revision, planning.model_revision + 1);
|
||||
assert!(!decisions[0].model_admission_receipt_id.is_empty());
|
||||
} else {
|
||||
assert_eq!(current.model_revision, planning.model_revision);
|
||||
assert!(decisions[0].model_admission_receipt_id.is_empty());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn immutable_mind_result_rejects_causal_substitution_and_foreign_cargo() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
for mutation in 0..7 {
|
||||
let suffix = format!("mind-hostile-{mutation}");
|
||||
let (store, state, launch, planning) =
|
||||
frontier_planning_launch_fixture(root.path(), &suffix)?;
|
||||
let imagination_job = format!("backend-imagination-{suffix}");
|
||||
let plan = plan_coordinator_job_launch(
|
||||
&state,
|
||||
&launch,
|
||||
&store,
|
||||
format!("launcher-imagination-{suffix}"),
|
||||
imagination_job.clone(),
|
||||
)?;
|
||||
commit_coordinator_job_launch(
|
||||
&store,
|
||||
&planning.thread_id,
|
||||
&state,
|
||||
&launch,
|
||||
&plan,
|
||||
"2026-07-15T09:00:03Z".into(),
|
||||
)?;
|
||||
let imagination =
|
||||
frontier_planning_result(&planning, &imagination_job, "2026-07-15T09:00:04Z")?;
|
||||
put_runtime_role_worker_result(&store, &imagination)?;
|
||||
let mut hostile = launch_frontier_mind_result(
|
||||
&store,
|
||||
&imagination,
|
||||
RepoFrontierPlanDecision::Adopt,
|
||||
&suffix,
|
||||
)?;
|
||||
match mutation {
|
||||
0 => hostile.frontier_plan_mind_request_id = Some("swapped-request".into()),
|
||||
1 => hostile.job_id = "swapped-job".into(),
|
||||
2 => hostile.role_id = "imagination".into(),
|
||||
3 => hostile.state_patch_msgpack = Some(vec![0]),
|
||||
4 => hostile.repo_model_patch_msgpack = Some(vec![0]),
|
||||
5 => {
|
||||
let mut payload = hostile.frontier_plan_mind_decision()?.unwrap();
|
||||
payload.candidate_sha256 = "swapped-candidate-hash".into();
|
||||
hostile.frontier_plan_mind_decision_msgpack =
|
||||
Some(rmp_serde::to_vec_named(&payload)?);
|
||||
}
|
||||
_ => hostile.item_error = Some("Mind failed after emitting judgment".into()),
|
||||
}
|
||||
let before = std::fs::read(&store)?;
|
||||
assert!(put_runtime_role_worker_result(&store, &hostile).is_err());
|
||||
assert_eq!(std::fs::read(&store)?, before);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concurrent_frontier_planning_launches_leave_only_winner_artifacts() -> Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
|
|
|
|||
|
|
@ -172,6 +172,18 @@ pub fn append_verification_hands_receipt_context(
|
|||
route.question,
|
||||
route.gap,
|
||||
));
|
||||
if let Some(plan) = route.adopted_plan.as_ref() {
|
||||
context.push_str(&format!(
|
||||
"adoptedCandidateSha256: {}\nadoptedAction: {}\nadoptedCommand: {}\nadoptedChecks: {}\nadoptedStopConditions: {}\nadoptedRollbackSteps: {}\nadoptedCommitMessage: {}\n",
|
||||
plan.candidate_sha256,
|
||||
plan.action,
|
||||
plan.command,
|
||||
plan.checks.join(" | "),
|
||||
plan.stop_conditions.join(" | "),
|
||||
plan.rollback_steps.join(" | "),
|
||||
plan.commit_message,
|
||||
));
|
||||
}
|
||||
context.push_str(
|
||||
"Soul is reviewing typed internal CultMesh telemetry for concrete Hands consequence evidence produced after the latest accepted Verification finding.\n",
|
||||
);
|
||||
|
|
@ -940,6 +952,8 @@ mod tests {
|
|||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
frontier_plan_mind_decision_msgpack: None,
|
||||
};
|
||||
crate::put_runtime_role_worker_result(store, &result)?;
|
||||
crate::commit_repo_model_admission(
|
||||
|
|
@ -1136,6 +1150,7 @@ mod tests {
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
created_at: "2026-06-02T00:00:00Z".to_string(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -1250,6 +1265,9 @@ mod tests {
|
|||
substrate_gate_grant_receipt_id: "substrate-grant-context".to_string(),
|
||||
requested_at: "2026-06-12T00:00:01Z".to_string(),
|
||||
contract: "Test Hands intent.".to_string(),
|
||||
frontier_route_id: String::new(),
|
||||
plan_candidate_sha256: String::new(),
|
||||
plan_action: String::new(),
|
||||
};
|
||||
crate::put_substrate_gate_repo_access_grant_receipt(
|
||||
&runtime_store,
|
||||
|
|
@ -1423,6 +1441,8 @@ mod tests {
|
|||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_candidate_msgpack: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
frontier_plan_mind_decision_msgpack: None,
|
||||
};
|
||||
crate::put_runtime_role_worker_result(&runtime_store, &verification_result)?;
|
||||
crate::put_soul_verdict_receipt(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub const HANDS_COMMIT_RECEIPT_TYPE: &str = "epiphany.hands.commit_receipt";
|
|||
pub const HANDS_PR_RECEIPT_TYPE: &str = "epiphany.hands.pr_receipt";
|
||||
pub const HANDS_ROLLBACK_RECEIPT_TYPE: &str = "epiphany.hands.rollback_receipt";
|
||||
pub const HANDS_ACTION_REFUSAL_RECEIPT_TYPE: &str = "epiphany.hands.action_refusal_receipt";
|
||||
pub const HANDS_ACTION_INTENT_SCHEMA_VERSION: &str = "epiphany.hands.action_intent.v0";
|
||||
pub const HANDS_ACTION_INTENT_SCHEMA_VERSION: &str = "epiphany.hands.action_intent.v1";
|
||||
pub const HANDS_ACTION_REVIEW_SCHEMA_VERSION: &str = "epiphany.hands.action_review.v0";
|
||||
pub const HANDS_COMMAND_RECEIPT_SCHEMA_VERSION: &str = "epiphany.hands.command_receipt.v0";
|
||||
pub const HANDS_PATCH_RECEIPT_SCHEMA_VERSION: &str = "epiphany.hands.patch_receipt.v0";
|
||||
|
|
@ -45,6 +45,12 @@ pub struct HandsActionIntent {
|
|||
pub requested_at: String,
|
||||
#[cultcache(key = 10)]
|
||||
pub contract: String,
|
||||
#[cultcache(key = 11, default)]
|
||||
pub frontier_route_id: String,
|
||||
#[cultcache(key = 12, default)]
|
||||
pub plan_candidate_sha256: String,
|
||||
#[cultcache(key = 13, default)]
|
||||
pub plan_action: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
|
||||
|
|
@ -475,6 +481,9 @@ mod tests {
|
|||
substrate_gate_grant_receipt_id: "substrate-grant-1".to_string(),
|
||||
requested_at: "2026-06-02T00:00:00Z".to_string(),
|
||||
contract: "Hands action intent is bounded by Substrate Gate.".to_string(),
|
||||
frontier_route_id: String::new(),
|
||||
plan_candidate_sha256: String::new(),
|
||||
plan_action: String::new(),
|
||||
};
|
||||
let review = hands_action_review_for_intent(
|
||||
"hands-review-1".to_string(),
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ mod weksa_interlingua;
|
|||
|
||||
pub use agent_launch::EPIPHANY_IMAGINATION_OWNER_ROLE;
|
||||
pub use agent_launch::EPIPHANY_IMAGINATION_ROLE_BINDING_ID;
|
||||
pub use agent_launch::EPIPHANY_MIND_OWNER_ROLE;
|
||||
pub use agent_launch::EPIPHANY_MIND_ROLE_BINDING_ID;
|
||||
pub use agent_launch::EPIPHANY_MODELING_OWNER_ROLE;
|
||||
pub use agent_launch::EPIPHANY_MODELING_ROLE_BINDING_ID;
|
||||
pub use agent_launch::EPIPHANY_REORIENT_LAUNCH_BINDING_ID;
|
||||
|
|
@ -52,6 +54,7 @@ pub use agent_launch::EpiphanyReorientationPromptConfig;
|
|||
pub use agent_launch::EpiphanyRolePromptConfig;
|
||||
pub use agent_launch::EpiphanySharedPromptConfig;
|
||||
pub use agent_launch::EpiphanySpecialistPromptConfig;
|
||||
pub use agent_launch::build_epiphany_frontier_plan_mind_launch_request;
|
||||
pub use agent_launch::build_epiphany_job_launch_request;
|
||||
pub use agent_launch::build_epiphany_reorient_launch_instruction;
|
||||
pub use agent_launch::build_epiphany_reorient_launch_request;
|
||||
|
|
@ -59,6 +62,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_plan_mind_output_schema;
|
||||
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;
|
||||
|
|
@ -614,6 +618,7 @@ pub use memory_graph::EpiphanyMemoryPatchCandidate;
|
|||
pub use memory_graph::MEMORY_GRAPH_KEY;
|
||||
pub use memory_graph::MEMORY_GRAPH_SCHEMA_VERSION;
|
||||
pub use memory_graph::MEMORY_GRAPH_TYPE;
|
||||
pub use memory_graph::RepoFrontierAdoptedPlan;
|
||||
pub use memory_graph::RepoFrontierItem;
|
||||
pub use memory_graph::RepoFrontierStatus;
|
||||
pub use memory_graph::RepoMemoryGraphRefresh;
|
||||
|
|
@ -780,6 +785,8 @@ 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_plan_decision;
|
||||
pub use runtime_spine::commit_repo_frontier_plan_mind_request;
|
||||
pub use runtime_spine::commit_repo_frontier_verification_request_for_chain;
|
||||
pub use runtime_spine::commit_repo_model_admission;
|
||||
pub use runtime_spine::commit_repo_model_claim_challenge;
|
||||
|
|
@ -978,6 +985,8 @@ pub use surfaces::EpiphanyTokenUsageSnapshot;
|
|||
pub use surfaces::EpiphanyViewLens;
|
||||
pub use surfaces::EpiphanyWorkerLaunchDocument;
|
||||
pub use surfaces::REORIENT_WORKER_OUTPUT_CONTRACT_ID;
|
||||
pub use surfaces::REPO_FRONTIER_PLAN_MIND_CONTEXT_CONTRACT;
|
||||
pub use surfaces::REPO_FRONTIER_PLAN_MIND_CONTEXT_SCHEMA_VERSION;
|
||||
pub use surfaces::REPO_FRONTIER_PLANNING_CONTEXT_CONTRACT;
|
||||
pub use surfaces::REPO_FRONTIER_PLANNING_CONTEXT_SCHEMA_VERSION;
|
||||
pub use surfaces::REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_CONTRACT;
|
||||
|
|
@ -985,6 +994,7 @@ pub use surfaces::REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_SCHEMA_VERSION;
|
|||
pub use surfaces::REPO_MODEL_CLAIM_REPAIR_CONTEXT_CONTRACT;
|
||||
pub use surfaces::REPO_MODEL_CLAIM_REPAIR_CONTEXT_SCHEMA_VERSION;
|
||||
pub use surfaces::ROLE_WORKER_OUTPUT_CONTRACT_ID;
|
||||
pub use surfaces::RepoFrontierPlanMindContextProjection;
|
||||
pub use surfaces::RepoFrontierPlanningContextProjection;
|
||||
pub use surfaces::RepoFrontierProposalModelingContextProjection;
|
||||
pub use surfaces::RepoModelClaimRepairContextProjection;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ pub use epiphany_state_model::EpiphanyMemoryNodeKind;
|
|||
pub use epiphany_state_model::EpiphanyMemoryPatchCandidate;
|
||||
pub use epiphany_state_model::EpiphanyMemoryProfile;
|
||||
pub use epiphany_state_model::EpiphanyMemorySummary;
|
||||
pub use epiphany_state_model::RepoFrontierAdoptedPlan;
|
||||
pub use epiphany_state_model::RepoFrontierItem;
|
||||
pub use epiphany_state_model::RepoFrontierStatus;
|
||||
pub use epiphany_state_model::RepoModelPatch;
|
||||
|
|
|
|||
|
|
@ -222,6 +222,43 @@ pub fn derive_repo_model_patch(
|
|||
current_hash
|
||||
));
|
||||
}
|
||||
match &patch.purpose {
|
||||
epiphany_state_model::RepoModelPatchPurpose::AdoptFrontierPlan {
|
||||
planning_request_id,
|
||||
result_id,
|
||||
candidate_id,
|
||||
} => {
|
||||
if patch.operations.len() != 1 {
|
||||
return Err(anyhow!(
|
||||
"AdoptFrontierPlan purpose requires exactly one dedicated operation"
|
||||
));
|
||||
}
|
||||
let RepoModelPatchOperation::AdoptFrontierPlan { adopted_plan, .. } =
|
||||
&patch.operations[0]
|
||||
else {
|
||||
return Err(anyhow!(
|
||||
"AdoptFrontierPlan purpose requires its dedicated operation"
|
||||
));
|
||||
};
|
||||
if adopted_plan.planning_request_id != *planning_request_id
|
||||
|| adopted_plan.result_id != *result_id
|
||||
|| adopted_plan.candidate_id != *candidate_id
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"AdoptFrontierPlan purpose and operation provenance mismatch"
|
||||
));
|
||||
}
|
||||
}
|
||||
_ if patch.operations.iter().any(|operation| {
|
||||
matches!(operation, RepoModelPatchOperation::AdoptFrontierPlan { .. })
|
||||
}) =>
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"only AdoptFrontierPlan purpose may carry the dedicated adoption operation"
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let mut next = current.clone();
|
||||
if next
|
||||
.lifecycle_receipts
|
||||
|
|
@ -231,7 +268,7 @@ pub fn derive_repo_model_patch(
|
|||
return Err(anyhow!("repo model patch_id has already been applied"));
|
||||
}
|
||||
for operation in &patch.operations {
|
||||
apply_operation(&mut next, operation, &patch.applied_at)?;
|
||||
apply_operation(&mut next, operation, &patch.purpose, &patch.applied_at)?;
|
||||
}
|
||||
next.model_revision = next
|
||||
.model_revision
|
||||
|
|
@ -288,6 +325,7 @@ pub fn derive_repo_model_patch(
|
|||
fn apply_operation(
|
||||
snapshot: &mut EpiphanyMemoryGraphSnapshot,
|
||||
operation: &RepoModelPatchOperation,
|
||||
purpose: &epiphany_state_model::RepoModelPatchPurpose,
|
||||
applied_at: &str,
|
||||
) -> Result<()> {
|
||||
match operation {
|
||||
|
|
@ -320,18 +358,55 @@ fn apply_operation(
|
|||
.ok_or_else(|| anyhow!("cannot retire missing edge {edge_id}"))?;
|
||||
edge.lifecycle = EpiphanyMemoryLifecycle::Retired;
|
||||
}
|
||||
RepoModelPatchOperation::UpsertFrontier { item } => insert_new(
|
||||
&mut snapshot.frontier,
|
||||
item.clone(),
|
||||
|value| &value.id,
|
||||
"frontier item",
|
||||
)?,
|
||||
RepoModelPatchOperation::ReviseFrontier { item } => revise(
|
||||
&mut snapshot.frontier,
|
||||
item.clone(),
|
||||
|value| &value.id,
|
||||
"frontier item",
|
||||
)?,
|
||||
RepoModelPatchOperation::UpsertFrontier { item } => {
|
||||
if item.adopted_plan.is_some() {
|
||||
return Err(anyhow!(
|
||||
"generic frontier upsert cannot author an adopted plan"
|
||||
));
|
||||
}
|
||||
insert_new(
|
||||
&mut snapshot.frontier,
|
||||
item.clone(),
|
||||
|value| &value.id,
|
||||
"frontier item",
|
||||
)?
|
||||
}
|
||||
RepoModelPatchOperation::ReviseFrontier { item } => {
|
||||
let existing = snapshot
|
||||
.frontier
|
||||
.iter()
|
||||
.find(|existing| existing.id == item.id)
|
||||
.ok_or_else(|| anyhow!("cannot revise missing frontier item {}", item.id))?;
|
||||
let verdict_may_close_adopted = matches!(
|
||||
purpose,
|
||||
epiphany_state_model::RepoModelPatchPurpose::IncorporateFrontierVerdict { .. }
|
||||
) && existing.adopted_plan.is_some()
|
||||
&& item.adopted_plan == existing.adopted_plan
|
||||
&& item.migration_body == existing.migration_body
|
||||
&& item.question == existing.question
|
||||
&& item.target_claim_ids == existing.target_claim_ids
|
||||
&& item.source_scope == existing.source_scope
|
||||
&& item.recommended_next_organ == existing.recommended_next_organ
|
||||
&& item.dependency_item_ids == existing.dependency_item_ids
|
||||
&& item.created_at == existing.created_at
|
||||
&& item.retired_at == existing.retired_at
|
||||
&& item.superseded_by == existing.superseded_by;
|
||||
if (existing.adopted_plan.is_some() && !verdict_may_close_adopted)
|
||||
|| item.adopted_plan != existing.adopted_plan
|
||||
|| (existing.recommended_next_organ == "Imagination"
|
||||
&& item.recommended_next_organ == "Hands")
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"generic frontier revision cannot alter adopted execution anatomy or own plan adoption"
|
||||
));
|
||||
}
|
||||
revise(
|
||||
&mut snapshot.frontier,
|
||||
item.clone(),
|
||||
|value| &value.id,
|
||||
"frontier item",
|
||||
)?
|
||||
}
|
||||
RepoModelPatchOperation::RetireFrontier {
|
||||
item_id,
|
||||
retired_at,
|
||||
|
|
@ -351,6 +426,56 @@ fn apply_operation(
|
|||
item.updated_at = Some(applied_at.to_string());
|
||||
item.superseded_by = superseded_by.clone();
|
||||
}
|
||||
RepoModelPatchOperation::AdoptFrontierPlan {
|
||||
frontier_item_id,
|
||||
expected_frontier_item_hash,
|
||||
adopted_plan,
|
||||
} => {
|
||||
let item = snapshot
|
||||
.frontier
|
||||
.iter_mut()
|
||||
.find(|item| &item.id == frontier_item_id)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("cannot adopt plan for missing frontier item {frontier_item_id}")
|
||||
})?;
|
||||
let current_hash = format!("{:x}", Sha256::digest(rmp_serde::to_vec_named(&*item)?));
|
||||
let safe_paths_are_bounded = !adopted_plan.safe_paths.is_empty()
|
||||
&& adopted_plan
|
||||
.safe_paths
|
||||
.windows(2)
|
||||
.all(|pair| pair[0] < pair[1])
|
||||
&& adopted_plan.safe_paths.iter().all(|path| {
|
||||
item.source_scope.iter().any(|scope| {
|
||||
path == scope
|
||||
|| path
|
||||
.starts_with(&format!("{}/", scope.trim_end_matches(['/', '\\'])))
|
||||
})
|
||||
});
|
||||
if current_hash != *expected_frontier_item_hash
|
||||
|| item.status != RepoFrontierStatus::Active
|
||||
|| item.recommended_next_organ != "Imagination"
|
||||
|| item.adopted_plan.is_some()
|
||||
|| adopted_plan.planning_request_id.trim().is_empty()
|
||||
|| adopted_plan.result_id.trim().is_empty()
|
||||
|| adopted_plan.job_id.trim().is_empty()
|
||||
|| adopted_plan.candidate_id.trim().is_empty()
|
||||
|| adopted_plan.candidate_sha256.trim().is_empty()
|
||||
|| adopted_plan.action.trim().is_empty()
|
||||
|| adopted_plan.command.trim().is_empty()
|
||||
|| adopted_plan.checks.is_empty()
|
||||
|| adopted_plan.stop_conditions.is_empty()
|
||||
|| adopted_plan.rollback_steps.is_empty()
|
||||
|| adopted_plan.commit_message.trim().is_empty()
|
||||
|| !safe_paths_are_bounded
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"frontier plan adoption requires the exact active Imagination frontier and complete bounded plan"
|
||||
));
|
||||
}
|
||||
item.adopted_plan = Some(adopted_plan.clone());
|
||||
item.recommended_next_organ = "Hands".to_string();
|
||||
item.updated_at = Some(applied_at.to_string());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -714,6 +714,60 @@ fn retiring_frontier_target_requires_resolving_frontier_in_same_patch() -> Resul
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verdict_incorporation_closes_adopted_frontier_without_rewriting_execution_plan() -> Result<()> {
|
||||
let mut snapshot = fixture_snapshot();
|
||||
let claim = snapshot.nodes[0].id.clone();
|
||||
let mut item = frontier_item("adopted-work", &claim);
|
||||
item.recommended_next_organ = "Hands".into();
|
||||
item.adopted_plan = Some(epiphany_state_model::RepoFrontierAdoptedPlan {
|
||||
planning_request_id: "planning-1".into(),
|
||||
result_id: "imagination-result-1".into(),
|
||||
job_id: "imagination-job-1".into(),
|
||||
candidate_id: "candidate-1".into(),
|
||||
candidate_sha256: "candidate-hash-1".into(),
|
||||
safe_paths: item.source_scope.clone(),
|
||||
action: "Implement exact bounded change".into(),
|
||||
command: "cargo test --lib".into(),
|
||||
checks: vec!["focused tests pass".into()],
|
||||
stop_conditions: vec!["scope changes".into()],
|
||||
rollback_steps: vec!["revert commit".into()],
|
||||
commit_message: "Implement exact bounded change".into(),
|
||||
});
|
||||
snapshot.frontier = vec![item.clone()];
|
||||
make_canonical(&mut snapshot, 1);
|
||||
let mut resolved = item.clone();
|
||||
resolved.status = RepoFrontierStatus::Resolved;
|
||||
resolved.evidence_refs = vec!["verification-request-1".into(), "soul-verdict-1".into()];
|
||||
resolved.updated_at = Some("2026-07-15T12:00:00Z".into());
|
||||
let patch = RepoModelPatch {
|
||||
patch_id: "incorporate-adopted-verdict".into(),
|
||||
base_revision: snapshot.model_revision,
|
||||
base_hash: memory_graph_model_hash(&snapshot)?,
|
||||
applied_at: "2026-07-15T12:00:00Z".into(),
|
||||
purpose: RepoModelPatchPurpose::IncorporateFrontierVerdict {
|
||||
route_id: "route-1".into(),
|
||||
soul_verdict_receipt_id: "soul-verdict-1".into(),
|
||||
},
|
||||
operations: vec![RepoModelPatchOperation::ReviseFrontier {
|
||||
item: resolved.clone(),
|
||||
}],
|
||||
};
|
||||
let next = derive_repo_model_patch(&snapshot, &patch)?;
|
||||
assert_eq!(next.frontier[0].status, RepoFrontierStatus::Resolved);
|
||||
assert_eq!(next.frontier[0].adopted_plan, item.adopted_plan);
|
||||
|
||||
let mut substituted = resolved;
|
||||
substituted.adopted_plan.as_mut().unwrap().command = "cargo test unrelated".into();
|
||||
let hostile = RepoModelPatch {
|
||||
patch_id: "rewrite-adopted-plan".into(),
|
||||
operations: vec![RepoModelPatchOperation::ReviseFrontier { item: substituted }],
|
||||
..patch
|
||||
};
|
||||
assert!(derive_repo_model_patch(&snapshot, &hostile).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memory_context_projects_frontier_prerequisites_first_under_budget() {
|
||||
let mut snapshot = fixture_snapshot();
|
||||
|
|
|
|||
|
|
@ -67,6 +67,12 @@ Heartbeat: every lane, including the Self, can be woken by the harness initiativ
|
|||
'''
|
||||
|
||||
[roles]
|
||||
mind = '''
|
||||
Act as one bounded admission-review procedure serving Epiphany Mind for one frontier-plan judgment.
|
||||
You are not an embodied lane, not Mind itself, not a second planner, and not an implementation worker. Mind remains persistent state and admission authority. Inspect only the exact typed review context: the immutable Imagination result, its embedded candidate, the planning request, and the current authority bindings carried there.
|
||||
Choose `adopt` only when the candidate is coherent, bounded by its safe paths, executable, falsifiable through its checks, honest about stop and rollback conditions, and suitable to become canonical Hands cargo. Choose `refuse` when it is wrong or unsafe. Choose `hold` when a specific missing fact prevents judgment. Give a concrete rationale grounded in the typed candidate.
|
||||
Echo every causal identifier and hash exactly. Do not emit state patches, repo-model patches, self patches, replacement plans, or authority belonging to another organ. Your immutable typed result is a judgment proposal; the admission transaction will independently replay its full causal chain before changing persistent state.
|
||||
'''
|
||||
imagination = '''
|
||||
Act as the Epiphany planning synthesis specialist for one bounded pass.
|
||||
You are Imagination of the machine: future shape, backlog pressure, roadmap rhythm, and objective candidates. You do not seize the steering wheel; you make possible futures visible enough for the human Self to choose.
|
||||
|
|
|
|||
|
|
@ -7,18 +7,18 @@ pub const REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION: &str =
|
|||
"epiphany.mind.repo_model_admission_review.v0";
|
||||
pub const REPO_MODEL_ADMISSION_RECEIPT_TYPE: &str = "epiphany.mind.repo_model_admission_receipt";
|
||||
pub const REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION: &str =
|
||||
"epiphany.mind.repo_model_admission_receipt.v3";
|
||||
"epiphany.mind.repo_model_admission_receipt.v4";
|
||||
pub const REPO_MODEL_MIGRATION_RECEIPT_TYPE: &str = "epiphany.mind.repo_model_migration_receipt";
|
||||
pub const REPO_MODEL_MIGRATION_RECEIPT_SCHEMA_VERSION: &str =
|
||||
"epiphany.mind.repo_model_migration_receipt.v0";
|
||||
pub const REPO_MODEL_ADMISSION_CONTRACT: &str = "epiphany.repo_model_admission.v3";
|
||||
pub const REPO_MODEL_ADMISSION_CONTRACT: &str = "epiphany.repo_model_admission.v4";
|
||||
pub const REPO_MODEL_MIGRATION_CONTRACT: &str = "epiphany.repo_model_migration.v0";
|
||||
pub const REPO_FRONTIER_ROUTE_TYPE: &str = "epiphany.self.repo_frontier_route";
|
||||
pub const REPO_FRONTIER_ROUTE_SCHEMA_VERSION: &str = "epiphany.self.repo_frontier_route.v0";
|
||||
pub const REPO_FRONTIER_ROUTE_SCHEMA_VERSION: &str = "epiphany.self.repo_frontier_route.v1";
|
||||
pub const REPO_FRONTIER_HANDS_AUTHORITY_TYPE: &str = "epiphany.hands.repo_frontier_authority";
|
||||
pub const REPO_FRONTIER_HANDS_AUTHORITY_SCHEMA_VERSION: &str =
|
||||
"epiphany.hands.repo_frontier_authority.v0";
|
||||
pub const REPO_FRONTIER_ROUTE_CONTRACT: &str = "epiphany.repo_frontier_route.v0";
|
||||
pub const REPO_FRONTIER_ROUTE_CONTRACT: &str = "epiphany.repo_frontier_route.v1";
|
||||
pub const REPO_FRONTIER_HANDS_AUTHORITY_CONTRACT: &str =
|
||||
"epiphany.repo_frontier_hands_authority.v0";
|
||||
pub const REPO_FRONTIER_MODELING_REQUEST_TYPE: &str =
|
||||
|
|
@ -33,8 +33,9 @@ pub const REPO_FRONTIER_PLANNING_REQUEST_SCHEMA_VERSION: &str =
|
|||
"epiphany.self.repo_frontier_planning_request.v1";
|
||||
pub const REPO_FRONTIER_PLAN_CANDIDATE_SCHEMA_VERSION: &str =
|
||||
"epiphany.imagination.repo_frontier_plan_candidate.v0";
|
||||
pub const REPO_FRONTIER_PLAN_ADOPTION_SCHEMA_VERSION: &str =
|
||||
"epiphany.mind.repo_frontier_plan_adoption.v0";
|
||||
pub const REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION: &str =
|
||||
"epiphany.mind.repo_frontier_plan_decision_receipt.v0";
|
||||
pub const REPO_FRONTIER_PLAN_DECISION_CONTRACT: &str = "epiphany.repo_frontier_plan_decision.v0";
|
||||
pub const REPO_FRONTIER_PLANNING_CONTRACT: &str = "epiphany.repo_frontier_planning.v1";
|
||||
pub const REPO_FRONTIER_WORK_PROPOSAL_CONTRACT: &str =
|
||||
"epiphany.repo_frontier_work_proposal.inert.v0";
|
||||
|
|
@ -61,6 +62,14 @@ pub const REPO_FRONTIER_PLANNING_LAUNCH_BINDING_SCHEMA_VERSION: &str =
|
|||
"epiphany.coordinator.repo_frontier_planning_launch_binding.v0";
|
||||
pub const REPO_FRONTIER_PLANNING_LAUNCH_BINDING_CONTRACT: &str =
|
||||
"epiphany.repo_frontier_planning_launch_binding.v0";
|
||||
pub const REPO_FRONTIER_PLAN_MIND_REQUEST_SCHEMA_VERSION: &str =
|
||||
"epiphany.self.repo_frontier_plan_mind_request.v0";
|
||||
pub const REPO_FRONTIER_PLAN_MIND_REQUEST_CONTRACT: &str =
|
||||
"epiphany.repo_frontier_plan_mind_request.v0";
|
||||
pub const REPO_FRONTIER_PLAN_MIND_LAUNCH_BINDING_SCHEMA_VERSION: &str =
|
||||
"epiphany.coordinator.repo_frontier_plan_mind_launch_binding.v0";
|
||||
pub const REPO_FRONTIER_PLAN_MIND_LAUNCH_BINDING_CONTRACT: &str =
|
||||
"epiphany.repo_frontier_plan_mind_launch_binding.v0";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
|
@ -443,35 +452,111 @@ pub enum RepoFrontierPlanDecision {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.mind.repo_frontier_plan_adoption",
|
||||
schema = "RepoFrontierPlanAdoption"
|
||||
type = "epiphany.self.repo_frontier_plan_mind_request",
|
||||
schema = "RepoFrontierPlanMindRequest"
|
||||
)]
|
||||
pub struct RepoFrontierPlanAdoption {
|
||||
pub struct RepoFrontierPlanMindRequest {
|
||||
#[cultcache(key = 0)]
|
||||
pub schema_version: String,
|
||||
#[cultcache(key = 1)]
|
||||
pub adoption_id: String,
|
||||
pub request_id: String,
|
||||
#[cultcache(key = 2)]
|
||||
pub candidate_id: String,
|
||||
#[cultcache(key = 3)]
|
||||
pub candidate_sha256: String,
|
||||
#[cultcache(key = 4)]
|
||||
pub planning_request_id: String,
|
||||
#[cultcache(key = 3)]
|
||||
pub imagination_result_id: String,
|
||||
#[cultcache(key = 4)]
|
||||
pub imagination_job_id: String,
|
||||
#[cultcache(key = 5)]
|
||||
pub model_revision: u64,
|
||||
pub candidate_id: String,
|
||||
#[cultcache(key = 6)]
|
||||
pub model_hash: String,
|
||||
pub candidate_sha256: String,
|
||||
#[cultcache(key = 7)]
|
||||
pub frontier_item_id: String,
|
||||
pub runtime_id: String,
|
||||
#[cultcache(key = 8)]
|
||||
pub frontier_item_hash: String,
|
||||
pub thread_id: String,
|
||||
#[cultcache(key = 9)]
|
||||
pub decision: RepoFrontierPlanDecision,
|
||||
pub requested_at: String,
|
||||
#[cultcache(key = 10)]
|
||||
pub contract: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.coordinator.repo_frontier_plan_mind_launch_binding",
|
||||
schema = "RepoFrontierPlanMindLaunchBinding"
|
||||
)]
|
||||
pub struct RepoFrontierPlanMindLaunchBinding {
|
||||
#[cultcache(key = 0)]
|
||||
pub schema_version: String,
|
||||
#[cultcache(key = 1)]
|
||||
pub binding_record_id: String,
|
||||
#[cultcache(key = 2)]
|
||||
pub mind_request_id: String,
|
||||
#[cultcache(key = 3)]
|
||||
pub job_id: String,
|
||||
#[cultcache(key = 4)]
|
||||
pub binding_id: String,
|
||||
#[cultcache(key = 5)]
|
||||
pub runtime_id: String,
|
||||
#[cultcache(key = 6)]
|
||||
pub thread_id: String,
|
||||
#[cultcache(key = 7)]
|
||||
pub launched_at: String,
|
||||
#[cultcache(key = 8)]
|
||||
pub worker_launch_document_sha256: String,
|
||||
#[cultcache(key = 9)]
|
||||
pub contract: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RepoFrontierPlanMindDecision {
|
||||
pub mind_request_id: String,
|
||||
pub planning_request_id: String,
|
||||
pub imagination_result_id: String,
|
||||
pub candidate_id: String,
|
||||
pub candidate_sha256: String,
|
||||
pub decision: RepoFrontierPlanDecision,
|
||||
pub rationale: String,
|
||||
#[cultcache(key = 11)]
|
||||
pub decided_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.mind.repo_frontier_plan_decision_receipt",
|
||||
schema = "RepoFrontierPlanDecisionReceipt"
|
||||
)]
|
||||
pub struct RepoFrontierPlanDecisionReceipt {
|
||||
#[cultcache(key = 0)]
|
||||
pub schema_version: String,
|
||||
#[cultcache(key = 1)]
|
||||
pub decision_id: String,
|
||||
#[cultcache(key = 2)]
|
||||
pub planning_request_id: String,
|
||||
#[cultcache(key = 3)]
|
||||
pub source_result_id: String,
|
||||
#[cultcache(key = 4)]
|
||||
pub source_job_id: String,
|
||||
#[cultcache(key = 5)]
|
||||
pub candidate_id: String,
|
||||
#[cultcache(key = 6)]
|
||||
pub candidate_sha256: String,
|
||||
#[cultcache(key = 7)]
|
||||
pub model_revision: u64,
|
||||
#[cultcache(key = 8)]
|
||||
pub model_hash: String,
|
||||
#[cultcache(key = 9)]
|
||||
pub frontier_item_id: String,
|
||||
#[cultcache(key = 10)]
|
||||
pub frontier_item_hash: String,
|
||||
#[cultcache(key = 11)]
|
||||
pub decision: RepoFrontierPlanDecision,
|
||||
#[cultcache(key = 12)]
|
||||
pub rationale: String,
|
||||
#[cultcache(key = 13)]
|
||||
pub decided_at: String,
|
||||
#[cultcache(key = 14, default)]
|
||||
pub model_admission_receipt_id: String,
|
||||
#[cultcache(key = 15)]
|
||||
pub contract: String,
|
||||
}
|
||||
|
||||
|
|
@ -558,9 +643,11 @@ pub struct RepoFrontierRoute {
|
|||
pub target_claim_ids: Vec<String>,
|
||||
#[cultcache(key = 12)]
|
||||
pub source_scope: Vec<String>,
|
||||
#[cultcache(key = 13)]
|
||||
pub selected_at: String,
|
||||
#[cultcache(key = 13, default)]
|
||||
pub adopted_plan: Option<epiphany_state_model::RepoFrontierAdoptedPlan>,
|
||||
#[cultcache(key = 14)]
|
||||
pub selected_at: String,
|
||||
#[cultcache(key = 15)]
|
||||
pub contract: String,
|
||||
}
|
||||
|
||||
|
|
@ -674,6 +761,8 @@ pub struct RepoModelAdmissionReceipt {
|
|||
pub proposal_modeling_request_id: String,
|
||||
#[cultcache(key = 18, default)]
|
||||
pub claim_repair_request_id: String,
|
||||
#[cultcache(key = 19, default)]
|
||||
pub frontier_plan_decision_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -252,6 +252,7 @@ mod tests {
|
|||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
frontier_plan_mind_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -277,6 +278,7 @@ mod tests {
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
};
|
||||
let grant = substrate_gate_repo_access_grant_for_launch(
|
||||
"grant-1".to_string(),
|
||||
|
|
|
|||
|
|
@ -144,6 +144,8 @@ pub use worker_launch::EpiphanyReorientWorkerLaunchDocument;
|
|||
pub use worker_launch::EpiphanyRoleWorkerLaunchDocument;
|
||||
pub use worker_launch::EpiphanyWorkerLaunchDocument;
|
||||
pub use worker_launch::REORIENT_WORKER_OUTPUT_CONTRACT_ID;
|
||||
pub use worker_launch::REPO_FRONTIER_PLAN_MIND_CONTEXT_CONTRACT;
|
||||
pub use worker_launch::REPO_FRONTIER_PLAN_MIND_CONTEXT_SCHEMA_VERSION;
|
||||
pub use worker_launch::REPO_FRONTIER_PLANNING_CONTEXT_CONTRACT;
|
||||
pub use worker_launch::REPO_FRONTIER_PLANNING_CONTEXT_SCHEMA_VERSION;
|
||||
pub use worker_launch::REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_CONTRACT;
|
||||
|
|
@ -151,6 +153,7 @@ pub use worker_launch::REPO_FRONTIER_PROPOSAL_MODELING_CONTEXT_SCHEMA_VERSION;
|
|||
pub use worker_launch::REPO_MODEL_CLAIM_REPAIR_CONTEXT_CONTRACT;
|
||||
pub use worker_launch::REPO_MODEL_CLAIM_REPAIR_CONTEXT_SCHEMA_VERSION;
|
||||
pub use worker_launch::ROLE_WORKER_OUTPUT_CONTRACT_ID;
|
||||
pub use worker_launch::RepoFrontierPlanMindContextProjection;
|
||||
pub use worker_launch::RepoFrontierPlanningContextProjection;
|
||||
pub use worker_launch::RepoFrontierProposalModelingContextProjection;
|
||||
pub use worker_launch::RepoModelClaimRepairContextProjection;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ pub const REPO_FRONTIER_PLANNING_CONTEXT_SCHEMA_VERSION: &str =
|
|||
"epiphany.worker.repo_frontier_planning_context.v0";
|
||||
pub const REPO_FRONTIER_PLANNING_CONTEXT_CONTRACT: &str =
|
||||
"epiphany.repo_frontier_planning_context.v0";
|
||||
pub const REPO_FRONTIER_PLAN_MIND_CONTEXT_SCHEMA_VERSION: &str =
|
||||
"epiphany.worker.repo_frontier_plan_mind_context.v0";
|
||||
pub const REPO_FRONTIER_PLAN_MIND_CONTEXT_CONTRACT: &str =
|
||||
"epiphany.repo_frontier_plan_mind_context.v0";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "documentKind")]
|
||||
|
|
@ -95,6 +99,8 @@ pub struct EpiphanyRoleWorkerLaunchDocument {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub frontier_planning_context: Option<RepoFrontierPlanningContextProjection>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub frontier_plan_mind_context: Option<RepoFrontierPlanMindContextProjection>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub active_subgoal_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub active_subgoals: Vec<EpiphanySubgoal>,
|
||||
|
|
@ -301,6 +307,32 @@ impl RepoFrontierPlanningContextProjection {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RepoFrontierPlanMindContextProjection {
|
||||
pub schema_version: String,
|
||||
pub contract: String,
|
||||
pub request: crate::RepoFrontierPlanMindRequest,
|
||||
pub planning_request: crate::RepoFrontierPlanningRequest,
|
||||
pub candidate: crate::RepoFrontierPlanCandidate,
|
||||
}
|
||||
|
||||
impl RepoFrontierPlanMindContextProjection {
|
||||
pub(crate) fn new(
|
||||
request: &crate::RepoFrontierPlanMindRequest,
|
||||
planning_request: &crate::RepoFrontierPlanningRequest,
|
||||
candidate: &crate::RepoFrontierPlanCandidate,
|
||||
) -> Self {
|
||||
Self {
|
||||
schema_version: REPO_FRONTIER_PLAN_MIND_CONTEXT_SCHEMA_VERSION.into(),
|
||||
contract: REPO_FRONTIER_PLAN_MIND_CONTEXT_CONTRACT.into(),
|
||||
request: request.clone(),
|
||||
planning_request: planning_request.clone(),
|
||||
candidate: candidate.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_reorient_job_launch_request(
|
||||
input: EpiphanyReorientLaunchRequestInput<'_>,
|
||||
) -> EpiphanyJobLaunchRequest {
|
||||
|
|
@ -344,6 +376,7 @@ pub fn build_reorient_job_launch_request(
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -974,6 +974,7 @@ mod tests {
|
|||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
frontier_plan_mind_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -998,6 +999,7 @@ mod tests {
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -1060,6 +1062,7 @@ mod tests {
|
|||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
frontier_plan_mind_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -1084,6 +1087,7 @@ mod tests {
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -1134,6 +1138,7 @@ mod tests {
|
|||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
frontier_plan_mind_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -1158,6 +1163,7 @@ mod tests {
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -1296,6 +1296,12 @@ fn worker_instructions(
|
|||
fn worker_output_schema_json(document: &EpiphanyWorkerLaunchDocument) -> Result<String> {
|
||||
let schema = match document {
|
||||
EpiphanyWorkerLaunchDocument::Role(document) => {
|
||||
if document.frontier_plan_mind_context.is_some() {
|
||||
return serde_json::to_string_pretty(
|
||||
&epiphany_core::epiphany_frontier_plan_mind_output_schema(),
|
||||
)
|
||||
.context("failed to render worker output schema");
|
||||
}
|
||||
let role_id = role_result_id_for_launch_role(&document.role_id)
|
||||
.with_context(|| format!("unknown role launch id {:?}", document.role_id))?;
|
||||
if document.frontier_planning_context.is_some() {
|
||||
|
|
@ -1327,6 +1333,11 @@ fn role_result_id_for_launch_role(
|
|||
|
||||
fn worker_output_contract_text(document: &EpiphanyWorkerLaunchDocument) -> &'static str {
|
||||
match document {
|
||||
EpiphanyWorkerLaunchDocument::Role(document)
|
||||
if document.frontier_plan_mind_context.is_some() =>
|
||||
{
|
||||
"Required Mind admission-review fields: roleId=mindAdmissionReview, verdict, summary, nextSafeMove, filesInspected, frontierPlanMindRequestId, frontierPlanMindDecision. Echo every causal identifier from the typed context exactly and choose adopt, refuse, or hold with a concrete rationale. This bounded procedure serves Mind; it is not an embodied lane and emits no patches or other organ authority."
|
||||
}
|
||||
EpiphanyWorkerLaunchDocument::Role(document)
|
||||
if document.frontier_planning_context.is_some() =>
|
||||
{
|
||||
|
|
@ -1367,6 +1378,21 @@ struct RoleWorkerResultIngress {
|
|||
claim_repair_request_id: Option<String>,
|
||||
frontier_planning_request_id: Option<String>,
|
||||
frontier_plan_candidate: Option<RepoFrontierPlanCandidateIngress>,
|
||||
frontier_plan_mind_request_id: Option<String>,
|
||||
frontier_plan_mind_decision: Option<RepoFrontierPlanMindDecisionIngress>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase", default)]
|
||||
struct RepoFrontierPlanMindDecisionIngress {
|
||||
mind_request_id: String,
|
||||
planning_request_id: String,
|
||||
imagination_result_id: String,
|
||||
candidate_id: String,
|
||||
candidate_sha256: String,
|
||||
decision: Option<epiphany_core::RepoFrontierPlanDecision>,
|
||||
rationale: String,
|
||||
decided_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Deserialize)]
|
||||
|
|
@ -1511,6 +1537,31 @@ fn role_worker_result_from_ingress(
|
|||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let (frontier_plan_mind_decision_msgpack, frontier_plan_mind_decision_error) =
|
||||
if let Some(ingress) = result.frontier_plan_mind_decision.as_ref() {
|
||||
if let Some(decision) = ingress.decision {
|
||||
encode_optional_document(
|
||||
&Some(epiphany_core::RepoFrontierPlanMindDecision {
|
||||
mind_request_id: ingress.mind_request_id.trim().into(),
|
||||
planning_request_id: ingress.planning_request_id.trim().into(),
|
||||
imagination_result_id: ingress.imagination_result_id.trim().into(),
|
||||
candidate_id: ingress.candidate_id.trim().into(),
|
||||
candidate_sha256: ingress.candidate_sha256.trim().into(),
|
||||
decision,
|
||||
rationale: ingress.rationale.trim().into(),
|
||||
decided_at: ingress.decided_at.trim().into(),
|
||||
}),
|
||||
"frontierPlanMindDecision",
|
||||
)
|
||||
} else {
|
||||
(
|
||||
None,
|
||||
Some("frontierPlanMindDecision: missing decision".into()),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
EpiphanyRuntimeRoleWorkerResult {
|
||||
schema_version: epiphany_core::RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.to_string(),
|
||||
result_id: result_id.to_string(),
|
||||
|
|
@ -1540,7 +1591,10 @@ fn role_worker_result_from_ingress(
|
|||
merge_optional_errors(state_patch_error, self_patch_error),
|
||||
repo_model_patch_error,
|
||||
),
|
||||
frontier_plan_candidate_error,
|
||||
merge_optional_errors(
|
||||
frontier_plan_candidate_error,
|
||||
frontier_plan_mind_decision_error,
|
||||
),
|
||||
),
|
||||
metadata: std::collections::BTreeMap::new(),
|
||||
repo_model_patch_msgpack,
|
||||
|
|
@ -1557,6 +1611,10 @@ fn role_worker_result_from_ingress(
|
|||
result.frontier_planning_request_id.as_deref(),
|
||||
),
|
||||
frontier_plan_candidate_msgpack,
|
||||
frontier_plan_mind_request_id: clean_optional_string(
|
||||
result.frontier_plan_mind_request_id.as_deref(),
|
||||
),
|
||||
frontier_plan_mind_decision_msgpack,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1697,6 +1755,7 @@ mod tests {
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
};
|
||||
let result = role_worker_result_from_ingress(
|
||||
&launch,
|
||||
|
|
@ -1761,6 +1820,7 @@ mod tests {
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: Some("planning-request-1".into()),
|
||||
frontier_plan_mind_request_id: None,
|
||||
};
|
||||
let result = role_worker_result_from_ingress(
|
||||
&launch,
|
||||
|
|
@ -1790,6 +1850,56 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mind_ingress_preserves_exact_typed_judgment() -> Result<()> {
|
||||
let parsed = parse_assistant_json::<RoleWorkerResultIngress>(
|
||||
r#"{"roleId":"mindAdmissionReview","verdict":"adopt","summary":"bounded","nextSafeMove":"admit","filesInspected":[],"frontierPlanMindRequestId":"mind-request-1","frontierPlanMindDecision":{"mindRequestId":"mind-request-1","planningRequestId":"planning-request-1","imaginationResultId":"imagination-result-1","candidateId":"candidate-1","candidateSha256":"candidate-hash-1","decision":"adopt","rationale":"Exact candidate is bounded and falsifiable.","decidedAt":"2026-07-15T10:00:00Z"}}"#,
|
||||
)?;
|
||||
let launch = EpiphanyRuntimeWorkerLaunchRequest {
|
||||
schema_version: epiphany_core::RUNTIME_WORKER_LAUNCH_REQUEST_SCHEMA_VERSION.into(),
|
||||
job_id: "mind-job-1".into(),
|
||||
binding_id: epiphany_core::EPIPHANY_MIND_ROLE_BINDING_ID.into(),
|
||||
role: epiphany_core::EPIPHANY_MIND_OWNER_ROLE.into(),
|
||||
authority_scope: "epiphany.procedure.mind_admission_review".into(),
|
||||
instruction: "judge".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.procedure.mind_admission_review",
|
||||
"role",
|
||||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: Some("mind-request-1".into()),
|
||||
};
|
||||
let result = role_worker_result_from_ingress(
|
||||
&launch,
|
||||
"mindAdmissionReview",
|
||||
"mind-result-1",
|
||||
&parsed,
|
||||
Vec::new(),
|
||||
);
|
||||
assert_eq!(
|
||||
result.frontier_plan_mind_request_id.as_deref(),
|
||||
Some("mind-request-1")
|
||||
);
|
||||
let decision = result
|
||||
.frontier_plan_mind_decision()?
|
||||
.expect("typed Mind decision");
|
||||
assert_eq!(
|
||||
decision.decision,
|
||||
epiphany_core::RepoFrontierPlanDecision::Adopt
|
||||
);
|
||||
assert_eq!(decision.candidate_sha256, "candidate-hash-1");
|
||||
assert!(result.state_patch_msgpack.is_none());
|
||||
assert!(result.repo_model_patch_msgpack.is_none());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_openai_event(
|
||||
request_id: &str,
|
||||
sequence: u64,
|
||||
|
|
@ -1965,6 +2075,7 @@ mod tests {
|
|||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
frontier_plan_mind_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -1987,8 +2098,9 @@ mod tests {
|
|||
epiphany_core::ROLE_WORKER_OUTPUT_CONTRACT_ID,
|
||||
),
|
||||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -2096,6 +2208,7 @@ mod tests {
|
|||
proposal_modeling_context: None,
|
||||
claim_repair_context: None,
|
||||
frontier_planning_context: None,
|
||||
frontier_plan_mind_context: None,
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: Vec::new(),
|
||||
|
|
@ -2120,6 +2233,7 @@ mod tests {
|
|||
proposal_modeling_request_id: None,
|
||||
claim_repair_request_id: None,
|
||||
frontier_planning_request_id: None,
|
||||
frontier_plan_mind_request_id: None,
|
||||
created_at: now(),
|
||||
},
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -778,6 +778,12 @@ pub struct RepoFrontierItem {
|
|||
#[ts(type = "Array<string>")]
|
||||
pub source_scope: Vec<String>,
|
||||
pub recommended_next_organ: String,
|
||||
/// The exact plan admitted by Mind for this frontier item. Imagination may
|
||||
/// propose this payload, but only the dedicated model transition may make
|
||||
/// it part of Modeling-owned truth.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(type = "RepoFrontierAdoptedPlan | null")]
|
||||
pub adopted_plan: Option<RepoFrontierAdoptedPlan>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[ts(type = "Array<string>")]
|
||||
pub dependency_item_ids: Vec<String>,
|
||||
|
|
@ -800,6 +806,30 @@ pub struct RepoFrontierItem {
|
|||
pub superseded_by: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS, Default)]
|
||||
pub struct RepoFrontierAdoptedPlan {
|
||||
pub planning_request_id: String,
|
||||
pub result_id: String,
|
||||
pub job_id: String,
|
||||
pub candidate_id: String,
|
||||
pub candidate_sha256: String,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[ts(type = "Array<string>")]
|
||||
pub safe_paths: Vec<String>,
|
||||
pub action: String,
|
||||
pub command: String,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[ts(type = "Array<string>")]
|
||||
pub checks: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[ts(type = "Array<string>")]
|
||||
pub stop_conditions: Vec<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[ts(type = "Array<string>")]
|
||||
pub rollback_steps: Vec<String>,
|
||||
pub commit_message: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, JsonSchema, TS, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(rename_all = "snake_case")]
|
||||
|
|
@ -836,6 +866,11 @@ pub enum RepoModelPatchPurpose {
|
|||
soul_verdict_receipt_id: String,
|
||||
},
|
||||
RepairClaim,
|
||||
AdoptFrontierPlan {
|
||||
planning_request_id: String,
|
||||
result_id: String,
|
||||
candidate_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS)]
|
||||
|
|
@ -873,6 +908,11 @@ pub enum RepoModelPatchOperation {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
superseded_by: Option<String>,
|
||||
},
|
||||
AdoptFrontierPlan {
|
||||
frontier_item_id: String,
|
||||
expected_frontier_item_hash: String,
|
||||
adopted_plan: RepoFrontierAdoptedPlan,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS)]
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ embodiment titles, role-memory diaries, or Persona state.
|
|||
|
||||
- Body is sensed and changed; it does not decide.
|
||||
- Mind admits durable state; it is not a chatty lane.
|
||||
- Mind may invoke a bounded, stateless admission-review procedure when a typed
|
||||
proposal requires model judgment. That procedure serves Mind; it is not an
|
||||
embodied sub-agent, has no heartbeat or lane memory, cannot `roleAccept` or
|
||||
`selfPatch`, and its immutable judgment has no consequence until Mind replays
|
||||
the causal chain and admits it.
|
||||
- Continuity preserves/rebuilds context across rupture; it is protocol
|
||||
machinery, not an embodied identity.
|
||||
- Substrate Gate grants substrate access; it does not inspect evidence, mutate
|
||||
|
|
|
|||
|
|
@ -982,3 +982,54 @@ 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.
|
||||
|
||||
## Prompted frontier admission review and canonical adoption (2026-07-15)
|
||||
|
||||
The repo-frontier planning nerve is complete and the preceding “Next implement
|
||||
Mind” sentence is superseded. Self binds one exact admitted model/frontier
|
||||
request; coordinator atomically binds the Imagination launch; Imagination
|
||||
persists one immutable result with its sole embedded candidate; the runtime
|
||||
derives a CAS-bound Mind review request; coordinator atomically launches a
|
||||
bounded non-embodied admission-review procedure; that procedure returns one
|
||||
immutable typed Adopt/Refuse/Hold judgment; and Mind's admission transaction
|
||||
replays the entire chain before consequence.
|
||||
|
||||
Refuse and Hold are inert terminal receipts. Adopt atomically installs the exact
|
||||
candidate and provenance on the canonical frontier, emits specialized review
|
||||
and admission receipts, and alone changes Imagination to Hands. Self derives the
|
||||
route from that model. Hands intent binds route, candidate hash, plan action,
|
||||
and safe paths; Soul admission requires the exact plan command and receives the
|
||||
checks, stop conditions, rollback steps, and commit message. Modeling may close
|
||||
the frontier after Soul verdict only while preserving adopted execution anatomy.
|
||||
There is no standalone candidate/adoption truth and no caller-authored decision.
|
||||
|
||||
Mind remains persistent state and admission authority, not an embodied chatty
|
||||
lane. The prompted model call is explicitly a bounded admission-review
|
||||
procedure serving Mind, with no heartbeat, lane memory, `roleAccept`,
|
||||
`selfPatch`, or foreign organ patch mouths.
|
||||
|
||||
Hostile coverage includes swapped causal ids/hashes/jobs, failed worker output,
|
||||
foreign cargo, duplicate/racing launch and terminal claims, generic adoption
|
||||
bypass, post-adoption execution-anatomy mutation, and same-path command
|
||||
substitution. The composed happy path proves Adopt through exact Hands command,
|
||||
Verification/Soul, accepted Modeling incorporation, Resolved closure, and
|
||||
byte-identical plan survival. Final proof: core 330 passed with one intentional
|
||||
cross-process helper ignored; OpenAI runtime library 10 passed, both runtime
|
||||
binary suites passed 6 each, and all core binaries check. Soul's final landing
|
||||
audit passed. Exact concurrent admission retries are forced through a two-party
|
||||
pre-CAS barrier in test builds; the loser reloads and authenticates the winner's
|
||||
entire immutable decision/admission chain. The synchronization hook is compiled
|
||||
only for tests, so production exposes no executable seam at the commit boundary.
|
||||
The next organ is the production semantic index over typed graph claims so
|
||||
Modeling owns a live persistent searchable Body map instead of making Hands
|
||||
rediscover the repository.
|
||||
|
||||
The selected semantic substrate is Qdrant: available locally in Docker and
|
||||
intended on the new Yggdrasil host (64 GB RAM, GTX 1080 for embedding/model
|
||||
workers). It is a rebuildable projection, never canonical Mind or Modeling
|
||||
truth. CultCache/CultMesh remains authoritative. Use separate typed Mind and
|
||||
Modeling namespaces/collections or equivalently hard query partitions so
|
||||
private doctrine/memory cannot leak into Hands Body retrieval and repository
|
||||
claims cannot silently become doctrine. Every point must retain source document
|
||||
id, schema version, canonical hash, provenance, authority/visibility scope, and
|
||||
embedding-model version.
|
||||
|
|
|
|||
|
|
@ -1,87 +1,77 @@
|
|||
# Repo Frontier Planning Authority Map
|
||||
|
||||
## Current state
|
||||
## Current authority
|
||||
|
||||
The planning family now has a real Self-to-Imagination launch artery. Self's
|
||||
`RepoFrontierPlanningRequest` binds the uniquely admitted current model, exact
|
||||
actionable frontier, runtime, and authoritative thread. Coordinator commit
|
||||
replays those bytes, injects the exact typed Imagination projection, and
|
||||
atomically persists a request-keyed launch binding whose hash covers the stored
|
||||
worker launch document. Caller-authored projections, mixed authority carriers,
|
||||
stale requests, causal substitutions, and duplicate/racing launches are
|
||||
refused without loser artifacts.
|
||||
|
||||
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
|
||||
admission replace them.
|
||||
|
||||
## Target authority
|
||||
|
||||
- Owner — Self selects one dependency-ready, unchallenged, active Imagination
|
||||
frontier from the uniquely admitted current RepoModel and emits one immutable
|
||||
planning request.
|
||||
- Inputs — current RepoModel envelope and hash, its unique Mind admission
|
||||
receipt, exact frontier item and serialized hash, source scope, current claim
|
||||
challenges, runtime identity, and authoritative thread.
|
||||
- Coordinator output — one typed Imagination context projection and one
|
||||
request-keyed immutable launch binding inside the atomic coordinator launch
|
||||
transaction.
|
||||
- Imagination output — one immutable worker result that exclusively echoes the
|
||||
planning request and contains one typed candidate. It proposes; it does not
|
||||
route Hands or mutate RepoModel.
|
||||
- Mind output — one fully correlated Adopt, Refuse, or Hold receipt. Mind alone
|
||||
constructs and persists the decision after replaying request, launch binding,
|
||||
worker launch document, immutable result, candidate, current model, and exact
|
||||
frontier bytes.
|
||||
- Adopt effect — a narrow RepoModel transition must make the selected frontier
|
||||
executable without allowing the adoption receipt to become a shadow model.
|
||||
The downstream Hands route remains derived from admitted model bytes and may
|
||||
reference the exact adopted plan for bounded action/check/rollback cargo.
|
||||
- Refuse/Hold effect — immutable decision receipts only. They do not mutate the
|
||||
model, claim the deterministic Adopt identity, or grant Hands authority.
|
||||
- Self selects one dependency-ready, unchallenged, active Imagination frontier
|
||||
from the uniquely admitted current RepoModel. It writes one immutable
|
||||
`RepoFrontierPlanningRequest` binding the model revision/hash, admission,
|
||||
frontier bytes, source scope, runtime, and thread.
|
||||
- Coordinator commit replays that request, injects the exact typed Imagination
|
||||
projection, and atomically writes a request-keyed launch binding whose hash
|
||||
covers the stored worker launch document.
|
||||
- Imagination writes one immutable runtime result with an exclusive planning
|
||||
request echo and one embedded canonical `RepoFrontierPlanCandidate`. It does
|
||||
not route Hands or mutate RepoModel.
|
||||
- The runtime derives one `RepoFrontierPlanMindRequest` from the immutable
|
||||
Imagination result and candidate hash. Its insert is CAS-bound to the exact
|
||||
current model envelope.
|
||||
- Coordinator commit launches one bounded, non-embodied admission-review
|
||||
procedure serving Mind. It injects the exact request/planning/candidate
|
||||
projection and atomically writes the request-keyed launch binding. This
|
||||
procedure is not an embodied Mind lane; Mind remains persistent state and
|
||||
admission authority.
|
||||
- The admission reviewer writes one immutable typed Adopt, Refuse, or Hold
|
||||
judgment. Result persistence replays the request, both worker results, both
|
||||
launch bindings, stored launch bytes, runtime/thread, candidate identity, and
|
||||
current model/frontier while rejecting foreign organ cargo.
|
||||
- `commit_repo_frontier_plan_decision` consumes only that immutable review
|
||||
result. Refuse and Hold write one inert terminal receipt. Adopt atomically
|
||||
writes the next canonical RepoModel, its specialized Mind review/admission,
|
||||
and the terminal decision receipt.
|
||||
- The dedicated Adopt transition installs the exact candidate cargo and
|
||||
provenance on `RepoFrontierItem.adopted_plan` and alone changes
|
||||
Imagination to Hands. Generic frontier writers cannot author or rewrite it.
|
||||
- Self derives the Hands route from the admitted model. The route copies the
|
||||
exact plan and safe paths; Hands intent binds route, candidate hash, and plan
|
||||
action; verification requires the exact admitted command and shows Soul the
|
||||
checks, stop conditions, rollback steps, and commit message.
|
||||
- Modeling may incorporate Soul's verdict by changing lifecycle/gap/evidence
|
||||
fields while preserving the adopted execution anatomy byte-for-byte.
|
||||
|
||||
## Derived state
|
||||
|
||||
- Planning readiness is derived from current admitted model plus absence of a
|
||||
current terminal Mind decision.
|
||||
- Planning readiness is current admitted Imagination frontier plus absence of a
|
||||
terminal decision receipt.
|
||||
- Candidate visibility is a projection of the immutable Imagination result.
|
||||
- Hands readiness is derived from the post-Adopt admitted model and its exact
|
||||
plan provenance, never from a candidate or adoption document alone.
|
||||
- Hands readiness and execution cargo derive only from the post-Adopt canonical
|
||||
model, never from a candidate or decision shadow store.
|
||||
- Completion remains Soul verdict followed by Modeling verdict incorporation;
|
||||
adoption is not completion.
|
||||
|
||||
## Forbidden writers and substitutions
|
||||
|
||||
- No external or generic caller may persist an Imagination candidate or Mind
|
||||
decision directly.
|
||||
- Generic Imagination `statePatch.planning.objective_drafts` is not repo
|
||||
frontier planning authority.
|
||||
- Schema namespace, prompt prose, result metadata, evidence coincidence, or
|
||||
"the only current request" cannot substitute for an exact request echo and
|
||||
launch binding.
|
||||
- Adopt cannot merely suppress future planning while leaving the frontier and
|
||||
Hands route unchanged.
|
||||
- Candidate persistence must share a CAS with the current model expectation;
|
||||
validation followed by an unrelated absent-only insert is stale-authority
|
||||
leakage.
|
||||
- No generic work-order registry or mode router. This is one explicit organ
|
||||
nerve with one owner at each transition.
|
||||
- No standalone candidate or adoption store exists in the production path.
|
||||
- No caller supplies Mind's decision, rationale, or timestamp.
|
||||
- No prompted admission reviewer may emit state, Self, RepoModel, Verification,
|
||||
Modeling, or implementation authority cargo.
|
||||
- Prompt prose, namespaces, metadata, evidence coincidence, or “the current
|
||||
request” cannot substitute for exact typed echoes and launch bindings.
|
||||
- Generic Upsert/Revise cannot create an adopted plan or perform
|
||||
Imagination-to-Hands. Verdict incorporation cannot rewrite execution anatomy.
|
||||
- Same-path Hands work cannot substitute a different action, candidate, or
|
||||
command while retaining route authority.
|
||||
- Mind is not a chatty embodied lane. The bounded admission-review procedure
|
||||
serves Mind and has no heartbeat, lane memory, `roleAccept`, or `selfPatch`.
|
||||
|
||||
## Deletion line before build
|
||||
## Verification layer
|
||||
|
||||
1. Public candidate/adoption persistence exports are gone.
|
||||
2. Direct internal fixture writers die when result correlation and Mind
|
||||
admission land.
|
||||
3. Any Adopt path that does not change the model or downstream executable
|
||||
truth is removed rather than retained as compatibility behavior.
|
||||
- Hostile proofs cover stale/swapped model, frontier, request, result, job,
|
||||
launch binding, projection, candidate hash, escaped paths, failed worker
|
||||
output, foreign cargo, duplicate/racing launches, competing terminal writes,
|
||||
generic adoption bypass, post-adoption anatomy mutation, and same-path command
|
||||
substitution.
|
||||
- Exact retry authenticates immutable terminal decision plus its historical
|
||||
review/admission chain without requiring later canonical model revisions to
|
||||
remain frozen.
|
||||
- The next organ is the production semantic index over typed graph claims so
|
||||
Modeling can maintain a live persistent searchable Body map.
|
||||
|
|
|
|||
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. 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.
|
||||
next_action: Build the production Qdrant semantic projection over canonical typed state. Modeling needs a live persistent searchable Body map over repo claims/architecture/frontier; Mind needs semantic retrieval over doctrine, durable memory, evidence, decisions, and rehydration cues. CultCache/CultMesh remains authoritative and Qdrant stays rebuildable, with separate typed Mind/Modeling namespaces, provenance, visibility, schema/hash, and embedding-model version. Local Docker already hosts Qdrant; new Yggdrasil will provide the same substrate with 64GB RAM and a GTX 1080 for embedding/model workers. The repo-frontier planning nerve is complete: typed Self request, coordinator-bound Imagination launch, immutable embedded candidate, CAS-bound Mind review request, coordinator-bound non-embodied admission-review procedure, immutable Adopt/Refuse/Hold judgment, atomic canonical Adopt transition, model-derived Hands route, candidate/action/command-bound execution authority, Soul plan-contract visibility, and Modeling verdict closure without plan rewrite. Do not revive Proprioception, epiphany-work, metadata adapters, generic work-order routers, shadow candidate/adoption stores, embodied Mind lanes, or inference from namespaces/prompt prose/evidence coincidence. Keep closure's zero-substring invariant.
|
||||
|
|
|
|||
Reference in a new issue