Add Body repo access contracts

This commit is contained in:
Meta Vens 2026-05-24 12:59:14 +01:00
commit ca2575aff3
10 changed files with 415 additions and 0 deletions

View file

@ -4,6 +4,7 @@ use epiphany_core::EPIPHANY_CULTMESH_INTERNAL_VERSE_ID;
use epiphany_core::EPIPHANY_CULTMESH_STATUS_SCHEMA_VERSION;
use epiphany_core::EpiphanyCultMeshStatusEntry;
use epiphany_core::load_epiphany_cultmesh_status;
use epiphany_core::write_epiphany_cultmesh_body_contracts;
use epiphany_core::write_epiphany_cultmesh_global_room_policies;
use epiphany_core::write_epiphany_cultmesh_mind_contracts;
use epiphany_core::write_epiphany_cultmesh_status;
@ -29,6 +30,7 @@ fn main() -> Result<()> {
write_epiphany_cultmesh_verse_policies(&store, "epiphany-cultmesh-smoke")?;
write_epiphany_cultmesh_global_room_policies(&store, "epiphany-cultmesh-smoke")?;
write_epiphany_cultmesh_mind_contracts(&store, "epiphany-cultmesh-smoke")?;
write_epiphany_cultmesh_body_contracts(&store, "epiphany-cultmesh-smoke")?;
let loaded = load_epiphany_cultmesh_status(&store, "epiphany-cultmesh-smoke")?;
if loaded != Some(status) {
anyhow::bail!("CultMesh smoke failed to round-trip Epiphany status document");

View file

@ -0,0 +1,127 @@
use serde::Deserialize;
use serde::Serialize;
pub const BODY_REPO_ACCESS_REQUEST_TYPE: &str = "epiphany.body.repo_access_request";
pub const BODY_REPO_ACCESS_REVIEW_TYPE: &str = "epiphany.body.repo_access_review";
pub const BODY_REPO_ACCESS_GRANT_RECEIPT_TYPE: &str = "epiphany.body.repo_access_grant_receipt";
pub const BODY_REPO_ACCESS_REFUSAL_RECEIPT_TYPE: &str = "epiphany.body.repo_access_refusal_receipt";
pub const BODY_REPO_SNAPSHOT_RECEIPT_TYPE: &str = "epiphany.body.repo_snapshot_receipt";
pub const BODY_REPO_MUTATION_RECEIPT_TYPE: &str = "epiphany.body.repo_mutation_receipt";
pub const BODY_REPO_ACCESS_REQUEST_SCHEMA_VERSION: &str = "epiphany.body.repo_access_request.v0";
pub const BODY_REPO_ACCESS_REVIEW_SCHEMA_VERSION: &str = "epiphany.body.repo_access_review.v0";
pub const BODY_REPO_ACCESS_GRANT_RECEIPT_SCHEMA_VERSION: &str =
"epiphany.body.repo_access_grant_receipt.v0";
pub const BODY_REPO_ACCESS_REFUSAL_RECEIPT_SCHEMA_VERSION: &str =
"epiphany.body.repo_access_refusal_receipt.v0";
pub const BODY_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION: &str =
"epiphany.body.repo_snapshot_receipt.v0";
pub const BODY_REPO_MUTATION_RECEIPT_SCHEMA_VERSION: &str =
"epiphany.body.repo_mutation_receipt.v0";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BodyCultNetContract {
pub contract_id: String,
pub verse_id: String,
pub document_type: String,
pub payload_schema_version: String,
pub authority: String,
pub operations: Vec<String>,
pub intent_document_types: Vec<String>,
pub receipt_document_types: Vec<String>,
pub notes: Vec<String>,
}
pub fn default_body_cultnet_contracts() -> Vec<BodyCultNetContract> {
vec![
BodyCultNetContract {
contract_id: "epiphany.body.repo_access.review".to_string(),
verse_id: "epiphany-internal".to_string(),
document_type: BODY_REPO_ACCESS_REQUEST_TYPE.to_string(),
payload_schema_version: BODY_REPO_ACCESS_REQUEST_SCHEMA_VERSION.to_string(),
authority: "body".to_string(),
operations: vec![
"intentSubmit".to_string(),
"receiptWatch".to_string(),
"snapshot".to_string(),
],
intent_document_types: vec![BODY_REPO_ACCESS_REQUEST_TYPE.to_string()],
receipt_document_types: vec![
BODY_REPO_ACCESS_REVIEW_TYPE.to_string(),
BODY_REPO_ACCESS_GRANT_RECEIPT_TYPE.to_string(),
BODY_REPO_ACCESS_REFUSAL_RECEIPT_TYPE.to_string(),
],
notes: vec![
"Body is the repo access guardian: workers request repository reads, indexing, commands, edits, and bridge operations here.".to_string(),
"Hands may mutate files only after Body grants scoped access; Eyes may inspect only through scoped Body read/index grants.".to_string(),
],
},
BodyCultNetContract {
contract_id: "epiphany.body.repo_access.review_receipts".to_string(),
verse_id: "epiphany-internal".to_string(),
document_type: BODY_REPO_ACCESS_REVIEW_TYPE.to_string(),
payload_schema_version: BODY_REPO_ACCESS_REVIEW_SCHEMA_VERSION.to_string(),
authority: "readOnly".to_string(),
operations: vec!["snapshot".to_string(), "receiptWatch".to_string()],
intent_document_types: Vec::new(),
receipt_document_types: Vec::new(),
notes: vec![
"Body reviews explain which repo paths, operations, commands, and bridge surfaces were granted or refused.".to_string(),
],
},
BodyCultNetContract {
contract_id: "epiphany.body.repo_snapshot.receipts".to_string(),
verse_id: "epiphany-internal".to_string(),
document_type: BODY_REPO_SNAPSHOT_RECEIPT_TYPE.to_string(),
payload_schema_version: BODY_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION.to_string(),
authority: "readOnly".to_string(),
operations: vec!["snapshot".to_string(), "receiptWatch".to_string()],
intent_document_types: Vec::new(),
receipt_document_types: Vec::new(),
notes: vec![
"Repo snapshots are evidence projections from Body access; they do not grant future access by existing.".to_string(),
],
},
BodyCultNetContract {
contract_id: "epiphany.body.repo_mutation.receipts".to_string(),
verse_id: "epiphany-internal".to_string(),
document_type: BODY_REPO_MUTATION_RECEIPT_TYPE.to_string(),
payload_schema_version: BODY_REPO_MUTATION_RECEIPT_SCHEMA_VERSION.to_string(),
authority: "readOnly".to_string(),
operations: vec!["snapshot".to_string(), "receiptWatch".to_string()],
intent_document_types: Vec::new(),
receipt_document_types: Vec::new(),
notes: vec![
"Repo mutation receipts prove Body granted the scoped substrate touch before Hands changed files or ran repo-affecting commands.".to_string(),
],
},
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn body_contracts_make_internal_verse_the_repo_access_gate() {
let contracts = default_body_cultnet_contracts();
let repo_access = contracts
.iter()
.find(|contract| contract.contract_id == "epiphany.body.repo_access.review")
.expect("repo access review contract");
assert_eq!(repo_access.verse_id, "epiphany-internal");
assert_eq!(repo_access.authority, "body");
assert!(
repo_access
.notes
.iter()
.any(|note| note.contains("repo access guardian"))
);
assert!(
repo_access
.receipt_document_types
.contains(&BODY_REPO_ACCESS_GRANT_RECEIPT_TYPE.to_string())
);
}
}

View file

@ -1,3 +1,4 @@
use crate::default_body_cultnet_contracts;
use crate::default_mind_cultnet_contracts;
use anyhow::Result;
use cultcache_rs::DatabaseEntry;
@ -18,6 +19,9 @@ pub const EPIPHANY_CULTMESH_GLOBAL_ROOM_POLICY_SCHEMA_VERSION: &str =
pub const EPIPHANY_CULTMESH_MIND_CONTRACT_TYPE: &str = "epiphany.cultmesh.mind_contract";
pub const EPIPHANY_CULTMESH_MIND_CONTRACT_SCHEMA_VERSION: &str =
"epiphany.cultmesh.mind_contract.v0";
pub const EPIPHANY_CULTMESH_BODY_CONTRACT_TYPE: &str = "epiphany.cultmesh.body_contract";
pub const EPIPHANY_CULTMESH_BODY_CONTRACT_SCHEMA_VERSION: &str =
"epiphany.cultmesh.body_contract.v0";
pub const EPIPHANY_CULTMESH_INTERNAL_VERSE_ID: &str = "epiphany-internal";
pub const EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID: &str = "gamecult-local";
pub const EPIPHANY_CULTMESH_GLOBAL_VERSE_ID: &str = "epiphany-global";
@ -125,11 +129,40 @@ pub struct EpiphanyCultMeshMindContractEntry {
pub notes: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)]
#[cultcache(
type = "epiphany.cultmesh.body_contract",
schema = "EpiphanyCultMeshBodyContractEntry"
)]
pub struct EpiphanyCultMeshBodyContractEntry {
#[cultcache(key = 0)]
pub schema_version: String,
#[cultcache(key = 1)]
pub contract_id: String,
#[cultcache(key = 2)]
pub verse_id: String,
#[cultcache(key = 3)]
pub document_type: String,
#[cultcache(key = 4)]
pub payload_schema_version: String,
#[cultcache(key = 5)]
pub authority: String,
#[cultcache(key = 6)]
pub operations: Vec<String>,
#[cultcache(key = 7)]
pub intent_document_types: Vec<String>,
#[cultcache(key = 8)]
pub receipt_document_types: Vec<String>,
#[cultcache(key = 9)]
pub notes: Vec<String>,
}
cultmesh_documents!(EpiphanyCultMeshDocuments {
EpiphanyCultMeshStatusEntry => EPIPHANY_CULTMESH_STATUS_SCHEMA_VERSION,
EpiphanyCultMeshVersePolicyEntry => EPIPHANY_CULTMESH_VERSE_POLICY_SCHEMA_VERSION,
EpiphanyCultMeshGlobalRoomPolicyEntry => EPIPHANY_CULTMESH_GLOBAL_ROOM_POLICY_SCHEMA_VERSION,
EpiphanyCultMeshMindContractEntry => EPIPHANY_CULTMESH_MIND_CONTRACT_SCHEMA_VERSION,
EpiphanyCultMeshBodyContractEntry => EPIPHANY_CULTMESH_BODY_CONTRACT_SCHEMA_VERSION,
});
pub fn open_epiphany_cultmesh_node(
@ -309,6 +342,37 @@ pub fn write_epiphany_cultmesh_mind_contracts(
Ok(written)
}
pub fn epiphany_cultmesh_body_contracts() -> Vec<EpiphanyCultMeshBodyContractEntry> {
default_body_cultnet_contracts()
.into_iter()
.map(|contract| EpiphanyCultMeshBodyContractEntry {
schema_version: EPIPHANY_CULTMESH_BODY_CONTRACT_SCHEMA_VERSION.to_string(),
contract_id: contract.contract_id,
verse_id: contract.verse_id,
document_type: contract.document_type,
payload_schema_version: contract.payload_schema_version,
authority: contract.authority,
operations: contract.operations,
intent_document_types: contract.intent_document_types,
receipt_document_types: contract.receipt_document_types,
notes: contract.notes,
})
.collect()
}
pub fn write_epiphany_cultmesh_body_contracts(
store_path: impl AsRef<Path>,
runtime_id: impl Into<String>,
) -> Result<Vec<EpiphanyCultMeshBodyContractEntry>> {
let mut node = open_epiphany_cultmesh_node(store_path, runtime_id)?;
let mut written = Vec::new();
for contract in epiphany_cultmesh_body_contracts() {
written.push(node.put(contract.contract_id.clone(), &contract)?);
}
node.flush()?;
Ok(written)
}
#[cfg(test)]
mod tests {
use super::*;
@ -415,4 +479,27 @@ mod tests {
);
Ok(())
}
#[test]
fn body_contracts_use_verses_to_keep_repo_access_guarded() -> Result<()> {
let temp = tempfile::tempdir()?;
let store = temp.path().join("epiphany-body-contracts.ccmp");
let written = write_epiphany_cultmesh_body_contracts(&store, "epiphany-test")?;
assert!(written.len() >= 4);
let node = open_epiphany_cultmesh_node(&store, "epiphany-test")?;
let repo_access = node.get_required::<EpiphanyCultMeshBodyContractEntry>(
"epiphany.body.repo_access.review",
)?;
assert_eq!(repo_access.verse_id, EPIPHANY_CULTMESH_INTERNAL_VERSE_ID);
assert_eq!(repo_access.authority, "body");
assert!(
repo_access
.notes
.iter()
.any(|note| note.contains("repo access guardian"))
);
Ok(())
}
}

