From 09812bfce71fa68cd5578533d470ef19da77de24 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 19 Jul 2026 14:34:24 +0100 Subject: [PATCH] Add canonical Mind review operator commands --- .../src/bin/epiphany-mvp-coordinator.rs | 6 +- .../src/bin/epiphany-operator-command.rs | 6 + .../epiphany-persona-feedback-causal-smoke.rs | 6 +- epiphany-core/src/coordinator_acceptance.rs | 17 +- epiphany-core/src/coordinator_launch.rs | 155 +++++- .../src/coordinator_launch_context.rs | 8 +- epiphany-core/src/lib.rs | 3 + epiphany-core/src/operator_command.rs | 467 +++++++++++++++- epiphany-core/src/operator_command_service.rs | 14 +- epiphany-core/src/repo_model_gateway.rs | 214 +++++++- epiphany-core/src/repository_readiness.rs | 19 +- epiphany-core/src/runtime_spine.rs | 502 ++++++++++++++---- notes/epiphany-discord-operator-nerve.md | 34 +- notes/fresh-workspace-handoff.md | 24 + state/ledgers.msgpack | Bin 537625 -> 538031 bytes state/map.yaml | 1 + 16 files changed, 1341 insertions(+), 135 deletions(-) diff --git a/epiphany-core/src/bin/epiphany-mvp-coordinator.rs b/epiphany-core/src/bin/epiphany-mvp-coordinator.rs index 1a76e9ed..b27670a2 100644 --- a/epiphany-core/src/bin/epiphany-mvp-coordinator.rs +++ b/epiphany-core/src/bin/epiphany-mvp-coordinator.rs @@ -2123,7 +2123,7 @@ mod tests { schema_version: epiphany_core::REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION.to_string(), receipt_id: "coordinator-hands-admission-test".to_string(), review_id: "coordinator-hands-model-review-test".to_string(), - result_id: "coordinator-hands-model-result-test".to_string(), + result_id: Some("coordinator-hands-model-result-test".to_string()), patch_id: "coordinator-hands-model-patch-test".to_string(), patch_sha256: "fixture".to_string(), previous_revision: 0, @@ -2141,6 +2141,10 @@ mod tests { claim_repair_request_id: String::new(), frontier_plan_decision_id: String::new(), repository_body_observation_basis: None, + admission_source: Some(epiphany_core::RepoModelAdmissionSource::WorkerResult { + result_id: "coordinator-hands-model-result-test".into(), + job_id: "coordinator-hands-model-job-test".into(), + }), }; let mut cache = epiphany_core::runtime_spine_cache(store)?; cache.put(epiphany_core::MEMORY_GRAPH_KEY, &entry)?; diff --git a/epiphany-core/src/bin/epiphany-operator-command.rs b/epiphany-core/src/bin/epiphany-operator-command.rs index 275e0652..d47359c7 100644 --- a/epiphany-core/src/bin/epiphany-operator-command.rs +++ b/epiphany-core/src/bin/epiphany-operator-command.rs @@ -15,6 +15,7 @@ fn main() -> Result<()> { command_store: args.command_store.clone(), local_verse_store: args.local_verse_store.clone(), resident_self_store: args.resident_self_store.clone(), + runtime_store: args.runtime_store.clone(), policy: OperatorCommandPolicy { runtime_id: args.runtime_id.clone(), discord_guild_id: args.guild_id.clone(), @@ -65,6 +66,7 @@ struct Args { command_store: PathBuf, local_verse_store: PathBuf, resident_self_store: PathBuf, + runtime_store: PathBuf, guild_id: String, channel_ids: Vec, actor_capabilities: BTreeMap>, @@ -107,6 +109,7 @@ impl Args { | "--command-store" | "--local-verse-store" | "--resident-self-store" + | "--runtime-store" | "--guild-id" | "--max-ttl-seconds" => { let value = values @@ -144,6 +147,7 @@ impl Args { command_store: required("--command-store")?.into(), local_verse_store: required("--local-verse-store")?.into(), resident_self_store: required("--resident-self-store")?.into(), + runtime_store: required("--runtime-store")?.into(), guild_id: required("--guild-id")?, channel_ids: channels, actor_capabilities: actors, @@ -159,6 +163,8 @@ fn parse_capability(value: &str) -> Result { "sleep" => Ok(OperatorCapability::Sleep), "wake" => Ok(OperatorCapability::Wake), "directive" => Ok(OperatorCapability::Directive), + "reviews" => Ok(OperatorCapability::Reviews), + "review" => Ok(OperatorCapability::Review), _ => bail!("unknown operator capability {value}"), } } 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 cefe7d26..199d37e2 100644 --- a/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs +++ b/epiphany-core/src/bin/epiphany-persona-feedback-causal-smoke.rs @@ -213,7 +213,7 @@ fn seed_fixture_runtime(store: &Path) -> Result<()> { schema_version: REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION.into(), receipt_id: "fixture-model-admission".into(), review_id: "fixture-review".into(), - result_id: "fixture-result".into(), + result_id: Some("fixture-result".into()), patch_id: "fixture-patch".into(), patch_sha256: "sha256-fixture".into(), previous_revision: 0, @@ -231,6 +231,10 @@ fn seed_fixture_runtime(store: &Path) -> Result<()> { claim_repair_request_id: String::new(), frontier_plan_decision_id: String::new(), repository_body_observation_basis: None, + admission_source: Some(epiphany_core::RepoModelAdmissionSource::WorkerResult { + result_id: "fixture-result".into(), + job_id: "fixture-job".into(), + }), }, )?; Ok(()) diff --git a/epiphany-core/src/coordinator_acceptance.rs b/epiphany-core/src/coordinator_acceptance.rs index 9f04f387..12a29955 100644 --- a/epiphany-core/src/coordinator_acceptance.rs +++ b/epiphany-core/src/coordinator_acceptance.rs @@ -277,8 +277,8 @@ pub fn accept_coordinator_role_finding( let candidate_review = RepoModelAdmissionReview { schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.to_string(), review_id: format!("repo-model-review-{result_id}"), - result_id: result_id.to_string(), - job_id: job_id.to_string(), + result_id: Some(result_id.to_string()), + job_id: Some(job_id.to_string()), patch_id: patch.patch_id.clone(), patch_sha256: format!("{:x}", Sha256::digest(patch_bytes)), base_revision: patch.base_revision, @@ -288,6 +288,10 @@ pub fn accept_coordinator_role_finding( reviewed_at: accepted_at.clone(), contract: REPO_MODEL_ADMISSION_CONTRACT.to_string(), repository_body_observation_basis: Some(repository_body_observation_basis), + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: result_id.to_string(), + job_id: job_id.to_string(), + }), }; let review = stable_repo_model_admission_review(store, candidate_review)?; commit_repo_model_admission(store, result_id, &review)?; @@ -1149,8 +1153,8 @@ mod tests { let existing = RepoModelAdmissionReview { schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.to_string(), review_id: "repo-model-review-result-split".to_string(), - result_id: "result-split".to_string(), - job_id: "job-split".to_string(), + result_id: Some("result-split".to_string()), + job_id: Some("job-split".to_string()), patch_id: "patch-split".to_string(), patch_sha256: "a".repeat(64), base_revision: 4, @@ -1160,6 +1164,10 @@ mod tests { reviewed_at: "2026-07-13T09:00:01Z".to_string(), contract: REPO_MODEL_ADMISSION_CONTRACT.to_string(), repository_body_observation_basis: None, + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: "result-split".into(), + job_id: "job-split".into(), + }), }; let mut cache = coordinator_acceptance_cache(&store)?; cache.put(&existing.review_id, &existing)?; @@ -1187,6 +1195,7 @@ mod tests { claim_repair_request_id: String::new(), frontier_plan_decision_id: String::new(), repository_body_observation_basis: None, + admission_source: existing.admission_source.clone(), }, )?; let mut fresh = existing.clone(); diff --git a/epiphany-core/src/coordinator_launch.rs b/epiphany-core/src/coordinator_launch.rs index 86aece97..992fbbba 100644 --- a/epiphany-core/src/coordinator_launch.rs +++ b/epiphany-core/src/coordinator_launch.rs @@ -1299,6 +1299,42 @@ pub(crate) mod tests { }) } + pub(crate) fn operator_review_candidate_fixture( + root: &Path, + suffix: &str, + ) -> Result<( + std::path::PathBuf, + RepoFrontierPlanningRequest, + EpiphanyRuntimeRoleWorkerResult, + crate::RepoFrontierPlanMindRequest, + )> { + let (store, state, launch, planning) = frontier_planning_launch_fixture(root, suffix)?; + let job_id = format!("backend-operator-review-{suffix}"); + let plan = plan_coordinator_job_launch( + &state, + &launch, + &store, + format!("launcher-operator-review-{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 request = crate::commit_repo_frontier_plan_mind_request( + &store, + &result.result_id, + "2026-07-15T09:00:05Z", + )?; + Ok((store, planning, result, request)) + } + fn consideration_launch_fixture( root: &Path, suffix: &str, @@ -2624,8 +2660,8 @@ pub(crate) mod tests { 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(), + result_id: Some(modeling_result.result_id.clone()), + job_id: Some(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, @@ -2637,6 +2673,10 @@ pub(crate) mod tests { repository_body_observation_basis: modeling_result .repository_body_observation_basis .clone(), + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: modeling_result.result_id.clone(), + job_id: modeling_result.job_id.clone(), + }), }; let incorporation = crate::commit_repo_model_admission( &store, @@ -2721,6 +2761,117 @@ pub(crate) mod tests { Ok(()) } + #[test] + fn operator_review_uses_current_mind_candidate_and_canonical_terminal_transitions() -> Result<()> + { + let root = tempfile::tempdir()?; + for (suffix, decision) in [ + ("operator-refuse", RepoFrontierPlanDecision::Refuse), + ("operator-hold", RepoFrontierPlanDecision::Hold), + ] { + let (store, planning, _, request) = + operator_review_candidate_fixture(root.path(), suffix)?; + let summaries = crate::pending_repo_frontier_plan_reviews(&store, 10)?; + assert_eq!(summaries.len(), 1); + assert_eq!(summaries[0].mind_request_id, request.request_id); + assert_eq!(summaries[0].candidate_sha256, request.candidate_sha256); + let before_model = runtime_current_repo_model(&store)?.unwrap(); + let review = crate::RepoFrontierPlanOperatorReview { + command_id: format!("operator-review-command-{suffix}"), + admission_id: format!("operator-review-admission-{suffix}"), + packet_sha256: format!("sha256-{}", "a".repeat(64)), + source_actor_id: "operator-1".into(), + mind_request_id: request.request_id, + candidate_id: request.candidate_id, + candidate_sha256: request.candidate_sha256, + expected_model_revision: planning.model_revision, + expected_model_hash: planning.model_hash, + decision, + decided_at: "2026-07-15T09:00:06Z".into(), + }; + let receipt = crate::commit_operator_repo_frontier_plan_review(&store, &review)?; + assert_eq!(receipt.decision, decision); + assert!(receipt.model_admission_receipt_id.is_empty()); + assert_eq!(runtime_current_repo_model(&store)?.unwrap(), before_model); + assert!(crate::pending_repo_frontier_plan_reviews(&store, 10)?.is_empty()); + assert_eq!( + crate::commit_operator_repo_frontier_plan_review(&store, &review)?, + receipt + ); + } + Ok(()) + } + + #[test] + fn operator_review_refuses_tamper_and_stale_model_without_writes() -> Result<()> { + let root = tempfile::tempdir()?; + for mutation in ["candidate", "digest", "revision", "model"] { + let (store, planning, _, request) = + operator_review_candidate_fixture(root.path(), mutation)?; + let mut review = crate::RepoFrontierPlanOperatorReview { + command_id: format!("operator-review-hostile-{mutation}"), + admission_id: format!("operator-review-hostile-admission-{mutation}"), + packet_sha256: format!("sha256-{}", "b".repeat(64)), + source_actor_id: "operator-1".into(), + mind_request_id: request.request_id, + candidate_id: request.candidate_id, + candidate_sha256: request.candidate_sha256, + expected_model_revision: planning.model_revision, + expected_model_hash: planning.model_hash, + decision: RepoFrontierPlanDecision::Adopt, + decided_at: "2026-07-15T09:00:06Z".into(), + }; + match mutation { + "candidate" => review.candidate_id = "substituted".into(), + "digest" => review.candidate_sha256 = "0".repeat(64), + "revision" => review.expected_model_revision += 1, + "model" => review.expected_model_hash = "0".repeat(64), + _ => unreachable!(), + } + let before = std::fs::read(&store)?; + assert!(crate::commit_operator_repo_frontier_plan_review(&store, &review).is_err()); + assert_eq!(std::fs::read(&store)?, before); + } + Ok(()) + } + + #[test] + fn operator_adopt_uses_existing_model_commit_without_minting_hands_authority() -> Result<()> { + let root = tempfile::tempdir()?; + let (store, planning, _, request) = + operator_review_candidate_fixture(root.path(), "operator-adopt")?; + let review = crate::RepoFrontierPlanOperatorReview { + command_id: "operator-review-command-adopt".into(), + admission_id: "operator-review-admission-adopt".into(), + packet_sha256: format!("sha256-{}", "c".repeat(64)), + source_actor_id: "operator-1".into(), + mind_request_id: request.request_id, + candidate_id: request.candidate_id, + candidate_sha256: request.candidate_sha256, + expected_model_revision: planning.model_revision, + expected_model_hash: planning.model_hash, + decision: RepoFrontierPlanDecision::Adopt, + decided_at: "2026-07-15T09:00:06Z".into(), + }; + let receipt = crate::commit_operator_repo_frontier_plan_review(&store, &review)?; + assert!(!receipt.model_admission_receipt_id.is_empty()); + let current = runtime_current_repo_model(&store)?.unwrap(); + assert_eq!(current.model_revision, planning.model_revision + 1); + let mut cache = coordinator_acceptance_cache(&store)?; + cache.pull_all_backing_stores()?; + assert!( + cache + .get_all::()? + .is_empty() + ); + assert!( + cache + .get_all::()? + .is_empty() + ); + Ok(()) + } + #[test] fn concurrent_mind_admission_retries_converge_on_one_terminal_receipt() -> Result<()> { let root = tempfile::tempdir()?; diff --git a/epiphany-core/src/coordinator_launch_context.rs b/epiphany-core/src/coordinator_launch_context.rs index 87c9c531..cacf7b87 100644 --- a/epiphany-core/src/coordinator_launch_context.rs +++ b/epiphany-core/src/coordinator_launch_context.rs @@ -1017,8 +1017,8 @@ mod tests { &crate::RepoModelAdmissionReview { schema_version: crate::REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.to_string(), review_id: "context-route-admission".to_string(), - result_id: result.result_id.clone(), - job_id: result.job_id.clone(), + result_id: Some(result.result_id.clone()), + job_id: Some(result.job_id.clone()), patch_id: patch.patch_id, patch_sha256: format!("{:x}", sha2::Sha256::digest(&patch_bytes)), base_revision: patch.base_revision, @@ -1028,6 +1028,10 @@ mod tests { reviewed_at: "2026-06-12T00:00:04Z".to_string(), contract: crate::REPO_MODEL_ADMISSION_CONTRACT.to_string(), repository_body_observation_basis: result.repository_body_observation_basis.clone(), + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: result.result_id.clone(), + job_id: result.job_id.clone(), + }), }, )?; let route = crate::select_and_commit_repo_frontier_route(store, "2026-06-12T00:00:05Z")?; diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index a96ef054..8c838b09 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -962,6 +962,7 @@ 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; +pub use runtime_spine::commit_operator_repo_frontier_plan_review; 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; @@ -979,6 +980,8 @@ pub use runtime_spine::initialize_runtime_spine; pub use runtime_spine::intake_user_repo_frontier_proposal; pub use runtime_spine::migrate_legacy_repo_model_projection_obligation; pub use runtime_spine::open_runtime_spine_heartbeat_job; +pub use runtime_spine::operator_repo_frontier_plan_review_is_current; +pub use runtime_spine::pending_repo_frontier_plan_reviews; 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; diff --git a/epiphany-core/src/operator_command.rs b/epiphany-core/src/operator_command.rs index d2ac13dc..432d1db9 100644 --- a/epiphany-core/src/operator_command.rs +++ b/epiphany-core/src/operator_command.rs @@ -9,18 +9,26 @@ use std::path::Path; use crate::{ EpiphanyCultMeshSwarmBrakeEntry, HostIdentitySignature, HostIdentityTrustAnchorEntry, - ResidentSelfPressure, enqueue_resident_self_pressure, load_epiphany_cultmesh_swarm_brake, - load_latest_epiphany_cultmesh_operator_snapshot, resident_self_pressures, + RepoFrontierPlanDecision, RepoFrontierPlanOperatorReview, RepoFrontierPlanReviewSummary, + ResidentSelfPressure, commit_operator_repo_frontier_plan_review, + enqueue_resident_self_pressure, load_epiphany_cultmesh_swarm_brake, + load_latest_epiphany_cultmesh_operator_snapshot, operator_repo_frontier_plan_review_is_current, + pending_repo_frontier_plan_reviews, resident_self_pressures, verify_host_identity_trust_anchor_signature, write_epiphany_cultmesh_swarm_brake, }; pub const BIFROST_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION: &str = + "bifrost.operator_command.delivery.v1"; +pub const LEGACY_BIFROST_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION: &str = "bifrost.operator_command.delivery.v0"; pub const BIFROST_OPERATOR_COMMAND_DELIVERY_TYPE: &str = "bifrost.operator_command.delivery"; pub const LOCAL_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION: &str = + "epiphany.operator_command.admitted.v1"; +pub const LEGACY_LOCAL_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION: &str = "epiphany.operator_command.admitted.v0"; -pub const OPERATOR_COMMAND_RESULT_SCHEMA_VERSION: &str = "epiphany.operator_command.result.v0"; -const SIGNING_PURPOSE: &str = "bifrost.operator-command.delivery.v0"; +pub const OPERATOR_COMMAND_RESULT_SCHEMA_VERSION: &str = "epiphany.operator_command.result.v1"; +const SIGNING_PURPOSE: &str = "bifrost.operator-command.delivery.v1"; +const LEGACY_SIGNING_PURPOSE: &str = "bifrost.operator-command.delivery.v0"; const DISCORD_OPERATOR_BRAKE_ID: &str = "epiphany-discord-operator-brake"; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] @@ -30,15 +38,30 @@ pub enum OperatorCapability { Sleep, Wake, Directive, + Reviews, + Review, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] pub enum OperatorCommand { Status, - Sleep { reason: String }, + Sleep { + reason: String, + }, Wake, - Directive { objective: String }, + Directive { + objective: String, + }, + Reviews, + Review { + mind_request_id: String, + candidate_id: String, + candidate_sha256: String, + expected_model_revision: u64, + expected_model_hash: String, + decision: RepoFrontierPlanDecision, + }, } impl OperatorCommand { @@ -48,6 +71,8 @@ impl OperatorCommand { Self::Sleep { .. } => OperatorCapability::Sleep, Self::Wake => OperatorCapability::Wake, Self::Directive { .. } => OperatorCapability::Directive, + Self::Reviews => OperatorCapability::Reviews, + Self::Review { .. } => OperatorCapability::Review, } } } @@ -166,6 +191,12 @@ pub struct OperatorCommandResult { pub brake_status: String, #[cultcache(key = 14)] pub detail: String, + #[cultcache(key = 15, default)] + pub reviews: Vec, + #[cultcache(key = 16, default)] + pub review_candidate_id: String, + #[cultcache(key = 17, default)] + pub review_decision: String, } #[derive(Clone, Debug)] @@ -244,10 +275,39 @@ fn validate_admission( OperatorCommand::Directive { objective } => { !objective.trim().is_empty() && objective.len() <= 4096 } + OperatorCommand::Review { + mind_request_id, + candidate_id, + candidate_sha256, + expected_model_hash, + .. + } => { + !mind_request_id.trim().is_empty() + && mind_request_id.len() <= 256 + && !candidate_id.trim().is_empty() + && candidate_id.len() <= 256 + && candidate_sha256.len() == 64 + && candidate_sha256 + .bytes() + .all(|byte| byte.is_ascii_hexdigit()) + && expected_model_hash.len() == 64 + && expected_model_hash + .bytes() + .all(|byte| byte.is_ascii_hexdigit()) + } _ => true, }; + let legacy_admission = + admission.schema_version == LEGACY_BIFROST_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION; + let admission_version_valid = admission.schema_version + == BIFROST_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION + || (legacy_admission + && !matches!( + &packet.command, + OperatorCommand::Reviews | OperatorCommand::Review { .. } + )); if admission.schema_name != BIFROST_OPERATOR_COMMAND_DELIVERY_TYPE - || admission.schema_version != BIFROST_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION + || !admission_version_valid || admission.admission_id.trim().is_empty() || packet.command_id.trim().is_empty() || packet.nonce.trim().is_empty() @@ -284,7 +344,11 @@ fn validate_admission( } verify_host_identity_trust_anchor_signature( anchor, - SIGNING_PURPOSE, + if legacy_admission { + LEGACY_SIGNING_PURPOSE + } else { + SIGNING_PURPOSE + }, &operator_command_admission_signing_payload(admission)?, &HostIdentitySignature { identity_id: admission.provider_identity_id.clone(), @@ -298,6 +362,7 @@ pub fn admit_and_execute_bifrost_operator_command( command_store: &Path, local_verse_store: &Path, resident_self_store: &Path, + runtime_store: &Path, admission: &BifrostOperatorCommandAdmission, trusted_bifrost_identity: &HostIdentityTrustAnchorEntry, policy: &OperatorCommandPolicy, @@ -308,7 +373,13 @@ pub fn admit_and_execute_bifrost_operator_command( validate_admission(admission, trusted_bifrost_identity, policy, now_dt, false)?; let packet = &admission.packet; let mut admitted = LocalAdmittedOperatorCommand { - schema_version: LOCAL_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION.into(), + schema_version: if admission.schema_version + == LEGACY_BIFROST_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION + { + LEGACY_LOCAL_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION.into() + } else { + LOCAL_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION.into() + }, command_id: packet.command_id.clone(), admission_id: admission.admission_id.clone(), nonce: packet.nonce.clone(), @@ -369,6 +440,9 @@ pub fn admit_and_execute_bifrost_operator_command( coordinator_action, brake_status, detail, + reviews, + review_candidate_id, + review_decision, ) = match &packet.command { OperatorCommand::Status => { let snapshot = load_latest_epiphany_cultmesh_operator_snapshot( @@ -391,6 +465,9 @@ pub fn admit_and_execute_bifrost_operator_command( "", "", "operator status snapshot is unavailable", + Vec::new(), + "", + "", ); }; let brake_status = @@ -406,6 +483,9 @@ pub fn admit_and_execute_bifrost_operator_command( snapshot.coordinator_action, brake_status, String::from("bounded operator-safe status projection"), + Vec::new(), + String::new(), + String::new(), ) } OperatorCommand::Sleep { reason } => { @@ -437,6 +517,9 @@ pub fn admit_and_execute_bifrost_operator_command( "", ¤t.status, "operator sleep cannot replace a foreign or newer brake generation", + Vec::new(), + "", + "", ); } } @@ -477,6 +560,9 @@ pub fn admit_and_execute_bifrost_operator_command( String::new(), "engaged".into(), String::from("Discord operator brake engaged"), + Vec::new(), + String::new(), + String::new(), ) } OperatorCommand::Wake => { @@ -499,6 +585,9 @@ pub fn admit_and_execute_bifrost_operator_command( "", "engaged", "operator wake cannot release a brake owned by another authority", + Vec::new(), + "", + "", ); }; brake.status = "released".into(); @@ -521,6 +610,9 @@ pub fn admit_and_execute_bifrost_operator_command( String::new(), "released".into(), String::from("Discord operator brake released; no work scheduled"), + Vec::new(), + String::new(), + String::new(), ) } OperatorCommand::Directive { objective } => { @@ -557,6 +649,9 @@ pub fn admit_and_execute_bifrost_operator_command( "", "", "operator directive pressure identity collision", + Vec::new(), + "", + "", ); } None => enqueue_resident_self_pressure(resident_self_store, &pressure)?, @@ -570,6 +665,82 @@ pub fn admit_and_execute_bifrost_operator_command( String::new(), String::new(), String::from("directive admitted as pressure only"), + Vec::new(), + String::new(), + String::new(), + ) + } + OperatorCommand::Reviews => { + let reviews = pending_repo_frontier_plan_reviews(runtime_store, 10)?; + ( + OperatorCommandResultDisposition::Observed, + "mind-review-candidates", + String::new(), + String::new(), + String::new(), + String::new(), + String::new(), + format!("{} pending Mind review candidate(s)", reviews.len()), + reviews, + String::new(), + String::new(), + ) + } + OperatorCommand::Review { + mind_request_id, + candidate_id, + candidate_sha256, + expected_model_revision, + expected_model_hash, + decision, + } => { + let review = RepoFrontierPlanOperatorReview { + command_id: packet.command_id.clone(), + admission_id: admission.admission_id.clone(), + packet_sha256: admission.packet_sha256.clone(), + source_actor_id: packet.source_actor_id.clone(), + mind_request_id: mind_request_id.clone(), + candidate_id: candidate_id.clone(), + candidate_sha256: candidate_sha256.clone(), + expected_model_revision: *expected_model_revision, + expected_model_hash: expected_model_hash.clone(), + decision: *decision, + decided_at: packet.issued_at.clone(), + }; + if !operator_repo_frontier_plan_review_is_current(runtime_store, &review)? { + return persist_result( + command_store, + packet, + admission, + policy, + now, + result_id, + OperatorCommandResultDisposition::Refused, + "mind-review-decision", + "", + "", + "", + "", + "", + "Mind refused stale, terminal, or mismatched review candidate", + Vec::new(), + candidate_id, + &format!("{decision:?}").to_lowercase(), + ); + } + let receipt = commit_operator_repo_frontier_plan_review(runtime_store, &review)?; + ( + OperatorCommandResultDisposition::Applied, + "mind-review-decision", + receipt.decision_id, + String::new(), + String::new(), + String::new(), + String::new(), + String::from("Mind committed exact current-candidate disposition"), + Vec::new(), + candidate_id.clone(), + format!("{decision:?}").to_lowercase(), ) } }; @@ -588,6 +759,9 @@ pub fn admit_and_execute_bifrost_operator_command( &coordinator_action, &brake_status, &detail, + reviews, + &review_candidate_id, + &review_decision, ) } @@ -607,6 +781,9 @@ fn persist_result( coordinator_action: &str, brake_status: &str, detail: &str, + reviews: Vec, + review_candidate_id: &str, + review_decision: &str, ) -> Result { let result = OperatorCommandResult { schema_version: OPERATOR_COMMAND_RESULT_SCHEMA_VERSION.into(), @@ -624,6 +801,9 @@ fn persist_result( coordinator_action: coordinator_action.into(), brake_status: brake_status.into(), detail: detail.into(), + reviews, + review_candidate_id: review_candidate_id.into(), + review_decision: review_decision.into(), }; let refreshed = command_cache(command_store)?; let (entry, _) = refreshed.prepare_entry(&result_id, &result)?; @@ -644,6 +824,7 @@ fn issued_millis(value: &str) -> Result { #[cfg(test)] mod tests { use super::*; + use cultcache_rs::CultCacheEnvelope; fn policy() -> OperatorCommandPolicy { OperatorCommandPolicy { @@ -657,6 +838,8 @@ mod tests { OperatorCapability::Sleep, OperatorCapability::Wake, OperatorCapability::Directive, + OperatorCapability::Reviews, + OperatorCapability::Review, ], )]), max_ttl_seconds: 600, @@ -704,6 +887,22 @@ mod tests { Ok(admission) } + fn legacy_signed( + signer: &crate::HostIdentitySigner, + id: &str, + command: OperatorCommand, + ) -> Result { + let mut admission = signed(signer, id, command)?; + admission.schema_version = LEGACY_BIFROST_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION.into(); + admission.provider_signature = signer + .sign( + LEGACY_SIGNING_PURPOSE, + &operator_command_admission_signing_payload(&admission)?, + )? + .signature; + Ok(admission) + } + fn snapshot() -> crate::EpiphanyCultMeshOperatorSnapshotEntry { crate::EpiphanyCultMeshOperatorSnapshotEntry { schema_version: crate::EPIPHANY_CULTMESH_OPERATOR_SNAPSHOT_SCHEMA_VERSION.into(), @@ -761,6 +960,7 @@ mod tests { &commands, &verse, &resident, + &resident, &admission, &anchor, &policy(), @@ -773,6 +973,155 @@ mod tests { Ok(()) } + #[test] + fn legacy_v0_delivery_replays_across_v1_cutover_but_cannot_carry_review_commands() -> Result<()> + { + let temp = tempfile::tempdir()?; + let signer = crate::enroll_host_identity_at(&temp.path().join("identity.cc"))?; + let anchor = + crate::export_host_identity_trust_anchor(&signer, &temp.path().join("anchor.cc"))?; + let (commands, verse, resident, runtime) = ( + temp.path().join("commands.cc"), + temp.path().join("verse.cc"), + temp.path().join("resident.cc"), + temp.path().join("runtime.cc"), + ); + let legacy = legacy_signed( + &signer, + "legacy-sleep", + OperatorCommand::Sleep { + reason: "Drain the v0 delivery.".into(), + }, + )?; + let first = admit_and_execute_bifrost_operator_command( + &commands, + &verse, + &resident, + &runtime, + &legacy, + &anchor, + &policy(), + "2026-07-19T12:00:00Z", + )?; + let replay = admit_and_execute_bifrost_operator_command( + &commands, + &verse, + &resident, + &runtime, + &legacy, + &anchor, + &policy(), + "2026-07-19T12:10:00Z", + )?; + assert_eq!(replay, first); + let review = legacy_signed(&signer, "legacy-review", OperatorCommand::Reviews)?; + assert!( + admit_and_execute_bifrost_operator_command( + &commands, + &verse, + &resident, + &runtime, + &review, + &anchor, + &policy(), + "2026-07-19T12:00:00Z", + ) + .is_err() + ); + Ok(()) + } + + #[test] + fn persisted_v0_command_ledger_replays_after_v1_cutover() -> Result<()> { + let temp = tempfile::tempdir()?; + let signer = crate::enroll_host_identity_at(&temp.path().join("identity.cc"))?; + let anchor = + crate::export_host_identity_trust_anchor(&signer, &temp.path().join("anchor.cc"))?; + let (commands, verse, resident, runtime) = ( + temp.path().join("commands.cc"), + temp.path().join("verse.cc"), + temp.path().join("resident.cc"), + temp.path().join("runtime.cc"), + ); + let legacy = legacy_signed(&signer, "persisted-v0", OperatorCommand::Status)?; + let packet = &legacy.packet; + let result_id = format!( + "operator-command-result-{:x}", + Sha256::digest(format!("{}:{}", packet.command_id, legacy.packet_sha256).as_bytes()) + ); + let admitted_at = "2026-07-19T12:00:00Z"; + let old_admission = ( + LEGACY_LOCAL_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION, + packet.command_id.as_str(), + legacy.admission_id.as_str(), + packet.nonce.as_str(), + legacy.packet_sha256.as_str(), + packet.source_actor_id.as_str(), + "status", + packet.target_runtime_id.as_str(), + admitted_at, + packet.expires_at.as_str(), + "exact-command-only", + false, + ); + let old_result = ( + "epiphany.operator_command.result.v0", + result_id.as_str(), + packet.command_id.as_str(), + legacy.packet_sha256.as_str(), + packet.target_runtime_id.as_str(), + "observed", + "operator_snapshot", + "snapshot-v0", + admitted_at, + false, + "sleeping", + "ready", + "none", + "engaged", + "persisted v0 result", + ); + let backing = SingleFileMessagePackBackingStore::new(&commands); + for (key, ty, payload) in [ + ( + packet.command_id.as_str(), + LocalAdmittedOperatorCommand::TYPE, + rmp_serde::to_vec(&old_admission)?, + ), + ( + result_id.as_str(), + OperatorCommandResult::TYPE, + rmp_serde::to_vec(&old_result)?, + ), + ] { + assert!(backing.insert_entry_if_absent(CultCacheEnvelope { + key: key.into(), + r#type: ty.into(), + payload, + stored_at: admitted_at.into(), + schema_id: Some(ty.into()), + })?); + } + + let replay = admit_and_execute_bifrost_operator_command( + &commands, + &verse, + &resident, + &runtime, + &legacy, + &anchor, + &policy(), + admitted_at, + )?; + assert_eq!(replay.schema_version, "epiphany.operator_command.result.v0"); + assert_eq!(replay.consequence_ref, "snapshot-v0"); + assert!(replay.reviews.is_empty()); + assert!(replay.review_candidate_id.is_empty()); + assert!(replay.review_decision.is_empty()); + assert!(!verse.exists() && !resident.exists() && !runtime.exists()); + Ok(()) + } + #[test] fn commands_delegate_without_wake_or_directive_authority_leak() -> Result<()> { let temp = tempfile::tempdir()?; @@ -791,6 +1140,7 @@ mod tests { &commands, &verse, &resident, + &resident, &status, &anchor, &p, @@ -811,6 +1161,7 @@ mod tests { &commands, &verse, &resident, + &resident, &sleep, &anchor, &p, @@ -844,6 +1195,7 @@ mod tests { &commands, &verse, &resident, + &resident, &wake, &anchor, &p, @@ -870,6 +1222,7 @@ mod tests { &commands, &verse, &resident, + &resident, &directive, &anchor, &p, @@ -884,6 +1237,7 @@ mod tests { &commands, &verse, &resident, + &resident, &directive, &anchor, &p, @@ -916,6 +1270,7 @@ mod tests { &commands, &verse, &resident, + &resident, &wake, &anchor, &policy(), @@ -929,6 +1284,7 @@ mod tests { &commands, &verse, &resident, + &resident, &wake, &anchor, &policy(), @@ -992,6 +1348,7 @@ mod tests { &commands, &verse, &resident, + &resident, &admission, &anchor, &policy(), @@ -1004,6 +1361,7 @@ mod tests { &commands, &verse, &resident, + &resident, &admission, &anchor, &policy(), @@ -1013,4 +1371,95 @@ mod tests { ); Ok(()) } + + #[test] + fn review_replay_recovers_after_mind_consequence_without_command_result() -> Result<()> { + let temp = tempfile::tempdir()?; + let (runtime, planning, _, request) = + crate::coordinator_launch::tests::operator_review_candidate_fixture( + temp.path(), + "operator-command-crash", + )?; + let signer = crate::enroll_host_identity_at(&temp.path().join("identity.cc"))?; + let anchor = + crate::export_host_identity_trust_anchor(&signer, &temp.path().join("anchor.cc"))?; + let (commands, verse, resident) = ( + temp.path().join("commands.cc"), + temp.path().join("verse.cc"), + temp.path().join("resident.cc"), + ); + let admission = signed( + &signer, + "review-crash-recovery", + OperatorCommand::Review { + mind_request_id: request.request_id.clone(), + candidate_id: request.candidate_id.clone(), + candidate_sha256: request.candidate_sha256.clone(), + expected_model_revision: planning.model_revision, + expected_model_hash: planning.model_hash.clone(), + decision: RepoFrontierPlanDecision::Hold, + }, + )?; + let packet = &admission.packet; + let admitted = LocalAdmittedOperatorCommand { + schema_version: LOCAL_OPERATOR_COMMAND_ADMISSION_SCHEMA_VERSION.into(), + command_id: packet.command_id.clone(), + admission_id: admission.admission_id.clone(), + nonce: packet.nonce.clone(), + packet_sha256: admission.packet_sha256.clone(), + source_actor_id: packet.source_actor_id.clone(), + capability: "review".into(), + target_runtime_id: packet.target_runtime_id.clone(), + admitted_at: "2026-07-19T12:00:00Z".into(), + expires_at: packet.expires_at.clone(), + authority: "exact-command-only".into(), + private_state_exposed: false, + }; + let cache = command_cache(&commands)?; + let (entry, _) = cache.prepare_entry(&packet.command_id, &admitted)?; + assert!(SingleFileMessagePackBackingStore::new(&commands).insert_entry_if_absent(entry)?); + let canonical = commit_operator_repo_frontier_plan_review( + &runtime, + &RepoFrontierPlanOperatorReview { + command_id: packet.command_id.clone(), + admission_id: admission.admission_id.clone(), + packet_sha256: admission.packet_sha256.clone(), + source_actor_id: packet.source_actor_id.clone(), + mind_request_id: request.request_id, + candidate_id: request.candidate_id, + candidate_sha256: request.candidate_sha256, + expected_model_revision: planning.model_revision, + expected_model_hash: planning.model_hash, + decision: RepoFrontierPlanDecision::Hold, + decided_at: packet.issued_at.clone(), + }, + )?; + let recovered = admit_and_execute_bifrost_operator_command( + &commands, + &verse, + &resident, + &runtime, + &admission, + &anchor, + &policy(), + "2026-07-19T12:00:00Z", + )?; + assert_eq!(recovered.disposition, "applied"); + assert_eq!(recovered.consequence_ref, canonical.decision_id); + assert_eq!(recovered.review_decision, "hold"); + assert_eq!( + admit_and_execute_bifrost_operator_command( + &commands, + &verse, + &resident, + &runtime, + &admission, + &anchor, + &policy(), + "2026-07-19T12:00:01Z", + )?, + recovered + ); + Ok(()) + } } diff --git a/epiphany-core/src/operator_command_service.rs b/epiphany-core/src/operator_command_service.rs index 660ffdc3..06051173 100644 --- a/epiphany-core/src/operator_command_service.rs +++ b/epiphany-core/src/operator_command_service.rs @@ -19,8 +19,8 @@ use crate::{ pub const EPIPHANY_OPERATOR_COMMAND_RESULT_RECEIPT_TYPE: &str = "epiphany.operator_command.sealed_result"; pub const EPIPHANY_OPERATOR_COMMAND_RESULT_RECEIPT_SCHEMA_VERSION: &str = - "epiphany.operator_command.sealed_result.v0"; -const RESULT_SIGNING_PURPOSE: &str = "epiphany.operator-command.sealed-result.v0"; + "epiphany.operator_command.sealed_result.v1"; +const RESULT_SIGNING_PURPOSE: &str = "epiphany.operator-command.sealed-result.v1"; pub const EPIPHANY_OPERATOR_COMMAND_RUDP_CONNECTION_ID: u32 = 0xe91f_0001; #[derive(Clone, Debug)] @@ -28,6 +28,7 @@ pub struct OperatorCommandServiceConfig { pub command_store: PathBuf, pub local_verse_store: PathBuf, pub resident_self_store: PathBuf, + pub runtime_store: PathBuf, pub policy: OperatorCommandPolicy, pub trusted_bifrost_identity: HostIdentityTrustAnchorEntry, } @@ -65,6 +66,9 @@ pub struct EpiphanyOperatorCommandWireResult { pub coordinator_action: String, pub brake_status: String, pub detail: String, + pub reviews: Vec, + pub review_candidate_id: String, + pub review_decision: String, } impl From<&OperatorCommandResult> for EpiphanyOperatorCommandWireResult { @@ -85,6 +89,9 @@ impl From<&OperatorCommandResult> for EpiphanyOperatorCommandWireResult { coordinator_action: value.coordinator_action.clone(), brake_status: value.brake_status.clone(), detail: value.detail.clone(), + reviews: value.reviews.clone(), + review_candidate_id: value.review_candidate_id.clone(), + review_decision: value.review_decision.clone(), } } } @@ -150,6 +157,7 @@ pub fn execute_operator_command_admission( &config.command_store, &config.local_verse_store, &config.resident_self_store, + &config.runtime_store, &admission, &config.trusted_bifrost_identity, &config.policy, @@ -321,6 +329,7 @@ pub fn write_operator_command_interop_fixture( command_store: private.join("commands.cc"), local_verse_store: private.join("verse.cc"), resident_self_store: private.join("resident.cc"), + runtime_store: private.join("runtime.cc"), policy: OperatorCommandPolicy { runtime_id: "epiphany-interop-fixture".into(), discord_guild_id: "fixture-guild".into(), @@ -437,6 +446,7 @@ mod tests { command_store: root.join("commands.cc"), local_verse_store: root.join("verse.cc"), resident_self_store: root.join("resident.cc"), + runtime_store: root.join("runtime.cc"), policy: OperatorCommandPolicy { runtime_id: "epiphany-yggdrasil".into(), discord_guild_id: "guild".into(), diff --git a/epiphany-core/src/repo_model_gateway.rs b/epiphany-core/src/repo_model_gateway.rs index f65f0456..a3552928 100644 --- a/epiphany-core/src/repo_model_gateway.rs +++ b/epiphany-core/src/repo_model_gateway.rs @@ -4,14 +4,33 @@ use serde::{Deserialize, Serialize}; pub const REPO_MODEL_ADMISSION_REVIEW_TYPE: &str = "epiphany.mind.repo_model_admission_review"; pub const REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION: &str = + "epiphany.mind.repo_model_admission_review.v2"; +pub const LEGACY_REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION: &str = "epiphany.mind.repo_model_admission_review.v1"; 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.v6"; +pub const LEGACY_REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION: &str = "epiphany.mind.repo_model_admission_receipt.v5"; 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.v5"; +pub const REPO_MODEL_ADMISSION_CONTRACT: &str = "epiphany.repo_model_admission.v6"; +pub const LEGACY_REPO_MODEL_ADMISSION_CONTRACT: &str = "epiphany.repo_model_admission.v5"; + +pub fn repo_model_admission_review_schema_supported(schema: &str, contract: &str) -> bool { + (schema == REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION + && contract == REPO_MODEL_ADMISSION_CONTRACT) + || (schema == LEGACY_REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION + && contract == LEGACY_REPO_MODEL_ADMISSION_CONTRACT) +} + +pub fn repo_model_admission_receipt_schema_supported(schema: &str, contract: &str) -> bool { + (schema == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION + && contract == REPO_MODEL_ADMISSION_CONTRACT) + || (schema == LEGACY_REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION + && contract == LEGACY_REPO_MODEL_ADMISSION_CONTRACT) +} 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.v1"; @@ -34,6 +53,8 @@ pub const REPO_FRONTIER_PLANNING_REQUEST_SCHEMA_VERSION: &str = pub const REPO_FRONTIER_PLAN_CANDIDATE_SCHEMA_VERSION: &str = "epiphany.imagination.repo_frontier_plan_candidate.v0"; pub const REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION: &str = + "epiphany.mind.repo_frontier_plan_decision_receipt.v1"; +pub const LEGACY_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"; @@ -620,9 +641,9 @@ pub struct RepoFrontierPlanDecisionReceipt { #[cultcache(key = 2)] pub planning_request_id: String, #[cultcache(key = 3)] - pub source_result_id: String, + pub legacy_mind_worker_result_id: Option, #[cultcache(key = 4)] - pub source_job_id: String, + pub legacy_mind_worker_job_id: Option, #[cultcache(key = 5)] pub candidate_id: String, #[cultcache(key = 6)] @@ -645,6 +666,54 @@ pub struct RepoFrontierPlanDecisionReceipt { pub model_admission_receipt_id: String, #[cultcache(key = 15)] pub contract: String, + #[cultcache(key = 16, default)] + pub decision_source: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] +pub enum RepoFrontierPlanDecisionSource { + MindWorker { + result_id: String, + job_id: String, + }, + AuthenticatedOperatorReview { + command_id: String, + admission_id: String, + packet_sha256: String, + source_actor_id: String, + }, +} + +/// Operator-safe identity projection of one current Mind review candidate. +/// Proposal text, commands, paths, and private state deliberately remain in +/// the canonical runtime store owned by Mind. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct RepoFrontierPlanReviewSummary { + pub mind_request_id: String, + pub candidate_id: String, + pub candidate_sha256: String, + pub model_revision: u64, + pub model_hash: String, + pub frontier_item_id: String, + pub requested_at: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct RepoFrontierPlanOperatorReview { + pub command_id: String, + pub admission_id: String, + pub packet_sha256: String, + pub source_actor_id: String, + pub mind_request_id: String, + pub candidate_id: String, + pub candidate_sha256: String, + pub expected_model_revision: u64, + pub expected_model_hash: String, + pub decision: RepoFrontierPlanDecision, + pub decided_at: String, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] @@ -783,9 +852,9 @@ pub struct RepoModelAdmissionReview { #[cultcache(key = 1)] pub review_id: String, #[cultcache(key = 2)] - pub result_id: String, + pub result_id: Option, #[cultcache(key = 3)] - pub job_id: String, + pub job_id: Option, #[cultcache(key = 4)] pub patch_id: String, #[cultcache(key = 5)] @@ -804,6 +873,15 @@ pub struct RepoModelAdmissionReview { pub contract: String, #[cultcache(key = 12)] pub repository_body_observation_basis: Option, + #[cultcache(key = 13, default)] + pub admission_source: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] +pub enum RepoModelAdmissionSource { + WorkerResult { result_id: String, job_id: String }, + FrontierPlanDecision { decision_id: String }, } #[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)] @@ -819,7 +897,7 @@ pub struct RepoModelAdmissionReceipt { #[cultcache(key = 2)] pub review_id: String, #[cultcache(key = 3)] - pub result_id: String, + pub result_id: Option, #[cultcache(key = 4)] pub patch_id: String, #[cultcache(key = 5)] @@ -854,6 +932,8 @@ pub struct RepoModelAdmissionReceipt { pub frontier_plan_decision_id: String, #[cultcache(key = 20)] pub repository_body_observation_basis: Option, + #[cultcache(key = 21, default)] + pub admission_source: Option, } #[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)] @@ -879,3 +959,125 @@ pub struct RepoModelMigrationReceipt { #[cultcache(key = 7)] pub contract: String, } + +#[cfg(test)] +mod migration_tests { + use super::*; + + #[test] + fn v0_tuple_decision_receipt_decodes_as_explicit_legacy_worker_source() { + let bytes = rmp_serde::to_vec(&( + LEGACY_REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION, + "decision-legacy", + "planning-legacy", + "mind-result-legacy", + "mind-job-legacy", + "candidate-legacy", + "a".repeat(64), + 7_u64, + "b".repeat(64), + "frontier-legacy", + "c".repeat(64), + RepoFrontierPlanDecision::Hold, + "Legacy Mind hold.", + "2026-07-15T09:00:06Z", + "", + REPO_FRONTIER_PLAN_DECISION_CONTRACT, + )) + .unwrap(); + let decoded: RepoFrontierPlanDecisionReceipt = rmp_serde::from_slice(&bytes).unwrap(); + assert_eq!( + decoded.legacy_mind_worker_result_id.as_deref(), + Some("mind-result-legacy") + ); + assert_eq!( + decoded.legacy_mind_worker_job_id.as_deref(), + Some("mind-job-legacy") + ); + assert!(decoded.decision_source.is_none()); + } + + #[test] + fn legacy_repo_model_review_and_receipt_tuples_decode_with_typed_optional_provenance() { + #[derive(Serialize)] + struct LegacyRepoModelAdmissionReceipt<'a> { + schema_version: &'a str, + receipt_id: &'a str, + review_id: &'a str, + result_id: &'a str, + patch_id: &'a str, + patch_sha256: String, + prior_model_revision: u64, + prior_model_hash: String, + committed_model_revision: u64, + committed_model_hash: String, + committed_at: &'a str, + contract: &'a str, + purpose: epiphany_state_model::RepoModelPatchPurpose, + field_13: &'a str, + field_14: &'a str, + field_15: &'a str, + field_16: &'a str, + field_17: &'a str, + field_18: &'a str, + field_19: &'a str, + observation_basis: Option, + } + let review_bytes = rmp_serde::to_vec(&( + LEGACY_REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION, + "review-legacy", + "result-legacy", + "job-legacy", + "patch-legacy", + "a".repeat(64), + 7_u64, + "b".repeat(64), + MindGatewayDecision::Accept, + vec!["evidence-legacy"], + "2026-07-15T09:00:06Z", + LEGACY_REPO_MODEL_ADMISSION_CONTRACT, + Option::::None, + )) + .unwrap(); + let review: RepoModelAdmissionReview = rmp_serde::from_slice(&review_bytes).unwrap(); + assert_eq!(review.result_id.as_deref(), Some("result-legacy")); + assert_eq!(review.job_id.as_deref(), Some("job-legacy")); + assert!(review.admission_source.is_none()); + assert!(repo_model_admission_review_schema_supported( + &review.schema_version, + &review.contract + )); + + let receipt_bytes = rmp_serde::to_vec(&LegacyRepoModelAdmissionReceipt { + schema_version: LEGACY_REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION, + receipt_id: "receipt-legacy", + review_id: "review-legacy", + result_id: "result-legacy", + patch_id: "patch-legacy", + patch_sha256: "a".repeat(64), + prior_model_revision: 7, + prior_model_hash: "b".repeat(64), + committed_model_revision: 8, + committed_model_hash: "c".repeat(64), + committed_at: "2026-07-15T09:00:06Z", + contract: LEGACY_REPO_MODEL_ADMISSION_CONTRACT, + purpose: epiphany_state_model::RepoModelPatchPurpose::Evolution, + field_13: "", + field_14: "", + field_15: "", + field_16: "", + field_17: "", + field_18: "", + field_19: "", + observation_basis: None, + }) + .unwrap(); + let receipt: RepoModelAdmissionReceipt = rmp_serde::from_slice(&receipt_bytes).unwrap(); + assert_eq!(receipt.result_id.as_deref(), Some("result-legacy")); + assert!(receipt.admission_source.is_none()); + assert!(repo_model_admission_receipt_schema_supported( + &receipt.schema_version, + &receipt.contract + )); + } +} diff --git a/epiphany-core/src/repository_readiness.rs b/epiphany-core/src/repository_readiness.rs index 2c02b47c..e31171b8 100644 --- a/epiphany-core/src/repository_readiness.rs +++ b/epiphany-core/src/repository_readiness.rs @@ -11,9 +11,8 @@ use crate::{ MEMORY_SEMANTIC_PROJECTION_OBLIGATION_SCHEMA_VERSION, MemorySemanticIndexReceipt, MemorySemanticProjectionAttempt, MemorySemanticProjectionClaim, MemorySemanticProjectionInput, MemorySemanticProjectionObligation, MemorySemanticProjectionReadiness, - MemorySemanticProjectionSourceHead, REPO_MODEL_ADMISSION_CONTRACT, - REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION, RepoModelAdmissionReceipt, - RepositoryBodyObservationBasis, SEMANTIC_PROJECTION_SCHEMA_VERSION, WorkspaceCoveragePolicy, + MemorySemanticProjectionSourceHead, RepoModelAdmissionReceipt, RepositoryBodyObservationBasis, + SEMANTIC_PROJECTION_SCHEMA_VERSION, WorkspaceCoveragePolicy, load_memory_semantic_projection_readiness, memory_graph_model_hash, observe_runtime_repository_body_basis, runtime_modeling_semantic_projection_input, runtime_spine_cache, validate_memory_semantic_projection_obligation, @@ -411,9 +410,10 @@ fn select_current_admission( model_hash: &str, ) -> Result { let mut matching = receipts.into_iter().filter(|(_, receipt)| { - receipt.schema_version == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - && receipt.contract == REPO_MODEL_ADMISSION_CONTRACT - && receipt.admitted_revision == model_revision + crate::repo_model_admission_receipt_schema_supported( + &receipt.schema_version, + &receipt.contract, + ) && receipt.admitted_revision == model_revision && receipt.admitted_hash == model_hash }); let selected = matching.next().ok_or_else(|| { @@ -881,6 +881,7 @@ fn observe_repository_readiness_with_clock( #[cfg(test)] mod tests { use super::*; + use crate::{REPO_MODEL_ADMISSION_CONTRACT, REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION}; use epiphany_state_model::RepoModelPatchPurpose; #[derive(Clone)] @@ -1005,7 +1006,7 @@ mod tests { schema_version: REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION.into(), receipt_id: "admission".into(), review_id: "review".into(), - result_id: "result".into(), + result_id: Some("result".into()), patch_id: "patch".into(), patch_sha256: "patch-hash".into(), previous_revision: 6, @@ -1023,6 +1024,10 @@ mod tests { claim_repair_request_id: String::new(), frontier_plan_decision_id: String::new(), repository_body_observation_basis: Some(body), + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: "result".into(), + job_id: "job".into(), + }), } } diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index b6a2e454..b5fad41a 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -2525,9 +2525,10 @@ pub fn commit_repo_model_admission( ) -> Result { validate_non_empty(result_id, "repo model admission result id")?; validate_non_empty(&review.review_id, "repo model admission review id")?; - if review.schema_version != REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION - || review.contract != REPO_MODEL_ADMISSION_CONTRACT - { + if !crate::repo_model_admission_review_schema_supported( + &review.schema_version, + &review.contract, + ) { return Err(anyhow!("unsupported repo model admission review contract")); } if review.decision != MindGatewayDecision::Accept { @@ -2535,7 +2536,7 @@ pub fn commit_repo_model_admission( } chrono::DateTime::parse_from_rfc3339(&review.reviewed_at) .map_err(|_| anyhow!("repo model admission review timestamp must be RFC3339"))?; - if review.result_id != result_id { + if review.result_id.as_deref() != Some(result_id) { return Err(anyhow!( "repo model admission review/result binding mismatch" )); @@ -2556,8 +2557,8 @@ pub fn commit_repo_model_admission( } let result = matching_results.into_iter().next().expect("one result"); if !result.role_id.eq_ignore_ascii_case("modeling") - || result.job_id != review.job_id - || result.result_id != review.result_id + || review.job_id.as_deref() != Some(result.job_id.as_str()) + || review.result_id.as_deref() != Some(result.result_id.as_str()) || result.schema_version != RUNTIME_ROLE_WORKER_RESULT_SCHEMA_VERSION || result.item_error.is_some() { @@ -2565,6 +2566,18 @@ pub fn commit_repo_model_admission( "repo model admission result role/job binding mismatch" )); } + let expected_admission_source = + if review.schema_version == crate::LEGACY_REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION { + None + } else { + Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: result.result_id.clone(), + job_id: result.job_id.clone(), + }) + }; + if review.admission_source != expected_admission_source { + return Err(anyhow!("repo model admission review provenance mismatch")); + } let worker_launch = cache .get::(&result.job_id)? .ok_or_else(|| anyhow!("repo model admission requires the immutable Modeling launch"))?; @@ -2652,13 +2665,15 @@ pub fn commit_repo_model_admission( let current_hash = crate::memory_graph_model_hash(¤t_model)?; if existing_receipt.receipt_id != receipt_id || existing_receipt.review_id != review.review_id - || existing_receipt.result_id != result_id + || existing_receipt.result_id.as_deref() != Some(result_id) || existing_receipt.patch_id != patch.patch_id || existing_receipt.patch_sha256 != patch_sha256 - || existing_receipt.contract != REPO_MODEL_ADMISSION_CONTRACT - || existing_receipt.schema_version - != REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION + || !crate::repo_model_admission_receipt_schema_supported( + &existing_receipt.schema_version, + &existing_receipt.contract, + ) || existing_receipt.purpose != patch.purpose + || existing_receipt.admission_source != expected_admission_source || existing_receipt.claim_repair_request_id != repair_request_id || !existing_receipt.frontier_plan_decision_id.is_empty() || existing_receipt.previous_revision != repair_request.model_revision @@ -2834,7 +2849,7 @@ pub fn commit_repo_model_admission( .any(|receipt| { receipt.claim_repair_request_id == request.request_id && (receipt.review_id != review.review_id - || receipt.result_id != result.result_id) + || receipt.result_id.as_deref() != Some(result.result_id.as_str())) }) { return Err(anyhow!("claim repair request is already incorporated")); @@ -3090,7 +3105,7 @@ pub fn commit_repo_model_admission( .any(|receipt| { receipt.proposal_modeling_request_id == request.request_id && (receipt.review_id != review.review_id - || receipt.result_id != result.result_id) + || receipt.result_id.as_deref() != Some(result.result_id.as_str())) }) { return Err(anyhow!("proposal Modeling request already incorporated")); @@ -3314,11 +3329,13 @@ pub fn commit_repo_model_admission( match (existing_review, existing_receipt) { (Some(existing_review), Some(existing_receipt)) if existing_review == *review => { if existing_receipt.review_id != review.review_id - || existing_receipt.result_id != result_id + || existing_receipt.result_id.as_deref() != Some(result_id) || existing_receipt.patch_id != patch.patch_id || existing_receipt.patch_sha256 != patch_sha256 - || existing_receipt.contract != REPO_MODEL_ADMISSION_CONTRACT - || existing_receipt.schema_version != REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION + || !crate::repo_model_admission_receipt_schema_supported( + &existing_receipt.schema_version, + &existing_receipt.contract, + ) || existing_receipt.purpose != patch.purpose || existing_receipt.frontier_route_id != frontier_route_id || existing_receipt.verification_request_id != verification_request_id @@ -3439,7 +3456,7 @@ pub fn commit_repo_model_admission( schema_version: REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION.to_string(), receipt_id: receipt_id.clone(), review_id: review.review_id.clone(), - result_id: result_id.to_string(), + result_id: Some(result_id.to_string()), patch_id: patch.patch_id.clone(), patch_sha256, previous_revision: current.model_revision, @@ -3457,6 +3474,7 @@ pub fn commit_repo_model_admission( claim_repair_request_id, frontier_plan_decision_id: String::new(), repository_body_observation_basis: review.repository_body_observation_basis.clone(), + admission_source: expected_admission_source, }; let (next_model_envelope, _) = cache.prepare_entry(crate::MEMORY_GRAPH_KEY, &next_entry)?; let (review_envelope, _) = cache.prepare_entry(&review.review_id, review)?; @@ -4380,8 +4398,7 @@ pub fn select_and_commit_repo_frontier_planning_request( .get_all::()? .into_iter() .filter(|r| { - r.schema_version == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - && r.contract == REPO_MODEL_ADMISSION_CONTRACT + crate::repo_model_admission_receipt_schema_supported(&r.schema_version, &r.contract) && r.admitted_revision == model.model_revision && r.admitted_hash == model_hash }) @@ -4477,9 +4494,10 @@ pub(crate) fn validate_current_repo_frontier_planning_request( .get_all::()? .into_iter() .filter(|receipt| { - receipt.schema_version == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - && receipt.contract == REPO_MODEL_ADMISSION_CONTRACT - && receipt.admitted_revision == model.model_revision + crate::repo_model_admission_receipt_schema_supported( + &receipt.schema_version, + &receipt.contract, + ) && receipt.admitted_revision == model.model_revision && receipt.admitted_hash == model_hash }) .collect::>(); @@ -4776,9 +4794,10 @@ fn validate_repo_model_claim_challenge_chain( .get_all::()? .into_iter() .filter(|receipt| { - receipt.schema_version == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - && receipt.contract == REPO_MODEL_ADMISSION_CONTRACT - && receipt.admitted_revision == challenge.model_revision + crate::repo_model_admission_receipt_schema_supported( + &receipt.schema_version, + &receipt.contract, + ) && receipt.admitted_revision == challenge.model_revision && receipt.admitted_hash == challenge.model_hash }) .collect::>(); @@ -4939,9 +4958,10 @@ pub fn commit_repo_model_claim_repair_request( .get_all::()? .into_iter() .filter(|receipt| { - receipt.schema_version == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - && receipt.contract == REPO_MODEL_ADMISSION_CONTRACT - && receipt.admitted_revision == model.model_revision + crate::repo_model_admission_receipt_schema_supported( + &receipt.schema_version, + &receipt.contract, + ) && receipt.admitted_revision == model.model_revision && receipt.admitted_hash == model_hash }) .collect::>(); @@ -5112,9 +5132,10 @@ pub(crate) fn validate_current_repo_model_claim_repair_request( .get_all::()? .into_iter() .filter(|receipt| { - receipt.schema_version == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - && receipt.contract == REPO_MODEL_ADMISSION_CONTRACT - && receipt.admitted_revision == model.model_revision + crate::repo_model_admission_receipt_schema_supported( + &receipt.schema_version, + &receipt.contract, + ) && receipt.admitted_revision == model.model_revision && receipt.admitted_hash == model_hash }) .collect::>(); @@ -5192,9 +5213,10 @@ fn validate_repo_frontier_plan_candidate_against_request( let admission = cache .get::(&request.admission_receipt_id)? .ok_or_else(|| anyhow!("frontier planning candidate requires exact admission receipt"))?; - if admission.schema_version != REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - || admission.contract != REPO_MODEL_ADMISSION_CONTRACT - || admission.admitted_revision != request.model_revision + if !crate::repo_model_admission_receipt_schema_supported( + &admission.schema_version, + &admission.contract, + ) || admission.admitted_revision != request.model_revision || admission.admitted_hash != request.model_hash { return Err(anyhow!( @@ -5259,44 +5281,219 @@ pub fn commit_repo_frontier_plan_decision( runtime_store: impl AsRef, mind_result_id: &str, ) -> Result { - commit_repo_frontier_plan_decision_inner(runtime_store, mind_result_id, None) + commit_repo_frontier_plan_decision_inner( + runtime_store, + FrontierPlanDecisionSource::MindWorker(mind_result_id), + None, + ) +} + +enum FrontierPlanDecisionSource<'a> { + MindWorker(&'a str), + Operator(&'a crate::RepoFrontierPlanOperatorReview), +} + +/// Read-only, bounded identities for candidates already routed to canonical +/// Mind review. Invalid, stale, or terminal candidates are not projected. +pub fn pending_repo_frontier_plan_reviews( + runtime_store: impl AsRef, + limit: usize, +) -> Result> { + if limit == 0 || limit > 25 { + return Err(anyhow!( + "Mind review projection limit must be within 1..=25" + )); + } + let mut cache = runtime_spine_cache(runtime_store.as_ref())?; + cache.pull_all_backing_stores()?; + let terminal = cache + .get_all::()? + .into_iter() + .map(|receipt| receipt.planning_request_id) + .collect::>(); + let mut summaries = Vec::new(); + for request in cache.get_all::()? { + let Ok((planning, candidate)) = validate_repo_frontier_plan_mind_request(&cache, &request) + else { + continue; + }; + if terminal.contains(&planning.request_id) { + continue; + } + summaries.push(crate::RepoFrontierPlanReviewSummary { + mind_request_id: request.request_id, + candidate_id: candidate.candidate_id, + candidate_sha256: request.candidate_sha256, + model_revision: planning.model_revision, + model_hash: planning.model_hash, + frontier_item_id: planning.frontier_item_id, + requested_at: request.requested_at, + }); + } + summaries.sort_by(|a, b| { + a.requested_at + .cmp(&b.requested_at) + .then_with(|| a.mind_request_id.cmp(&b.mind_request_id)) + }); + summaries.truncate(limit); + Ok(summaries) +} + +/// Canonical Mind commit path for an authenticated operator's exact review +/// request. The operator selects a disposition; Mind revalidates the complete +/// immutable candidate and current model before performing the same atomic +/// terminal transition used by a Mind worker. +pub fn commit_operator_repo_frontier_plan_review( + runtime_store: impl AsRef, + review: &crate::RepoFrontierPlanOperatorReview, +) -> Result { + commit_repo_frontier_plan_decision_inner( + runtime_store, + FrontierPlanDecisionSource::Operator(review), + None, + ) +} + +/// Classifies only operator-visible precondition refusal. Store corruption, +/// decoding failure, and I/O remain errors and must never be fossilized as a +/// terminal Refused command result. +pub fn operator_repo_frontier_plan_review_is_current( + runtime_store: impl AsRef, + review: &crate::RepoFrontierPlanOperatorReview, +) -> Result { + let mut cache = runtime_spine_cache(runtime_store.as_ref())?; + cache.pull_all_backing_stores()?; + let Some(request) = cache.get::(&review.mind_request_id)? else { + return Ok(false); + }; + let (planning, candidate) = + validate_repo_frontier_plan_mind_request_identity(&cache, &request)?; + if review.candidate_id != candidate.candidate_id + || review.candidate_sha256 != request.candidate_sha256 + || review.expected_model_revision != planning.model_revision + || review.expected_model_hash != planning.model_hash + { + return Ok(false); + } + let terminal = cache + .get_all::()? + .into_iter() + .filter(|receipt| receipt.planning_request_id == planning.request_id) + .collect::>(); + if !terminal.is_empty() { + if terminal.len() != 1 { + return Err(anyhow!( + "Mind review candidate has multiple terminal decisions" + )); + } + let receipt = &terminal[0]; + return Ok(receipt.decision == review.decision + && receipt.candidate_id == review.candidate_id + && receipt.candidate_sha256 == review.candidate_sha256 + && receipt.model_revision == review.expected_model_revision + && receipt.model_hash == review.expected_model_hash + && receipt.decision_source.as_ref() + == Some( + &crate::RepoFrontierPlanDecisionSource::AuthenticatedOperatorReview { + command_id: review.command_id.clone(), + admission_id: review.admission_id.clone(), + packet_sha256: review.packet_sha256.clone(), + source_actor_id: review.source_actor_id.clone(), + }, + )); + } + let current = cache + .get::(crate::MEMORY_GRAPH_KEY)? + .ok_or_else(|| anyhow!("operator Mind review requires canonical RepoModel"))? + .snapshot()?; + if current.model_revision != planning.model_revision + || crate::memory_graph_model_hash(¤t)? != planning.model_hash + { + return Ok(false); + } + validate_repo_frontier_plan_candidate_against_request(&cache, &candidate, &planning)?; + Ok(true) } fn commit_repo_frontier_plan_decision_inner( runtime_store: impl AsRef, - mind_result_id: &str, + source: FrontierPlanDecisionSource<'_>, pre_cas: Option<&(dyn Fn() + Sync)>, ) -> Result { - validate_non_empty(mind_result_id, "frontier plan Mind result id")?; let runtime_store = runtime_store.as_ref(); let mut cache = runtime_spine_cache(runtime_store)?; cache.pull_all_backing_stores()?; - let results = cache - .get_all::()? - .into_iter() - .filter(|result| result.result_id == mind_result_id) - .collect::>(); - if results.len() != 1 { - return Err(anyhow!( - "frontier plan decision requires exactly one immutable Mind result" - )); - } - let mind_result = &results[0]; - let mind_request_id = mind_result - .frontier_plan_mind_request_id - .as_deref() - .ok_or_else(|| anyhow!("frontier plan decision Mind result lacks request echo"))?; + let (mind_request_id, decision, rationale, decided_at, decision_source) = match source { + FrontierPlanDecisionSource::MindWorker(mind_result_id) => { + validate_non_empty(mind_result_id, "frontier plan Mind result id")?; + let results = cache + .get_all::()? + .into_iter() + .filter(|result| result.result_id == mind_result_id) + .collect::>(); + if results.len() != 1 { + return Err(anyhow!( + "frontier plan decision requires exactly one immutable Mind result" + )); + } + let mind_result = &results[0]; + let mind_request_id = mind_result + .frontier_plan_mind_request_id + .clone() + .ok_or_else(|| anyhow!("frontier plan decision Mind result lacks request echo"))?; + let typed = mind_result.frontier_plan_mind_decision()?.ok_or_else(|| { + anyhow!("frontier plan decision requires typed immutable Mind decision") + })?; + ( + mind_request_id, + typed.decision, + typed.rationale, + typed.decided_at, + crate::RepoFrontierPlanDecisionSource::MindWorker { + result_id: mind_result.result_id.clone(), + job_id: mind_result.job_id.clone(), + }, + ) + } + FrontierPlanDecisionSource::Operator(review) => { + validate_non_empty(&review.command_id, "operator review command id")?; + validate_non_empty(&review.admission_id, "operator review admission id")?; + validate_non_empty(&review.packet_sha256, "operator review packet digest")?; + validate_non_empty(&review.source_actor_id, "operator review actor id")?; + chrono::DateTime::parse_from_rfc3339(&review.decided_at) + .map_err(|_| anyhow!("operator review timestamp must be RFC3339"))?; + ( + review.mind_request_id.clone(), + review.decision, + format!("Authenticated operator requested {:?}.", review.decision).to_lowercase(), + review.decided_at.clone(), + crate::RepoFrontierPlanDecisionSource::AuthenticatedOperatorReview { + command_id: review.command_id.clone(), + admission_id: review.admission_id.clone(), + packet_sha256: review.packet_sha256.clone(), + source_actor_id: review.source_actor_id.clone(), + }, + ) + } + }; let mind_request = cache - .get::(mind_request_id)? + .get::(&mind_request_id)? .ok_or_else(|| anyhow!("frontier plan decision requires typed Mind request"))?; let (request, candidate) = validate_repo_frontier_plan_mind_request_identity(&cache, &mind_request)?; - let typed_decision = mind_result - .frontier_plan_mind_decision()? - .ok_or_else(|| anyhow!("frontier plan decision requires typed immutable Mind decision"))?; - let decision = typed_decision.decision; - let rationale = typed_decision.rationale.as_str(); - let decided_at = typed_decision.decided_at.as_str(); + if let FrontierPlanDecisionSource::Operator(review) = source { + if review.candidate_id != candidate.candidate_id + || review.candidate_sha256 != mind_request.candidate_sha256 + || review.expected_model_revision != request.model_revision + || review.expected_model_hash != request.model_hash + { + return Err(anyhow!( + "operator review does not exactly bind current Mind candidate" + )); + } + } + let rationale = rationale.as_str(); + let decided_at = decided_at.as_str(); let result_id = mind_request.imagination_result_id.as_str(); let source_results = cache .get_all::()? @@ -5337,11 +5534,28 @@ fn commit_repo_frontier_plan_decision_inner( } else { String::new() }; - if existing.schema_version != REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION + if ![ + REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION, + crate::LEGACY_REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION, + ] + .contains(&existing.schema_version.as_str()) || existing.contract != REPO_FRONTIER_PLAN_DECISION_CONTRACT || existing.decision_id != decision_id - || existing.source_result_id != mind_result.result_id - || existing.source_job_id != mind_result.job_id + || (existing.schema_version == REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION + && (existing.decision_source.as_ref() != Some(&decision_source) + || existing.legacy_mind_worker_result_id.is_some() + || existing.legacy_mind_worker_job_id.is_some())) + || (existing.schema_version + == crate::LEGACY_REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION + && !matches!( + &decision_source, + crate::RepoFrontierPlanDecisionSource::MindWorker { result_id, job_id } + if existing.decision_source.is_none() + && existing.legacy_mind_worker_result_id.as_deref() + == Some(result_id.as_str()) + && existing.legacy_mind_worker_job_id.as_deref() + == Some(job_id.as_str()) + )) || existing.candidate_id != candidate.candidate_id || existing.candidate_sha256 != candidate_sha256 || existing.model_revision != request.model_revision @@ -5365,11 +5579,19 @@ fn commit_repo_frontier_plan_decision_inner( let review = cache .get::(&expected_review_id)? .ok_or_else(|| anyhow!("Adopt retry lost its Mind admission review"))?; - if admission.schema_version != REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - || admission.contract != REPO_MODEL_ADMISSION_CONTRACT - || admission.receipt_id != expected_admission_id + let expected_admission_source = if existing.schema_version + == crate::LEGACY_REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION + { + None + } else { + Some(repo_model_admission_source(&decision_source, &decision_id)) + }; + if !crate::repo_model_admission_receipt_schema_supported( + &admission.schema_version, + &admission.contract, + ) || admission.receipt_id != expected_admission_id || admission.review_id != expected_review_id - || admission.result_id != mind_result.result_id + || admission.result_id.as_deref() != decision_source_reference(&decision_source) || admission.patch_id != expected_patch_id || admission.patch_sha256.trim().is_empty() || admission.previous_revision != request.model_revision @@ -5390,11 +5612,14 @@ fn commit_repo_frontier_plan_decision_inner( || admission.proposal_modeling_request_id != "" || admission.claim_repair_request_id != "" || admission.frontier_plan_decision_id != decision_id - || review.schema_version != REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION - || review.contract != REPO_MODEL_ADMISSION_CONTRACT + || admission.admission_source != expected_admission_source + || !crate::repo_model_admission_review_schema_supported( + &review.schema_version, + &review.contract, + ) || review.review_id != expected_review_id - || review.result_id != mind_result.result_id - || review.job_id != mind_result.job_id + || review.result_id.as_deref() != decision_source_reference(&decision_source) + || review.job_id.as_deref() != decision_source_job_reference(&decision_source) || review.patch_id != expected_patch_id || review.patch_sha256 != admission.patch_sha256 || review.base_revision != request.model_revision @@ -5404,9 +5629,10 @@ fn commit_repo_frontier_plan_decision_inner( != vec![ request.request_id.clone(), result.result_id.clone(), - mind_result.result_id.clone(), + decision_evidence_reference(&decision_source, &decision_id).to_string(), ] || review.reviewed_at != decided_at + || review.admission_source != expected_admission_source { return Err(anyhow!("Adopt retry model admission chain mismatch")); } @@ -5424,7 +5650,14 @@ fn commit_repo_frontier_plan_decision_inner( // Exact retry through the immutable result validator also replays request, // launch binding, launch bytes, runtime/thread, model, admission, frontier, // and candidate scope immediately before Mind decides. - put_runtime_role_worker_result(runtime_store, mind_result)?; + if let FrontierPlanDecisionSource::MindWorker(mind_result_id) = source { + let mind_result = cache + .get_all::()? + .into_iter() + .find(|result| result.result_id == mind_result_id) + .ok_or_else(|| anyhow!("frontier plan Mind result disappeared"))?; + put_runtime_role_worker_result(runtime_store, &mind_result)?; + } put_runtime_role_worker_result(runtime_store, result)?; validate_current_repo_frontier_planning_request(&cache, &request)?; validate_repo_frontier_plan_candidate_against_request(&cache, &candidate, &request)?; @@ -5432,8 +5665,8 @@ fn commit_repo_frontier_plan_decision_inner( schema_version: REPO_FRONTIER_PLAN_DECISION_RECEIPT_SCHEMA_VERSION.into(), decision_id: decision_id.clone(), planning_request_id: request.request_id.clone(), - source_result_id: mind_result.result_id.clone(), - source_job_id: mind_result.job_id.clone(), + legacy_mind_worker_result_id: None, + legacy_mind_worker_job_id: None, candidate_id: candidate.candidate_id.clone(), candidate_sha256: candidate_sha256.clone(), model_revision: request.model_revision, @@ -5445,6 +5678,7 @@ fn commit_repo_frontier_plan_decision_inner( decided_at: decided_at.to_string(), model_admission_receipt_id: String::new(), contract: REPO_FRONTIER_PLAN_DECISION_CONTRACT.into(), + decision_source: Some(decision_source.clone()), }; let backing = SingleFileMessagePackBackingStore::new(runtime_store); let current_envelope = backing @@ -5506,8 +5740,8 @@ fn commit_repo_frontier_plan_decision_inner( let review = RepoModelAdmissionReview { schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.into(), review_id: review_id.clone(), - result_id: mind_result.result_id.clone(), - job_id: mind_result.job_id.clone(), + result_id: decision_source_reference(&decision_source).map(str::to_string), + job_id: decision_source_job_reference(&decision_source).map(str::to_string), patch_id: patch.patch_id.clone(), patch_sha256: patch_sha256.clone(), base_revision: request.model_revision, @@ -5516,17 +5750,18 @@ fn commit_repo_frontier_plan_decision_inner( evidence_ids: vec![ request.request_id.clone(), result.result_id.clone(), - mind_result.result_id.clone(), + decision_evidence_reference(&decision_source, &decision_id).to_string(), ], reviewed_at: decided_at.to_string(), contract: REPO_MODEL_ADMISSION_CONTRACT.into(), repository_body_observation_basis: None, + admission_source: Some(repo_model_admission_source(&decision_source, &decision_id)), }; let admission = RepoModelAdmissionReceipt { schema_version: REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION.into(), receipt_id: admission_id.clone(), review_id, - result_id: mind_result.result_id.clone(), + result_id: decision_source_reference(&decision_source).map(str::to_string), patch_id: patch.patch_id, patch_sha256, previous_revision: request.model_revision, @@ -5544,6 +5779,7 @@ fn commit_repo_frontier_plan_decision_inner( claim_repair_request_id: String::new(), frontier_plan_decision_id: decision_id.clone(), repository_body_observation_basis: None, + admission_source: Some(repo_model_admission_source(&decision_source, &decision_id)), }; let obligation = modeling_projection_obligation( &cache, @@ -5574,18 +5810,72 @@ fn commit_repo_frontier_plan_decision_inner( // decision and admission chain. Reload through the public validator: // it returns that winner only when every identity field matches, while // a competing decision or unrelated model change remains an error. - return commit_repo_frontier_plan_decision(runtime_store, mind_result_id); + return match source { + FrontierPlanDecisionSource::MindWorker(mind_result_id) => { + commit_repo_frontier_plan_decision(runtime_store, mind_result_id) + } + FrontierPlanDecisionSource::Operator(review) => { + commit_operator_repo_frontier_plan_review(runtime_store, review) + } + }; } Ok(receipt) } +fn decision_source_reference(source: &crate::RepoFrontierPlanDecisionSource) -> Option<&str> { + match source { + crate::RepoFrontierPlanDecisionSource::MindWorker { result_id, .. } => Some(result_id), + crate::RepoFrontierPlanDecisionSource::AuthenticatedOperatorReview { .. } => None, + } +} + +fn decision_source_job_reference(source: &crate::RepoFrontierPlanDecisionSource) -> Option<&str> { + match source { + crate::RepoFrontierPlanDecisionSource::MindWorker { job_id, .. } => Some(job_id), + crate::RepoFrontierPlanDecisionSource::AuthenticatedOperatorReview { .. } => None, + } +} + +fn decision_evidence_reference<'a>( + source: &'a crate::RepoFrontierPlanDecisionSource, + decision_id: &'a str, +) -> &'a str { + match source { + crate::RepoFrontierPlanDecisionSource::MindWorker { result_id, .. } => result_id, + crate::RepoFrontierPlanDecisionSource::AuthenticatedOperatorReview { .. } => decision_id, + } +} + +fn repo_model_admission_source( + source: &crate::RepoFrontierPlanDecisionSource, + decision_id: &str, +) -> crate::RepoModelAdmissionSource { + match source { + crate::RepoFrontierPlanDecisionSource::MindWorker { result_id, job_id } => { + crate::RepoModelAdmissionSource::WorkerResult { + result_id: result_id.clone(), + job_id: job_id.clone(), + } + } + crate::RepoFrontierPlanDecisionSource::AuthenticatedOperatorReview { .. } => { + crate::RepoModelAdmissionSource::FrontierPlanDecision { + decision_id: decision_id.to_string(), + } + } + } +} + #[cfg(test)] pub(crate) fn commit_repo_frontier_plan_decision_with_pre_cas( runtime_store: impl AsRef, mind_result_id: &str, pre_cas: &(dyn Fn() + Sync), ) -> Result { - commit_repo_frontier_plan_decision_inner(runtime_store, mind_result_id, Some(pre_cas)) + commit_repo_frontier_plan_decision_inner( + runtime_store, + FrontierPlanDecisionSource::MindWorker(mind_result_id), + Some(pre_cas), + ) } impl RepoFrontierPlanCandidate { @@ -5633,9 +5923,10 @@ pub fn select_and_commit_repo_frontier_route( .get_all::()? .into_iter() .filter(|receipt| { - receipt.schema_version == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - && receipt.contract == REPO_MODEL_ADMISSION_CONTRACT - && receipt.admitted_revision == current.model_revision + crate::repo_model_admission_receipt_schema_supported( + &receipt.schema_version, + &receipt.contract, + ) && receipt.admitted_revision == current.model_revision && receipt.admitted_hash == current_hash }) .collect::>(); @@ -5751,9 +6042,10 @@ pub fn runtime_has_actionable_hands_frontier(runtime_store: impl AsRef) -> .get_all::()? .into_iter() .filter(|receipt| { - receipt.schema_version == REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION - && receipt.contract == REPO_MODEL_ADMISSION_CONTRACT - && receipt.admitted_revision == model.model_revision + crate::repo_model_admission_receipt_schema_supported( + &receipt.schema_version, + &receipt.contract, + ) && receipt.admitted_revision == model.model_revision && receipt.admitted_hash == model_hash }) .count(); @@ -8925,8 +9217,8 @@ pub(crate) mod tests { let review = RepoModelAdmissionReview { schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.to_string(), review_id: review_id.to_string(), - result_id: result_id.to_string(), - job_id: job_id.to_string(), + result_id: Some(result_id.to_string()), + job_id: Some(job_id.to_string()), patch_id: patch.patch_id, patch_sha256: format!("{:x}", Sha256::digest(&patch_bytes)), base_revision: patch.base_revision, @@ -8936,6 +9228,10 @@ pub(crate) mod tests { reviewed_at: "2026-07-13T04:00:01Z".to_string(), contract: REPO_MODEL_ADMISSION_CONTRACT.to_string(), repository_body_observation_basis: result.repository_body_observation_basis.clone(), + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: result_id.to_string(), + job_id: job_id.to_string(), + }), }; Ok((result, review)) } @@ -9163,8 +9459,8 @@ pub(crate) mod tests { let review = RepoModelAdmissionReview { schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.into(), review_id: format!("claim-repair-review-{suffix}"), - result_id: result.result_id.clone(), - job_id: result.job_id.clone(), + result_id: Some(result.result_id.clone()), + job_id: Some(result.job_id.clone()), patch_id: patch.patch_id, patch_sha256: format!("{:x}", Sha256::digest(&patch_bytes)), base_revision: patch.base_revision, @@ -9174,6 +9470,10 @@ pub(crate) mod tests { reviewed_at: "2026-07-14T09:00:05Z".into(), contract: REPO_MODEL_ADMISSION_CONTRACT.into(), repository_body_observation_basis: result.repository_body_observation_basis.clone(), + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: result.result_id.clone(), + job_id: result.job_id.clone(), + }), }; Ok((store, result, review, repair)) } @@ -9259,7 +9559,7 @@ pub(crate) mod tests { schema_version: REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION.into(), receipt_id: receipt_id.clone(), review_id: review.review_id.clone(), - result_id: result.result_id.clone(), + result_id: Some(result.result_id.clone()), patch_id: review.patch_id.clone(), patch_sha256: review.patch_sha256.clone(), previous_revision: repair.model_revision, @@ -9279,6 +9579,7 @@ pub(crate) mod tests { repository_body_observation_basis: review .repository_body_observation_basis .clone(), + admission_source: review.admission_source.clone(), }, )?; } @@ -9704,8 +10005,8 @@ pub(crate) mod tests { 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(), + result_id: Some(hostile_result.result_id.clone()), + job_id: Some(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, @@ -9717,6 +10018,10 @@ pub(crate) mod tests { repository_body_observation_basis: hostile_result .repository_body_observation_basis .clone(), + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: hostile_result.result_id.clone(), + job_id: modeling_job.into(), + }), }; let before_direct_hands_admission = std::fs::read(&store)?; assert!( @@ -10099,7 +10404,7 @@ pub(crate) mod tests { schema_version: REPO_MODEL_ADMISSION_RECEIPT_SCHEMA_VERSION.into(), receipt_id: receipt_id.clone(), review_id: review.review_id.clone(), - result_id: result.result_id.clone(), + result_id: Some(result.result_id.clone()), patch_id: patch.patch_id, patch_sha256: review.patch_sha256.clone(), previous_revision: current.model_revision, @@ -10119,6 +10424,7 @@ pub(crate) mod tests { repository_body_observation_basis: review .repository_body_observation_basis .clone(), + admission_source: review.admission_source.clone(), }; overwrite_test_entry(&store, &review.review_id, &review)?; overwrite_test_entry(&store, &receipt_id, &receipt)?; @@ -10798,8 +11104,8 @@ pub(crate) mod tests { let review = RepoModelAdmissionReview { schema_version: REPO_MODEL_ADMISSION_REVIEW_SCHEMA_VERSION.to_string(), review_id: format!("incorporation-review-{suffix}"), - result_id: result.result_id.clone(), - job_id: result.job_id.clone(), + result_id: Some(result.result_id.clone()), + job_id: Some(result.job_id.clone()), patch_id: patch.patch_id, patch_sha256: format!("{:x}", Sha256::digest(&bytes)), base_revision: patch.base_revision, @@ -10809,6 +11115,10 @@ pub(crate) mod tests { reviewed_at: "2026-07-13T06:00:10Z".to_string(), contract: REPO_MODEL_ADMISSION_CONTRACT.to_string(), repository_body_observation_basis: result.repository_body_observation_basis.clone(), + admission_source: Some(crate::RepoModelAdmissionSource::WorkerResult { + result_id: result.result_id.clone(), + job_id: result.job_id.clone(), + }), }; Ok((result, review)) } @@ -10875,7 +11185,7 @@ pub(crate) mod tests { let bytes_before = fs::read(&store)?; let mut swapped_result = review.clone(); - swapped_result.result_id = "other-result".to_string(); + swapped_result.result_id = Some("other-result".to_string()); assert!(commit_repo_model_admission(&store, &result.result_id, &swapped_result).is_err()); let mut swapped_patch = review.clone(); swapped_patch.review_id = "review-swapped-patch".to_string(); diff --git a/notes/epiphany-discord-operator-nerve.md b/notes/epiphany-discord-operator-nerve.md index d995cc01..5d06010f 100644 --- a/notes/epiphany-discord-operator-nerve.md +++ b/notes/epiphany-discord-operator-nerve.md @@ -92,8 +92,8 @@ release authorization, or deployment. 4. `direct`: admits one expiring operator-objective pressure; never parses the text as a command family or consequence grant. 5. `reviews`: read-only bounded list of pending review summaries. -6. `review`: exact request id plus `Adopt|Refuse|Hold`, expected revision, and - optional bounded rationale. +6. `review`: exact Mind request id, candidate id/digest, model revision/hash, + and `Adopt|Refuse|Hold`. The result contains identities and disposition only. Every mutating command carries a unique command id/nonce, issued-at, expires-at, target runtime/repository, actor identity, Bifrost capability reference, @@ -145,10 +145,34 @@ With Starfire disconnected and no SSH tunnel dependency: without changing Epiphany state; 10. Ygg services survive restart and continue the route without Starfire. -## Current gap +## Implemented Epiphany review owner (2026-07-19) -The live Ygg body has the Persona feedback nerve but none of these explicit -Discord operator command crossings. `epiphany-verse-query swarm-brake` and +`Reviews` projects at most ten current `RepoFrontierPlanMindRequest` identities. +Mind revalidates the immutable Imagination result, candidate digest, current +RepoModel revision/hash, frontier item, runtime, and thread. Plan action, +command, paths, checks, rollback, proposal text, and private state stay sealed. + +`Review` enters the existing `RepoFrontierPlanDecisionReceipt` owner. `Refuse` +and `Hold` are canonical terminal receipts without RepoModel mutation. `Adopt` +uses the existing atomic RepoModel admission CAS; it creates no route, Hands +authority, Substrate grant, Persona speech authority, release, or deployment. +Decision provenance is typed as a Mind worker result or an authenticated +operator review carrying command, admission, packet digest, and actor identity. +Downstream RepoModel provenance is independently typed as worker result or +frontier-plan decision. Absence is `None`, never an empty worker-id sentinel. + +The v1 command and sealed-result contracts derive review decision time from the +immutable packet `issuedAt`, so consequence-before-result replay is independent +of the retry clock. Candidate mismatch becomes a terminal refusal; storage, +decoding, CAS, and corruption faults propagate. CultCache v0 decision tuples +decode worker provenance into explicit legacy Options. Signed v0 operator +deliveries may drain and replay the original four families, but cannot acquire +the v1 review vocabulary. + +## Remaining deployment gap + +The live Ygg body has the Persona feedback nerve but does not yet run this +successor Discord operator crossing. `epiphany-verse-query swarm-brake` and other local CLIs rely on filesystem authority and caller-supplied operator labels; they are implementation primitives, not remote authentication. Generic VoidBot `provider-status`, `queue-codex`, and job approval operate VoidBot's own diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 066d85d3..17b49671 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -1,5 +1,29 @@ # Fresh Workspace Handoff +## Typed Discord Mind review owner (2026-07-19) + +The operator-command core now implements `Reviews` and `Review` without a +parallel review store. `Reviews` is a bounded identity-only projection of +current canonical `RepoFrontierPlanMindRequest` records. `Review` binds request, +candidate id/digest, expected RepoModel revision/hash, and exact +Adopt/Refuse/Hold. Mind revalidates and commits the existing decision owner; +Hold/Refuse are terminal inert receipts and Adopt uses the existing RepoModel +CAS. It creates no Hands/Substrate, Persona, release, or deployment authority. + +Decision provenance is typed (`MindWorker` or +`AuthenticatedOperatorReview`). RepoModel review/admission provenance is typed +separately (`WorkerResult` or `FrontierPlanDecision`); legacy worker ids are +Options and operator adoption writes None. Crash replay derives decision time +from packet `issuedAt` and recognizes only exact typed provenance. Candidate +mismatch is a terminal refusal; storage/corruption/CAS faults propagate. Actual +CultCache v0 tuple decoding and signed v0 delivery replay are covered; v0 +cannot acquire Reviews/Review. + +The service CLI now requires the canonical runtime-spine store explicitly and +returns sealed Discord-safe review projections. No successor is deployed. +Bifrost/VoidBot must advance to the v1 schemas before the Ygg rehearsal; brakes +remain engaged. + ## Organizational product posture and current release blockers — 2026-07-18 This is not a special dogfood experiment. Epiphany's normal organizational diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 9b06c05f404bea5b5e710db905edfb16dcd8f7b8..4c88df928956f531c43a2b0beb663b663531fe6f 100644 GIT binary patch delta 453 zcmXv~J#Q2-5M?iXTth=ahcpsx?)E++5L|VFBF#};u1Ytt=XM==J;wH4*eXSk(xl{s z_!S^gMEL{xA*qn^2PiY20#_4{@4xhVc# zY2jh3EaTfVLzbjsyuRofoIA~x` z0TTyU(#FtPEO!+gpMej2N-9M-;nr!=;K^y@Dp(k&L7`kUCIX|$C6f$AkT+1=G)7qT z1yzE%W}ifL-9{=8*=k62IOn#ivjRJ#(C!2?cS)WXtjWTM`Oy$oozLF}-jGA5=%0R6 zH8lv0sl>Tq7iEAVg$;qF3RMR&iLDs~lnfN2iHY=JGm^HhC_o_NEq42mu#tW#I delta 67 zcmZ4gT4Cl3g$-hiyoOc=MplMqQO6iKjJ|DFW-Mf6{N6m3seLLFBM^gVW*}w(Vpbq# N17h~=Q<*qg3<3PH6+!?2 diff --git a/state/map.yaml b/state/map.yaml index 2c70d25b..bcb147a2 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -15,6 +15,7 @@ experiment: 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." sleep_brake_semantics: "An engaged swarm brake is sleep mode, not daemon death. Resident services may remain supervised, publish liveness/health, refresh leases, and expose read-only operator sight. The brake blocks cognition launches, scheduling that creates cognitive work, Hands consequences, publication, and deployment action." + discord_mind_review_owner: "The typed Discord operator core routes Reviews/Review into the canonical frontier-plan Mind owner, not a parallel queue. Reviews exposes only bounded request/candidate/model/frontier identities. Exact Adopt/Refuse/Hold revalidates immutable Imagination and current RepoModel bindings; Hold/Refuse are terminal inert receipts; Adopt alone uses the existing RepoModel CAS and creates no Hands authority, Substrate grant, Persona speech, Bifrost release, or Idunn deployment. Decision provenance is typed as Mind worker or authenticated operator command/admission/packet/actor; downstream RepoModel provenance is typed as worker result or frontier-plan decision, with Option absence instead of empty sentinels. Replay time derives from immutable issuance, machine faults are not terminal refusals, CultCache v0 decision tuples decode explicitly, and signed v0 deliveries may drain only the original four commands. The successor is not deployed and Ygg remains braked pending coherent v1 Bifrost/VoidBot interop and live rehearsal." status: active phase: phase_6_reflection_boundary premise: >