From 2a88bf1de948377e0f2857eba471aefc2071dffb Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 30 May 2026 01:51:26 +0100 Subject: [PATCH] Enforce Substrate Gate grants for research --- epiphany-codex-bridge/src/mutation_service.rs | 60 ++++++++++- epiphany-core/src/lib.rs | 4 + epiphany-core/src/runtime_spine.rs | 61 +++++++++++ epiphany-core/src/substrate_gate.rs | 102 ++++++++++++++++++ notes/fresh-workspace-handoff.md | 14 +-- notes/perfect-machine-audit-roadmap.md | 18 ++-- state/ledgers.msgpack | Bin 169770 -> 170076 bytes state/map.yaml | 4 +- 8 files changed, 247 insertions(+), 16 deletions(-) diff --git a/epiphany-codex-bridge/src/mutation_service.rs b/epiphany-codex-bridge/src/mutation_service.rs index 59301fbf..8103b49c 100644 --- a/epiphany-codex-bridge/src/mutation_service.rs +++ b/epiphany-codex-bridge/src/mutation_service.rs @@ -4,6 +4,7 @@ use std::path::Path; use std::path::PathBuf; use epiphany_core::EPIPHANY_REORIENT_LAUNCH_BINDING_ID; +use epiphany_core::EPIPHANY_RESEARCH_ROLE_BINDING_ID; use epiphany_core::EpiphanyJobInterruptRequest; use epiphany_core::EpiphanyJobInterruptResult; use epiphany_core::EpiphanyJobLaunchRequest; @@ -19,6 +20,8 @@ use epiphany_core::EpiphanyRoleStatePatchDocument; use epiphany_core::EpiphanyStateUpdate; use epiphany_core::EpiphanyStateUpdatedField; use epiphany_core::MIND_GATEWAY_REVIEW_TYPE; +use epiphany_core::SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_TYPE; +use epiphany_core::EYES_EVIDENCE_PACKET_TYPE; use epiphany_core::mind_state_commit_receipt; use epiphany_core::put_mind_gateway_review; use epiphany_core::put_mind_state_commit_receipt; @@ -41,10 +44,12 @@ use epiphany_core::plan_runtime_spine_heartbeat_launch; use epiphany_core::put_eyes_evidence_packet; use epiphany_core::receipt_proof_evaluation_errors; use epiphany_core::replace_or_append_epiphany_job_binding; +use epiphany_core::put_substrate_gate_repo_access_grant_receipt; use epiphany_core::runtime_job_snapshot; use epiphany_core::runtime_eyes_evidence_packet; use epiphany_core::runtime_mind_gateway_review; -use epiphany_core::EYES_EVIDENCE_PACKET_TYPE; +use epiphany_core::runtime_substrate_gate_repo_access_grant_receipt; +use epiphany_core::substrate_gate_repo_access_grant_for_launch; use epiphany_state_model::EpiphanyEvidenceRecord; use epiphany_state_model::EpiphanyJobKind as CoreEpiphanyJobKind; use epiphany_state_model::EpiphanyRetrievalState; @@ -285,6 +290,11 @@ pub async fn launch_epiphany_job_on_thread( &request, backend_job_id.as_str(), )?; + maybe_put_substrate_gate_launch_grant( + runtime_store.as_path(), + &request, + backend_job_id.as_str(), + )?; let epiphany_state = apply_epiphany_state_update_to_thread( thread, @@ -507,6 +517,11 @@ pub async fn apply_thread_epiphany_role_accept( runtime_store_path.as_path(), packet.packet_id.as_str(), )?); + available_document_types.extend(persisted_substrate_gate_grant_types( + runtime_store_path.as_path(), + substrate_gate_grant_receipt_id(finding.runtime_job_id.as_deref().unwrap_or_default()) + .as_str(), + )?); } enforce_current_receipt_proofs( &organ_contract, @@ -933,6 +948,48 @@ fn persisted_eyes_evidence_packet_types( .unwrap_or_default()) } +fn maybe_put_substrate_gate_launch_grant( + runtime_store_path: &Path, + request: &EpiphanyJobLaunchRequest, + backend_job_id: &str, +) -> BridgeResult<()> { + if request.binding_id != EPIPHANY_RESEARCH_ROLE_BINDING_ID { + return Ok(()); + } + let grant = substrate_gate_repo_access_grant_for_launch( + substrate_gate_grant_receipt_id(backend_job_id), + backend_job_id.to_string(), + request, + Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true), + ); + put_substrate_gate_repo_access_grant_receipt(runtime_store_path, &grant).map_err(|err| { + EpiphanyBridgeError::Fatal(format!( + "failed to persist Substrate Gate access grant for research launch: {err}" + )) + }) +} + +fn persisted_substrate_gate_grant_types( + runtime_store_path: &Path, + receipt_id: &str, +) -> BridgeResult> { + let grant = runtime_substrate_gate_repo_access_grant_receipt(runtime_store_path, receipt_id) + .map_err(|err| { + EpiphanyBridgeError::Fatal(format!( + "failed to verify persisted Substrate Gate grant {:?}: {err}", + receipt_id + )) + })?; + Ok(grant + .is_some() + .then(|| vec![SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_TYPE.to_string()]) + .unwrap_or_default()) +} + +fn substrate_gate_grant_receipt_id(runtime_job_id: &str) -> String { + format!("substrate-grant-{runtime_job_id}") +} + fn role_acceptance_claimed_effects( role_id: EpiphanyRoleResultRoleId, changed_fields: &[EpiphanyStateUpdatedField], @@ -955,6 +1012,7 @@ fn role_acceptance_claimed_effects( fn role_acceptance_enforceable_receipts(role_id: EpiphanyRoleResultRoleId) -> Vec { let mut receipts = vec![MIND_GATEWAY_REVIEW_TYPE.to_string()]; if role_id == EpiphanyRoleResultRoleId::Research { + receipts.push(SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_TYPE.to_string()); receipts.push(EYES_EVIDENCE_PACKET_TYPE.to_string()); } receipts diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index 62857747..34034f1b 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -422,6 +422,7 @@ pub use runtime_spine::put_mind_gateway_review; pub use runtime_spine::put_mind_state_commit_receipt; pub use runtime_spine::put_runtime_reorient_worker_result; pub use runtime_spine::put_runtime_role_worker_result; +pub use runtime_spine::put_substrate_gate_repo_access_grant_receipt; pub use runtime_spine::replace_or_append_epiphany_job_binding; pub use runtime_spine::runtime_eyes_evidence_packet; pub use runtime_spine::runtime_hello_frame; @@ -433,6 +434,7 @@ pub use runtime_spine::runtime_role_worker_result; pub use runtime_spine::runtime_schema_catalog_response; pub use runtime_spine::runtime_spine_cache; pub use runtime_spine::runtime_spine_status; +pub use runtime_spine::runtime_substrate_gate_repo_access_grant_receipt; pub use runtime_spine::runtime_tool_invocation_statuses; pub use runtime_spine::runtime_worker_launch_request; pub use runtime_spine::write_runtime_hello_frame; @@ -463,7 +465,9 @@ pub use substrate_gate::SUBSTRATE_GATE_REPO_MUTATION_RECEIPT_TYPE; pub use substrate_gate::SUBSTRATE_GATE_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION; pub use substrate_gate::SUBSTRATE_GATE_REPO_SNAPSHOT_RECEIPT_TYPE; pub use substrate_gate::SubstrateGateCultNetContract; +pub use substrate_gate::SubstrateGateRepoAccessGrantReceipt; pub use substrate_gate::default_substrate_gate_cultnet_contracts; +pub use substrate_gate::substrate_gate_repo_access_grant_for_launch; pub use surfaces::EpiphanyAcceptanceBundle; pub use surfaces::EpiphanyContext; pub use surfaces::EpiphanyContextMissing; diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index 55e82d54..dad6ebfd 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -55,6 +55,7 @@ use crate::substrate_gate::SUBSTRATE_GATE_REPO_MUTATION_RECEIPT_SCHEMA_VERSION; use crate::substrate_gate::SUBSTRATE_GATE_REPO_MUTATION_RECEIPT_TYPE; use crate::substrate_gate::SUBSTRATE_GATE_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION; use crate::substrate_gate::SUBSTRATE_GATE_REPO_SNAPSHOT_RECEIPT_TYPE; +use crate::substrate_gate::SubstrateGateRepoAccessGrantReceipt; use crate::thread_state_store::THREAD_STATE_SCHEMA_VERSION; use crate::thread_state_store::THREAD_STATE_TYPE; use anyhow::Context; @@ -713,6 +714,7 @@ pub fn runtime_spine_cache(store_path: impl AsRef) -> Result { cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; + cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; @@ -1206,6 +1208,47 @@ pub fn runtime_eyes_evidence_packet( cache.get::(packet_id) } +pub fn put_substrate_gate_repo_access_grant_receipt( + store_path: impl AsRef, + receipt: &SubstrateGateRepoAccessGrantReceipt, +) -> Result<()> { + validate_non_empty( + &receipt.receipt_id, + "Substrate Gate access grant receipt id", + )?; + validate_non_empty( + &receipt.runtime_job_id, + "Substrate Gate access grant runtime job", + )?; + validate_non_empty(&receipt.binding_id, "Substrate Gate access grant binding")?; + validate_non_empty(&receipt.role, "Substrate Gate access grant role")?; + validate_non_empty( + &receipt.authority_scope, + "Substrate Gate access grant authority scope", + )?; + validate_non_empty(&receipt.granted_at, "Substrate Gate access grant timestamp")?; + if receipt.granted_operations.is_empty() { + return Err(anyhow!( + "Substrate Gate access grant must name granted operations" + )); + } + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + require_identity(&cache)?; + cache.put(&receipt.receipt_id, receipt)?; + Ok(()) +} + +pub fn runtime_substrate_gate_repo_access_grant_receipt( + store_path: impl AsRef, + receipt_id: &str, +) -> Result> { + validate_non_empty(receipt_id, "Substrate Gate access grant receipt id")?; + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + cache.get::(receipt_id) +} + pub fn put_coordinator_run_receipt( store_path: impl AsRef, receipt: &EpiphanyCoordinatorRunReceipt, @@ -3169,6 +3212,20 @@ mod tests { contract: "Eyes packet persists as runtime-spine proof.".to_string(), }; put_eyes_evidence_packet(&store, &eyes_packet)?; + let substrate_grant = SubstrateGateRepoAccessGrantReceipt { + schema_version: crate::SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_SCHEMA_VERSION + .to_string(), + receipt_id: "substrate-grant-1".to_string(), + runtime_job_id: "job-1".to_string(), + binding_id: "research-source-gather-worker".to_string(), + role: "epiphany-eyes".to_string(), + authority_scope: "epiphany.role.research".to_string(), + granted_operations: vec!["read".to_string(), "snapshot".to_string()], + granted_paths: vec![".".to_string()], + granted_at: "2026-05-06T00:06:00Z".to_string(), + contract: "Substrate Gate grant persists as runtime-spine proof.".to_string(), + }; + put_substrate_gate_repo_access_grant_receipt(&store, &substrate_grant)?; let stored = runtime_mind_gateway_review(&store, "mind-role-modeling-job-1")? .expect("Mind review receipt should persist"); @@ -3179,6 +3236,10 @@ mod tests { let stored_packet = runtime_eyes_evidence_packet(&store, "eyes-packet-1")? .expect("Eyes evidence packet should persist"); assert_eq!(stored_packet, eyes_packet); + let stored_grant = + runtime_substrate_gate_repo_access_grant_receipt(&store, "substrate-grant-1")? + .expect("Substrate Gate grant should persist"); + assert_eq!(stored_grant, substrate_grant); Ok(()) } diff --git a/epiphany-core/src/substrate_gate.rs b/epiphany-core/src/substrate_gate.rs index dfadf0ad..c2c6a014 100644 --- a/epiphany-core/src/substrate_gate.rs +++ b/epiphany-core/src/substrate_gate.rs @@ -1,3 +1,5 @@ +use crate::EpiphanyJobLaunchRequest; +use cultcache_rs::DatabaseEntry; use serde::Deserialize; use serde::Serialize; @@ -26,6 +28,34 @@ pub const SUBSTRATE_GATE_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION: &str = pub const SUBSTRATE_GATE_REPO_MUTATION_RECEIPT_SCHEMA_VERSION: &str = "epiphany.substrate_gate.repo_mutation_receipt.v0"; +#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)] +#[cultcache( + type = "epiphany.substrate_gate.repo_access_grant_receipt", + schema = "SubstrateGateRepoAccessGrantReceipt" +)] +pub struct SubstrateGateRepoAccessGrantReceipt { + #[cultcache(key = 0)] + pub schema_version: String, + #[cultcache(key = 1)] + pub receipt_id: String, + #[cultcache(key = 2)] + pub runtime_job_id: String, + #[cultcache(key = 3)] + pub binding_id: String, + #[cultcache(key = 4)] + pub role: String, + #[cultcache(key = 5)] + pub authority_scope: String, + #[cultcache(key = 6)] + pub granted_operations: Vec, + #[cultcache(key = 7)] + pub granted_paths: Vec, + #[cultcache(key = 8)] + pub granted_at: String, + #[cultcache(key = 9)] + pub contract: String, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubstrateGateCultNetContract { @@ -106,6 +136,26 @@ pub fn default_substrate_gate_cultnet_contracts() -> Vec SubstrateGateRepoAccessGrantReceipt { + SubstrateGateRepoAccessGrantReceipt { + schema_version: SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_SCHEMA_VERSION.to_string(), + receipt_id, + runtime_job_id, + binding_id: request.binding_id.clone(), + role: request.owner_role.clone(), + authority_scope: request.authority_scope.clone(), + granted_operations: vec!["read".to_string(), "snapshot".to_string()], + granted_paths: vec![".".to_string()], + granted_at, + contract: "Substrate Gate granted scoped repository read/snapshot access for this worker launch; mutation remains forbidden without a separate mutation receipt.".to_string(), + } +} + #[cfg(test)] mod tests { use super::*; @@ -132,4 +182,56 @@ mod tests { .contains(&SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_TYPE.to_string()) ); } + + #[test] + fn substrate_gate_grant_for_launch_is_read_only() { + let request = EpiphanyJobLaunchRequest { + expected_revision: Some(7), + binding_id: "research-source-gather-worker".to_string(), + kind: epiphany_state_model::EpiphanyJobKind::Specialist, + scope: "research".to_string(), + owner_role: "epiphany-eyes".to_string(), + authority_scope: "epiphany.role.research".to_string(), + linked_subgoal_ids: Vec::new(), + linked_graph_node_ids: Vec::new(), + instruction: "Gather source evidence.".to_string(), + launch_document: crate::EpiphanyWorkerLaunchDocument::Role( + crate::EpiphanyRoleWorkerLaunchDocument { + thread_id: "thread-1".to_string(), + role_id: "research".to_string(), + state_revision: 7, + objective: None, + active_subgoal_id: None, + active_subgoals: Vec::new(), + active_graph_node_ids: Vec::new(), + investigation_checkpoint: None, + scratch: None, + invariants: Vec::new(), + graphs: None, + recent_evidence: Vec::new(), + recent_observations: Vec::new(), + graph_frontier: None, + graph_checkpoint: None, + planning: None, + churn: None, + }, + ), + output_contract_id: crate::ROLE_WORKER_OUTPUT_CONTRACT_ID.to_string(), + organ_launch_contract: crate::default_launch_organ_contract( + "epiphany.role.research", + "role", + crate::ROLE_WORKER_OUTPUT_CONTRACT_ID, + ), + max_runtime_seconds: None, + }; + let grant = substrate_gate_repo_access_grant_for_launch( + "grant-1".to_string(), + "job-1".to_string(), + &request, + "2026-05-30T00:00:00Z".to_string(), + ); + assert!(grant.granted_operations.contains(&"read".to_string())); + assert!(!grant.granted_operations.contains(&"write".to_string())); + assert!(grant.contract.contains("mutation remains forbidden")); + } } diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 73f7d82b..629e0f51 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -34,7 +34,7 @@ remembers doctrine; the branch remembers the blade. - Do not copy exact branch or HEAD from this note. Run `git status --short --branch` and `git log --oneline -5`. - Canonical anatomy now lives in `notes/epiphany-anatomy.md`. Embodied sub-agents are Self, Face, Imagination, Eyes, Proprioception, Hands, and Soul. Body, Mind, Continuity, and Substrate Gate are substrate/state/protocol surfaces, not sub-agent identities. State is split by layer: `work_organ` for resident Epiphany sub-agents, `persona` for portable public/person-shaped state shared by Epiphany Face, VoidBot repo Faces, and Ghostlight characters, and heartbeat/swarm state for scheduling and coordination physiology. Persona state now carries provenance, required public presentation, `date-time` timestamps, typed `candidateActions`, strict anchored thoughts with non-authoritative extensions, custom enum companion labels, and typed affect records for bonds/status reads/doctrine stances. - The repo now has a one-command local operator path: `.\tools\epiphany_local_run.ps1`. Default mode is `smoke`; it builds the retained Codex app-server compatibility edge, builds native Epiphany operator binaries, runs the coordinator smoke, and writes launcher artifacts under `.epiphany-run/` plus coordinator artifacts under `.epiphany-dogfood/`. `-Mode status` is now Epiphany-native: it builds only Epiphany operator binaries, reads `state/thread-state.msgpack` when present, derives status through `epiphany-core`, and does not start or require `codex-app-server`. Use `-Mode plan` or `-Mode run -MaxSteps 4` for the raw bridge-equipped coordinator entrypoints; use `-Mode mvp -FaceInput "" -MaxSteps 4` for the local product cycle that projects Face's character turn, writes a Face/Aquarium bubble, bootstraps a minimal local checkpoint for fresh threads, runs the coordinator with auto-tools, then runs heartbeat sleep/dream maintenance. Live plan/run/mvp modes use the workspace `state/runtime-spine.msgpack`, because CodexThread launch writes worker requests there; the run bundle remains artifact evidence, not runtime lifecycle authority. Add `-ThreadId ` and `-Workspace ` to inspect or continue an existing thread. `status`, `plan`, and `smoke` do not spend model calls; `run` and `mvp` build and use `epiphany-openai-runtime`. Coordinator workers now launch detached and are polled through runtime-spine instead of blocking the coordinator. MVP runs are non-ephemeral by default so `-ThreadId ` can resume and poll later. `-MaxRuntimeSeconds ` is passed into worker runtime; timeout writes a failed worker receipt and exits. Failed modeling now blocks downstream verification until the failed result is reviewed. -- Current Mom-ready status: the normal MVP pass reaches Face -> coordinator -> detached runtime -> heartbeat sleep, and modeling workers now complete through the ChatGPT subscription transport. The first failure was a stack of violated observability/runtime invariants: worker prompts prepended the whole shared persistent-memory rite, Responses frame liveness was invisible until completion, and the runtime tried to persist hundreds of text deltas before completing the worker job. Role/reorient workers now get a compact Epiphany worker boundary instead of full persistent memory; runtime-spine records sampled raw Responses frame kinds with small previews; contiguous reasoning/text deltas compact before durable storage; and live proofs completed `2fe38dcd-d99e-4c73-9a58-afcc81f851c7`, `241e3851-e3ea-4877-a63b-0d2bbd96a7e2`, and `85149a26-6975-403f-ac93-092b717c5825` instead of timing out. The second failure was stale lifecycle authority: completed runtime jobs remained as unresolved `runtime_links`, replacement launches treated historical active links as current, and one bad validation path opened an orphan job before rejecting the state patch. Latest-link-wins is now the runtime-link invariant, terminal links project completed jobs, stale active history cannot veto replacement, launch validation happens before opening runtime jobs, and completed-but-unreviewable modeling findings stop for review instead of relaunching the same lane. The Eyes/source-gather fork is now landed: Research is a fixed Eyes lane with launch/result/accept/status/coordinator/protocol support, and `regather-needed` modeling findings route to `LaunchResearch` instead of relaunching blind Proprioception. Research statePatch admission is Eyes-shaped only: observations, evidence, scratch, and optional investigationCheckpoint; no graph, planning, objective, or checkpoint authority. Mind admission now leaves typed runtime-spine proof: role/reorient acceptance persists `epiphany.mind.gateway_review` before state admission and `epiphany.mind.state_commit_receipt` after admission with the resulting durable revision. Launch contracts now split the broad receipt catalogue from effect-specific proof profiles for state admission, evidence promotion, repo action, verification, and continuity recovery, so gates do not accidentally demand the whole organ catalogue for every finding. Acceptance evaluates claimed effects against the launch-carried profiles, rereads persisted receipts from runtime-spine, enforces Mind proof generally, and now enforces Eyes evidence-packet proof for accepted Research findings. The next concrete rebuild layer is Substrate Gate access proof for Research or Soul verdict proof for Verification. +- Current Mom-ready status: the normal MVP pass reaches Face -> coordinator -> detached runtime -> heartbeat sleep, and modeling workers now complete through the ChatGPT subscription transport. The first failure was a stack of violated observability/runtime invariants: worker prompts prepended the whole shared persistent-memory rite, Responses frame liveness was invisible until completion, and the runtime tried to persist hundreds of text deltas before completing the worker job. Role/reorient workers now get a compact Epiphany worker boundary instead of full persistent memory; runtime-spine records sampled raw Responses frame kinds with small previews; contiguous reasoning/text deltas compact before durable storage; and live proofs completed `2fe38dcd-d99e-4c73-9a58-afcc81f851c7`, `241e3851-e3ea-4877-a63b-0d2bbd96a7e2`, and `85149a26-6975-403f-ac93-092b717c5825` instead of timing out. The second failure was stale lifecycle authority: completed runtime jobs remained as unresolved `runtime_links`, replacement launches treated historical active links as current, and one bad validation path opened an orphan job before rejecting the state patch. Latest-link-wins is now the runtime-link invariant, terminal links project completed jobs, stale active history cannot veto replacement, launch validation happens before opening runtime jobs, and completed-but-unreviewable modeling findings stop for review instead of relaunching the same lane. The Eyes/source-gather fork is now landed: Research is a fixed Eyes lane with launch/result/accept/status/coordinator/protocol support, and `regather-needed` modeling findings route to `LaunchResearch` instead of relaunching blind Proprioception. Research statePatch admission is Eyes-shaped only: observations, evidence, scratch, and optional investigationCheckpoint; no graph, planning, objective, or checkpoint authority. Mind admission now leaves typed runtime-spine proof: role/reorient acceptance persists `epiphany.mind.gateway_review` before state admission and `epiphany.mind.state_commit_receipt` after admission with the resulting durable revision. Launch contracts now split the broad receipt catalogue from effect-specific proof profiles for state admission, evidence promotion, repo action, verification, and continuity recovery, so gates do not accidentally demand the whole organ catalogue for every finding. Acceptance evaluates claimed effects against the launch-carried profiles and rereads persisted receipts from runtime-spine. Research evidence promotion now enforces Substrate Gate read/snapshot grant, Eyes evidence packet, and Mind review before state admission. The next concrete rebuild layer is Soul verdict proof for Verification. - Local operator runs now write typed CultMesh intent/receipt documents to `.epiphany-run/cultmesh/operator-runs.ccmp` through `epiphany-operator-run`. The wrapper writes intent before acting and a completion receipt after success; this is the local consent/trace rail, not a hidden scheduler queue. - Status-mode local runs now also write a native CultMesh operator snapshot to `.epiphany-run/cultmesh/operator-snapshots.ccmp` through `epiphany-operator-snapshot`. That snapshot distills the operator-safe MVP status artifact into typed fields such as thread id, state status, coordinator action, CRRC action, reorient action, available actions, and artifact refs; it does not preserve the raw JSON status blob as internal state. - `epiphany-mvp-status --source native` is the current provider-neutral status boundary. It projects scene, pressure, jobs, roles, planning, reorient, CRRC, coordinator status, and runtime-linked worker lifecycle from native core surfaces. It reads runtime-spine job/result receipts for linked jobs so failed workers show as failed in role results, jobs, roles, and coordinator source signals, while successful semantic findings still require typed role/reorient worker result documents. Keep future pluggable model-provider work behind launch/result/runtime documents; do not let provider choice leak into status, prompt authority, scheduler policy, or durable `EpiphanyThreadState`. @@ -1426,11 +1426,13 @@ The launch contract now carries `receiptProofProfiles` beside the broad former is gate law. Profiles currently cover state admission, evidence promotion, repo action, verification, and continuity recovery. Role/reorient acceptance evaluates the claimed effects through those profiles. -It currently enforces only persisted Mind gateway review, verified by rereading -runtime-spine after the review write. Research acceptance now also emits, -persists, rereads, and enforces an `epiphany.eyes.evidence_packet` built from -the accepted Eyes-shaped statePatch. Other non-Mind profile gaps remain deferred -because those producers are not live yet. +It enforces persisted Mind gateway review, verified by rereading runtime-spine +after the review write. Research launch now emits a typed Substrate Gate +read/snapshot access grant, and Research acceptance emits, persists, rereads, +and enforces an `epiphany.eyes.evidence_packet` built from the accepted +Eyes-shaped statePatch. Research evidence promotion therefore has a live +Substrate Gate -> Eyes -> Mind proof chain. Other non-Mind profile gaps remain +deferred because those producers are not live yet. Mind review is now a persisted receipt chain for acceptance rather than an in-memory blessing: the bridge writes `epiphany.mind.gateway_review` before role/reorient state admission and `epiphany.mind.state_commit_receipt` after diff --git a/notes/perfect-machine-audit-roadmap.md b/notes/perfect-machine-audit-roadmap.md index 5cb246cd..52392b1d 100644 --- a/notes/perfect-machine-audit-roadmap.md +++ b/notes/perfect-machine-audit-roadmap.md @@ -139,10 +139,12 @@ admission and `epiphany.mind.state_commit_receipt` after admission with the resulting state revision. Research acceptance also emits a typed `epiphany.eyes.evidence_packet` from its accepted Eyes-shaped statePatch, persists it in runtime-spine, rereads it, and enforces Eyes packet proof for the -evidence-promotion profile. Remaining weakness: Substrate Gate, Hands, Soul, and -Continuity receipts are still mostly launch-carried expectations, not enforced -proof chains before state/action promotion, and the current Mind state mutation -plus post-commit receipt is not a single transaction yet. +evidence-promotion profile. Research launches now also persist a typed +Substrate Gate read/snapshot access grant, and Research acceptance enforces that +grant before admitting evidence-shaped state. Remaining weakness: Hands, Soul, +and Continuity receipts are still mostly launch-carried expectations, not +enforced proof chains before state/action promotion, and the current Mind state +mutation plus post-commit receipt is not a single transaction yet. ### 4. Self Still Shares A Throne With Compatibility Plumbing @@ -334,9 +336,11 @@ comfort tunnels. blockers. - Landed: accepted Research findings produce and enforce typed Eyes evidence packets for the evidence-promotion proof profile. -- Next: add Substrate Gate access/snapshot receipts for Research launches or - Soul verdict receipts for Verification findings, then make that profile proof - enforceable. +- Landed: Research launches produce and enforce typed Substrate Gate + read/snapshot grants, closing the Research evidence-promotion chain through + Substrate Gate -> Eyes -> Mind. +- Next: add Soul verdict receipts for Verification findings, then make that + profile proof enforceable. - Make Aquarium read the contract catalog and available receipts instead of hard-coding the route zoo. - Starve `codex_message_processor` down to Codex auth/model transport and diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 912739fdad6ad8f97dcec9d088b5453c588944bd..f149fc0458111189b57b7d54f1e46697eecf9da3 100644 GIT binary patch delta 305 zcmYL@u}T9$7=+y|uM`^v;Rjn!k3@yLR>aCi#UkCGy+7H-b8p>$FCmqMg)fjZ?;t1v z3-JYf3QK$O4ctSB%@i}=4D6~TnO8JV~pG=@L$ z3S8B62EoZJCNwc6b*w{mO>xwgaAu