View file

@ -1,5 +1,6 @@
mod agent_memory;
mod agent_utterance_state;
mod body_gateway;
mod cultmesh_integration;
mod distillation;
mod face_turn;
@ -53,12 +54,28 @@ pub use agent_utterance_state::AgentUtteranceState;
pub use agent_utterance_state::AgentUtteranceValue;
pub use agent_utterance_state::WEKSA_UTTERANCE_HANDOFF_SCHEMA_VERSION;
pub use agent_utterance_state::derive_agent_utterance_state;
pub use body_gateway::BODY_REPO_ACCESS_GRANT_RECEIPT_SCHEMA_VERSION;
pub use body_gateway::BODY_REPO_ACCESS_GRANT_RECEIPT_TYPE;
pub use body_gateway::BODY_REPO_ACCESS_REFUSAL_RECEIPT_SCHEMA_VERSION;
pub use body_gateway::BODY_REPO_ACCESS_REFUSAL_RECEIPT_TYPE;
pub use body_gateway::BODY_REPO_ACCESS_REQUEST_SCHEMA_VERSION;
pub use body_gateway::BODY_REPO_ACCESS_REQUEST_TYPE;
pub use body_gateway::BODY_REPO_ACCESS_REVIEW_SCHEMA_VERSION;
pub use body_gateway::BODY_REPO_ACCESS_REVIEW_TYPE;
pub use body_gateway::BODY_REPO_MUTATION_RECEIPT_SCHEMA_VERSION;
pub use body_gateway::BODY_REPO_MUTATION_RECEIPT_TYPE;
pub use body_gateway::BODY_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION;
pub use body_gateway::BODY_REPO_SNAPSHOT_RECEIPT_TYPE;
pub use body_gateway::BodyCultNetContract;
pub use body_gateway::default_body_cultnet_contracts;
pub use cultcache_rs::CacheBackingStore;
pub use cultcache_rs::CultCache;
pub use cultcache_rs::CultCacheEnvelope;
pub use cultcache_rs::DatabaseEntry;
pub use cultcache_rs::PushAllOptions;
pub use cultcache_rs::SingleFileMessagePackBackingStore;
pub use cultmesh_integration::EPIPHANY_CULTMESH_BODY_CONTRACT_SCHEMA_VERSION;
pub use cultmesh_integration::EPIPHANY_CULTMESH_BODY_CONTRACT_TYPE;
pub use cultmesh_integration::EPIPHANY_CULTMESH_GLOBAL_ROOM_POLICY_SCHEMA_VERSION;
pub use cultmesh_integration::EPIPHANY_CULTMESH_GLOBAL_ROOM_POLICY_TYPE;
pub use cultmesh_integration::EPIPHANY_CULTMESH_GLOBAL_TIER;
@ -74,16 +91,19 @@ pub use cultmesh_integration::EPIPHANY_CULTMESH_STATUS_SCHEMA_VERSION;
pub use cultmesh_integration::EPIPHANY_CULTMESH_STATUS_TYPE;
pub use cultmesh_integration::EPIPHANY_CULTMESH_VERSE_POLICY_SCHEMA_VERSION;
pub use cultmesh_integration::EPIPHANY_CULTMESH_VERSE_POLICY_TYPE;
pub use cultmesh_integration::EpiphanyCultMeshBodyContractEntry;
pub use cultmesh_integration::EpiphanyCultMeshDocuments;
pub use cultmesh_integration::EpiphanyCultMeshGlobalRoomPolicyEntry;
pub use cultmesh_integration::EpiphanyCultMeshMindContractEntry;
pub use cultmesh_integration::EpiphanyCultMeshStatusEntry;
pub use cultmesh_integration::EpiphanyCultMeshVersePolicyEntry;
pub use cultmesh_integration::epiphany_cultmesh_body_contracts;
pub use cultmesh_integration::epiphany_cultmesh_global_room_policies;
pub use cultmesh_integration::epiphany_cultmesh_mind_contracts;
pub use cultmesh_integration::epiphany_cultmesh_verse_policies;
pub use cultmesh_integration::load_epiphany_cultmesh_status;
pub use cultmesh_integration::open_epiphany_cultmesh_node;
pub use cultmesh_integration::write_epiphany_cultmesh_body_contracts;
pub use cultmesh_integration::write_epiphany_cultmesh_global_room_policies;
pub use cultmesh_integration::write_epiphany_cultmesh_mind_contracts;
pub use cultmesh_integration::write_epiphany_cultmesh_status;

