diff --git a/poc-memory/src/main.rs b/poc-memory/src/main.rs index 7fe2e30..4e82b06 100644 --- a/poc-memory/src/main.rs +++ b/poc-memory/src/main.rs @@ -844,6 +844,41 @@ fn install_default_file(data_dir: &std::path::Path, name: &str, content: &str) - fn cmd_fsck() -> Result<(), String> { let mut store = store::Store::load()?; + // Check cache vs log consistency + let log_store = store::Store::load_from_logs()?; + let mut cache_issues = 0; + + // Nodes in logs but missing from cache + for key in log_store.nodes.keys() { + if !store.nodes.contains_key(key) { + eprintln!("CACHE MISSING: '{}' exists in capnp log but not in cache", key); + cache_issues += 1; + } + } + // Nodes in cache but not in logs (phantom nodes) + for key in store.nodes.keys() { + if !log_store.nodes.contains_key(key) { + eprintln!("CACHE PHANTOM: '{}' exists in cache but not in capnp log", key); + cache_issues += 1; + } + } + // Version mismatches + for (key, log_node) in &log_store.nodes { + if let Some(cache_node) = store.nodes.get(key) { + if cache_node.version != log_node.version { + eprintln!("CACHE STALE: '{}' cache v{} vs log v{}", + key, cache_node.version, log_node.version); + cache_issues += 1; + } + } + } + + if cache_issues > 0 { + eprintln!("{} cache inconsistencies found — rebuilding from logs", cache_issues); + store = log_store; + store.save().map_err(|e| format!("rebuild save: {}", e))?; + } + // Check node-key consistency let mut issues = 0; for (key, node) in &store.nodes { @@ -893,8 +928,8 @@ fn cmd_fsck() -> Result<(), String> { } let g = store.build_graph(); - println!("fsck: {} nodes, {} edges, {} issues, {} dangling", - store.nodes.len(), g.edge_count(), issues, dangling); + println!("fsck: {} nodes, {} edges, {} issues, {} dangling, {} cache", + store.nodes.len(), g.edge_count(), issues, dangling, cache_issues); Ok(()) } diff --git a/poc-memory/src/store/persist.rs b/poc-memory/src/store/persist.rs index a55ec4f..ca608c7 100644 --- a/poc-memory/src/store/persist.rs +++ b/poc-memory/src/store/persist.rs @@ -102,6 +102,26 @@ impl Store { Ok(store) } + /// Load store directly from capnp logs, bypassing all caches. + /// Used by fsck to verify cache consistency. + pub fn load_from_logs() -> Result { + let nodes_p = nodes_path(); + let rels_p = relations_path(); + + let mut store = Store::default(); + if nodes_p.exists() { + store.replay_nodes(&nodes_p)?; + } + if rels_p.exists() { + store.replay_relations(&rels_p)?; + } + let visits_p = visits_path(); + if visits_p.exists() { + store.replay_visits(&visits_p)?; + } + Ok(store) + } + /// Replay node log, keeping latest version per UUID. /// Tracks all UUIDs seen per key to detect duplicates. fn replay_nodes(&mut self, path: &Path) -> Result<(), String> {