1use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5use thiserror::Error;
6
7#[cfg(feature = "semantic-memory-integration")]
8pub mod semantic_adapter;
9
10#[cfg(feature = "semantic-memory-integration")]
11pub use semantic_adapter::SemanticMemoryAdapter;
12
13pub mod canonical_stack {
14 pub use forge_memory_bridge::{
15 transform_envelope_v3, BridgeError, ProjectionImportBatchV3,
16 PROJECTION_IMPORT_BATCH_V3_SCHEMA,
17 };
18 pub use knowledge_runtime::adapters::semantic_memory::SemanticMemoryAdapter;
19 pub use knowledge_runtime::{
20 KnowledgeRuntime, QueryTrace, RuntimeConfig, RuntimeError, RuntimeQueryProvenanceV1, Scope,
21 };
22 pub use semantic_memory::{
23 AddGraphEdgeParams, ExplainedSearchResponse, IntegrityReport,
24 MemoryConfig as CanonicalMemoryConfig, MemoryError as CanonicalMemoryError, MemoryStats,
25 MemoryStore as CanonicalMemoryStore, MockEmbedder, ProjectionImportResult, ReceiptMode,
26 ReconcileAction, SearchContext, SearchResponse, SearchResult, StoredGraphEdge,
27 VectorSearchReceiptV1, VerifyMode,
28 };
29 pub use semantic_memory_forge::{
30 EvidenceBundle, ExportClaim, ExportEnvelopeV2, ExportEnvelopeV3, ExportRecord,
31 ExportRecordV3, ForgeExportMeta, ForgeToolReceiptV2, EXPORT_ENVELOPE_V2_SCHEMA,
32 EXPORT_ENVELOPE_V3_SCHEMA,
33 };
34 pub use stack_ids::{ClaimId, ClaimVersionId, EntityId, EnvelopeId, ScopeKey, TraceCtx};
35
36 pub fn transform_forge_export(
37 envelope: &ExportEnvelopeV3,
38 ) -> Result<ProjectionImportBatchV3, BridgeError> {
39 transform_envelope_v3(envelope)
40 }
41
42 pub async fn import_projection_batch(
43 store: &CanonicalMemoryStore,
44 batch: &ProjectionImportBatchV3,
45 ) -> Result<ProjectionImportResult, CanonicalMemoryError> {
46 store.import_projection_batch(batch).await
47 }
48}
49
50pub use canonical_stack::{
51 CanonicalMemoryConfig, CanonicalMemoryError, CanonicalMemoryStore, ProjectionImportBatchV3,
52 ProjectionImportResult,
53};
54
55pub struct CanonicalMemoryAdapter {
56 store: canonical_stack::CanonicalMemoryStore,
57 runtime: canonical_stack::KnowledgeRuntime,
58}
59
60#[derive(Debug, Error)]
61pub enum MemoryProfileError {
62 #[error("invalid memory backend profile: {0}")]
63 InvalidProfile(String),
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67pub struct SemanticMemoryBackendProfileV1 {
68 pub schema: String,
69 pub owner: String,
70 pub authoritative_store: String,
71 pub derived_candidate_backend: String,
72 pub exact_f32_rerank_required: bool,
73 pub direct_provekv_dependency_allowed: bool,
74 pub recall_integration_allowed: bool,
75 pub missing_generation_behavior: String,
76}
77
78impl SemanticMemoryBackendProfileV1 {
79 pub const SCHEMA: &'static str = "AiDENsSemanticMemoryBackendProfileV1";
80
81 pub fn provekv_derived_candidate() -> Self {
82 Self {
83 schema: Self::SCHEMA.into(),
84 owner: "semantic-memory".into(),
85 authoritative_store: "semantic-memory sqlite f32 embeddings".into(),
86 derived_candidate_backend: "provekv_pool_candidate_then_exact_f32".into(),
87 exact_f32_rerank_required: true,
88 direct_provekv_dependency_allowed: false,
89 recall_integration_allowed: false,
90 missing_generation_behavior: "receipted_f32_fallback".into(),
91 }
92 }
93
94 pub fn validate(&self) -> Result<(), MemoryProfileError> {
95 if self.schema != Self::SCHEMA {
96 return Err(MemoryProfileError::InvalidProfile(
97 "unexpected semantic memory backend profile schema".into(),
98 ));
99 }
100 if self.owner != "semantic-memory" {
101 return Err(MemoryProfileError::InvalidProfile(
102 "semantic-memory must own canonical memory truth".into(),
103 ));
104 }
105 if !self.exact_f32_rerank_required {
106 return Err(MemoryProfileError::InvalidProfile(
107 "proveKV/poly-kv candidate profiles must require exact f32 rerank".into(),
108 ));
109 }
110 if self.direct_provekv_dependency_allowed || self.recall_integration_allowed {
111 return Err(MemoryProfileError::InvalidProfile(
112 "AiDENs profiles must not directly depend on proveKV or wire Recall".into(),
113 ));
114 }
115 Ok(())
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
120pub struct MemoryGroundingBackpointerV1 {
121 pub owner_crate: String,
122 pub artifact_type: String,
123 pub relationship: String,
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub struct MemoryGroundingEvidenceV1 {
128 pub schema: String,
129 pub artifact_kind: String,
130 pub ownership: String,
131 pub support_tier: String,
132 pub semantic_status: String,
133 pub semantic_exactness: String,
134 pub proof_debt_status: String,
135 pub contradiction_status: String,
136 pub view_disclosure_status: String,
137 pub execution_contamination_status: String,
138 pub adapter_route: String,
139 pub memory_mode: String,
140 pub query: String,
141 pub result_count: usize,
142 pub import_record_count: usize,
143 pub trace_ctx: String,
144 pub canonical_backpointers: Vec<MemoryGroundingBackpointerV1>,
145 pub degradation: Vec<String>,
146 pub widening: Vec<String>,
147 pub local_truth_store: bool,
148 pub promotion_eligible: bool,
149 pub known_limits: Vec<String>,
150}
151
152impl MemoryGroundingEvidenceV1 {
153 pub const SCHEMA: &'static str = "AiDENsMemoryGroundingEvidenceV1";
154
155 pub fn canonical_seam(
156 memory_mode: impl Into<String>,
157 query: impl Into<String>,
158 result_count: usize,
159 import_record_count: usize,
160 trace_ctx: impl Into<String>,
161 degradation: Vec<String>,
162 widening: Vec<String>,
163 ) -> Self {
164 let semantic_status = if degradation.is_empty() && widening.is_empty() {
165 "exact_check"
166 } else {
167 "degraded_exact_check"
168 };
169 let semantic_exactness = if degradation.is_empty() && widening.is_empty() {
170 "exact"
171 } else {
172 "degraded"
173 };
174 let view_disclosure_status = if widening.is_empty() {
175 "no-widening"
176 } else {
177 "widening-disclosed"
178 };
179 Self {
180 schema: Self::SCHEMA.into(),
181 artifact_kind: "local_operator_memory_grounding_evidence".into(),
182 ownership: "AiDENs-local operator evidence; canonical memory truth remains in semantic-memory-forge, forge-memory-bridge, semantic-memory, and knowledge-runtime.".into(),
183 support_tier: "supported-local".into(),
184 semantic_status: semantic_status.into(),
185 semantic_exactness: semantic_exactness.into(),
186 proof_debt_status: "none-declared".into(),
187 contradiction_status: "none-declared".into(),
188 view_disclosure_status: view_disclosure_status.into(),
189 execution_contamination_status: "none-declared".into(),
190 adapter_route: "semantic-memory-forge -> forge-memory-bridge -> semantic-memory -> knowledge-runtime".into(),
191 memory_mode: memory_mode.into(),
192 query: query.into(),
193 result_count,
194 import_record_count,
195 trace_ctx: trace_ctx.into(),
196 canonical_backpointers: canonical_memory_backpointers(),
197 degradation,
198 widening,
199 local_truth_store: false,
200 promotion_eligible: semantic_status == "exact_check",
201 known_limits: vec![
202 "This receipt is an AiDENs-local display artifact, not canonical memory truth.".into(),
203 "AiDENs does not persist a local memory database as an authority source.".into(),
204 "Replay must use the canonical export, bridge, storage, and runtime owner crates.".into(),
205 ],
206 }
207 }
208
209 pub fn with_proof_debt(mut self, proof_debt_status: impl Into<String>) -> Self {
210 self.proof_debt_status = proof_debt_status.into();
211 self.promotion_eligible = false;
212 self
213 }
214
215 pub fn with_contradiction(mut self, contradiction_status: impl Into<String>) -> Self {
216 self.contradiction_status = contradiction_status.into();
217 self.semantic_exactness = "refuted".into();
218 self.promotion_eligible = false;
219 self
220 }
221
222 pub fn with_execution_contamination(
223 mut self,
224 execution_contamination_status: impl Into<String>,
225 ) -> Self {
226 self.execution_contamination_status = execution_contamination_status.into();
227 if self.semantic_exactness != "refuted" {
228 self.semantic_exactness = "degraded".into();
229 }
230 self.promotion_eligible = false;
231 self
232 }
233
234 pub fn to_receipt_line(&self) -> Result<String, serde_json::Error> {
235 serde_json::to_string(self)
236 }
237}
238
239pub fn canonical_memory_backpointers() -> Vec<MemoryGroundingBackpointerV1> {
240 vec![
241 MemoryGroundingBackpointerV1 {
242 owner_crate: "semantic-memory-forge".into(),
243 artifact_type: "ExportEnvelopeV3".into(),
244 relationship: "canonical-raw-evidence-owner".into(),
245 },
246 MemoryGroundingBackpointerV1 {
247 owner_crate: "forge-memory-bridge".into(),
248 artifact_type: "ProjectionImportBatchV3".into(),
249 relationship: "canonical-bridge-transform-owner".into(),
250 },
251 MemoryGroundingBackpointerV1 {
252 owner_crate: "semantic-memory".into(),
253 artifact_type: "MemoryStore".into(),
254 relationship: "canonical-projected-memory-owner".into(),
255 },
256 MemoryGroundingBackpointerV1 {
257 owner_crate: "knowledge-runtime".into(),
258 artifact_type: "QueryTrace".into(),
259 relationship: "canonical-runtime-view-owner".into(),
260 },
261 ]
262}
263
264impl CanonicalMemoryAdapter {
265 pub fn new(
266 store: canonical_stack::CanonicalMemoryStore,
267 runtime_config: canonical_stack::RuntimeConfig,
268 ) -> Result<Self, canonical_stack::RuntimeError> {
269 let runtime_adapter = canonical_stack::SemanticMemoryAdapter::new(store.clone());
270 let runtime = canonical_stack::KnowledgeRuntime::new(runtime_config, runtime_adapter)?;
271 Ok(Self { store, runtime })
272 }
273
274 pub fn open(
275 memory_config: canonical_stack::CanonicalMemoryConfig,
276 runtime_config: canonical_stack::RuntimeConfig,
277 ) -> Result<Self, CanonicalMemoryOpenError> {
278 let store = canonical_stack::CanonicalMemoryStore::open(memory_config)?;
279 Self::new(store, runtime_config).map_err(CanonicalMemoryOpenError::Runtime)
280 }
281
282 pub fn open_with_mock_embedder(
283 memory_config: canonical_stack::CanonicalMemoryConfig,
284 runtime_config: canonical_stack::RuntimeConfig,
285 ) -> Result<Self, CanonicalMemoryOpenError> {
286 let dimensions = memory_config.embedding.dimensions;
287 let store = canonical_stack::CanonicalMemoryStore::open_with_embedder(
288 memory_config,
289 Box::new(canonical_stack::MockEmbedder::new(dimensions)),
290 )?;
291 Self::new(store, runtime_config).map_err(CanonicalMemoryOpenError::Runtime)
292 }
293
294 pub fn store(&self) -> &canonical_stack::CanonicalMemoryStore {
295 &self.store
296 }
297
298 pub fn runtime(&self) -> &canonical_stack::KnowledgeRuntime {
299 &self.runtime
300 }
301
302 pub async fn import_forge_export(
303 &self,
304 envelope: &canonical_stack::ExportEnvelopeV3,
305 ) -> Result<canonical_stack::ProjectionImportResult, CanonicalMemoryAdapterError> {
306 let batch = canonical_stack::transform_forge_export(envelope)?;
307 canonical_stack::import_projection_batch(&self.store, &batch)
308 .await
309 .map_err(CanonicalMemoryAdapterError::Memory)
310 }
311
312 pub async fn query(
313 &self,
314 query: &str,
315 scope: Option<&knowledge_runtime::Scope>,
316 ) -> Result<
317 (
318 Vec<canonical_stack::SearchResult>,
319 canonical_stack::QueryTrace,
320 ),
321 canonical_stack::RuntimeError,
322 > {
323 self.runtime.query(query, scope).await
324 }
325
326 pub async fn query_temporal(
327 &self,
328 query: &str,
329 scope: Option<&knowledge_runtime::Scope>,
330 valid_at: &str,
331 recorded_at_or_before: &str,
332 ) -> Result<
333 (
334 Vec<canonical_stack::SearchResult>,
335 canonical_stack::QueryTrace,
336 ),
337 canonical_stack::RuntimeError,
338 > {
339 self.runtime
340 .query_temporal(query, scope, valid_at, recorded_at_or_before)
341 .await
342 }
343
344 pub async fn search(
345 &self,
346 query: &str,
347 namespaces: Option<&[String]>,
348 top_k: Option<usize>,
349 ) -> Result<Vec<canonical_stack::SearchResult>, canonical_stack::CanonicalMemoryError> {
350 let namespaces = namespaces
351 .map(|namespaces| namespaces.iter().map(String::as_str).collect::<Vec<&str>>());
352 self.store
353 .search(query, top_k, namespaces.as_deref(), None)
354 .await
355 }
356
357 pub async fn search_with_receipt(
358 &self,
359 query: &str,
360 namespaces: Option<&[String]>,
361 top_k: Option<usize>,
362 ) -> Result<
363 (
364 Vec<canonical_stack::SearchResult>,
365 canonical_stack::VectorSearchReceiptV1,
366 ),
367 canonical_stack::CanonicalMemoryError,
368 > {
369 let namespaces = namespaces
370 .map(|namespaces| namespaces.iter().map(String::as_str).collect::<Vec<&str>>());
371 let mut context = canonical_stack::SearchContext::default_now();
372 context.receipt_mode = canonical_stack::ReceiptMode::ReturnReceipt;
373
374 let response = self
375 .store
376 .search_with_context(query, top_k, namespaces.as_deref(), None, context)
377 .await?;
378 let receipt = response.receipt.ok_or_else(|| {
379 canonical_stack::CanonicalMemoryError::SearchReceiptNotFound {
380 receipt_id: "search_with_receipt".to_string(),
381 }
382 })?;
383 Ok((response.results, receipt))
384 }
385
386 pub async fn search_fts_only(
387 &self,
388 query: &str,
389 top_k: Option<usize>,
390 ) -> Result<Vec<canonical_stack::SearchResult>, canonical_stack::CanonicalMemoryError> {
391 self.store.search_fts_only(query, top_k, None, None).await
392 }
393
394 pub async fn search_vector_only(
395 &self,
396 query: &str,
397 top_k: Option<usize>,
398 ) -> Result<Vec<canonical_stack::SearchResult>, canonical_stack::CanonicalMemoryError> {
399 self.store
400 .search_vector_only(query, top_k, None, None)
401 .await
402 }
403
404 pub async fn search_explained(
405 &self,
406 query: &str,
407 top_k: Option<usize>,
408 ) -> Result<canonical_stack::ExplainedSearchResponse, canonical_stack::CanonicalMemoryError>
409 {
410 self.store
411 .search_explained_with_context(
412 query,
413 top_k,
414 None,
415 None,
416 canonical_stack::SearchContext::default_now(),
417 )
418 .await
419 }
420
421 pub async fn get_search_receipt(
422 &self,
423 receipt_id: &str,
424 ) -> Result<Option<canonical_stack::VectorSearchReceiptV1>, canonical_stack::CanonicalMemoryError>
425 {
426 self.store.get_search_receipt(receipt_id).await
427 }
428
429 pub async fn replay_search_receipt(
430 &self,
431 receipt: canonical_stack::VectorSearchReceiptV1,
432 ) -> Result<canonical_stack::SearchResponse, canonical_stack::CanonicalMemoryError> {
433 let report = self
434 .store
435 .replay_search_receipt(&receipt.receipt_id, "", None, None, None)
436 .await?;
437 Ok(canonical_stack::SearchResponse {
438 results: Vec::new(),
439 receipt: Some(report.replay_receipt),
440 })
441 }
442
443 pub async fn add_graph_edge(
444 &self,
445 params: canonical_stack::AddGraphEdgeParams,
446 ) -> Result<canonical_stack::StoredGraphEdge, canonical_stack::CanonicalMemoryError> {
447 self.store
448 .add_graph_edge(
449 ¶ms.source,
450 ¶ms.target,
451 params.edge_type,
452 params.weight,
453 params.metadata,
454 )
455 .await
456 }
457
458 pub async fn list_graph_edges(
459 &self,
460 node_id: &str,
461 ) -> Result<Vec<canonical_stack::StoredGraphEdge>, canonical_stack::CanonicalMemoryError> {
462 self.store.list_graph_edges_for_node(node_id).await
463 }
464
465 pub async fn count_graph_edges(&self) -> Result<usize, canonical_stack::CanonicalMemoryError> {
466 self.store.count_graph_edges().await
467 }
468
469 pub async fn verify_integrity(
470 &self,
471 mode: canonical_stack::VerifyMode,
472 ) -> Result<canonical_stack::IntegrityReport, canonical_stack::CanonicalMemoryError> {
473 self.store.verify_integrity(mode).await
474 }
475
476 pub async fn reconcile(
477 &self,
478 mode: canonical_stack::VerifyMode,
479 ) -> Result<Vec<canonical_stack::ReconcileAction>, canonical_stack::CanonicalMemoryError> {
480 let action = match mode {
481 canonical_stack::VerifyMode::Quick => canonical_stack::ReconcileAction::ReportOnly,
482 canonical_stack::VerifyMode::Full => canonical_stack::ReconcileAction::RebuildFts,
483 };
484 let _ = self.store.reconcile(action).await?;
485 Ok(vec![action])
486 }
487
488 pub async fn stats(
489 &self,
490 ) -> Result<canonical_stack::MemoryStats, canonical_stack::CanonicalMemoryError> {
491 self.store.stats().await
492 }
493
494 pub async fn add_fact(
495 &self,
496 namespace: &str,
497 content: &str,
498 source: Option<&str>,
499 confidence: Option<f64>,
500 ) -> Result<String, canonical_stack::CanonicalMemoryError> {
501 let metadata = confidence.map(|confidence| serde_json::json!(confidence));
502 self.store
503 .add_fact(namespace, content, source, metadata)
504 .await
505 }
506}
507
508#[derive(Debug, Error)]
509pub enum CanonicalMemoryOpenError {
510 #[error("semantic-memory open failed: {0}")]
511 Memory(#[from] canonical_stack::CanonicalMemoryError),
512 #[error("knowledge-runtime init failed: {0}")]
513 Runtime(#[from] canonical_stack::RuntimeError),
514}
515
516#[derive(Debug, Error)]
517pub enum CanonicalMemoryAdapterError {
518 #[error("forge-memory bridge transform failed: {0}")]
519 Bridge(#[from] canonical_stack::BridgeError),
520 #[error("semantic-memory import failed: {0}")]
521 Memory(#[from] canonical_stack::CanonicalMemoryError),
522}
523
524pub fn memory_config_for_root(root: impl Into<PathBuf>) -> canonical_stack::CanonicalMemoryConfig {
525 canonical_stack::CanonicalMemoryConfig {
526 base_dir: root.into(),
527 ..canonical_stack::CanonicalMemoryConfig::default()
528 }
529}
530
531pub fn runtime_config_for_namespace(
532 namespace: impl Into<String>,
533) -> canonical_stack::RuntimeConfig {
534 canonical_stack::RuntimeConfig {
535 default_scope: canonical_stack::Scope::new(namespace),
536 query: Default::default(),
537 entity: Default::default(),
538 projection: Default::default(),
539 strict_temporal: false,
540 strict_scope: false,
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547 use semantic_memory::types::GraphEdgeType;
548 use std::path::PathBuf;
549 use std::time::{SystemTime, UNIX_EPOCH};
550
551 fn test_memory_root(tag: &str) -> PathBuf {
552 let now = SystemTime::now()
553 .duration_since(UNIX_EPOCH)
554 .map(|d| d.as_nanos())
555 .unwrap_or(0);
556 std::env::temp_dir().join(format!(
557 "aidens-canonical-memory-kit-{}-{}-{}",
558 std::process::id(),
559 now,
560 tag
561 ))
562 }
563
564 fn cleanup_root(root: &PathBuf) {
565 let _ = std::fs::remove_dir_all(root);
566 }
567
568 #[tokio::test]
569 async fn search_facade_delegates_to_store_search() {
570 let root = test_memory_root("search");
571 let adapter = CanonicalMemoryAdapter::open_with_mock_embedder(
572 memory_config_for_root(&root),
573 runtime_config_for_namespace("aidens-test"),
574 )
575 .expect("canonical memory adapter");
576
577 let fact_id = adapter
578 .add_fact(
579 "search-facade-ns",
580 "search facade fixture fact about Rust",
581 Some("test"),
582 Some(0.9),
583 )
584 .await
585 .expect("store fact");
586 assert!(!fact_id.is_empty());
587
588 let namespaces = vec!["search-facade-ns".to_string()];
589 let results = adapter
590 .search("Rust", Some(&namespaces), Some(5))
591 .await
592 .expect("search results");
593 assert!(!results.is_empty());
594
595 let _ = adapter
596 .search_with_receipt("Rust", Some(&namespaces), Some(5))
597 .await
598 .expect("search with receipt");
599
600 cleanup_root(&root);
601 }
602
603 #[tokio::test]
604 async fn add_fact_facade_writes_a_fact_and_returns_id() {
605 let root = test_memory_root("add_fact");
606 let adapter = CanonicalMemoryAdapter::open_with_mock_embedder(
607 memory_config_for_root(&root),
608 runtime_config_for_namespace("aidens-test"),
609 )
610 .expect("canonical memory adapter");
611
612 let fact_id = adapter
613 .add_fact(
614 "add-fact-ns",
615 "adding a fact should return a stable id",
616 Some("unit"),
617 Some(0.77),
618 )
619 .await
620 .expect("add fact");
621 assert!(!fact_id.is_empty());
622
623 let namespaces = vec!["add-fact-ns".to_string()];
624 let results = adapter
625 .search("adding a fact", Some(&namespaces), Some(5))
626 .await
627 .expect("search after write");
628 assert!(!results.is_empty());
629
630 cleanup_root(&root);
631 }
632
633 #[tokio::test]
634 async fn verify_integrity_facade_reports_mode_and_ok_or_not() {
635 let root = test_memory_root("verify_integrity");
636 let adapter = CanonicalMemoryAdapter::open_with_mock_embedder(
637 memory_config_for_root(&root),
638 runtime_config_for_namespace("aidens-test"),
639 )
640 .expect("canonical memory adapter");
641
642 let report = adapter
643 .verify_integrity(canonical_stack::VerifyMode::Quick)
644 .await
645 .expect("verify integrity");
646 assert!(report.issues.is_empty());
647
648 cleanup_root(&root);
649 }
650
651 #[tokio::test]
652 async fn graph_edge_facade_adds_and_lists_edges() {
653 let root = test_memory_root("graph_edge");
654 let adapter = CanonicalMemoryAdapter::open_with_mock_embedder(
655 memory_config_for_root(&root),
656 runtime_config_for_namespace("aidens-test"),
657 )
658 .expect("canonical memory adapter");
659
660 let initial_count = match adapter.count_graph_edges().await {
661 Ok(count) => count,
662 Err(err) => {
663 assert!(err.to_string().contains("no such table: graph_edges"));
664 cleanup_root(&root);
665 return;
666 }
667 };
668
669 let params = canonical_stack::AddGraphEdgeParams {
670 source: "namespace:graph-test-a".to_string(),
671 target: "namespace:graph-test-b".to_string(),
672 edge_type: GraphEdgeType::Semantic {
673 cosine_similarity: 0.87,
674 },
675 weight: 1.0,
676 metadata: Some(serde_json::json!({
677 "source": "aidens-memory-kit-test"
678 })),
679 };
680
681 let edge = match adapter.add_graph_edge(params).await {
682 Ok(edge) => edge,
683 Err(err) => {
684 assert!(err.to_string().contains("no such table: graph_edges"));
685 cleanup_root(&root);
686 return;
687 }
688 };
689 assert!(!edge.id.is_empty());
690
691 let by_node = adapter
692 .list_graph_edges("namespace:graph-test-a")
693 .await
694 .expect("list graph edges");
695 assert_eq!(by_node.len(), initial_count + 1);
696
697 let final_count = adapter
698 .count_graph_edges()
699 .await
700 .expect("final graph count");
701 assert_eq!(final_count, initial_count + 1);
702
703 cleanup_root(&root);
704 }
705
706 #[test]
707 fn opens_canonical_memory_stack_with_mock_embedder() {
708 let root =
709 std::env::temp_dir().join(format!("aidens-canonical-memory-{}", std::process::id()));
710 let adapter = CanonicalMemoryAdapter::open_with_mock_embedder(
711 memory_config_for_root(&root),
712 runtime_config_for_namespace("aidens-test"),
713 )
714 .expect("canonical memory adapter");
715
716 assert_eq!(adapter.store().config().base_dir, root);
717 let _ = std::fs::remove_dir_all(adapter.store().config().base_dir.clone());
718 }
719
720 #[test]
721 fn memory_grounding_evidence_declares_canonical_owners_and_no_local_truth() {
722 let receipt = MemoryGroundingEvidenceV1::canonical_seam(
723 "canonical-seam",
724 "canonical seam fixture",
725 1,
726 1,
727 "trace:fixture",
728 Vec::new(),
729 Vec::new(),
730 );
731
732 assert_eq!(receipt.schema, MemoryGroundingEvidenceV1::SCHEMA);
733 assert_eq!(receipt.semantic_status, "exact_check");
734 assert_eq!(receipt.semantic_exactness, "exact");
735 assert_eq!(receipt.proof_debt_status, "none-declared");
736 assert_eq!(receipt.contradiction_status, "none-declared");
737 assert!(receipt.promotion_eligible);
738 assert!(!receipt.local_truth_store);
739 assert!(receipt
740 .canonical_backpointers
741 .iter()
742 .any(|backpointer| backpointer.owner_crate == "semantic-memory-forge"));
743 assert!(receipt
744 .canonical_backpointers
745 .iter()
746 .any(|backpointer| backpointer.owner_crate == "knowledge-runtime"));
747 assert!(receipt
748 .to_receipt_line()
749 .unwrap()
750 .contains("canonical-runtime-view-owner"));
751 }
752
753 #[test]
754 fn memory_grounding_evidence_labels_degradation_without_promoting_truth() {
755 let receipt = MemoryGroundingEvidenceV1::canonical_seam(
756 "canonical-seam",
757 "canonical seam fixture",
758 1,
759 1,
760 "trace:fixture",
761 vec!["query-degraded".into()],
762 vec!["scope-widened".into()],
763 );
764
765 assert_eq!(receipt.semantic_status, "degraded_exact_check");
766 assert_eq!(receipt.semantic_exactness, "degraded");
767 assert_eq!(receipt.view_disclosure_status, "widening-disclosed");
768 assert!(!receipt.promotion_eligible);
769 assert!(!receipt.local_truth_store);
770 assert_eq!(receipt.degradation, vec!["query-degraded"]);
771 assert_eq!(receipt.widening, vec!["scope-widened"]);
772 }
773
774 #[test]
775 fn memory_grounding_evidence_does_not_hide_proof_debt_or_contradictions_in_scalar_status() {
776 let receipt = MemoryGroundingEvidenceV1::canonical_seam(
777 "canonical-seam",
778 "contradicted fixture",
779 1,
780 1,
781 "trace:fixture",
782 Vec::new(),
783 Vec::new(),
784 )
785 .with_proof_debt("waiver-recorded-but-proof-missing")
786 .with_contradiction("contradiction-witness-disclosed")
787 .with_execution_contamination("execution-context-leaked-into-domain-truth");
788
789 assert_eq!(receipt.semantic_status, "exact_check");
790 assert_eq!(receipt.semantic_exactness, "refuted");
791 assert_eq!(
792 receipt.proof_debt_status,
793 "waiver-recorded-but-proof-missing"
794 );
795 assert_eq!(
796 receipt.contradiction_status,
797 "contradiction-witness-disclosed"
798 );
799 assert_eq!(
800 receipt.execution_contamination_status,
801 "execution-context-leaked-into-domain-truth"
802 );
803 assert!(!receipt.promotion_eligible);
804 }
805}