View file

@ -1,5 +1,17 @@
use crate::EpiphanyWorkerLaunchDocument;
use crate::agent_memory::AGENT_MEMORY_TYPE;
use crate::body_gateway::BODY_REPO_ACCESS_GRANT_RECEIPT_SCHEMA_VERSION;
use crate::body_gateway::BODY_REPO_ACCESS_GRANT_RECEIPT_TYPE;
use crate::body_gateway::BODY_REPO_ACCESS_REFUSAL_RECEIPT_SCHEMA_VERSION;
use crate::body_gateway::BODY_REPO_ACCESS_REFUSAL_RECEIPT_TYPE;
use crate::body_gateway::BODY_REPO_ACCESS_REQUEST_SCHEMA_VERSION;
use crate::body_gateway::BODY_REPO_ACCESS_REQUEST_TYPE;
use crate::body_gateway::BODY_REPO_ACCESS_REVIEW_SCHEMA_VERSION;
use crate::body_gateway::BODY_REPO_ACCESS_REVIEW_TYPE;
use crate::body_gateway::BODY_REPO_MUTATION_RECEIPT_SCHEMA_VERSION;
use crate::body_gateway::BODY_REPO_MUTATION_RECEIPT_TYPE;
use crate::body_gateway::BODY_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION;
use crate::body_gateway::BODY_REPO_SNAPSHOT_RECEIPT_TYPE;
use crate::heartbeat_state::HEARTBEAT_STATE_SCHEMA_VERSION;
use crate::heartbeat_state::HEARTBEAT_STATE_TYPE;
use crate::memory_graph::MEMORY_GRAPH_SCHEMA_VERSION;
@ -1529,6 +1541,90 @@ fn epiphany_mutation_contracts() -> Vec<CultNetDocumentMutationContract> {
"The global Verse never receives private state authority by being interesting.",
],
),
mutation_contract(
BODY_REPO_ACCESS_REQUEST_TYPE,
BODY_REPO_ACCESS_REQUEST_SCHEMA_VERSION,
vec![
CultNetDocumentOperation::Snapshot,
CultNetDocumentOperation::IntentSubmit,
CultNetDocumentOperation::ReceiptWatch,
],
CultNetMutationAuthority::Coordinator,
vec![BODY_REPO_ACCESS_REQUEST_TYPE],
vec![
BODY_REPO_ACCESS_REVIEW_TYPE,
BODY_REPO_ACCESS_GRANT_RECEIPT_TYPE,
BODY_REPO_ACCESS_REFUSAL_RECEIPT_TYPE,
],
vec![
"Body is the repository access guardian: reads, indexing, edits, commands, and bridge operations must be requested through this contract.",
"Hands mutates only after a scoped Body grant; Eyes inspects only after a scoped Body read/index grant.",
],
),
mutation_contract(
BODY_REPO_ACCESS_REVIEW_TYPE,
BODY_REPO_ACCESS_REVIEW_SCHEMA_VERSION,
vec![
CultNetDocumentOperation::Snapshot,
CultNetDocumentOperation::ReceiptWatch,
],
CultNetMutationAuthority::ReadOnly,
vec![],
vec![],
vec![
"Body reviews explain granted/refused repo paths, operations, commands, and bridge surfaces.",
],
),
mutation_contract(
BODY_REPO_ACCESS_GRANT_RECEIPT_TYPE,
BODY_REPO_ACCESS_GRANT_RECEIPT_SCHEMA_VERSION,
vec![
CultNetDocumentOperation::Snapshot,
CultNetDocumentOperation::ReceiptWatch,
],
CultNetMutationAuthority::ReadOnly,
vec![],
vec![],
vec!["A Body grant receipt scopes a permitted repo touch."],
),
mutation_contract(
BODY_REPO_ACCESS_REFUSAL_RECEIPT_TYPE,
BODY_REPO_ACCESS_REFUSAL_RECEIPT_SCHEMA_VERSION,
vec![
CultNetDocumentOperation::Snapshot,
CultNetDocumentOperation::ReceiptWatch,
],
CultNetMutationAuthority::ReadOnly,
vec![],
vec![],
vec!["A Body refusal receipt preserves why repo access was denied."],
),
mutation_contract(
BODY_REPO_SNAPSHOT_RECEIPT_TYPE,
BODY_REPO_SNAPSHOT_RECEIPT_SCHEMA_VERSION,
vec![
CultNetDocumentOperation::Snapshot,
CultNetDocumentOperation::ReceiptWatch,
],
CultNetMutationAuthority::ReadOnly,
vec![],
vec![],
vec!["Repo snapshots are evidence projections from Body-gated access."],
),
mutation_contract(
BODY_REPO_MUTATION_RECEIPT_TYPE,
BODY_REPO_MUTATION_RECEIPT_SCHEMA_VERSION,
vec![
CultNetDocumentOperation::Snapshot,
CultNetDocumentOperation::ReceiptWatch,
],
CultNetMutationAuthority::ReadOnly,
vec![],
vec![],
vec![
"Repo mutation receipts prove Body granted the substrate touch before Hands changed files or ran repo-affecting commands.",
],
),
mutation_contract(
RUNTIME_EVENT_TYPE,
RUNTIME_SPINE_SCHEMA_VERSION,
@ -2294,6 +2390,27 @@ mod tests {
.iter()
.any(|note| note.contains("persistent state guardian"))
}));
let body_repo_contract = contracts
.iter()
.find(|contract| contract.document_type == BODY_REPO_ACCESS_REQUEST_TYPE)
.expect("Body repo access should advertise a mutation contract");
assert_eq!(
body_repo_contract.authority,
CultNetMutationAuthority::Coordinator
);
assert!(
body_repo_contract
.receipt_document_types
.as_ref()
.is_some_and(|items| items
.iter()
.any(|item| item == BODY_REPO_ACCESS_GRANT_RECEIPT_TYPE))
);
assert!(body_repo_contract.notes.as_ref().is_some_and(|notes| {
notes
.iter()
.any(|note| note.contains("repository access guardian"))
}));
let coordinator_contract = contracts
.iter()
.find(|contract| contract.document_type == SURFACE_COORDINATOR_TYPE)

