codemem_core/traits.rs
1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use crate::{
5 CodememError, Edge, GraphNode, MemoryNode, NodeKind, RelationshipType, Repository, Session,
6 UnresolvedRefData,
7};
8
9// ── Data types for trait return values ───────────────────────────────────────
10
11/// A pending unresolved reference stored for deferred cross-namespace linking.
12#[derive(Debug, Clone)]
13pub struct PendingUnresolvedRef {
14 /// ID of the unresolved ref record.
15 pub id: String,
16 /// Source symbol qualified name.
17 pub source_node: String,
18 /// The unresolved target name.
19 pub target_name: String,
20 /// Namespace the source belongs to.
21 pub namespace: String,
22 /// File path where the reference occurs.
23 pub file_path: String,
24 /// Line number.
25 pub line: usize,
26 /// Kind of reference: "call", "import", "inherits", etc.
27 pub ref_kind: String,
28 /// Package hint extracted from import context.
29 pub package_hint: Option<String>,
30}
31
32// ── Traits ──────────────────────────────────────────────────────────────────
33
34/// Vector backend trait for HNSW index operations.
35pub trait VectorBackend: Send + Sync {
36 /// Insert a vector with associated ID.
37 fn insert(&mut self, id: &str, embedding: &[f32]) -> Result<(), CodememError>;
38
39 /// Batch insert vectors.
40 fn insert_batch(&mut self, items: &[(String, Vec<f32>)]) -> Result<(), CodememError>;
41
42 /// Search for k nearest neighbors. Returns (id, distance) pairs.
43 fn search(&self, query: &[f32], k: usize) -> Result<Vec<(String, f32)>, CodememError>;
44
45 /// Remove a vector by ID.
46 fn remove(&mut self, id: &str) -> Result<bool, CodememError>;
47
48 /// Save the index to disk.
49 fn save(&self, path: &std::path::Path) -> Result<(), CodememError>;
50
51 /// Load the index from disk.
52 fn load(&mut self, path: &std::path::Path) -> Result<(), CodememError>;
53
54 /// Get index statistics.
55 fn stats(&self) -> VectorStats;
56}
57
58/// Statistics about the vector index.
59#[derive(Debug, Clone, Default, Serialize, Deserialize)]
60pub struct VectorStats {
61 pub count: usize,
62 pub dimensions: usize,
63 pub metric: String,
64 pub memory_bytes: usize,
65}
66
67/// Graph backend trait for graph operations.
68pub trait GraphBackend: Send + Sync {
69 /// Add a node to the graph.
70 fn add_node(&mut self, node: GraphNode) -> Result<(), CodememError>;
71
72 /// Get a node by ID.
73 fn get_node(&self, id: &str) -> Result<Option<GraphNode>, CodememError>;
74
75 /// Remove a node by ID.
76 fn remove_node(&mut self, id: &str) -> Result<bool, CodememError>;
77
78 /// Add an edge between two nodes.
79 fn add_edge(&mut self, edge: Edge) -> Result<(), CodememError>;
80
81 /// Get edges from a node.
82 fn get_edges(&self, node_id: &str) -> Result<Vec<Edge>, CodememError>;
83
84 /// Remove an edge by ID.
85 fn remove_edge(&mut self, id: &str) -> Result<bool, CodememError>;
86
87 /// BFS traversal from a start node up to max_depth.
88 fn bfs(&self, start_id: &str, max_depth: usize) -> Result<Vec<GraphNode>, CodememError>;
89
90 /// DFS traversal from a start node up to max_depth.
91 fn dfs(&self, start_id: &str, max_depth: usize) -> Result<Vec<GraphNode>, CodememError>;
92
93 /// BFS traversal with filtering: exclude certain node kinds and optionally
94 /// restrict to specific relationship types.
95 fn bfs_filtered(
96 &self,
97 start_id: &str,
98 max_depth: usize,
99 exclude_kinds: &[NodeKind],
100 include_relationships: Option<&[RelationshipType]>,
101 ) -> Result<Vec<GraphNode>, CodememError>;
102
103 /// DFS traversal with filtering: exclude certain node kinds and optionally
104 /// restrict to specific relationship types.
105 fn dfs_filtered(
106 &self,
107 start_id: &str,
108 max_depth: usize,
109 exclude_kinds: &[NodeKind],
110 include_relationships: Option<&[RelationshipType]>,
111 ) -> Result<Vec<GraphNode>, CodememError>;
112
113 /// Shortest path between two nodes.
114 fn shortest_path(&self, from: &str, to: &str) -> Result<Vec<String>, CodememError>;
115
116 /// Get graph statistics.
117 fn stats(&self) -> GraphStats;
118}
119
120/// Statistics about the graph.
121#[derive(Debug, Clone, Default, Serialize, Deserialize)]
122pub struct GraphStats {
123 pub node_count: usize,
124 pub edge_count: usize,
125 pub node_kind_counts: HashMap<String, usize>,
126 pub relationship_type_counts: HashMap<String, usize>,
127}
128
129// ── Embedding Provider Trait ────────────────────────────────────────────────
130
131/// Trait for pluggable embedding providers.
132pub trait EmbeddingProvider: Send + Sync {
133 /// Embedding vector dimensions.
134 fn dimensions(&self) -> usize;
135
136 /// Embed a single text string.
137 fn embed(&self, text: &str) -> Result<Vec<f32>, crate::CodememError>;
138
139 /// Embed a batch of texts (default: sequential).
140 fn embed_batch(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>, crate::CodememError> {
141 texts.iter().map(|t| self.embed(t)).collect()
142 }
143
144 /// Provider name for display.
145 fn name(&self) -> &str;
146
147 /// Cache statistics: (current_size, capacity). Returns (0, 0) if no cache.
148 fn cache_stats(&self) -> (usize, usize) {
149 (0, 0)
150 }
151}
152
153// ── Storage Stats & Consolidation Types ─────────────────────────────────
154
155/// Database statistics.
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct StorageStats {
158 pub memory_count: usize,
159 pub embedding_count: usize,
160 pub node_count: usize,
161 pub edge_count: usize,
162}
163
164/// A single consolidation log entry.
165#[derive(Debug, Clone)]
166pub struct ConsolidationLogEntry {
167 pub cycle_type: String,
168 pub run_at: i64,
169 pub affected_count: usize,
170}
171
172// ── Storage Backend Trait ───────────────────────────────────────────────
173
174/// Pluggable storage backend trait for all persistence operations.
175///
176/// This trait unifies every persistence concern behind a single interface so
177/// that the engine layer (`CodememEngine`) remains backend-agnostic.
178///
179/// # Method groups
180///
181/// | Group | Methods | Purpose |
182/// |-------|---------|---------|
183/// | **Memory CRUD** | `insert_memory`, `get_memory`, `update_memory`, `delete_memory`, `list_memory_ids`, … | Create, read, update, delete memory nodes |
184/// | **Embedding persistence** | `store_embedding`, `get_embedding`, `delete_embedding`, `list_all_embeddings` | Persist and retrieve embedding vectors |
185/// | **Graph node/edge storage** | `insert_graph_node`, `get_graph_node`, `all_graph_nodes`, `insert_graph_edge`, … | Persist the knowledge graph structure |
186/// | **Sessions** | `start_session`, `end_session`, `list_sessions`, `session_count` | Track interaction sessions |
187/// | **Consolidation** | `insert_consolidation_log`, `last_consolidation_runs` | Record and query memory consolidation runs |
188/// | **Pattern detection** | `get_repeated_searches`, `get_file_hotspots`, `get_tool_usage_stats`, `get_decision_chains` | Cross-session pattern queries |
189/// | **Bulk/batch operations** | `insert_memories_batch`, `store_embeddings_batch`, `insert_graph_nodes_batch`, `insert_graph_edges_batch` | Efficient multi-row inserts |
190/// | **Decay & forgetting** | `decay_stale_memories`, `find_forgettable`, `get_stale_memories_for_decay`, `batch_update_importance` | Power-law decay and garbage collection |
191/// | **Query helpers** | `find_unembedded_memories`, `search_graph_nodes`, `list_memories_filtered`, `find_hash_duplicates` | Filtered searches and dedup |
192/// | **File hash tracking** | `load_file_hashes`, `save_file_hashes` | Incremental indexing support |
193/// | **Session activity** | `record_session_activity`, `get_session_activity_summary`, `get_session_hot_directories`, … | Fine-grained activity tracking |
194/// | **Stats** | `stats` | Database-level statistics |
195///
196/// Implementations include SQLite (default) and can be extended for
197/// SurrealDB, FalkorDB, or other backends.
198pub trait StorageBackend: Send + Sync {
199 // ── Memory CRUD ─────────────────────────────────────────────────
200
201 /// Insert a new memory. Returns Err(Duplicate) if content hash already exists.
202 fn insert_memory(&self, memory: &MemoryNode) -> Result<(), CodememError>;
203
204 /// Get a memory by ID. Updates access_count and last_accessed_at.
205 fn get_memory(&self, id: &str) -> Result<Option<MemoryNode>, CodememError>;
206
207 /// Get a memory by ID without updating access_count or last_accessed_at.
208 /// Use this for internal/system reads (consolidation checks, stats, batch processing).
209 fn get_memory_no_touch(&self, id: &str) -> Result<Option<MemoryNode>, CodememError> {
210 // Default: falls back to get_memory for backwards compatibility.
211 self.get_memory(id)
212 }
213
214 /// Get multiple memories by IDs in a single batch operation.
215 fn get_memories_batch(&self, ids: &[&str]) -> Result<Vec<MemoryNode>, CodememError>;
216
217 /// Update a memory's content and optionally its importance. Re-computes content hash.
218 fn update_memory(
219 &self,
220 id: &str,
221 content: &str,
222 importance: Option<f64>,
223 ) -> Result<(), CodememError>;
224
225 /// Delete a memory by ID. Returns true if a row was deleted.
226 fn delete_memory(&self, id: &str) -> Result<bool, CodememError>;
227
228 /// Delete a memory and all related data (graph nodes/edges, embeddings) atomically.
229 /// Returns true if the memory existed and was deleted.
230 /// Default falls back to individual deletes (non-transactional) for backwards compatibility.
231 fn delete_memory_cascade(&self, id: &str) -> Result<bool, CodememError> {
232 let deleted = self.delete_memory(id)?;
233 if deleted {
234 let _ = self.delete_graph_edges_for_node(id);
235 let _ = self.delete_graph_node(id);
236 let _ = self.delete_embedding(id);
237 }
238 Ok(deleted)
239 }
240
241 /// Delete multiple memories and all related data (graph nodes/edges, embeddings) atomically.
242 /// Returns the number of memories that were actually deleted.
243 /// Default falls back to calling `delete_memory_cascade` per ID for backwards compatibility.
244 fn delete_memories_batch_cascade(&self, ids: &[&str]) -> Result<usize, CodememError> {
245 let mut count = 0;
246 for id in ids {
247 if self.delete_memory_cascade(id)? {
248 count += 1;
249 }
250 }
251 Ok(count)
252 }
253
254 /// List all memory IDs, ordered by created_at descending.
255 fn list_memory_ids(&self) -> Result<Vec<String>, CodememError>;
256
257 /// List memory IDs scoped to a specific namespace.
258 fn list_memory_ids_for_namespace(&self, namespace: &str) -> Result<Vec<String>, CodememError>;
259
260 /// Find memory IDs whose tags contain the given tag value.
261 /// Optionally scoped to a namespace. Excludes `exclude_id`.
262 fn find_memory_ids_by_tag(
263 &self,
264 tag: &str,
265 namespace: Option<&str>,
266 exclude_id: &str,
267 ) -> Result<Vec<String>, CodememError>;
268
269 /// List all distinct namespaces.
270 fn list_namespaces(&self) -> Result<Vec<String>, CodememError>;
271
272 /// Get total memory count.
273 fn memory_count(&self) -> Result<usize, CodememError>;
274
275 // ── Embedding Persistence ───────────────────────────────────────
276
277 /// Store an embedding vector for a memory.
278 fn store_embedding(&self, memory_id: &str, embedding: &[f32]) -> Result<(), CodememError>;
279
280 /// Get an embedding by memory ID.
281 fn get_embedding(&self, memory_id: &str) -> Result<Option<Vec<f32>>, CodememError>;
282
283 /// Delete an embedding by memory ID. Returns true if a row was deleted.
284 fn delete_embedding(&self, memory_id: &str) -> Result<bool, CodememError>;
285
286 /// List all stored embeddings as (memory_id, embedding_vector) pairs.
287 fn list_all_embeddings(&self) -> Result<Vec<(String, Vec<f32>)>, CodememError>;
288
289 // ── Graph Node/Edge Persistence ─────────────────────────────────
290
291 /// Insert or replace a graph node.
292 fn insert_graph_node(&self, node: &GraphNode) -> Result<(), CodememError>;
293
294 /// Get a graph node by ID.
295 fn get_graph_node(&self, id: &str) -> Result<Option<GraphNode>, CodememError>;
296
297 /// Delete a graph node by ID. Returns true if a row was deleted.
298 fn delete_graph_node(&self, id: &str) -> Result<bool, CodememError>;
299
300 /// Get all graph nodes.
301 fn all_graph_nodes(&self) -> Result<Vec<GraphNode>, CodememError>;
302
303 /// Insert or replace a graph edge.
304 fn insert_graph_edge(&self, edge: &Edge) -> Result<(), CodememError>;
305
306 /// Get all edges from or to a node.
307 fn get_edges_for_node(&self, node_id: &str) -> Result<Vec<Edge>, CodememError>;
308
309 /// Get all graph edges.
310 fn all_graph_edges(&self) -> Result<Vec<Edge>, CodememError>;
311
312 /// Delete a single graph edge by ID. Returns true if a row was deleted.
313 fn delete_graph_edge(&self, edge_id: &str) -> Result<bool, CodememError> {
314 // Default: fall back to querying all edges and deleting via for_node.
315 // Backends should override with a direct DELETE WHERE id = ?1.
316 let _ = edge_id;
317 Ok(false)
318 }
319
320 /// Delete all graph edges connected to a node. Returns count deleted.
321 fn delete_graph_edges_for_node(&self, node_id: &str) -> Result<usize, CodememError>;
322
323 /// Delete all graph nodes, edges, and embeddings whose node ID starts with the given prefix.
324 /// Returns count of nodes deleted.
325 fn delete_graph_nodes_by_prefix(&self, prefix: &str) -> Result<usize, CodememError>;
326
327 // ── Sessions ────────────────────────────────────────────────────
328
329 /// Start a new session.
330 fn start_session(&self, id: &str, namespace: Option<&str>) -> Result<(), CodememError>;
331
332 /// End a session with optional summary.
333 fn end_session(&self, id: &str, summary: Option<&str>) -> Result<(), CodememError>;
334
335 /// List sessions, optionally filtered by namespace, up to limit.
336 fn list_sessions(
337 &self,
338 namespace: Option<&str>,
339 limit: usize,
340 ) -> Result<Vec<Session>, CodememError>;
341
342 // ── Consolidation ───────────────────────────────────────────────
343
344 /// Record a consolidation run.
345 fn insert_consolidation_log(
346 &self,
347 cycle_type: &str,
348 affected_count: usize,
349 ) -> Result<(), CodememError>;
350
351 /// Get the last consolidation run for each cycle type.
352 fn last_consolidation_runs(&self) -> Result<Vec<ConsolidationLogEntry>, CodememError>;
353
354 // ── Pattern Detection Queries ───────────────────────────────────
355
356 /// Find repeated search patterns. Returns (pattern, count, memory_ids).
357 fn get_repeated_searches(
358 &self,
359 min_count: usize,
360 namespace: Option<&str>,
361 ) -> Result<Vec<(String, usize, Vec<String>)>, CodememError>;
362
363 /// Find file hotspots. Returns (file_path, count, memory_ids).
364 fn get_file_hotspots(
365 &self,
366 min_count: usize,
367 namespace: Option<&str>,
368 ) -> Result<Vec<(String, usize, Vec<String>)>, CodememError>;
369
370 /// Get tool usage statistics. Returns (tool_name, count) pairs.
371 fn get_tool_usage_stats(
372 &self,
373 namespace: Option<&str>,
374 ) -> Result<Vec<(String, usize)>, CodememError>;
375
376 /// Find decision chains. Returns (file_path, count, memory_ids).
377 fn get_decision_chains(
378 &self,
379 min_count: usize,
380 namespace: Option<&str>,
381 ) -> Result<Vec<(String, usize, Vec<String>)>, CodememError>;
382
383 // ── Bulk Operations ─────────────────────────────────────────────
384
385 /// Decay importance of stale memories older than threshold_ts by decay_factor.
386 /// Returns count of affected memories.
387 fn decay_stale_memories(
388 &self,
389 threshold_ts: i64,
390 decay_factor: f64,
391 ) -> Result<usize, CodememError>;
392
393 /// List memories for creative consolidation: (id, memory_type, tags).
394 fn list_memories_for_creative(
395 &self,
396 ) -> Result<Vec<(String, String, Vec<String>)>, CodememError>;
397
398 /// Find near-duplicate memories by content hash prefix matching.
399 /// Returns (id1, id2, similarity) pairs. Only catches exact content matches
400 /// (hash prefix), not semantic near-duplicates.
401 fn find_hash_duplicates(&self) -> Result<Vec<(String, String, f64)>, CodememError>;
402
403 /// Find memories eligible for forgetting (low importance).
404 /// Returns list of memory IDs.
405 fn find_forgettable(&self, importance_threshold: f64) -> Result<Vec<String>, CodememError>;
406
407 // ── Batch Operations ────────────────────────────────────────────
408
409 /// Insert multiple memories in a single batch. Default impl calls insert_memory in a loop.
410 fn insert_memories_batch(&self, memories: &[MemoryNode]) -> Result<(), CodememError> {
411 for memory in memories {
412 self.insert_memory(memory)?;
413 }
414 Ok(())
415 }
416
417 /// Store multiple embeddings in a single batch. Default impl calls store_embedding in a loop.
418 fn store_embeddings_batch(&self, items: &[(&str, &[f32])]) -> Result<(), CodememError> {
419 for (id, embedding) in items {
420 self.store_embedding(id, embedding)?;
421 }
422 Ok(())
423 }
424
425 /// Insert multiple graph nodes in a single batch. Default impl calls insert_graph_node in a loop.
426 fn insert_graph_nodes_batch(&self, nodes: &[GraphNode]) -> Result<(), CodememError> {
427 for node in nodes {
428 self.insert_graph_node(node)?;
429 }
430 Ok(())
431 }
432
433 /// Insert multiple graph edges in a single batch. Default impl calls insert_graph_edge in a loop.
434 fn insert_graph_edges_batch(&self, edges: &[Edge]) -> Result<(), CodememError> {
435 for edge in edges {
436 self.insert_graph_edge(edge)?;
437 }
438 Ok(())
439 }
440
441 // ── Query Helpers ───────────────────────────────────────────────
442
443 /// Find memories that have no embeddings yet. Returns (id, content) pairs.
444 fn find_unembedded_memories(&self) -> Result<Vec<(String, String)>, CodememError>;
445
446 /// Search graph nodes by label (case-insensitive LIKE). Returns matching nodes
447 /// sorted by centrality descending, limited to `limit` results.
448 fn search_graph_nodes(
449 &self,
450 query: &str,
451 namespace: Option<&str>,
452 limit: usize,
453 ) -> Result<Vec<GraphNode>, CodememError>;
454
455 /// List memories matching a specific tag, with optional namespace filter.
456 fn list_memories_by_tag(
457 &self,
458 tag: &str,
459 namespace: Option<&str>,
460 limit: usize,
461 ) -> Result<Vec<MemoryNode>, CodememError>;
462
463 /// List memories with optional namespace and memory_type filters.
464 fn list_memories_filtered(
465 &self,
466 namespace: Option<&str>,
467 memory_type: Option<&str>,
468 ) -> Result<Vec<MemoryNode>, CodememError>;
469
470 /// Fetch stale memories with access metadata for power-law decay.
471 /// Returns (id, importance, access_count, last_accessed_at).
472 fn get_stale_memories_for_decay(
473 &self,
474 threshold_ts: i64,
475 ) -> Result<Vec<(String, f64, u32, i64)>, CodememError>;
476
477 /// Batch-update importance values. Returns count of updated rows.
478 fn batch_update_importance(&self, updates: &[(String, f64)]) -> Result<usize, CodememError>;
479
480 /// Total session count, optionally filtered by namespace.
481 fn session_count(&self, namespace: Option<&str>) -> Result<usize, CodememError>;
482
483 // ── File Hash Tracking ──────────────────────────────────────────
484
485 /// Load file hashes for incremental indexing, scoped to a namespace.
486 /// Returns path -> hash map.
487 fn load_file_hashes(&self, namespace: &str) -> Result<HashMap<String, String>, CodememError>;
488
489 /// Save file hashes for incremental indexing, scoped to a namespace.
490 fn save_file_hashes(
491 &self,
492 namespace: &str,
493 hashes: &HashMap<String, String>,
494 ) -> Result<(), CodememError>;
495
496 // ── Session Activity Tracking ─────────────────────────────────
497
498 /// Record a session activity event (tool use with context).
499 fn record_session_activity(
500 &self,
501 session_id: &str,
502 tool_name: &str,
503 file_path: Option<&str>,
504 directory: Option<&str>,
505 pattern: Option<&str>,
506 ) -> Result<(), CodememError>;
507
508 /// Get a summary of session activity counts.
509 fn get_session_activity_summary(
510 &self,
511 session_id: &str,
512 ) -> Result<crate::SessionActivitySummary, CodememError>;
513
514 /// Get the most active directories in a session. Returns (directory, count) pairs.
515 fn get_session_hot_directories(
516 &self,
517 session_id: &str,
518 limit: usize,
519 ) -> Result<Vec<(String, usize)>, CodememError>;
520
521 /// Check whether a particular auto-insight dedup tag already exists for a session.
522 fn has_auto_insight(&self, session_id: &str, dedup_tag: &str) -> Result<bool, CodememError>;
523
524 /// Count how many Read events occurred in a directory during a session.
525 fn count_directory_reads(
526 &self,
527 session_id: &str,
528 directory: &str,
529 ) -> Result<usize, CodememError>;
530
531 /// Check if a file was read in the current session.
532 fn was_file_read_in_session(
533 &self,
534 session_id: &str,
535 file_path: &str,
536 ) -> Result<bool, CodememError>;
537
538 /// Count how many times a search pattern was used in a session.
539 fn count_search_pattern_in_session(
540 &self,
541 session_id: &str,
542 pattern: &str,
543 ) -> Result<usize, CodememError>;
544
545 // ── Repository Management ────────────────────────────────────────
546
547 /// List all registered repositories.
548 fn list_repos(&self) -> Result<Vec<Repository>, CodememError>;
549
550 /// Add a new repository.
551 fn add_repo(&self, repo: &Repository) -> Result<(), CodememError>;
552
553 /// Get a repository by ID.
554 fn get_repo(&self, id: &str) -> Result<Option<Repository>, CodememError>;
555
556 /// Remove a repository by ID. Returns true if it existed.
557 fn remove_repo(&self, id: &str) -> Result<bool, CodememError>;
558
559 /// Update a repository's status and optionally its last-indexed timestamp.
560 fn update_repo_status(
561 &self,
562 id: &str,
563 status: &str,
564 indexed_at: Option<&str>,
565 ) -> Result<(), CodememError>;
566
567 // ── Stats ───────────────────────────────────────────────────────
568
569 /// Get database statistics.
570 fn stats(&self) -> Result<StorageStats, CodememError>;
571
572 // ── Transaction Control ────────────────────────────────────────
573
574 /// Begin an explicit transaction.
575 ///
576 /// While a transaction is active, individual storage methods (e.g.
577 /// `insert_memory`, `insert_graph_node`) participate in it instead of
578 /// starting their own. Call `commit_transaction` to persist or
579 /// `rollback_transaction` to discard.
580 ///
581 /// Default implementation is a no-op for backends that don't support
582 /// explicit transaction control.
583 fn begin_transaction(&self) -> Result<(), CodememError> {
584 Ok(())
585 }
586
587 /// Commit the active transaction started by `begin_transaction`.
588 ///
589 /// Default implementation is a no-op.
590 fn commit_transaction(&self) -> Result<(), CodememError> {
591 Ok(())
592 }
593
594 /// Roll back the active transaction started by `begin_transaction`.
595 ///
596 /// Default implementation is a no-op.
597 fn rollback_transaction(&self) -> Result<(), CodememError> {
598 Ok(())
599 }
600
601 // ── Cross-Repo Persistence ────────────────────────────────────────
602
603 /// Get graph edges where either the source or destination node belongs to the
604 /// given namespace (cross-namespace query). When `include_cross_namespace` is false,
605 /// this behaves like `graph_edges_for_namespace` (both endpoints in namespace).
606 fn graph_edges_for_namespace_with_cross(
607 &self,
608 _namespace: &str,
609 _include_cross_namespace: bool,
610 ) -> Result<Vec<Edge>, CodememError> {
611 Ok(Vec::new())
612 }
613
614 /// Upsert a package into the cross-repo package registry.
615 fn upsert_package_registry(
616 &self,
617 _package_name: &str,
618 _namespace: &str,
619 _version: &str,
620 _manifest: &str,
621 ) -> Result<(), CodememError> {
622 Ok(())
623 }
624
625 /// Store an unresolved reference for future cross-namespace linking.
626 #[allow(clippy::too_many_arguments)]
627 fn store_unresolved_ref(
628 &self,
629 _source_qualified_name: &str,
630 _target_name: &str,
631 _source_namespace: &str,
632 _file_path: &str,
633 _line: usize,
634 _ref_kind: &str,
635 _package_hint: Option<&str>,
636 ) -> Result<(), CodememError> {
637 Ok(())
638 }
639
640 /// Batch store unresolved references. Default falls back to per-ref calls.
641 fn store_unresolved_refs_batch(
642 &self,
643 refs: &[UnresolvedRefData],
644 ) -> Result<usize, CodememError> {
645 let mut count = 0;
646 for r in refs {
647 self.store_unresolved_ref(
648 &r.source_qualified_name,
649 &r.target_name,
650 &r.namespace,
651 &r.file_path,
652 r.line,
653 &r.ref_kind,
654 r.package_hint.as_deref(),
655 )?;
656 count += 1;
657 }
658 Ok(count)
659 }
660
661 /// List all registered packages. Returns (name, namespace, manifest_path) tuples.
662 fn list_registered_packages(&self) -> Result<Vec<(String, String, String)>, CodememError> {
663 Ok(Vec::new())
664 }
665
666 /// List all pending unresolved refs with full context.
667 fn list_pending_unresolved_refs(&self) -> Result<Vec<PendingUnresolvedRef>, CodememError> {
668 Ok(Vec::new())
669 }
670
671 /// Delete a resolved unresolved ref by ID.
672 fn delete_unresolved_ref(&self, _id: &str) -> Result<(), CodememError> {
673 Ok(())
674 }
675
676 /// Count unresolved refs for a given namespace.
677 fn count_unresolved_refs(&self, _namespace: &str) -> Result<usize, CodememError> {
678 Ok(0)
679 }
680
681 /// List registered packages for a given namespace.
682 /// Returns (name, namespace, manifest_path) tuples.
683 fn list_registered_packages_for_namespace(
684 &self,
685 _namespace: &str,
686 ) -> Result<Vec<(String, String, String)>, CodememError> {
687 Ok(Vec::new())
688 }
689
690 /// Store a detected API endpoint.
691 fn store_api_endpoint(
692 &self,
693 _method: &str,
694 _path: &str,
695 _handler_symbol: &str,
696 _namespace: &str,
697 ) -> Result<(), CodememError> {
698 Ok(())
699 }
700
701 /// Store a detected client call.
702 fn store_api_client_call(
703 &self,
704 _library: &str,
705 _method: Option<&str>,
706 _caller_symbol: &str,
707 _namespace: &str,
708 ) -> Result<(), CodememError> {
709 Ok(())
710 }
711
712 /// List detected API endpoints for a namespace.
713 /// Returns (method, path, handler_symbol, namespace) tuples.
714 fn list_api_endpoints(
715 &self,
716 _namespace: &str,
717 ) -> Result<Vec<(String, String, String, String)>, CodememError> {
718 Ok(Vec::new())
719 }
720}