Convert store and CLI to anyhow::Result for cleaner error handling

Replace Result<_, String> with anyhow::Result throughout:
- hippocampus/store module (persist, ops, types, view, mod)
- CLI modules (admin, agent, graph, journal, node)
- Run trait in main.rs

Use .context() and .with_context() instead of .map_err(|e| format!(...))
patterns. Add bail!() for early error returns.

Add access_local() helper in hippocampus/mod.rs that returns
Result<Arc<Mutex<Store>>> for direct local store access.

Fix store access patterns to properly lock Arc<Mutex<Store>> before
accessing fields in mind/unconscious.rs, mind/mod.rs, subconscious/learn.rs,
and hippocampus/memory.rs.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-13 18:05:04 -04:00
parent 5db00e083f
commit b8db8754be
17 changed files with 282 additions and 295 deletions

View file

@ -40,6 +40,7 @@ pub use ops::current_provenance;
use crate::graph::{self, Graph};
use anyhow::{bail, Context, Result};
use std::fs;
use std::io::Write as IoWrite;
use std::path::Path;
@ -62,7 +63,7 @@ impl Store {
graph::build_graph(self)
}
pub fn resolve_key(&self, target: &str) -> Result<String, String> {
pub fn resolve_key(&self, target: &str) -> Result<String> {
// Strip .md suffix if present — keys no longer use it
let bare = strip_md_suffix(target);
@ -75,13 +76,13 @@ impl Store {
.cloned().collect();
match matches.len() {
0 => Err(format!("No entry for '{}'. Run 'init'?", target)),
0 => bail!("No entry for '{}'. Run 'init'?", target),
1 => Ok(matches[0].clone()),
n if n <= 10 => {
let list = matches.join("\n ");
Err(format!("Ambiguous '{}'. Matches:\n {}", target, list))
bail!("Ambiguous '{}'. Matches:\n {}", target, list)
}
n => Err(format!("Too many matches for '{}' ({}). Be more specific.", target, n)),
n => bail!("Too many matches for '{}' ({}). Be more specific.", target, n),
}
}
@ -103,7 +104,7 @@ impl Store {
}
/// Scan markdown files and index all memory units
pub fn init_from_markdown(&mut self) -> Result<usize, String> {
pub fn init_from_markdown(&mut self) -> Result<usize> {
let dir = memory_dir();
let mut count = 0;
if dir.exists() {
@ -128,10 +129,10 @@ impl Store {
&mut self,
dir: &Path,
edge_set: &mut std::collections::HashSet<([u8; 16], [u8; 16])>,
) -> Result<usize, String> {
) -> Result<usize> {
let mut count = 0;
let entries = fs::read_dir(dir)
.map_err(|e| format!("read dir {}: {}", dir.display(), e))?;
.with_context(|| format!("read dir {}", dir.display()))?;
for entry in entries.flatten() {
let path = entry.path();
@ -144,7 +145,7 @@ impl Store {
let filename = path.file_name().unwrap().to_string_lossy().to_string();
let content = fs::read_to_string(&path)
.map_err(|e| format!("read {}: {}", path.display(), e))?;
.with_context(|| format!("read {}", path.display()))?;
let units = parse_units(&filename, &content);
let (new_count, _) = self.ingest_units(&units, &filename)?;
@ -192,7 +193,7 @@ impl Store {
/// Process parsed memory units: diff against existing nodes, persist changes.
/// Holds StoreLock across refresh + check + write to prevent duplicate UUIDs.
fn ingest_units(&mut self, units: &[MemoryUnit], filename: &str) -> Result<(usize, usize), String> {
fn ingest_units(&mut self, units: &[MemoryUnit], filename: &str) -> Result<(usize, usize)> {
let _lock = types::StoreLock::acquire()?;
self.refresh_nodes()?;
@ -239,10 +240,10 @@ impl Store {
}
/// Import a markdown file into the store, parsing it into nodes.
pub fn import_file(&mut self, path: &Path) -> Result<(usize, usize), String> {
pub fn import_file(&mut self, path: &Path) -> Result<(usize, usize)> {
let filename = path.file_name().unwrap().to_string_lossy().to_string();
let content = fs::read_to_string(path)
.map_err(|e| format!("read {}: {}", path.display(), e))?;
.with_context(|| format!("read {}", path.display()))?;
let units = parse_units(&filename, &content);
self.ingest_units(&units, &filename)
}