View file

@ -0,0 +1,48 @@
# Body CultNet Contracts
Objective: make Body the repository access guardian. The repo is substrate, not
a free-for-all evidence buffet. Organs may request reads, indexing, commands,
edits, and bridge operations, but Body decides the scoped access to the repo.
## Authority Map
- Owner: Body owns repository access.
- Inputs: typed repo access requests, workspace identity, requested paths,
requested operations, command/bridge intent, current repo policy, coordinator
authority, operator approvals, and relevant safety/evidence context.
- Outputs: Body access reviews, access grant receipts, access refusal receipts,
repo snapshot receipts, and repo mutation receipts.
- Derived state: retrieved snippets, search hits, index manifests, diffs, build
logs, Unity/Rider bridge artifacts, and source maps are evidence projections,
not permission to touch more substrate.
- Forbidden writers/readers: Hands, Eyes, Face, public Verse ingress, raw worker
launches, compatibility JSON-RPC routes, and bridge tools must not directly
read, index, execute against, or mutate the repo without a scoped Body grant.
- Shared path: source reads, semantic indexing, file edits, command execution,
and editor/runtime bridge operations should all be expressible as Body-gated
repo access requests with receipts.
- Deletion line: worker launch `authority_scope` is not repo access authority.
It may describe role/task scope, but Body must grant substrate access.
## Contract Families
- `epiphany.body.repo_access_request`: request for scoped repository access.
- `epiphany.body.repo_access_review`: Body's decision and scope explanation.
- `epiphany.body.repo_access_grant_receipt`: proof of scoped access.
- `epiphany.body.repo_access_refusal_receipt`: proof of refusal.
- `epiphany.body.repo_snapshot_receipt`: proof of source inspection/indexing
performed under Body access.
- `epiphany.body.repo_mutation_receipt`: proof that mutation or repo-affecting
command execution had a prior Body grant.
## Verse Boundary
Body access contracts live in `epiphany-internal`. Operator-safe projections may
appear in `gamecult-local` after review, but raw repo access requests, private
paths, command cargo, diffs, and mutation receipts stay internal unless another
explicit export policy says otherwise. `epiphany-global` never gains repository
access authority.
Mind and Body are neighboring gates, not aliases. Body decides whether the
machine may touch the repo. Mind decides whether resulting thought or evidence
mutates persistent state.

