Seed fresh RepoModel as keyed Mind
This commit is contained in:
parent
64b2bb6dbe
commit
8ad16be0ab
2 changed files with 418 additions and 42 deletions
|
|
@ -1,13 +1,12 @@
|
|||
use anyhow::{Result, bail};
|
||||
use anyhow::{bail, Result};
|
||||
use chrono::Utc;
|
||||
use epiphany_core::{
|
||||
EpiphanyMemoryAnchor, EpiphanyMemoryDomain, EpiphanyMemoryGraphSnapshot,
|
||||
EpiphanyMemoryLifecycle, EpiphanyMemoryNode, EpiphanyMemoryNodeKind, EpiphanyMemoryProfile,
|
||||
MEMORY_GRAPH_SCHEMA_VERSION, ObserveOutcome, RuntimeSpineInitOptions,
|
||||
admit_legacy_agent_memory_generation, bind_repository_body, bind_runtime_to_agent_memory_swarm,
|
||||
bind_runtime_workspace_coverage_store, ensure_agent_memory_swarm_identity,
|
||||
ensure_runtime_repo_model, initialize_runtime_spine, load_repository_body_status,
|
||||
observe_repository_body,
|
||||
initialize_keyed_repo_model, initialize_runtime_spine, load_repository_body_status,
|
||||
observe_repository_body, EpiphanyMemoryAnchor, EpiphanyMemoryDomain, EpiphanyMemoryLifecycle,
|
||||
EpiphanyMemoryNode, EpiphanyMemoryNodeKind, EpiphanyMemoryProfile, EpiphanyRepoModelSeed,
|
||||
EpiphanyRepoModelSeedDocuments, ObserveOutcome, RuntimeSpineInitOptions,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
|
@ -42,10 +41,14 @@ fn main() -> Result<()> {
|
|||
let binding = bind_repository_body(&repo, &store, &runtime_store, workspace_id)?;
|
||||
bind_runtime_workspace_coverage_store(&runtime_store, &workspace_coverage_store, &at)?;
|
||||
observe_repository_body(&repo, &store, &runtime_store)?;
|
||||
let bootstrap = EpiphanyMemoryGraphSnapshot {
|
||||
schema_version: Some(MEMORY_GRAPH_SCHEMA_VERSION.to_string()),
|
||||
graph_id: format!("{}-repo-model", binding.runtime_id),
|
||||
domains: vec![EpiphanyMemoryDomain {
|
||||
let seed = EpiphanyRepoModelSeed::new(
|
||||
format!("repo-model-seed-{}", binding.runtime_id),
|
||||
format!("{}-repo-model", binding.runtime_id),
|
||||
binding.swarm_id.clone(),
|
||||
binding.workspace_id.clone(),
|
||||
binding.source_identity_sha256.clone(),
|
||||
EpiphanyRepoModelSeedDocuments {
|
||||
domains: vec![EpiphanyMemoryDomain {
|
||||
id: "repository-body".to_string(),
|
||||
profile: EpiphanyMemoryProfile::RepoArchitecture,
|
||||
title: "Deployed repository Body".to_string(),
|
||||
|
|
@ -54,42 +57,42 @@ fn main() -> Result<()> {
|
|||
.to_string(),
|
||||
),
|
||||
lifecycle: EpiphanyMemoryLifecycle::Accepted,
|
||||
}],
|
||||
nodes: vec![EpiphanyMemoryNode {
|
||||
id: "claim-deployed-repository-body".to_string(),
|
||||
domain_id: "repository-body".to_string(),
|
||||
profile: EpiphanyMemoryProfile::RepoArchitecture,
|
||||
kind: EpiphanyMemoryNodeKind::RuntimeContract,
|
||||
title: "Runtime is bound to the deployed repository Body".to_string(),
|
||||
claim: format!(
|
||||
"Runtime {} models workspace {} at its authenticated Git Body.",
|
||||
binding.runtime_id, binding.workspace_id
|
||||
),
|
||||
question: "What architecture does live Modeling admit from this Body?"
|
||||
.to_string(),
|
||||
action_implication: "Expand only through Body-grounded Modeling admission."
|
||||
.to_string(),
|
||||
anchors: vec![EpiphanyMemoryAnchor {
|
||||
id: "anchor-deployed-repository-body".to_string(),
|
||||
kind: "repository_body_binding".to_string(),
|
||||
target: binding.git_top_level.clone(),
|
||||
source_hash: Some(binding.source_identity_sha256.clone()),
|
||||
note: Some(
|
||||
"Cold-start anchor to the authenticated deployed Git Body.".to_string(),
|
||||
}],
|
||||
nodes: vec![EpiphanyMemoryNode {
|
||||
id: "claim-deployed-repository-body".to_string(),
|
||||
domain_id: "repository-body".to_string(),
|
||||
profile: EpiphanyMemoryProfile::RepoArchitecture,
|
||||
kind: EpiphanyMemoryNodeKind::RuntimeContract,
|
||||
title: "Runtime is bound to the deployed repository Body".to_string(),
|
||||
claim: format!(
|
||||
"Runtime {} models workspace {} at its authenticated Git Body.",
|
||||
binding.runtime_id, binding.workspace_id
|
||||
),
|
||||
question: "What architecture does live Modeling admit from this Body?"
|
||||
.to_string(),
|
||||
action_implication: "Expand only through Body-grounded Modeling admission."
|
||||
.to_string(),
|
||||
anchors: vec![EpiphanyMemoryAnchor {
|
||||
id: "anchor-deployed-repository-body".to_string(),
|
||||
kind: "repository_body_binding".to_string(),
|
||||
target: binding.git_top_level.clone(),
|
||||
source_hash: Some(binding.source_identity_sha256.clone()),
|
||||
note: Some(
|
||||
"Cold-start anchor to the authenticated deployed Git Body."
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
}],
|
||||
lifecycle: EpiphanyMemoryLifecycle::Accepted,
|
||||
..Default::default()
|
||||
}],
|
||||
lifecycle: EpiphanyMemoryLifecycle::Accepted,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
ensure_runtime_repo_model(
|
||||
&runtime_store,
|
||||
runtime_store.with_extension("absent-legacy-repo-model"),
|
||||
&bootstrap,
|
||||
&at,
|
||||
edges: Vec::new(),
|
||||
summaries: Vec::new(),
|
||||
frontier: Vec::new(),
|
||||
lifecycle_receipts: Vec::new(),
|
||||
},
|
||||
)?;
|
||||
initialize_keyed_repo_model(&runtime_store, &seed, &at)?;
|
||||
println!(
|
||||
"bootstrapped workspace={} swarm={} runtime={} scope={} root={}",
|
||||
binding.workspace_id,
|
||||
|
|
|
|||
|
|
@ -124,6 +124,119 @@ pub struct EpiphanyRepoModelView {
|
|||
|
||||
pub const REPO_MODEL_MUTATION_PROPOSAL_SCHEMA_VERSION: &str =
|
||||
"epiphany.repo_model.mutation_proposal.v1";
|
||||
pub const REPO_MODEL_SEED_SCHEMA_VERSION: &str = "epiphany.repo_model.seed.v1";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct EpiphanyRepoModelSeedDocuments {
|
||||
pub domains: Vec<EpiphanyMemoryDomain>,
|
||||
pub nodes: Vec<EpiphanyMemoryNode>,
|
||||
pub edges: Vec<EpiphanyMemoryEdge>,
|
||||
pub summaries: Vec<EpiphanyMemorySummary>,
|
||||
pub frontier: Vec<RepoFrontierItem>,
|
||||
pub lifecycle_receipts: Vec<EpiphanyMemoryLifecycleReceipt>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.body.repo_model_seed.v1",
|
||||
schema = "EpiphanyRepoModelSeed"
|
||||
)]
|
||||
pub struct EpiphanyRepoModelSeed {
|
||||
#[cultcache(key = 0)]
|
||||
pub schema_version: String,
|
||||
#[cultcache(key = 1)]
|
||||
pub seed_id: String,
|
||||
#[cultcache(key = 2)]
|
||||
pub graph_id: String,
|
||||
#[cultcache(key = 3)]
|
||||
pub swarm_id: String,
|
||||
#[cultcache(key = 4)]
|
||||
pub workspace_id: String,
|
||||
#[cultcache(key = 5)]
|
||||
pub body_binding_sha256: String,
|
||||
#[cultcache(key = 6)]
|
||||
pub documents_msgpack: Vec<u8>,
|
||||
}
|
||||
|
||||
impl EpiphanyRepoModelSeed {
|
||||
pub fn new(
|
||||
seed_id: impl Into<String>,
|
||||
graph_id: impl Into<String>,
|
||||
swarm_id: impl Into<String>,
|
||||
workspace_id: impl Into<String>,
|
||||
body_binding_sha256: impl Into<String>,
|
||||
mut documents: EpiphanyRepoModelSeedDocuments,
|
||||
) -> Result<Self> {
|
||||
documents
|
||||
.domains
|
||||
.sort_by(|left, right| left.id.cmp(&right.id));
|
||||
documents
|
||||
.nodes
|
||||
.sort_by(|left, right| left.id.cmp(&right.id));
|
||||
documents
|
||||
.edges
|
||||
.sort_by(|left, right| left.id.cmp(&right.id));
|
||||
documents
|
||||
.summaries
|
||||
.sort_by(|left, right| left.id.cmp(&right.id));
|
||||
documents
|
||||
.frontier
|
||||
.sort_by(|left, right| left.id.cmp(&right.id));
|
||||
documents
|
||||
.lifecycle_receipts
|
||||
.sort_by(|left, right| left.id.cmp(&right.id));
|
||||
let seed = Self {
|
||||
schema_version: REPO_MODEL_SEED_SCHEMA_VERSION.into(),
|
||||
seed_id: seed_id.into(),
|
||||
graph_id: graph_id.into(),
|
||||
swarm_id: swarm_id.into(),
|
||||
workspace_id: workspace_id.into(),
|
||||
body_binding_sha256: body_binding_sha256.into(),
|
||||
documents_msgpack: rmp_serde::to_vec_named(&documents)?,
|
||||
};
|
||||
seed.validate()?;
|
||||
Ok(seed)
|
||||
}
|
||||
|
||||
pub fn documents(&self) -> Result<EpiphanyRepoModelSeedDocuments> {
|
||||
rmp_serde::from_slice(&self.documents_msgpack)
|
||||
.map_err(|error| anyhow!("invalid RepoModel seed documents: {error}"))
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
if self.schema_version != REPO_MODEL_SEED_SCHEMA_VERSION {
|
||||
return Err(anyhow!("unsupported RepoModel seed schema"));
|
||||
}
|
||||
for (value, label) in [
|
||||
(&self.seed_id, "seed"),
|
||||
(&self.graph_id, "graph"),
|
||||
(&self.swarm_id, "swarm"),
|
||||
(&self.workspace_id, "workspace"),
|
||||
(&self.body_binding_sha256, "Body binding"),
|
||||
] {
|
||||
require_semantic_id(value, label)?;
|
||||
}
|
||||
let documents = self.documents()?;
|
||||
let obligations = claim_obligations_for_frontier(&documents.frontier);
|
||||
validate_repo_model_parts(
|
||||
&EpiphanyRepoModelIdentityDocument {
|
||||
schema_epoch: REPO_MODEL_SCHEMA_EPOCH.into(),
|
||||
graph_id: self.graph_id.clone(),
|
||||
runtime_id: "seed-validation".into(),
|
||||
swarm_id: self.swarm_id.clone(),
|
||||
workspace_id: self.workspace_id.clone(),
|
||||
body_binding_sha256: self.body_binding_sha256.clone(),
|
||||
},
|
||||
documents.domains,
|
||||
documents.nodes,
|
||||
documents.edges,
|
||||
documents.summaries,
|
||||
documents.frontier,
|
||||
documents.lifecycle_receipts,
|
||||
obligations,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "operation", rename_all = "snake_case")]
|
||||
|
|
@ -198,9 +311,151 @@ pub(crate) fn register_repo_model_document_types(cache: &mut CultCache) -> Resul
|
|||
cache.register_entry_type::<EpiphanyRepoModelLifecycleReceiptDocument>()?;
|
||||
cache.register_entry_type::<EpiphanyRepoModelClaimObligationsDocument>()?;
|
||||
cache.register_entry_type::<EpiphanyRepoModelMutationProposal>()?;
|
||||
cache.register_entry_type::<EpiphanyRepoModelSeed>()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn initialize_keyed_repo_model(
|
||||
store_path: impl AsRef<Path>,
|
||||
seed: &EpiphanyRepoModelSeed,
|
||||
seeded_at: &str,
|
||||
) -> Result<EpiphanyRepoModelView> {
|
||||
seed.validate()?;
|
||||
chrono::DateTime::parse_from_rfc3339(seeded_at)
|
||||
.map_err(|error| anyhow!("RepoModel seed time is invalid: {error}"))?;
|
||||
let store_path = store_path.as_ref();
|
||||
let mut cache = runtime_spine_cache(store_path)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
if cache.snapshot_envelopes().into_iter().any(|envelope| {
|
||||
envelope.r#type == crate::MEMORY_GRAPH_TYPE && envelope.key == crate::MEMORY_GRAPH_KEY
|
||||
}) {
|
||||
return Err(anyhow!(
|
||||
"keyed RepoModel initialization refuses an aggregate RepoModel store"
|
||||
));
|
||||
}
|
||||
let runtime = cache
|
||||
.get::<crate::EpiphanyRuntimeIdentity>(crate::RUNTIME_IDENTITY_KEY)?
|
||||
.ok_or_else(|| anyhow!("RepoModel seed has no runtime identity"))?;
|
||||
let mind_identity = cache
|
||||
.get::<crate::EpiphanyMindIdentity>(crate::MIND_SCHEMA_EPOCH)?
|
||||
.ok_or_else(|| anyhow!("RepoModel seed has no Mind schema identity"))?;
|
||||
if mind_identity.schema_epoch != crate::MIND_SCHEMA_EPOCH
|
||||
|| mind_identity.runtime_id != runtime.runtime_id
|
||||
{
|
||||
return Err(anyhow!("RepoModel seed crosses the Mind schema epoch"));
|
||||
}
|
||||
let identity = EpiphanyRepoModelIdentityDocument {
|
||||
schema_epoch: REPO_MODEL_SCHEMA_EPOCH.into(),
|
||||
graph_id: seed.graph_id.clone(),
|
||||
runtime_id: runtime.runtime_id,
|
||||
swarm_id: seed.swarm_id.clone(),
|
||||
workspace_id: seed.workspace_id.clone(),
|
||||
body_binding_sha256: seed.body_binding_sha256.clone(),
|
||||
};
|
||||
if let Some(existing) =
|
||||
cache.get::<EpiphanyRepoModelIdentityDocument>(REPO_MODEL_IDENTITY_KEY)?
|
||||
{
|
||||
if existing != identity
|
||||
|| cache.get::<EpiphanyRepoModelSeed>(&seed.seed_id)?.as_ref() != Some(seed)
|
||||
{
|
||||
return Err(anyhow!("RepoModel seed identity collision"));
|
||||
}
|
||||
let view = assemble_repo_model_view(store_path)?;
|
||||
if !repo_model_view_matches_seed(&view, seed)? {
|
||||
return Err(anyhow!("RepoModel seed replay found divergent keyed state"));
|
||||
}
|
||||
return Ok(view);
|
||||
}
|
||||
|
||||
let documents = seed.documents()?;
|
||||
let obligations = claim_obligations_for_frontier(&documents.frontier);
|
||||
let mut writes = Vec::new();
|
||||
writes.push(cache.prepare_entry(REPO_MODEL_IDENTITY_KEY, &identity)?.0);
|
||||
for value in &documents.domains {
|
||||
writes.push(
|
||||
cache
|
||||
.prepare_entry(&value.id, &EpiphanyRepoModelDomainDocument::new(value)?)?
|
||||
.0,
|
||||
);
|
||||
}
|
||||
for value in &documents.nodes {
|
||||
writes.push(
|
||||
cache
|
||||
.prepare_entry(&value.id, &EpiphanyRepoModelNodeDocument::new(value)?)?
|
||||
.0,
|
||||
);
|
||||
}
|
||||
for value in &documents.edges {
|
||||
writes.push(
|
||||
cache
|
||||
.prepare_entry(&value.id, &EpiphanyRepoModelEdgeDocument::new(value)?)?
|
||||
.0,
|
||||
);
|
||||
}
|
||||
for value in &documents.summaries {
|
||||
writes.push(
|
||||
cache
|
||||
.prepare_entry(&value.id, &EpiphanyRepoModelSummaryDocument::new(value)?)?
|
||||
.0,
|
||||
);
|
||||
}
|
||||
for value in &documents.frontier {
|
||||
writes.push(
|
||||
cache
|
||||
.prepare_entry(&value.id, &EpiphanyRepoModelFrontierDocument::new(value)?)?
|
||||
.0,
|
||||
);
|
||||
}
|
||||
for value in &documents.lifecycle_receipts {
|
||||
writes.push(
|
||||
cache
|
||||
.prepare_entry(
|
||||
&value.id,
|
||||
&EpiphanyRepoModelLifecycleReceiptDocument::new(value)?,
|
||||
)?
|
||||
.0,
|
||||
);
|
||||
}
|
||||
for obligation in obligations {
|
||||
writes.push(cache.prepare_entry(&obligation.node_id, &obligation)?.0);
|
||||
}
|
||||
let mind_envelope = cache
|
||||
.get_envelope::<crate::EpiphanyMindIdentity>(crate::MIND_SCHEMA_EPOCH)?
|
||||
.ok_or_else(|| anyhow!("RepoModel seed lost its Mind identity envelope"))?;
|
||||
let provenance = cache.prepare_entry(&seed.seed_id, seed)?.0;
|
||||
match crate::commit_operator_mind_mutation(
|
||||
store_path,
|
||||
provenance,
|
||||
"Modeling.repo_model_seed",
|
||||
vec![mind_envelope],
|
||||
writes,
|
||||
seeded_at,
|
||||
)? {
|
||||
crate::EpiphanyMindCommitOutcome::Committed(_) => assemble_repo_model_view(store_path),
|
||||
crate::EpiphanyMindCommitOutcome::Conflict { .. } => {
|
||||
Err(anyhow!("RepoModel seed lost its exact-envelope commit"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn repo_model_view_matches_seed(
|
||||
view: &EpiphanyRepoModelView,
|
||||
seed: &EpiphanyRepoModelSeed,
|
||||
) -> Result<bool> {
|
||||
let documents = seed.documents()?;
|
||||
Ok(view.identity.graph_id == seed.graph_id
|
||||
&& view.identity.swarm_id == seed.swarm_id
|
||||
&& view.identity.workspace_id == seed.workspace_id
|
||||
&& view.identity.body_binding_sha256 == seed.body_binding_sha256
|
||||
&& view.domains == documents.domains
|
||||
&& view.nodes == documents.nodes
|
||||
&& view.edges == documents.edges
|
||||
&& view.summaries == documents.summaries
|
||||
&& view.frontier == documents.frontier
|
||||
&& view.lifecycle_receipts == documents.lifecycle_receipts
|
||||
&& view.claim_obligations == claim_obligations_for_frontier(&documents.frontier))
|
||||
}
|
||||
|
||||
pub(crate) fn repo_model_write_key(envelope: &CultCacheEnvelope) -> Result<Option<String>> {
|
||||
let key = if envelope.r#type == EpiphanyRepoModelIdentityDocument::TYPE {
|
||||
let value: EpiphanyRepoModelIdentityDocument = rmp_serde::from_slice(&envelope.payload)?;
|
||||
|
|
@ -676,6 +931,29 @@ fn frontier_is_unresolved(item: &RepoFrontierItem) -> bool {
|
|||
)
|
||||
}
|
||||
|
||||
fn claim_obligations_for_frontier(
|
||||
frontier: &[RepoFrontierItem],
|
||||
) -> Vec<EpiphanyRepoModelClaimObligationsDocument> {
|
||||
let mut obligations = BTreeMap::<String, BTreeSet<String>>::new();
|
||||
for item in frontier.iter().filter(|item| frontier_is_unresolved(item)) {
|
||||
for node_id in &item.target_claim_ids {
|
||||
obligations
|
||||
.entry(node_id.clone())
|
||||
.or_default()
|
||||
.insert(item.id.clone());
|
||||
}
|
||||
}
|
||||
obligations
|
||||
.into_iter()
|
||||
.map(
|
||||
|(node_id, unresolved_frontier_ids)| EpiphanyRepoModelClaimObligationsDocument {
|
||||
node_id,
|
||||
unresolved_frontier_ids: unresolved_frontier_ids.into_iter().collect(),
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn assemble_repo_model_view(store_path: impl AsRef<Path>) -> Result<EpiphanyRepoModelView> {
|
||||
let mut cache = runtime_spine_cache(store_path.as_ref())?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
|
|
@ -835,6 +1113,101 @@ mod tests {
|
|||
EpiphanyMemoryProfile, RepoFrontierStatus,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn fresh_repo_model_seed_commits_keyed_documents_and_refuses_divergent_replay() -> Result<()> {
|
||||
let temp = tempfile::tempdir()?;
|
||||
let store = temp.path().join("repo-model-seed.cc");
|
||||
initialize_runtime_spine(
|
||||
&store,
|
||||
RuntimeSpineInitOptions {
|
||||
runtime_id: "repo-model-seed".into(),
|
||||
display_name: "RepoModel seed".into(),
|
||||
created_at: "2026-08-14T00:00:00Z".into(),
|
||||
},
|
||||
)?;
|
||||
let domain = EpiphanyMemoryDomain {
|
||||
id: "domain-seed".into(),
|
||||
profile: EpiphanyMemoryProfile::RepoArchitecture,
|
||||
title: "Seed domain".into(),
|
||||
lifecycle: EpiphanyMemoryLifecycle::Accepted,
|
||||
..Default::default()
|
||||
};
|
||||
let node = EpiphanyMemoryNode {
|
||||
id: "node-seed".into(),
|
||||
domain_id: domain.id.clone(),
|
||||
profile: EpiphanyMemoryProfile::RepoArchitecture,
|
||||
kind: EpiphanyMemoryNodeKind::Module,
|
||||
title: "Seed node".into(),
|
||||
claim: "Bootstrap is keyed".into(),
|
||||
question: "Can the aggregate enter?".into(),
|
||||
action_implication: "Refuse it".into(),
|
||||
source_hashes: vec!["anchor:missing".into()],
|
||||
lifecycle: EpiphanyMemoryLifecycle::Accepted,
|
||||
..Default::default()
|
||||
};
|
||||
let frontier = RepoFrontierItem {
|
||||
id: "frontier-seed".into(),
|
||||
migration_body: "repo".into(),
|
||||
question: "What remains?".into(),
|
||||
target_claim_ids: vec![node.id.clone()],
|
||||
source_scope: vec!["epiphany-core".into()],
|
||||
recommended_next_organ: "Modeling".into(),
|
||||
status: RepoFrontierStatus::Active,
|
||||
..Default::default()
|
||||
};
|
||||
let seed = EpiphanyRepoModelSeed::new(
|
||||
"seed-1",
|
||||
"graph-seed",
|
||||
"swarm-seed",
|
||||
"workspace-seed",
|
||||
"sha256:body-seed",
|
||||
EpiphanyRepoModelSeedDocuments {
|
||||
domains: vec![domain],
|
||||
nodes: vec![node],
|
||||
edges: Vec::new(),
|
||||
summaries: Vec::new(),
|
||||
frontier: vec![frontier],
|
||||
lifecycle_receipts: Vec::new(),
|
||||
},
|
||||
)?;
|
||||
let first = initialize_keyed_repo_model(&store, &seed, "2026-08-14T00:00:01Z")?;
|
||||
let replay = initialize_keyed_repo_model(&store, &seed, "2026-08-14T00:00:02Z")?;
|
||||
assert_eq!(first, replay);
|
||||
assert_eq!(first.claim_obligations[0].node_id, "node-seed");
|
||||
let mut cache = runtime_spine_cache(&store)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
assert_eq!(
|
||||
cache.get::<EpiphanyRepoModelSeed>("seed-1")?,
|
||||
Some(seed.clone())
|
||||
);
|
||||
assert!(cache
|
||||
.get_all::<crate::EpiphanyMindCommitReceipt>()?
|
||||
.iter()
|
||||
.any(|receipt| receipt.invariant_owner == "Modeling.repo_model_seed"));
|
||||
assert!(cache
|
||||
.snapshot_envelopes()
|
||||
.iter()
|
||||
.all(|envelope| envelope.r#type != crate::MEMORY_GRAPH_TYPE));
|
||||
|
||||
let divergent = EpiphanyRepoModelSeed::new(
|
||||
"seed-1",
|
||||
"other-graph",
|
||||
"swarm-seed",
|
||||
"workspace-seed",
|
||||
"sha256:body-seed",
|
||||
EpiphanyRepoModelSeedDocuments {
|
||||
domains: Vec::new(),
|
||||
nodes: Vec::new(),
|
||||
edges: Vec::new(),
|
||||
summaries: Vec::new(),
|
||||
frontier: Vec::new(),
|
||||
lifecycle_receipts: Vec::new(),
|
||||
},
|
||||
)?;
|
||||
assert!(initialize_keyed_repo_model(&store, &divergent, "2026-08-14T00:00:03Z").is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keyed_repo_model_view_is_deterministic_and_has_no_global_revision() -> Result<()> {
|
||||
let temp = tempfile::tempdir()?;
|
||||
|
|
|
|||
Reference in a new issue