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

@ -1,9 +1,9 @@
// cli/agent.rs — agent subcommand handlers
use anyhow::{bail, Context, Result};
use crate::hippocampus as memory;
use crate::store;
pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query: Option<&str>, dry_run: bool, _local: bool, state_dir: Option<&str>) -> Result<(), String> {
pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query: Option<&str>, dry_run: bool, _local: bool, state_dir: Option<&str>) -> Result<()> {
// Mark as agent so tool calls (e.g. poc-memory render) don't
// pollute the user's seen set as a side effect
// SAFETY: single-threaded at this point (CLI startup, before any agent work)
@ -11,7 +11,7 @@ pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query:
// Override agent output/state directory if specified
if let Some(dir) = state_dir {
std::fs::create_dir_all(dir).map_err(|e| format!("create state dir: {}", e))?;
std::fs::create_dir_all(dir).context("create state dir")?;
unsafe { std::env::set_var("POC_AGENT_OUTPUT_DIR", dir); }
}
@ -25,14 +25,13 @@ pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query:
} else if let Some(q) = query {
// Resolve query via typed API
let q_str = format!("{} | limit:{}", q, count);
let result = memory::memory_query(None, &q_str, None).await
.map_err(|e| e.to_string())?;
let result = memory::memory_query(None, &q_str, None).await?;
let keys: Vec<String> = result.lines()
.filter(|l| !l.is_empty() && *l != "no results")
.map(|s| s.to_string())
.collect();
if keys.is_empty() {
return Err(format!("query returned no results: {}", q));
bail!("query returned no results: {}", q);
}
println!("[{}] query matched {} nodes", agent, keys.len());
keys
@ -40,10 +39,12 @@ pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query:
vec![] // use agent's built-in query
};
let arc = memory::access_local()?;
if !resolved_targets.is_empty() {
for (i, key) in resolved_targets.iter().enumerate() {
println!("[{}] [{}/{}] {}", agent, i + 1, resolved_targets.len(), key);
let mut store = store::Store::load()?;
let mut store = arc.lock().await;
if let Err(e) = crate::agent::oneshot::run_one_agent(
&mut store, agent, count, Some(&[key.clone()]),
).await {
@ -52,10 +53,10 @@ pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query:
}
} else {
// Local execution (--local, --debug, dry-run, or daemon unavailable)
let mut store = store::Store::load()?;
let mut store = arc.lock().await;
crate::agent::oneshot::run_one_agent(
&mut store, agent, count, None,
).await?;
).await.map_err(|e| anyhow::anyhow!("{}", e))?;
}
Ok(())
}