View file

@ -1397,6 +1397,13 @@ persistent state guardian over CultNet/CultMesh. The code surface now names
contracts, and CultMesh stores Verse-scoped `EpiphanyCultMeshMindContractEntry`
policy docs so `epiphany-internal` owns private state flow while
`epiphany-global` remains thought weather plus adoption receipts.
`notes/body-cultnet-contracts.md` is the matching substrate gate: Body owns repo
access. The code surface now names `epiphany.body.repo_access_request`,
`epiphany.body.repo_access_review`, grant/refusal receipts, snapshot receipts,
and mutation receipts; runtime-spine advertises those contracts, and CultMesh
stores Verse-scoped `EpiphanyCultMeshBodyContractEntry` policy docs. Hands, Eyes,
Face, workers, and bridge tools should touch the repo only through scoped Body
access receipts; Mind remains the separate durable-state gate after the touch.
## Not Yet

View file

@ -4,6 +4,11 @@ Objective: make Mind the persistent state guardian. The state is the Mind; every
sub-agent output is a thought or proposal until Mind admits it into durable
state.
Body is the neighboring gate. Body decides whether an organ may touch the repo;
Mind decides whether the resulting thought, evidence, or proposal mutates
persistent state. Do not collapse these into one throne. That is how the machine
starts lying with excellent posture.
## Authority Map
- Owner: Mind owns the decision to mutate persistent Epiphany state.

