diff --git a/epiphany-core/src/admitted_model_direction_consideration.rs b/epiphany-core/src/admitted_model_direction_consideration.rs index 47c6dd71..dc789697 100644 --- a/epiphany-core/src/admitted_model_direction_consideration.rs +++ b/epiphany-core/src/admitted_model_direction_consideration.rs @@ -211,6 +211,10 @@ pub fn validate_result( request: &AdmittedModelDirectionConsiderationRequest, result: &AdmittedModelDirectionConsiderationResult, ) -> Result<()> { + let requested_at = chrono::DateTime::parse_from_rfc3339(&request.requested_at) + .map_err(|_| anyhow!("model direction request timestamp must be RFC3339"))?; + let proposed_at = chrono::DateTime::parse_from_rfc3339(&result.proposed_at) + .map_err(|_| anyhow!("model direction result timestamp must be RFC3339"))?; if result.schema_version != RESULT_SCHEMA || result.contract != RESULT_CONTRACT || !result.proposal_only @@ -223,7 +227,7 @@ pub fn validate_result( || result.model_admission_receipt_id != request.model_admission_receipt_id || result.result_id.trim().is_empty() || result.summary.trim().is_empty() - || chrono::DateTime::parse_from_rfc3339(&result.proposed_at).is_err() + || proposed_at < requested_at { bail!("model direction result substituted causal identity"); } diff --git a/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs b/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs index dd5b563a..cefe7d26 100644 --- a/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs +++ b/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs @@ -46,11 +46,14 @@ fn main() -> Result<()> { bail!("fixture requires exact public, organization, and private feedback classes"); } + bind_runtime_repository_domain(&runtime_store, "GameCult/Epiphany", "2026-07-18T00:00:00Z")?; let inserted = ingest_resident_self_domain_pressure( &resident_store, &runtime_store, &feedback_store, "epiphany-yggdrasil", + "GameCult/Epiphany", + runtime_store.parent().unwrap().to_str().unwrap(), 1_752_796_800_000, )?; if inserted < admitted.len() { diff --git a/epiphany-core/src/bin/epiphany-swarm.rs b/epiphany-core/src/bin/epiphany-swarm.rs index 3b13d851..087435f2 100644 --- a/epiphany-core/src/bin/epiphany-swarm.rs +++ b/epiphany-core/src/bin/epiphany-swarm.rs @@ -5,11 +5,12 @@ use epiphany_core::{ ProcessInstanceObservation, ResidentProviderReadiness, ResidentReadinessRequest, ResidentSelfOutcome, ResidentSelfPolicy, ResidentSelfPorts, ResidentSelfPressure, ResidentSelfState, acknowledge_resident_self_launch, authenticate_resident_self_policy, - cancel_resident_self_turn, capture_process_instance, complete_resident_self_turn, - coordinator_run_receipts, derive_resident_cognition_readiness, enqueue_resident_self_pressure, - import_bifrost_persona_feedback_deliveries, ingest_resident_self_domain_pressure, - load_epiphany_cultmesh_swarm_brake, load_resident_self_state, observe_process_instance, - prepare_resident_self_launch, publish_resident_provider_readiness, resident_self_child_claim, + bind_runtime_repository_domain, cancel_resident_self_turn, capture_process_instance, + complete_resident_self_turn, coordinator_run_receipts, derive_resident_cognition_readiness, + enqueue_resident_self_pressure, import_bifrost_persona_feedback_deliveries, + ingest_resident_self_domain_pressure, load_epiphany_cultmesh_swarm_brake, + load_resident_self_state, observe_process_instance, prepare_resident_self_launch, + publish_resident_provider_readiness, resident_self_child_claim, resident_self_local_provider_status, terminate_process_instance, validate_bifrost_persona_feedback_source, validate_persona_feedback_store_separation, validate_resident_self_store_separation, @@ -152,11 +153,18 @@ fn cycle( &args.feedback_target_repository, &args.feedback_target_persona, )?; + bind_runtime_repository_domain( + &args.policy.runtime_store, + &args.feedback_target_repository, + &Utc::now().to_rfc3339(), + )?; ingest_resident_self_domain_pressure( &args.state_store, &args.policy.runtime_store, &args.persona_feedback_store, &args.policy.release_runtime_id, + &args.feedback_target_repository, + &args.policy.workspace.display().to_string(), now, )?; *state = load_resident_self_state(&args.state_store)?; diff --git a/epiphany-core/src/coordinator_launch.rs b/epiphany-core/src/coordinator_launch.rs index 341ac516..86aece97 100644 --- a/epiphany-core/src/coordinator_launch.rs +++ b/epiphany-core/src/coordinator_launch.rs @@ -965,6 +965,9 @@ fn validate_proposal_modeling_launch( .get::(&selection.proposal_id)? .ok_or_else(|| anyhow!("proposal Modeling selection references a missing proposal"))?; crate::runtime_spine::validate_repo_frontier_work_proposal(&proposal)?; + if proposal.source_kind == crate::RepoFrontierProposalSourceKind::Imagination { + crate::runtime_spine::validate_autonomous_proposal_binding(cache, &proposal)?; + } if cache .get_all::()? .iter() diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index 27e5da27..efcc91f7 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -937,6 +937,7 @@ pub use runtime_spine::RuntimeSpineJobOptions; pub use runtime_spine::RuntimeSpineJobResultOptions; pub use runtime_spine::RuntimeSpineSessionOptions; pub use runtime_spine::append_runtime_event; +pub use runtime_spine::bind_runtime_repository_domain; pub use runtime_spine::bind_runtime_to_agent_memory_swarm; pub use runtime_spine::canonical_repo_frontier_plan_candidate_id; pub use runtime_spine::clear_epiphany_job_binding_backend; @@ -959,6 +960,7 @@ pub use runtime_spine::migrate_legacy_repo_model_projection_obligation; pub use runtime_spine::open_runtime_spine_heartbeat_job; pub use runtime_spine::plan_runtime_spine_heartbeat_launch; pub use runtime_spine::prepare_runtime_spine_heartbeat_job; +pub use runtime_spine::promote_autonomous_direction_options_for_modeling; pub use runtime_spine::put_continuity_recovery_receipt; pub use runtime_spine::put_coordinator_run_receipt; pub use runtime_spine::put_eyes_evidence_packet; diff --git a/epiphany-core/src/repo_model_gateway.rs b/epiphany-core/src/repo_model_gateway.rs index 70eb8166..f65f0456 100644 --- a/epiphany-core/src/repo_model_gateway.rs +++ b/epiphany-core/src/repo_model_gateway.rs @@ -39,6 +39,14 @@ pub const REPO_FRONTIER_PLAN_DECISION_CONTRACT: &str = "epiphany.repo_frontier_p 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"; +pub const REPO_FRONTIER_AUTONOMOUS_PROPOSAL_BINDING_SCHEMA_VERSION: &str = + "epiphany.self.repo_frontier_autonomous_proposal_binding.v0"; +pub const REPO_FRONTIER_AUTONOMOUS_PROPOSAL_BINDING_CONTRACT: &str = + "epiphany.repo_frontier_autonomous_proposal_binding.v0"; +pub const RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY: &str = "runtime-repository-domain-binding"; +pub const RUNTIME_REPOSITORY_DOMAIN_BINDING_SCHEMA_VERSION: &str = + "epiphany.runtime.repository_domain_binding.v0"; +pub const RUNTIME_REPOSITORY_DOMAIN_BINDING_CONTRACT: &str = "deployment configuration binds one organizational repository name to one exact authenticated repository Body; Self may consume but not relabel it"; pub const REPO_FRONTIER_PROPOSAL_MODELING_REQUEST_SCHEMA_VERSION: &str = "epiphany.coordinator.repo_frontier_proposal_modeling_request.v0"; pub const REPO_FRONTIER_PROPOSAL_MODELING_REQUEST_CONTRACT: &str = @@ -213,6 +221,7 @@ pub enum RepoFrontierProposalSourceKind { User, Persona, Bifrost, + Imagination, } #[derive(Debug, Clone)] @@ -280,6 +289,84 @@ pub struct RepoFrontierWorkProposal { pub contract: String, } +#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)] +#[cultcache( + type = "epiphany.self.repo_frontier_autonomous_proposal_binding", + schema = "RepoFrontierAutonomousProposalBinding" +)] +pub struct RepoFrontierAutonomousProposalBinding { + #[cultcache(key = 0)] + pub schema_version: String, + #[cultcache(key = 1)] + pub binding_id: String, + #[cultcache(key = 2)] + pub proposal_id: String, + #[cultcache(key = 3)] + pub proposal_payload_sha256: String, + #[cultcache(key = 4)] + pub direction_request_id: String, + #[cultcache(key = 5)] + pub direction_result_id: String, + #[cultcache(key = 6)] + pub direction_result_sha256: String, + #[cultcache(key = 7)] + pub model_revision: u64, + #[cultcache(key = 8)] + pub model_hash: String, + #[cultcache(key = 9)] + pub model_admission_receipt_id: String, + #[cultcache(key = 10)] + pub option_ordinal: u32, + #[cultcache(key = 11)] + pub option_sha256: String, + #[cultcache(key = 12)] + pub runtime_id: String, + #[cultcache(key = 13)] + pub thread_id: String, + #[cultcache(key = 14)] + pub workspace_id: String, + #[cultcache(key = 15)] + pub body_binding_sha256: String, + #[cultcache(key = 16)] + pub created_at: String, + #[cultcache(key = 17)] + pub contract: String, + #[cultcache(key = 18)] + pub direction_worker_job_id: String, + #[cultcache(key = 19)] + pub direction_worker_result_id: String, + #[cultcache(key = 20)] + pub direction_worker_result_sha256: String, + #[cultcache(key = 21)] + pub direction_worker_launch_sha256: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)] +#[cultcache( + type = "epiphany.runtime.repository_domain_binding", + schema = "RuntimeRepositoryDomainBinding" +)] +pub struct RuntimeRepositoryDomainBinding { + #[cultcache(key = 0)] + pub schema_version: String, + #[cultcache(key = 1)] + pub binding_id: String, + #[cultcache(key = 2)] + pub repository_full_name: String, + #[cultcache(key = 3)] + pub runtime_id: String, + #[cultcache(key = 4)] + pub swarm_id: String, + #[cultcache(key = 5)] + pub workspace_id: String, + #[cultcache(key = 6)] + pub body_binding_sha256: String, + #[cultcache(key = 7)] + pub bound_at: String, + #[cultcache(key = 8)] + pub contract: String, +} + #[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)] #[cultcache( type = "epiphany.coordinator.repo_frontier_proposal_modeling_request", diff --git a/epiphany-core/src/repository_body_observer.rs b/epiphany-core/src/repository_body_observer.rs index fcfdcd4a..842c2ece 100644 --- a/epiphany-core/src/repository_body_observer.rs +++ b/epiphany-core/src/repository_body_observer.rs @@ -1329,7 +1329,7 @@ fn validate_runtime_body_store_binding( } Ok(()) } -fn body_binding_sha256(binding: &RepositoryBodyBinding) -> Result { +pub(crate) fn body_binding_sha256(binding: &RepositoryBodyBinding) -> Result { Ok(format!( "{:x}", Sha256::digest(rmp_serde::to_vec_named(binding)?) diff --git a/epiphany-core/src/resident_self.rs b/epiphany-core/src/resident_self.rs index aa30b069..60f0c8be 100644 --- a/epiphany-core/src/resident_self.rs +++ b/epiphany-core/src/resident_self.rs @@ -52,6 +52,7 @@ impl ResidentSelfPressure { | "persona-feedback" | "imagination-consideration" | "imagination-proposal" + | "repo-frontier-proposal-modeling" ) || self.pressure_id.trim().is_empty() || self.provenance_ref.trim().is_empty() @@ -811,6 +812,8 @@ pub fn ingest_resident_self_domain_pressure( runtime_store: &Path, persona_feedback_store: &Path, runtime_id: &str, + repository: &str, + workspace: &str, now_millis: u64, ) -> Result { let mut inserted = 0; @@ -829,6 +832,30 @@ pub fn ingest_resident_self_domain_pressure( created_at_millis: now_millis, status: "pending".into(), consumed_by_grant_id: None, private_state_exposed: false, })?); } + for selection in crate::promote_autonomous_direction_options_for_modeling( + runtime_store, + repository, + workspace, + &requested_at, + )? { + inserted += usize::from(enqueue_resident_self_pressure_idempotent( + resident_store, + &ResidentSelfPressure { + schema_version: RESIDENT_SELF_PRESSURE_SCHEMA_VERSION.into(), + pressure_id: format!("repo-frontier-proposal-modeling-{}", selection.request_id), + kind: "repo-frontier-proposal-modeling".into(), + provenance_ref: format!( + "cultcache://repo-frontier-proposal-modeling/{}", + selection.request_id + ), + objective: "Launch the exact autonomous proposal Modeling review; do not admit, adopt, act, release, or deploy.".into(), + created_at_millis: now_millis, + status: "pending".into(), + consumed_by_grant_id: None, + private_state_exposed: false, + }, + )?); + } for feedback in crate::admitted_persona_feedback(persona_feedback_store, runtime_id)? { if feedback.target_runtime_id != runtime_id { return Err(anyhow!( @@ -1009,18 +1036,23 @@ pub fn prepare_resident_self_launch( let mut argv = coordinator_argv(policy, &turn_id, &wake); if matches!( grant.pressure_kind.as_str(), - "imagination-consideration" | "admitted-model-direction-consideration" + "imagination-consideration" + | "admitted-model-direction-consideration" + | "repo-frontier-proposal-modeling" ) { - let (prefix, request_flag) = if grant.pressure_kind == "imagination-consideration" { - ( + let (prefix, request_flag) = match grant.pressure_kind.as_str() { + "imagination-consideration" => ( "cultcache://imagination-consideration/", "--imagination-consideration-request-id", - ) - } else { - ( + ), + "admitted-model-direction-consideration" => ( "cultcache://admitted-model-direction-consideration/", "--admitted-model-direction-consideration-request-id", - ) + ), + _ => ( + "cultcache://repo-frontier-proposal-modeling/", + "--proposal-modeling-request-id", + ), }; let request_id = grant .provenance_ref @@ -1751,6 +1783,43 @@ mod tests { Ok(()) } + #[test] + fn autonomous_frontier_pressure_launches_exact_modeling_selection_without_objective() + -> Result<()> { + let temp = tempfile::tempdir()?; + let store = temp.path().join("resident-autonomous.cc"); + let coordinator = temp.path().join("epiphany-mvp-coordinator"); + std::fs::write(&coordinator, b"witnessed executable")?; + let mut policy = policy(); + policy.coordinator_bin = coordinator; + enqueue_resident_self_pressure( + &store, + &ResidentSelfPressure { + schema_version: RESIDENT_SELF_PRESSURE_SCHEMA_VERSION.into(), + pressure_id: "pressure-autonomous-modeling-1".into(), + kind: "repo-frontier-proposal-modeling".into(), + provenance_ref: "cultcache://repo-frontier-proposal-modeling/selection-1".into(), + objective: "Launch exact autonomous proposal Modeling review.".into(), + created_at_millis: 1, + status: "pending".into(), + consumed_by_grant_id: None, + private_state_exposed: false, + }, + )?; + heartbeat_issue_resident_self_grant(&store, "heartbeat-autonomous", "action-1", 2)? + .expect("autonomous grant"); + let prepared = prepare_resident_self_launch(&store, &policy, 3)? + .expect("autonomous Modeling preparation"); + assert!(!prepared.argv.iter().any(|arg| arg == "--objective")); + assert!( + prepared + .argv + .windows(2) + .any(|pair| { pair == ["--proposal-modeling-request-id", "selection-1"] }) + ); + Ok(()) + } + #[test] fn standard_heartbeat_owns_self_grant_selection() -> Result<()> { let temp = tempfile::tempdir()?; diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index 2ccd7051..4f0a5e75 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -50,6 +50,8 @@ use crate::mind_gateway::MindGatewayReview; use crate::mind_gateway::MindStateCommitReceipt; use crate::organ_dependencies::EpiphanyLaunchOrganContract; use crate::repo_model_gateway::{ + REPO_FRONTIER_AUTONOMOUS_PROPOSAL_BINDING_CONTRACT, + REPO_FRONTIER_AUTONOMOUS_PROPOSAL_BINDING_SCHEMA_VERSION, REPO_FRONTIER_HANDS_AUTHORITY_CONTRACT, REPO_FRONTIER_HANDS_AUTHORITY_SCHEMA_VERSION, REPO_FRONTIER_MODELING_REQUEST_CONTRACT, REPO_FRONTIER_MODELING_REQUEST_SCHEMA_VERSION, REPO_FRONTIER_PLAN_CANDIDATE_SCHEMA_VERSION, REPO_FRONTIER_PLAN_DECISION_CONTRACT, @@ -71,7 +73,9 @@ use crate::repo_model_gateway::{ REPO_MODEL_CLAIM_CHALLENGE_CONTRACT, REPO_MODEL_CLAIM_CHALLENGE_SCHEMA_VERSION, REPO_MODEL_CLAIM_REPAIR_REQUEST_CONTRACT, REPO_MODEL_CLAIM_REPAIR_REQUEST_SCHEMA_VERSION, REPO_MODEL_MIGRATION_CONTRACT, REPO_MODEL_MIGRATION_RECEIPT_SCHEMA_VERSION, - REPO_MODEL_MIGRATION_RECEIPT_TYPE, RepoFrontierHandsAuthority, RepoFrontierModelingRequest, + REPO_MODEL_MIGRATION_RECEIPT_TYPE, RUNTIME_REPOSITORY_DOMAIN_BINDING_CONTRACT, + RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY, RUNTIME_REPOSITORY_DOMAIN_BINDING_SCHEMA_VERSION, + RepoFrontierAutonomousProposalBinding, RepoFrontierHandsAuthority, RepoFrontierModelingRequest, RepoFrontierNextOrgan, RepoFrontierPlanCandidate, RepoFrontierPlanDecision, RepoFrontierPlanDecisionReceipt, RepoFrontierPlanMindDecision, RepoFrontierPlanMindLaunchBinding, RepoFrontierPlanMindRequest, @@ -80,7 +84,7 @@ use crate::repo_model_gateway::{ RepoFrontierRoute, RepoFrontierVerdictDisposition, RepoFrontierWorkProposal, RepoModelAdmissionReceipt, RepoModelAdmissionReview, RepoModelClaimChallenge, RepoModelClaimRepairFrontierRef, RepoModelClaimRepairLaunchBinding, - RepoModelClaimRepairRequest, RepoModelMigrationReceipt, + RepoModelClaimRepairRequest, RepoModelMigrationReceipt, RuntimeRepositoryDomainBinding, }; use crate::soul_gateway::SoulVerdictReceipt; use crate::soul_gateway::*; @@ -896,6 +900,8 @@ pub fn runtime_spine_cache(store_path: impl AsRef) -> Result { cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; + cache.register_entry_type::()?; + cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; @@ -2033,6 +2039,7 @@ pub fn put_runtime_role_worker_result( )); } } + let mut model_direction_companion = None; let has_model_direction_echo = result .admitted_model_direction_consideration_request_id .is_some(); @@ -2101,6 +2108,7 @@ pub fn put_runtime_role_worker_result( "model direction result does not exactly bind request and launch" )); } + model_direction_companion = Some(direction_result); } let has_consideration_echo = result.imagination_consideration_request_id.is_some(); let has_consideration_candidate = result.imagination_consideration_candidate_msgpack.is_some(); @@ -2271,26 +2279,58 @@ pub fn put_runtime_role_worker_result( } } if let Some(existing) = cache.get::(&result.job_id)? { - return if existing == *result { - Ok(()) - } else { - Err(anyhow!( + if existing != *result { + return Err(anyhow!( "role worker result is immutable for its runtime job" - )) - }; + )); + } + if let Some(companion) = model_direction_companion.as_ref() { + return match cache + .get::(&companion.result_id)? + { + Some(existing) if existing == *companion => Ok(()), + _ => Err(anyhow!( + "model direction worker result lost its exact typed companion" + )), + }; + } + return Ok(()); } let (envelope, _) = cache.prepare_entry(&result.job_id, result)?; + let mut writes = vec![envelope]; + if let Some(companion) = model_direction_companion.as_ref() { + if cache + .get::(&companion.result_id)? + .is_some() + { + return Err(anyhow!( + "model direction result companion identity already exists without its worker result" + )); + } + let (companion_envelope, _) = cache.prepare_entry(&companion.result_id, companion)?; + writes.push(companion_envelope); + } let backing = SingleFileMessagePackBackingStore::new(store_path); - if backing.insert_entry_if_absent(envelope)? { + if backing.compare_and_swap_batch(&[], writes)? { return Ok(()); } let mut reloaded = runtime_spine_cache(store_path)?; reloaded.pull_all_backing_stores()?; - match reloaded.get::(&result.job_id)? { - Some(existing) if existing == *result => Ok(()), - _ => Err(anyhow!( - "role worker result lost immutable insertion to a different result" - )), + let worker_matches = reloaded + .get::(&result.job_id)? + .is_some_and(|existing| existing == *result); + let companion_matches = match model_direction_companion.as_ref() { + Some(companion) => reloaded + .get::(&companion.result_id)? + .is_some_and(|existing| existing == *companion), + None => true, + }; + if worker_matches && companion_matches { + Ok(()) + } else { + Err(anyhow!( + "role worker result lost immutable insertion to a different result or companion" + )) } } @@ -2849,6 +2889,9 @@ pub fn commit_repo_model_admission( .get::(&request.proposal_id)? .ok_or_else(|| anyhow!("proposal Evolution requires exact proposal"))?; validate_repo_frontier_work_proposal(&proposal)?; + if proposal.source_kind == crate::RepoFrontierProposalSourceKind::Imagination { + validate_autonomous_proposal_binding(&cache, &proposal)?; + } let launch_bindings = cache .get_all::()? .into_iter() @@ -3064,6 +3107,14 @@ pub fn commit_repo_model_admission( "proposal Evolution requires one proposal-citing frontier upsert and exact evidence" )); } + if proposal.source_kind == crate::RepoFrontierProposalSourceKind::Imagination + && (upserts[0].recommended_next_organ != "Imagination" + || upserts[0].adopted_plan.is_some()) + { + return Err(anyhow!( + "autonomous proposal Modeling may admit only an unadopted Imagination-planning frontier; Mind alone may adopt a plan before Hands" + )); + } request.request_id } else { if frontier_operation_count != 0 { @@ -3491,6 +3542,169 @@ pub(crate) fn validate_repo_frontier_work_proposal( Ok(()) } +pub(crate) fn validate_autonomous_proposal_binding( + cache: &CultCache, + proposal: &RepoFrontierWorkProposal, +) -> Result { + if proposal.source_kind != crate::RepoFrontierProposalSourceKind::Imagination { + return Err(anyhow!( + "autonomous binding requires an Imagination proposal" + )); + } + let binding_id = format!("autonomous-proposal-binding-{}", proposal.proposal_id); + let binding = cache + .get::(&binding_id)? + .ok_or_else(|| anyhow!("Imagination proposal lacks its autonomous origin binding"))?; + let request = cache + .get::(&binding.direction_request_id)? + .ok_or_else(|| anyhow!("autonomous proposal binding lost its direction request"))?; + let result = cache + .get::(&binding.direction_result_id)? + .ok_or_else(|| anyhow!("autonomous proposal binding lost its direction result"))?; + let worker_result = cache + .get::(&binding.direction_worker_job_id)? + .ok_or_else(|| anyhow!("autonomous proposal binding lost its Imagination worker result"))?; + let worker_launch = cache + .get::(&binding.direction_worker_job_id)? + .ok_or_else(|| anyhow!("autonomous proposal binding lost its Imagination worker launch"))?; + crate::validate_current_admitted_model_direction_consideration_request(cache, &request)?; + crate::validate_admitted_model_direction_consideration_result(&request, &result)?; + let result_sha256 = format!("{:x}", Sha256::digest(rmp_serde::to_vec_named(&result)?)); + let worker_result_sha256 = format!( + "{:x}", + Sha256::digest(rmp_serde::to_vec_named(&worker_result)?) + ); + let worker_launch_sha256 = format!( + "{:x}", + Sha256::digest(rmp_serde::to_vec_named(&worker_launch)?) + ); + let worker_direction = worker_result + .admitted_model_direction_consideration_result()? + .ok_or_else(|| anyhow!("autonomous proposal worker result lost its direction cargo"))?; + let launch_projection = match worker_launch.launch_document()? { + EpiphanyWorkerLaunchDocument::Role(document) => { + document.admitted_model_direction_consideration_context + } + EpiphanyWorkerLaunchDocument::Reorient(_) => None, + }; + let option = result + .option_drafts + .get(binding.option_ordinal as usize) + .ok_or_else(|| anyhow!("autonomous proposal binding names a missing option"))?; + let option_sha256 = format!("{:x}", Sha256::digest(rmp_serde::to_vec_named(option)?)); + let route = cache + .get::(crate::RUNTIME_BODY_STORE_BINDING_KEY)? + .ok_or_else(|| anyhow!("autonomous proposal requires repository Body binding"))?; + let domain = cache + .get::(RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY)? + .ok_or_else(|| anyhow!("autonomous proposal requires repository domain binding"))?; + let (body_binding, _) = crate::load_repository_body_status(Path::new(&route.body_store_path))? + .ok_or_else(|| anyhow!("autonomous proposal requires authenticated Body status"))?; + let chain_checks = [ + ( + "worker result identity", + binding.direction_worker_result_id == worker_result.result_id, + ), + ( + "worker result hash", + binding.direction_worker_result_sha256 == worker_result_sha256, + ), + ( + "worker launch hash", + binding.direction_worker_launch_sha256 == worker_launch_sha256, + ), + ( + "worker job", + worker_result.job_id == binding.direction_worker_job_id, + ), + ( + "worker role", + worker_result.role_id.eq_ignore_ascii_case("imagination"), + ), + ( + "worker request echo", + worker_result + .admitted_model_direction_consideration_request_id + .as_deref() + == Some(request.request_id.as_str()), + ), + ("worker direction cargo", worker_direction == result), + ( + "direction launch identity", + result.result_id + == crate::admitted_model_direction_consideration_result_id_for_launch( + &request.request_id, + &binding.direction_worker_job_id, + ), + ), + ( + "launch role", + worker_launch + .role + .eq_ignore_ascii_case(EPIPHANY_IMAGINATION_OWNER_ROLE), + ), + ( + "launch binding", + worker_launch.binding_id == EPIPHANY_IMAGINATION_ROLE_BINDING_ID, + ), + ( + "launch request echo", + worker_launch + .admitted_model_direction_consideration_request_id + .as_deref() + == Some(request.request_id.as_str()), + ), + ( + "launch request projection", + launch_projection.map(|projection| projection.request) == Some(request.clone()), + ), + ]; + if let Some((failed, _)) = chain_checks.into_iter().find(|(_, matches)| !matches) { + return Err(anyhow!( + "autonomous proposal Imagination chain mismatch: {failed}" + )); + } + if binding.schema_version != REPO_FRONTIER_AUTONOMOUS_PROPOSAL_BINDING_SCHEMA_VERSION + || binding.contract != REPO_FRONTIER_AUTONOMOUS_PROPOSAL_BINDING_CONTRACT + || binding.binding_id != binding_id + || binding.proposal_id != proposal.proposal_id + || binding.proposal_payload_sha256 != proposal.payload_sha256 + || binding.direction_request_id != result.request_id + || binding.direction_result_id != result.result_id + || binding.direction_result_sha256 != result_sha256 + || binding.model_revision != result.model_revision + || binding.model_hash != result.model_hash + || binding.model_admission_receipt_id != result.model_admission_receipt_id + || binding.option_sha256 != option_sha256 + || binding.runtime_id != proposal.runtime_id + || binding.thread_id != proposal.thread_id + || binding.workspace_id != route.workspace_id + || binding.body_binding_sha256 != route.body_binding_sha256 + || domain.schema_version != RUNTIME_REPOSITORY_DOMAIN_BINDING_SCHEMA_VERSION + || domain.contract != RUNTIME_REPOSITORY_DOMAIN_BINDING_CONTRACT + || domain.binding_id != RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY + || domain.repository_full_name != proposal.repository + || domain.runtime_id != route.runtime_id + || domain.swarm_id != route.swarm_id + || domain.workspace_id != route.workspace_id + || domain.body_binding_sha256 != route.body_binding_sha256 + || body_binding.runtime_id != route.runtime_id + || body_binding.swarm_id != route.swarm_id + || body_binding.workspace_id != route.workspace_id + || crate::repository_body_observer::body_binding_sha256(&body_binding)? + != route.body_binding_sha256 + || proposal.workspace != body_binding.git_top_level + || chrono::DateTime::parse_from_rfc3339(&binding.created_at).is_err() + || proposal.source_actor != EPIPHANY_IMAGINATION_OWNER_ROLE + || proposal.source_ref != result.result_id + || proposal.title != option.title + || proposal.body != option.summary + { + return Err(anyhow!("autonomous proposal origin binding mismatch")); + } + Ok(binding) +} + pub(crate) fn validate_repo_frontier_proposal_modeling_request( request: &RepoFrontierProposalModelingRequest, ) -> Result<()> { @@ -3517,6 +3731,11 @@ pub fn put_repo_frontier_work_proposal( proposal: &RepoFrontierWorkProposal, ) -> Result<()> { validate_repo_frontier_work_proposal(proposal)?; + if proposal.source_kind == crate::RepoFrontierProposalSourceKind::Imagination { + return Err(anyhow!( + "generic proposal intake cannot author Imagination provenance" + )); + } let mut cache = runtime_spine_cache(store_path.as_ref())?; cache.pull_all_backing_stores()?; let identity = require_identity(&cache)?; @@ -3579,6 +3798,380 @@ pub fn runtime_repo_frontier_work_proposal( cache.get::(proposal_id) } +pub fn bind_runtime_repository_domain( + runtime_store: impl AsRef, + repository_full_name: &str, + bound_at: &str, +) -> Result { + if !repository_full_name.starts_with("GameCult/") + || repository_full_name["GameCult/".len()..].trim().is_empty() + || repository_full_name.chars().any(char::is_whitespace) + { + return Err(anyhow!( + "runtime repository domain requires a canonical GameCult name" + )); + } + chrono::DateTime::parse_from_rfc3339(bound_at) + .map_err(|_| anyhow!("runtime repository domain timestamp must be RFC3339"))?; + let runtime_store = runtime_store.as_ref(); + let mut cache = runtime_spine_cache(runtime_store)?; + cache.pull_all_backing_stores()?; + let identity = require_identity(&cache)?; + let route = cache + .get::(crate::RUNTIME_BODY_STORE_BINDING_KEY)? + .ok_or_else(|| anyhow!("runtime repository domain requires Body binding"))?; + let (body, _) = crate::load_repository_body_status(Path::new(&route.body_store_path))? + .ok_or_else(|| anyhow!("runtime repository domain requires authenticated Body status"))?; + if route.runtime_id != identity.runtime_id + || body.runtime_id != route.runtime_id + || body.swarm_id != route.swarm_id + || body.workspace_id != route.workspace_id + || crate::repository_body_observer::body_binding_sha256(&body)? != route.body_binding_sha256 + { + return Err(anyhow!("runtime repository domain Body authority mismatch")); + } + let binding = RuntimeRepositoryDomainBinding { + schema_version: RUNTIME_REPOSITORY_DOMAIN_BINDING_SCHEMA_VERSION.into(), + binding_id: RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY.into(), + repository_full_name: repository_full_name.into(), + runtime_id: route.runtime_id.clone(), + swarm_id: route.swarm_id.clone(), + workspace_id: route.workspace_id.clone(), + body_binding_sha256: route.body_binding_sha256.clone(), + bound_at: bound_at.into(), + contract: RUNTIME_REPOSITORY_DOMAIN_BINDING_CONTRACT.into(), + }; + if let Some(existing) = + cache.get::(RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY)? + { + let mut replay = binding; + replay.bound_at = existing.bound_at.clone(); + return if replay == existing { + Ok(existing) + } else { + Err(anyhow!("runtime repository domain is immutable")) + }; + } + let identity_envelope = cache + .get_envelope::(RUNTIME_IDENTITY_KEY)? + .ok_or_else(|| anyhow!("runtime repository domain identity disappeared"))?; + let route_envelope = cache + .get_envelope::( + crate::RUNTIME_BODY_STORE_BINDING_KEY, + )? + .ok_or_else(|| anyhow!("runtime repository domain Body route disappeared"))?; + let (binding_envelope, _) = + cache.prepare_entry(RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY, &binding)?; + let expected = vec![identity_envelope, route_envelope]; + let mut replacement = expected.clone(); + replacement.push(binding_envelope); + if !SingleFileMessagePackBackingStore::new(runtime_store) + .compare_and_swap_batch(&expected, replacement)? + { + return Err(anyhow!("runtime repository domain lost atomic binding")); + } + Ok(binding) +} + +pub fn promote_autonomous_direction_options_for_modeling( + runtime_store: impl AsRef, + repository: &str, + workspace: &str, + selected_at: &str, +) -> Result> { + chrono::DateTime::parse_from_rfc3339(selected_at) + .map_err(|_| anyhow!("autonomous proposal promotion timestamp must be RFC3339"))?; + validate_non_empty(repository, "autonomous proposal repository")?; + validate_non_empty(workspace, "autonomous proposal workspace")?; + let runtime_store = runtime_store.as_ref(); + let mut opening = runtime_spine_cache(runtime_store)?; + opening.pull_all_backing_stores()?; + let identity = require_identity(&opening)?; + let thread = opening + .get::(crate::THREAD_STATE_KEY)? + .ok_or_else(|| anyhow!("autonomous proposal promotion requires thread state"))?; + let model = opening + .get::(crate::MEMORY_GRAPH_KEY)? + .ok_or_else(|| anyhow!("autonomous proposal promotion requires admitted Modeling map"))? + .snapshot()?; + let model_hash = crate::memory_graph_model_hash(&model)?; + let receipts = opening + .get_all::()? + .into_iter() + .filter(|receipt| { + receipt.admitted_revision == model.model_revision && receipt.admitted_hash == model_hash + }) + .collect::>(); + if receipts.len() != 1 { + return Err(anyhow!( + "autonomous proposal promotion requires one current model receipt" + )); + } + let mut results = opening + .get_all::()? + .into_iter() + .filter(|result| { + result.disposition == crate::AdmittedModelDirectionDisposition::Suggest + && result.model_revision == model.model_revision + && result.model_hash == model_hash + && result.model_admission_receipt_id == receipts[0].receipt_id + }) + .collect::>(); + if results.is_empty() { + return Ok(Vec::new()); + } + let route = opening + .get::(crate::RUNTIME_BODY_STORE_BINDING_KEY)? + .ok_or_else(|| anyhow!("autonomous proposal promotion requires Body binding"))?; + if route.runtime_id != identity.runtime_id { + return Err(anyhow!("autonomous proposal Body binding runtime mismatch")); + } + let domain = opening + .get::(RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY)? + .ok_or_else(|| { + anyhow!("autonomous proposal promotion requires repository domain binding") + })?; + let body_store = PathBuf::from(&route.body_store_path); + let (body_binding, _) = crate::load_repository_body_status(&body_store)? + .ok_or_else(|| anyhow!("autonomous proposal Body store has no authenticated status"))?; + if body_binding.workspace_id != route.workspace_id + || body_binding.runtime_id != route.runtime_id + || body_binding.swarm_id != route.swarm_id + || crate::repository_body_observer::body_binding_sha256(&body_binding)? + != route.body_binding_sha256 + || Path::new(workspace).canonicalize()? + != Path::new(&body_binding.git_top_level).canonicalize()? + || domain.schema_version != RUNTIME_REPOSITORY_DOMAIN_BINDING_SCHEMA_VERSION + || domain.contract != RUNTIME_REPOSITORY_DOMAIN_BINDING_CONTRACT + || domain.binding_id != RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY + || domain.repository_full_name != repository + || domain.runtime_id != route.runtime_id + || domain.swarm_id != route.swarm_id + || domain.workspace_id != route.workspace_id + || domain.body_binding_sha256 != route.body_binding_sha256 + { + return Err(anyhow!( + "autonomous proposal workspace is not the bound repository Body" + )); + } + results.sort_by(|left, right| left.result_id.cmp(&right.result_id)); + let mut promoted = Vec::new(); + for result in results { + let request = opening + .get::(&result.request_id)? + .ok_or_else(|| anyhow!("autonomous proposal result lost its request"))?; + crate::validate_admitted_model_direction_consideration_result(&request, &result)?; + let mut direction_workers = opening + .get_all::()? + .into_iter() + .filter_map(|worker| { + worker + .admitted_model_direction_consideration_result() + .ok() + .flatten() + .filter(|embedded| embedded == &result) + .map(|_| worker) + }) + .collect::>(); + if direction_workers.len() != 1 { + return Err(anyhow!( + "autonomous proposal requires exactly one immutable Imagination worker result" + )); + } + let direction_worker = direction_workers.remove(0); + let direction_launch = opening + .get::(&direction_worker.job_id)? + .ok_or_else(|| anyhow!("autonomous proposal direction worker lost its launch"))?; + let direction_worker_result_sha256 = format!( + "{:x}", + Sha256::digest(rmp_serde::to_vec_named(&direction_worker)?) + ); + let direction_worker_launch_sha256 = format!( + "{:x}", + Sha256::digest(rmp_serde::to_vec_named(&direction_launch)?) + ); + let result_sha256 = format!("{:x}", Sha256::digest(rmp_serde::to_vec_named(&result)?)); + for (ordinal, option) in result.option_drafts.iter().enumerate() { + if option.title.trim().is_empty() || option.summary.trim().is_empty() { + return Err(anyhow!("autonomous direction option is empty")); + } + let option_sha256 = format!("{:x}", Sha256::digest(rmp_serde::to_vec_named(option)?)); + let proposal_id = format!( + "repo-frontier-autonomous-{:x}", + Sha256::digest(rmp_serde::to_vec_named(&( + &result.result_id, + ordinal as u32, + &option_sha256 + ))?) + ); + let evidence_refs = result + .evidence_refs + .iter() + .cloned() + .chain([ + format!( + "cultcache://admitted-model-direction/{}", + request.request_id + ), + format!( + "cultcache://admitted-model-direction-result/{}", + result.result_id + ), + format!( + "cultcache://repo-model-admission/{}", + result.model_admission_receipt_id + ), + ]) + .collect::>(); + let content = rmp_serde::to_vec_named(&( + &option.title, + &option.summary, + &option.summary, + &result.uncertainties, + &Vec::::new(), + &evidence_refs, + ))?; + let payload_sha256 = format!("{:x}", Sha256::digest(content)); + let proposal = RepoFrontierWorkProposal { + schema_version: REPO_FRONTIER_WORK_PROPOSAL_SCHEMA_VERSION.into(), + proposal_id: proposal_id.clone(), + source_kind: crate::RepoFrontierProposalSourceKind::Imagination, + source_actor: EPIPHANY_IMAGINATION_OWNER_ROLE.into(), + source_ref: result.result_id.clone(), + repository: repository.into(), + workspace: body_binding.git_top_level.clone(), + thread_id: thread.thread_id.clone(), + runtime_id: identity.runtime_id.clone(), + payload_sha256: payload_sha256.clone(), + title: option.title.clone(), + body: option.summary.clone(), + desired_outcome: option.summary.clone(), + constraints: result.uncertainties.clone(), + scope_hints: Vec::new(), + evidence_refs, + private_state_included: false, + proposed_at: result.proposed_at.clone(), + contract: REPO_FRONTIER_WORK_PROPOSAL_CONTRACT.into(), + }; + validate_repo_frontier_work_proposal(&proposal)?; + let binding = RepoFrontierAutonomousProposalBinding { + schema_version: REPO_FRONTIER_AUTONOMOUS_PROPOSAL_BINDING_SCHEMA_VERSION.into(), + binding_id: format!("autonomous-proposal-binding-{proposal_id}"), + proposal_id: proposal_id.clone(), + proposal_payload_sha256: payload_sha256.clone(), + direction_request_id: request.request_id.clone(), + direction_result_id: result.result_id.clone(), + direction_result_sha256: result_sha256.clone(), + model_revision: result.model_revision, + model_hash: result.model_hash.clone(), + model_admission_receipt_id: result.model_admission_receipt_id.clone(), + option_ordinal: ordinal as u32, + option_sha256, + runtime_id: identity.runtime_id.clone(), + thread_id: thread.thread_id.clone(), + workspace_id: route.workspace_id.clone(), + body_binding_sha256: route.body_binding_sha256.clone(), + created_at: selected_at.into(), + contract: REPO_FRONTIER_AUTONOMOUS_PROPOSAL_BINDING_CONTRACT.into(), + direction_worker_job_id: direction_worker.job_id.clone(), + direction_worker_result_id: direction_worker.result_id.clone(), + direction_worker_result_sha256: direction_worker_result_sha256.clone(), + direction_worker_launch_sha256: direction_worker_launch_sha256.clone(), + }; + let selection = RepoFrontierProposalModelingRequest { + schema_version: REPO_FRONTIER_PROPOSAL_MODELING_REQUEST_SCHEMA_VERSION.into(), + request_id: format!( + "repo-frontier-proposal-modeling-{:x}", + Sha256::digest(format!("{}:{}", proposal_id, payload_sha256).as_bytes()) + ), + proposal_id: proposal_id.clone(), + proposal_payload_sha256: payload_sha256, + runtime_id: identity.runtime_id.clone(), + thread_id: thread.thread_id.clone(), + repository: repository.into(), + workspace: body_binding.git_top_level.clone(), + selected_at: selected_at.into(), + contract: REPO_FRONTIER_PROPOSAL_MODELING_REQUEST_CONTRACT.into(), + }; + validate_repo_frontier_proposal_modeling_request(&selection)?; + let mut current = runtime_spine_cache(runtime_store)?; + current.pull_all_backing_stores()?; + let existing = ( + current.get::(&proposal.proposal_id)?, + current.get::(&binding.binding_id)?, + current.get::(&selection.request_id)?, + ); + if let (Some(existing_proposal), Some(_), Some(existing_selection)) = &existing { + validate_autonomous_proposal_binding(¤t, existing_proposal)?; + let mut replay_selection = selection.clone(); + replay_selection.selected_at = existing_selection.selected_at.clone(); + if existing_proposal == &proposal && existing_selection == &replay_selection { + promoted.push(existing_selection.clone()); + continue; + } + } + if existing.0.is_some() || existing.1.is_some() || existing.2.is_some() { + return Err(anyhow!("autonomous proposal promotion companion collision")); + } + let (proposal_envelope, _) = current.prepare_entry(&proposal.proposal_id, &proposal)?; + let (binding_envelope, _) = current.prepare_entry(&binding.binding_id, &binding)?; + let (selection_envelope, _) = + current.prepare_entry(&selection.request_id, &selection)?; + let expected = vec![ + current + .get_envelope::(RUNTIME_IDENTITY_KEY)? + .ok_or_else(|| anyhow!("runtime identity envelope disappeared"))?, + current + .get_envelope::(crate::THREAD_STATE_KEY)? + .ok_or_else(|| anyhow!("thread envelope disappeared"))?, + current + .get_envelope::(crate::MEMORY_GRAPH_KEY)? + .ok_or_else(|| anyhow!("model envelope disappeared"))?, + current + .get_envelope::(&result.model_admission_receipt_id)? + .ok_or_else(|| anyhow!("model receipt envelope disappeared"))?, + current + .get_envelope::( + crate::RUNTIME_BODY_STORE_BINDING_KEY, + )? + .ok_or_else(|| anyhow!("Body route envelope disappeared"))?, + current + .get_envelope::( + RUNTIME_REPOSITORY_DOMAIN_BINDING_KEY, + )? + .ok_or_else(|| anyhow!("repository domain envelope disappeared"))?, + current + .get_envelope::( + &request.request_id, + )? + .ok_or_else(|| anyhow!("direction request envelope disappeared"))?, + current + .get_envelope::( + &result.result_id, + )? + .ok_or_else(|| anyhow!("direction result envelope disappeared"))?, + current + .get_envelope::(&direction_worker.job_id)? + .ok_or_else(|| anyhow!("direction launch envelope disappeared"))?, + current + .get_envelope::(&direction_worker.job_id)? + .ok_or_else(|| anyhow!("direction worker envelope disappeared"))?, + ]; + let mut replacement = expected.clone(); + replacement.extend([proposal_envelope, binding_envelope, selection_envelope]); + if !SingleFileMessagePackBackingStore::new(runtime_store) + .compare_and_swap_batch(&expected, replacement)? + { + return Err(anyhow!( + "autonomous proposal promotion lost atomic insertion" + )); + } + promoted.push(selection); + } + } + Ok(promoted) +} + pub fn runtime_repo_frontier_proposal_modeling_request( store_path: impl AsRef, request_id: &str, @@ -3692,6 +4285,9 @@ pub fn select_repo_frontier_work_proposal_for_modeling( .get::(proposal_id)? .ok_or_else(|| anyhow!("proposal selection requires exact persisted proposal"))?; validate_repo_frontier_work_proposal(&proposal)?; + if proposal.source_kind == crate::RepoFrontierProposalSourceKind::Imagination { + validate_autonomous_proposal_binding(&cache, &proposal)?; + } let thread = cache .get::(crate::THREAD_STATE_KEY)? .ok_or_else(|| anyhow!("proposal selection requires authoritative thread state"))?; @@ -8803,6 +9399,350 @@ pub(crate) mod tests { assert!(!public_surface.contains("put_repo_frontier_plan_adoption")); } + #[test] + fn autonomous_direction_option_is_atomically_bound_and_selected_for_modeling() -> Result<()> { + let root = tempfile::tempdir()?; + let (store, modeling_result, review) = + proposal_admission_fixture(root.path(), "autonomous-bridge")?; + put_runtime_role_worker_result(&store, &modeling_result)?; + let admitted = commit_repo_model_admission(&store, &modeling_result.result_id, &review)?; + let request = crate::commit_admitted_model_direction_consideration_request( + &store, + "2026-07-18T01:00:00Z", + )? + .expect("current admitted model direction request"); + assert_eq!(request.model_admission_receipt_id, admitted.receipt_id); + let mut launch_cache = runtime_spine_cache(&store)?; + launch_cache.pull_all_backing_stores()?; + let thread = launch_cache + .get::(crate::THREAD_STATE_KEY)? + .expect("direction thread state"); + let state = thread.state()?; + let launch = crate::build_epiphany_admitted_model_direction_consideration_launch_request( + &thread.thread_id, + Some(state.revision), + Some(60), + &state, + request.request_id.clone(), + ) + .map_err(|error| anyhow!(error))?; + let job_id = "direction-job-autonomous-bridge"; + let plan = crate::plan_coordinator_job_launch( + &state, + &launch, + &store, + "direction-launcher-autonomous-bridge".into(), + job_id.into(), + )?; + crate::commit_coordinator_job_launch( + &store, + &thread.thread_id, + &state, + &launch, + &plan, + "2026-07-18T01:00:01Z".into(), + )?; + let direction = crate::AdmittedModelDirectionConsiderationResult { + schema_version: crate::ADMITTED_MODEL_DIRECTION_CONSIDERATION_RESULT_SCHEMA_VERSION + .into(), + result_id: crate::admitted_model_direction_consideration_result_id_for_launch( + &request.request_id, + job_id, + ), + request_id: request.request_id.clone(), + runtime_id: request.runtime_id.clone(), + thread_id: request.thread_id.clone(), + model_revision: request.model_revision, + model_hash: request.model_hash.clone(), + model_admission_receipt_id: request.model_admission_receipt_id.clone(), + disposition: crate::AdmittedModelDirectionDisposition::Suggest, + summary: "One coherent option fits the admitted map.".into(), + option_drafts: vec![crate::ImaginationOptionDraft { + title: "Persist autonomous frontier causality".into(), + summary: "Carry exact direction and Body provenance into Modeling review.".into(), + }], + uncertainties: vec!["Modeling must verify source scope.".into()], + evidence_refs: vec!["evidence:admitted-map".into()], + proposed_at: "2026-07-18T01:00:01Z".into(), + contract: crate::ADMITTED_MODEL_DIRECTION_CONSIDERATION_RESULT_CONTRACT.into(), + proposal_only: true, + terminal: true, + }; + crate::validate_admitted_model_direction_consideration_result(&request, &direction)?; + let body_route = launch_cache + .get::(crate::RUNTIME_BODY_STORE_BINDING_KEY)? + .expect("autonomous Body route"); + let (body_binding, _) = + crate::load_repository_body_status(Path::new(&body_route.body_store_path))? + .expect("autonomous Body status"); + let workspace = body_binding.git_top_level; + bind_runtime_repository_domain(&store, "GameCult/Epiphany", "2026-07-18T01:00:01Z")?; + + let counterfeit_store = root.path().join("counterfeit-direction-companion.cc"); + std::fs::copy(&store, &counterfeit_store)?; + let mut counterfeit_cache = runtime_spine_cache(&counterfeit_store)?; + counterfeit_cache.put(&direction.result_id, &direction)?; + let before_counterfeit_promotion = std::fs::read(&counterfeit_store)?; + assert!( + promote_autonomous_direction_options_for_modeling( + &counterfeit_store, + "GameCult/Epiphany", + &workspace, + "2026-07-18T01:00:02Z", + ) + .is_err() + ); + assert_eq!( + std::fs::read(&counterfeit_store)?, + before_counterfeit_promotion + ); + let worker_result = EpiphanyRuntimeRoleWorkerResult { + schema_version: RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION.into(), + result_id: "worker-result-autonomous-bridge".into(), + job_id: job_id.into(), + role_id: "imagination".into(), + verdict: "proposal-only".into(), + summary: direction.summary.clone(), + next_safe_move: "Self may route exact options to Modeling review.".into(), + checkpoint_summary: None, + scratch_summary: None, + files_inspected: Vec::new(), + frontier_node_ids: Vec::new(), + evidence_ids: direction.evidence_refs.clone(), + artifact_refs: Vec::new(), + open_questions: direction.uncertainties.clone(), + evidence_gaps: Vec::new(), + risks: Vec::new(), + state_patch_msgpack: None, + self_patch_msgpack: None, + item_error: None, + metadata: 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: None, + frontier_plan_mind_decision_msgpack: None, + repository_body_observation_basis: None, + imagination_consideration_request_id: None, + imagination_consideration_candidate_msgpack: None, + admitted_model_direction_consideration_request_id: Some(request.request_id.clone()), + admitted_model_direction_consideration_result_msgpack: Some(rmp_serde::to_vec_named( + &direction, + )?), + }; + put_runtime_role_worker_result(&store, &worker_result)?; + let mut companion_cache = runtime_spine_cache(&store)?; + companion_cache.pull_all_backing_stores()?; + assert_eq!( + companion_cache + .get::(&direction.result_id)?, + Some(direction.clone()) + ); + let alien_workspace = root.path().join("alien-workspace"); + std::fs::create_dir_all(&alien_workspace)?; + let before_substitution = std::fs::read(&store)?; + assert!( + promote_autonomous_direction_options_for_modeling( + &store, + "GameCult/Epiphany", + alien_workspace.to_str().expect("UTF-8 alien workspace"), + "2026-07-18T01:00:02Z", + ) + .is_err() + ); + assert_eq!(std::fs::read(&store)?, before_substitution); + assert!( + promote_autonomous_direction_options_for_modeling( + &store, + "GameCult/Alien", + &workspace, + "2026-07-18T01:00:02Z", + ) + .is_err() + ); + assert_eq!(std::fs::read(&store)?, before_substitution); + + let selections = promote_autonomous_direction_options_for_modeling( + &store, + "GameCult/Epiphany", + &workspace, + "2026-07-18T01:00:02Z", + )?; + assert_eq!(selections.len(), 1); + let selection = &selections[0]; + let mut persisted = runtime_spine_cache(&store)?; + persisted.pull_all_backing_stores()?; + let proposal = persisted + .get::(&selection.proposal_id)? + .expect("autonomous proposal"); + assert!( + persisted + .get_all::()? + .is_empty() + ); + assert_eq!( + proposal.source_kind, + crate::RepoFrontierProposalSourceKind::Imagination + ); + let binding = validate_autonomous_proposal_binding(&persisted, &proposal)?; + assert_eq!(binding.direction_result_id, direction.result_id); + assert_eq!(binding.model_admission_receipt_id, admitted.receipt_id); + + let pre_modeling_state = + crate::read_coordinator_state(&store)?.expect("autonomous Modeling coordinator state"); + let modeling_thread = persisted + .get::(crate::THREAD_STATE_KEY)? + .expect("autonomous Modeling thread"); + let modeling_state = crate::interrupt_coordinator_job( + &store, + &modeling_thread.thread_id, + &pre_modeling_state, + EpiphanyJobInterruptRequest { + expected_revision: Some(pre_modeling_state.revision), + binding_id: "modeling-checkpoint-worker".into(), + reason: Some("exercise autonomous direct-Hands refusal".into()), + }, + )? + .epiphany_state; + let mut modeling_launch = crate::build_epiphany_role_launch_request( + &modeling_thread.thread_id, + crate::EpiphanyRoleResultRoleId::Modeling, + Some(modeling_state.revision), + Some(60), + &modeling_state, + ) + .map_err(|error| anyhow!(error))?; + modeling_launch.proposal_modeling_request_id = Some(selection.request_id.clone()); + let modeling_job = "autonomous-direct-hands-modeling-job"; + let modeling_plan = crate::plan_coordinator_job_launch( + &modeling_state, + &modeling_launch, + &store, + "autonomous-direct-hands-launcher".into(), + modeling_job.into(), + )?; + crate::commit_coordinator_job_launch( + &store, + &modeling_thread.thread_id, + &modeling_state, + &modeling_launch, + &modeling_plan, + "2026-07-18T01:00:04Z".into(), + )?; + let current_model = runtime_current_repo_model(&store)?.expect("autonomous current model"); + let hostile_patch = crate::RepoModelPatch { + patch_id: "autonomous-direct-hands-patch".into(), + base_revision: current_model.model_revision, + base_hash: crate::memory_graph_model_hash(¤t_model)?, + applied_at: "2026-07-18T01:00:05Z".into(), + purpose: crate::RepoModelPatchPurpose::Evolution, + operations: vec![crate::RepoModelPatchOperation::UpsertFrontier { + item: crate::RepoFrontierItem { + id: "autonomous-direct-hands-frontier".into(), + migration_body: "Attempt to skip plan adoption.".into(), + question: "Can Modeling route an autonomous idea directly to Hands?".into(), + gap: "Mind has not adopted a plan.".into(), + target_claim_ids: vec!["claim-runtime-model".into()], + source_scope: vec!["epiphany-core/src/runtime_spine.rs".into()], + recommended_next_organ: "Hands".into(), + evidence_refs: vec![proposal.proposal_id.clone()], + status: crate::RepoFrontierStatus::Active, + ..Default::default() + }, + }], + }; + let hostile_patch_bytes = rmp_serde::to_vec_named(&hostile_patch)?; + let mut hostile_result = worker_result.clone(); + hostile_result.result_id = "autonomous-direct-hands-result".into(); + hostile_result.job_id = modeling_job.into(); + hostile_result.role_id = "modeling".into(); + hostile_result.verdict = "checkpoint-ready".into(); + hostile_result.summary = "Attempted direct Hands routing.".into(); + hostile_result.next_safe_move = "Mind adoption is required.".into(); + hostile_result.files_inspected = vec!["epiphany-core/src/runtime_spine.rs".into()]; + hostile_result.frontier_node_ids = vec!["claim-runtime-model".into()]; + hostile_result.evidence_ids = vec![proposal.proposal_id.clone()]; + hostile_result.repo_model_patch_msgpack = Some(hostile_patch_bytes.clone()); + hostile_result.proposal_modeling_request_id = Some(selection.request_id.clone()); + hostile_result.repository_body_observation_basis = + runtime_worker_launch_body_basis(&store, modeling_job)?; + hostile_result.admitted_model_direction_consideration_request_id = None; + hostile_result.admitted_model_direction_consideration_result_msgpack = None; + put_runtime_role_worker_result(&store, &hostile_result)?; + let hostile_review = RepoModelAdmissionReview { + schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.into(), + review_id: "autonomous-direct-hands-review".into(), + result_id: hostile_result.result_id.clone(), + job_id: modeling_job.into(), + patch_id: hostile_patch.patch_id.clone(), + patch_sha256: format!("{:x}", Sha256::digest(&hostile_patch_bytes)), + base_revision: hostile_patch.base_revision, + base_hash: hostile_patch.base_hash.clone(), + decision: MindGatewayDecision::Accept, + evidence_ids: hostile_result.evidence_ids.clone(), + reviewed_at: "2026-07-18T01:00:06Z".into(), + contract: REPO_MODEL_ADMISSION_CONTRACT.into(), + repository_body_observation_basis: hostile_result + .repository_body_observation_basis + .clone(), + }; + let before_direct_hands_admission = std::fs::read(&store)?; + assert!( + commit_repo_model_admission(&store, &hostile_result.result_id, &hostile_review,) + .is_err() + ); + assert_eq!(std::fs::read(&store)?, before_direct_hands_admission); + assert!( + runtime_spine_cache(&store)? + .get_all::()? + .is_empty() + ); + assert_eq!( + promote_autonomous_direction_options_for_modeling( + &store, + "GameCult/Epiphany", + &workspace, + "2026-07-18T01:00:03Z", + )?, + selections + ); + let resident_store = root.path().join("autonomous-resident.cc"); + let feedback_store = root.path().join("autonomous-feedback.cc"); + assert_eq!( + crate::ingest_resident_self_domain_pressure( + &resident_store, + &store, + &feedback_store, + "proposal-runtime-autonomous-bridge", + "GameCult/Epiphany", + &workspace, + 1_752_796_803_000, + )?, + 1 + ); + let grant = crate::resident_self::heartbeat_issue_resident_self_grant( + &resident_store, + "autonomous-heartbeat", + "autonomous-action", + 1_752_796_803_001, + )? + .expect("autonomous Modeling grant"); + assert_eq!(grant.pressure_kind, "repo-frontier-proposal-modeling"); + assert!(grant.provenance_ref.ends_with(&selection.request_id)); + + let before = std::fs::read(&store)?; + let mut forged = proposal; + forged.proposal_id = "forged-imagination-proposal".into(); + assert!(put_repo_frontier_work_proposal(&store, &forged).is_err()); + assert_eq!(std::fs::read(&store)?, before); + Ok(()) + } + fn proposal_selection_fixture(root: &Path, suffix: &str) -> Result<(PathBuf, String)> { let store = root.join(format!("proposal-selection-{suffix}.cc")); let runtime_id = format!("selection-runtime-{suffix}"); diff --git a/notes/epiphany-current-algorithmic-map.md b/notes/epiphany-current-algorithmic-map.md index f1fb451d..99f20bf0 100644 --- a/notes/epiphany-current-algorithmic-map.md +++ b/notes/epiphany-current-algorithmic-map.md @@ -1128,5 +1128,27 @@ Idunn separately own release and deployment. Operator silence permits bounded observation, map maintenance, rumination, and proposals. It grants none of the later consequences. +The autonomous proposal crossing is source-grounded in +`runtime_spine.rs`, `admitted_model_direction_consideration.rs`, +`coordinator_launch.rs`, and `repo_model_gateway.rs`. A deployment-configured +`RuntimeRepositoryDomainBinding` immutably joins the runtime/swarm/workspace to +the exact repository Body binding. It names organizational jurisdiction only; +it is not proof of a Git remote. Self promotion reloads the exact direction +request/result, worker result and launch, admitted model receipt, domain +binding, and Body-owned canonical Git root, then one preserved-authority CAS +creates an inert frontier proposal and its Modeling selection. The generic +proposal mouth rejects Imagination provenance so it cannot substitute for this +causal crossing. + +Proposal Modeling is structurally bounded: its sole frontier upsert must +recommend `Imagination` and carry no adopted plan. Ordinary Evolution cannot +mutate frontier state, and proposal Evolution is insert-only. Therefore map +direction can become an inspectable proposal and planning request, but not a +Hands route. The full-chain hostile test supplies an exact proposal-citing +Modeling result that recommends direct Hands; Mind admission rejects it before +mutation, the store remains byte-identical, and Hands authority remains absent. +Only the existing explicit Mind adoption transition may create the route that +Hands consumes. + This map must change when ownership changes. Historical scars belong in git, evidence, or an explicitly archived note—not in the machine's Modeling state. diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 64b5e540..55aa782e 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -3146,15 +3146,32 @@ manual and automatic paths share it, and stale automatic work is dropped while the actuator is occupied. Durable request-head/result CAS remains unchanged. All 49 Idunn tests pass. No Ygg deployment has yet used this binary. -The autonomous option-to-frontier bridge is concurrently implemented but is -not release-ready. Exact worker launch/result provenance, Body-workspace -binding, preserved-authority CAS, true Self selection time, idempotent replay, -counterfeit companion refusal, and alien-workspace refusal pass the full -Epiphany library (578 passed, one ignored). Remaining blockers are typed -canonical repository-name authority and a direct negative proof that no Hands -authority appears without explicit Mind adoption. +The autonomous option-to-frontier bridge is now locally release-ready. A typed, +immutable deployment-configured repository-domain binding joins runtime, +swarm, workspace, and the exact Body binding. It is an organizational label, +not authenticated Git-remote identity. Promotion reloads the exact direction +request/result, worker result and launch, admitted model and receipt, canonical +Body Git root, and domain binding before one CAS creates an inert proposal and +Self selection for Modeling. Generic Imagination intake cannot forge this path. +Proposal Modeling may recommend only Imagination and may not carry an adopted +plan; ordinary Evolution cannot mutate frontier state. The hostile full-chain +test attempts a proposal-citing direct Hands route, is rejected with a +byte-identical store, and leaves Hands empty. Full core proof is 578 passed, +zero failed, one ignored; all binaries compile; Soul found no release blocker. -Next action: install `a17b0b6` on Ygg, commit/push only this state boundary as a -new Epiphany successor, authorize that exact commit through Bifrost, and require -signed health admission plus `deployment.env` under the all-scope brake. Do not -include the uncommitted autonomous bridge in that deployment release. +Live certification of state-only Epiphany commit `36f687e` exposed a separate +Idunn verifier wound. Epiphany sent a 2,260-byte signed active health document +with the exact request, release, witness, commit, signer, and Body terminal +receipt. Idunn received it but tried to create +`/etc/gamecult/idunn/epiphany-health-identity.ccmp.lock` while reading the +root-owned trust anchor, so no signed admission or deployment witness was +published. Making the config directory daemon-writable is forbidden because it +would let the verifier alter its own trust root. Odin commit `64bb679` uses an +atomic read-only CultCache snapshot for this one pinned-anchor read, proves no +sibling lock in a read-only directory, and passes 50/50 Idunn tests. + +Next action: install exact Idunn `64bb679` on Ygg, allow the incomplete +transaction to roll back cleanly, then retry the exact Bifrost-authorized 36f +release under the all-scope brake. Require signed active admission and final +`deployment.env`. Then commit/push the autonomous bridge and certify its exact +successor separately. No host reboot is authorized. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index c3379365..f2303cf0 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index 0471e05b..89372fc4 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -9,8 +9,8 @@ experiment: coordinator_grounding_loop: "Cold start is owned by one typed UserObjectiveIntake that atomically seeds thread state and refuses objective replacement; a missing checkpoint routes to Modeling rather than a fictional prepareCheckpoint actuator. Explicit local-Verse path authority flows into launch context without sibling-path inference. Eyes and Modeling receive bounded source tools. Accepted CRRC regather findings hand source gathering to Eyes instead of recursively relaunching the continuity judge. Runtime/display/coordinator reads share the canonical typed role and reorient result readers. Failed or completed-unreviewable role outputs may be explicitly superseded by a typed review receipt; JSON null is not a state patch. Provider output schema is formatting guidance only, while canonical typed ingress and Mind remain admission authority. Modeling's provider schema now describes tagged patch operations, closed node/edge/profile vocabularies, canonical node/edge fields, anchors, code refs, observations, evidence, frontier items, and Body-basis echo. Live dogfood has accepted one code-grounded Eyes result, but no current Body-grounded Modeling admission yet: the latest Modeling run used eleven source-tool rounds then failed at the provider request boundary before publishing typed output." modeling_admission_dogfood: "Source-grounded Modeling now completes with typed Body-basis ingress and substantial RepoModel node/edge proposals, proving the source/tool/output path. Mind first refused an ordinary Evolution patch that attempted unrequested frontier mutation, then refused a nodes/edges-only patch whose invented workspace-derived domain and Active lifecycle violated the canonical aggregate. Both were superseded through typed admission-rejection review. Modeling launch context now carries the current model revision/hash and exact existing domain ids plus legal repo lifecycles; Evolution mutates nodes/edges only and frontier mutation remains request-bound. A fresh run against the post-fix Body is still required before repository readiness can join." resident_direction_pressure: "Resident Self no longer invents Body drift from a current RepoModel hash. In epiphany-core/src/resident_self.rs, admitted-model-direction-consideration is emitted only for the exact current admitted model revision/hash and unique admission receipt when no terminal direction consideration exists. The request is typed and Imagination remains proposal-only. Attributed organizational feedback may add separate pressure but is not required for the resident swarm to inspect its admitted map and suggest direction." - autonomous_frontier_bridge: "Work in progress, not release authority. The current cut atomically persists an Imagination direction result with its worker result, binds promoted options to the exact worker launch/result, current admitted model receipt, runtime/thread, and authenticated repository Body workspace, then lets Self create an inert proposal and Modeling-selection request under preserved-authority CAS. Counterfeit standalone results, alien workspaces, causal time reversal, and timestamp-rewriting replay are refused without writes. Release remains withheld until canonical repository-name authority is typed rather than caller/configuration-shaped and a negative proof shows no Hands authority without explicit Mind adoption." - idunn_observation_actuation_split: "Odin commit a17b0b6 makes continuous target observation/evaluation/publication the target loop's sole authority and runs deploy/restart consequences separately under one per-target actuation reservation. Manual and automatic consequences share the reservation; automatic work is dropped rather than queued stale while occupied; durable current-request/result CAS is unchanged. This replaces the failed 75bd5f2 cut, which moved the mutex but left observation awaiting the long automatic deployment command. Idunn tests pass 49/49. Ygg installation/certification is pending." + autonomous_frontier_bridge: "Accepted normal product path. An immutable deployment-configured repository-domain binding joins the runtime/swarm/workspace and exact Body binding; it labels organizational jurisdiction but does not impersonate authenticated Git-remote identity. Epiphany atomically persists an Imagination direction result with its exact worker result, binds promotion to the exact worker launch/result, current admitted model receipt, runtime/thread, repository domain, and canonical Body Git root, then lets Self create one inert proposal and Modeling-selection request under preserved-authority CAS. Counterfeit companions, generic Imagination intake, alien workspaces/domains, causal time reversal, and replay substitution are refused without writes. Proposal Modeling may recommend only Imagination and carries no adopted plan; ordinary Evolution cannot mutate frontier state. The hostile full-chain proof rejects a proposal-citing Modeling result that attempts direct Hands routing, leaves the store byte-identical, and grants no Hands authority. Explicit Mind adoption remains the only route from proposal/planning into Hands. Full core proof: 578 passed, zero failed, one ignored; all binaries compile." + idunn_observation_actuation_split: "Odin commit a17b0b6 makes continuous target observation/evaluation/publication the target loop's sole authority and runs deploy/restart consequences separately under one per-target actuation reservation. Manual and automatic consequences share the reservation; automatic work is dropped rather than queued stale while occupied; durable current-request/result CAS is unchanged. Live deployment then exposed that signed Epiphany health admission read the root-owned pinned trust anchor through a writer-locking store path. Odin commit 64bb679 replaces only that trust-anchor read with an atomic read-only snapshot and proves a read-only directory creates no sibling lock; 50/50 Idunn tests pass. Ygg certification awaits installation of that exact verifier and a clean braked retry." release_publication_ownership: "epiphany-core/src/packaged_release.rs constructs and verifies immutable package bytes and witnesses. Application-owned store publication is distinct from Bifrost release approval and Idunn deployment: packaging proves what artifact exists; downstream applications own their typed publication, while Bifrost and Idunn own crossing and deployment consequences." verifier_read_purity: "Verifier/status reads do not acquire writer authority merely to inspect provider state. epiphany-core/src/persona_feedback_admission.rs reads a provider-owned snapshot through pull_all_read_only_snapshot without taking the provider lock. Status derives from immutable/provider snapshots; mutation locks belong only to the owning writer transaction." resident_readiness_ownership: "Resident cognition readiness is a join, not one store pretending to own the organism. epiphany-core/src/resident_readiness.rs derives readiness from separately owned provider readiness, packaged release identity/witness, runtime/Verse observations, Mind state, and brake state. Each source remains authoritative for its own fact; the readiness projection is read-derived and grants no cognition or action authority." @@ -804,4 +804,4 @@ current_status: - Candidate `b8517d9d7905eb62314db675cc2f0688510aa5bd` proved stale launch terminalization and current-policy replacement launch: supervisor PID `1147968` launched replacement workspace projector PID `1148157`, whose authenticated ready heartbeat advanced while the engaged brake held. Recovery still stalled because the old Body claim could be read only through current-claim authentication, which correctly rejected its historical policy before the mismatch recovery branch. The candidate was rolled back and revoked; no deployment witness exists. Claim sight now has two explicit authorities: current authentication is required for provider projection work and rejects stale launches; recovery authentication proves the old signed claim/body/route lineage only. Supervisor recovery and directive writing use the recovery reader, while old launch authentication inside the atomic lease-transfer transaction is historical and the replacement launch/heartbeat remain current-policy evidence. The full causal test performs an actual policy rotation and proves current refusal, historical recovery, directive fencing, lease transfer, successor current sight, and replay/tamper boundaries. Full core passes 573/573 with one ignored; all binaries compile. - Candidate `351355d3baee2d475f84168136380d2f50197ef8` authenticated the historical claim and started all three owners under the brake, but recovery refused because the claim named an older launch than the current replacement's direct parent. Repeated failed candidate deployments had formed a valid signed replacement chain, while recovery still assumed one direct old-to-new edge. The exact Idunn process group was terminated, all services stopped, active pointer and witness removed, and Bifrost authority revoked. No deployment witness exists. Recovery now authenticates the complete immutable chain from the claim's historical launch to the live current-policy descendant: every intermediate launch is host-signed, every parent termination id and envelope digest is exact, chronology is ordered, cycles and broken edges refuse, and the selected endpoint must be current. Directive writing, receipt verification, atomic lease transfer, and supervisor resume share this proof. Typed refusal now exposes the exact lineage fault instead of swallowing it. The causal recovery test contains a real intermediate generation and still proves the full fenced transaction. Full library passes 573/573 with one ignored; supervisor authority tests pass 24/24. - Candidate `a70fe275195740ecefc2984b32ebd8ae93658c18` proved the full signed multi-generation chain and emitted recovery directive `06165fb6-97bd-4c27-97e3-38af03dec72d` from historical claim `c6df4335-7080-4d5f-80fc-4fa18a9d16ce` to current launch `9414c32f-f53c-450c-bc19-a69f5ba04a29`. The replacement projector nevertheless kept refusing stale-policy claim sight because its pulse authenticated the not-yet-created current successor claim before looking for the recovery directive. Supervisor replayed the same directive without mutating Body, exposing a circular ordering fault. The exact Idunn process group was terminated, services stopped, pointer and witness removed, and Bifrost authority revoked; no deployment witness exists. Projector recovery directive consumption now precedes all ordinary projection work and invokes the already idempotent atomic transfer directly. Current claim sight is an output of recovery, never a prerequisite for it. Full library now passes 574/574 with one ignored; supervisor authority tests pass 24/24. - next_action: "The runtime-to-repository Body binding cut is committed and pushed at 67f0fe0: supervisor terminal sight now authenticates the runtime store against the separately bound Body store, and 25/25 supervisor tests pass. Its live candidate reached terminal-observer-alive, three stable braked services, and a healthy pre-witness check, but final admission was correctly withheld after Idunn's automatic worker superseded the manual deployment request head. Exact authority was revoked and rollback removed all candidate runtime pointers and witnesses. Odin successor eefd47f makes local manual/automatic actuation per-target serialized and makes the persistent request/result CAS chain the durable cross-process authority: a live head cannot be superseded without its exact terminal result; 48/48 Idunn tests and independent Soul audit pass. That Idunn binary is live on Ygg at SHA-256 49e09d1989342737f6c996be267894555e8c23886f090a5ae5d334e4aa29ff8d with rollback binary preserved. Bifrost permanently refuses reauthorization of revoked 67f0fe0, so commit this state successor and certify that new exact Epiphany commit through Bifrost and Idunn under the all-scope brake. Require one stable request head, terminal-observer-alive, three stable services, signed aggregate health, and deployment witness. Then build the ordinary organizational product bridge: autonomous admitted-model option -> immutable frontier proposal -> automatic Self selection for Modeling review -> existing Imagination planning -> explicit Mind adoption -> Hands. Organization feedback through Persona remains additional attributed pressure; conversation/proposal alone grants no Mind, Hands, Persona-speech, release, or deployment authority. No host reboot is authorized." + next_action: "Install exact Idunn read-only trust-anchor verifier commit 64bb679 on Ygg without making /etc writable, let the incomplete 36f deployment roll back cleanly, and retry the exact Bifrost-authorized release under the all-scope brake. Require one stable request head, terminal-observer-alive, three stable services, exact signed active health admission, and final deployment.env witness. The autonomous organizational bridge is accepted locally: Modeling map -> exact direction result -> Imagination proposal -> Self selection -> proposal-bounded Modeling -> Imagination planning -> explicit Mind adoption -> Hands. Commit and push that code plus its map/evidence after the deployment boundary is stable. Organization feedback through Persona remains additional attributed pressure; conversation/proposal alone grants no Mind, Hands, Persona-speech, release, or deployment authority. No host reboot is authorized."