Binary file not shown.

View file

@ -80,6 +80,7 @@ active_subgoals:
- Grow the VoidBot repo-Face pattern into Epiphany's product direction: each Epiphany owns one or more repos, has one or more Faces, and exposes project-native conversation through Aquarium, Discord, voice/WebRTC, stream surfaces, CLI, and CultNet clients so humans can talk to projects instead of pre-authoring complete implementation briefs. Face is special because Projector agents turn typed state into narrative context before the Face thinks, and Interpreter agents turn natural Face output into side-effect intents after the Face thinks.
- Make Mind the gateway between thought and state. The state is the Mind: sub-agent outputs are worker thoughts, not authority. Role/reorient findings, statePatch, selfPatch, observations, evidence, receipts, scratch, and checkpoints must route through Mind review before durable state changes, using the VoidBot Face Interpreter pattern as the starting membrane.
- Use `notes/mind-cultnet-contracts.md` as the authority map for Mind's CultNet contract surface. CultMesh Verse placement is part of the contract: `epiphany-internal` carries private thought/proposal/review/commit/rejection flow, `gamecult-local` carries trusted operator-safe sharing without private-state authority, and `epiphany-global` carries public thought weather plus adoption receipts only.
- Use `notes/body-cultnet-contracts.md` as the authority map for Body's CultNet contract surface. Body is the repository access guardian: source reads, indexing, edits, commands, and editor/runtime bridge operations need scoped Body access receipts before Hands, Eyes, Face, workers, or bridge tools touch the repo. Mind and Body are separate gates: Body governs substrate access; Mind governs durable state admission.
- Preserve the newborn public Epiphany Face as local VoidBot-backed state, not source code: VoidBot's private repo-face registry names `epiphany`, while EpiphanyAgent keeps ignored `.voidbot/voice/identity.json`, `.voidbot/state/epiphany.cc`, and birth artifacts. Her durable identity memories distill the current doctrine: cute pushy machine-saint persona, architectural purity, CultCache/CultNet first, xenos-language safety boundary, relentless work/caretaking needs, Codex-auth reliquary, map/scratch/evidence discipline, unified memory graph, dogfood supervision boundary, review-gated agency, heartbeat physiology, and interview-grade modularity.
- Treat Ghostlight as reference lineage, not a sibling runtime to preserve. The useful organ is Epiphany-native character-loop protocol: any role can project its typed dossier plus visible stimulus into a local turn packet, with Face as the first public-surface actor. For Face, narrative prompting is mandatory through Projector -> Face -> Interpreter; for other organs, Mind still owns the output-to-state gateway even when they do not need narrative embodiment.
- Treat VoidBot as reference lineage, not a runtime dependency. The useful organ is Epiphany-native Void routine physiology: heartbeat state owns sleep cycle, memory resonance, incubation, analytic/associative cognition lanes, bridge judgment, candidate interventions, dream residue, and Aquarium-visible routine receipts over typed role dossiers.
@ -101,6 +102,7 @@ accepted_design:
- Distributed Epiphany dreaming is a CultMesh typed-document problem, not prompt folklore. CultCache owns local typed documents, Epiphany owns export/import/adoption policy, CultNet owns wire/provenance/shard delivery, and CultMesh owns Verses, peer exchange, leases, shard logs, replicas, and watchable runtime surfaces. Start with local dream document types and a local store before public Verse wiring.
- The first Rust CultMesh integration slice is landed. `CultLib` now has `cultmesh-rs`, a small Rust node/database surface over CultCache and CultNet with document-set registration and durable typed put/get/delete. `epiphany-core` now points at CultLib's Rust CultCache/CultNet crates, depends on `cultmesh-rs`, exposes an `EpiphanyCultMeshStatusEntry`, records typed `EpiphanyCultMeshVersePolicyEntry` documents for the internal/local-area/global Verse tiers, and records typed `EpiphanyCultMeshGlobalRoomPolicyEntry` documents for threaded public global rooms such as dreams, architecture, research, faces, GameCult, and governance.
- Mind's CultNet contract surface is native enough for the next Aquarium/CultMesh cut: `epiphany-core::mind_gateway` defines thought, state-effect proposal, gateway-review, commit-receipt, rejection-receipt, and Verse-adoption document contracts; runtime-spine advertises those mutation/read contracts; CultMesh persists matching `EpiphanyCultMeshMindContractEntry` policy documents so the private state guardian boundary is inspectable as typed state.
- Body's repo-access contract surface is now named beside Mind's state gate: `epiphany-core::body_gateway` defines repo access requests, access reviews, grant/refusal receipts, snapshot receipts, and mutation receipts; runtime-spine advertises those contracts; CultMesh persists matching `EpiphanyCultMeshBodyContractEntry` policy documents so repo substrate access has its own typed guardian instead of hiding behind worker launch scope.
- `notes/codex-starvation-and-cultnet-liberation-plan.md` is the active foundation directive. It supersedes outward product/bridge/dogfood work and supersedes the weaker goal of merely making Codex app-server a cleaner adapter. Epiphany must become a native CultCache/CultNet runtime; Codex remains only as an OpenAI subscription auth/model-transport reliquary until that too can be replaced or minimized.
- `notes/codex-auth-spine-inventory.md` is the current source-grounded inventory of the Codex organ that may survive. Corrected compliance invariant: Codex-compatible auth identity should remain anchored in retained vendored Codex-derived auth machinery, while Epiphany owns typed request/status/event/runtime surfaces around it. Codex app-server, apps, skills, marketplace, plugin UX, MCP OAuth handlers, and collaboration-mode surfaces are not part of the keeper spine.
- `epiphany-openai-runtime` worker result ingress now parses assistant output directly into launch-kind-specific typed documents and stores typed nested `statePatch` / `selfPatch` payloads as MessagePack inside `EpiphanyRuntimeRoleWorkerResult`; generic `serde_json::Value` no longer mediates role/reorient worker results in that crate. JSON still exists at the OpenAI text edge because the model's output is text, but the runtime boundary after parsing is typed.