From 7e131862d635ddd92c7bdb5be3db49f306a9392e Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 16 Mar 2026 18:09:56 -0400 Subject: [PATCH] poc-memory: POC_MEMORY_DRY_RUN=1 for agent testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All mutating commands (write, delete, rename, link-add, journal write, used, wrong, not-useful, gap) check POC_MEMORY_DRY_RUN after argument validation but before mutation. If set, process exits silently — agent tool calls are visible in the LLM output so we can see what it tried to do without applying changes. Read commands (render, search, graph link, journal tail) work normally in dry-run mode so agents can still explore the graph. Co-Authored-By: Claude Opus 4.6 (1M context) --- poc-memory/src/cli/graph.rs | 1 + poc-memory/src/cli/journal.rs | 1 + poc-memory/src/cli/mod.rs | 7 +++++++ poc-memory/src/cli/node.rs | 9 +++++++++ 4 files changed, 18 insertions(+) diff --git a/poc-memory/src/cli/graph.rs b/poc-memory/src/cli/graph.rs index 59f6111..6fceb2c 100644 --- a/poc-memory/src/cli/graph.rs +++ b/poc-memory/src/cli/graph.rs @@ -134,6 +134,7 @@ pub fn cmd_triangle_close(min_degree: usize, sim_threshold: f32, max_per_hub: us } pub fn cmd_link_add(source: &str, target: &str, reason: &[String]) -> Result<(), String> { + super::check_dry_run(); let mut store = store::Store::load()?; let source = store.resolve_key(source)?; let target = store.resolve_key(target)?; diff --git a/poc-memory/src/cli/journal.rs b/poc-memory/src/cli/journal.rs index 182f331..b81ca22 100644 --- a/poc-memory/src/cli/journal.rs +++ b/poc-memory/src/cli/journal.rs @@ -176,6 +176,7 @@ pub fn cmd_journal_write(text: &[String]) -> Result<(), String> { if text.is_empty() { return Err("journal-write requires text".into()); } + super::check_dry_run(); let text = text.join(" "); let timestamp = crate::store::format_datetime(crate::store::now_epoch()); diff --git a/poc-memory/src/cli/mod.rs b/poc-memory/src/cli/mod.rs index 4d111d8..2020e55 100644 --- a/poc-memory/src/cli/mod.rs +++ b/poc-memory/src/cli/mod.rs @@ -9,3 +9,10 @@ pub mod agent; pub mod admin; pub mod journal; pub mod misc; + +/// Exit silently if POC_MEMORY_DRY_RUN=1. +pub fn check_dry_run() { + if std::env::var("POC_MEMORY_DRY_RUN").map_or(false, |v| v == "1" || v == "true") { + std::process::exit(0); + } +} diff --git a/poc-memory/src/cli/node.rs b/poc-memory/src/cli/node.rs index a6ee1c4..697e2be 100644 --- a/poc-memory/src/cli/node.rs +++ b/poc-memory/src/cli/node.rs @@ -11,6 +11,7 @@ pub fn cmd_used(key: &[String]) -> Result<(), String> { if key.is_empty() { return Err("used requires a key".into()); } + super::check_dry_run(); let key = key.join(" "); let mut store = store::Store::load()?; let resolved = store.resolve_key(&key)?; @@ -38,6 +39,7 @@ pub fn cmd_used(key: &[String]) -> Result<(), String> { pub fn cmd_wrong(key: &str, context: &[String]) -> Result<(), String> { let ctx = if context.is_empty() { None } else { Some(context.join(" ")) }; + super::check_dry_run(); let mut store = store::Store::load()?; let resolved = store.resolve_key(key)?; store.mark_wrong(&resolved, ctx.as_deref()); @@ -71,6 +73,8 @@ pub fn cmd_not_relevant(key: &str) -> Result<(), String> { } pub fn cmd_not_useful(key: &str) -> Result<(), String> { + // no args to validate + super::check_dry_run(); let mut store = store::Store::load()?; let resolved = store.resolve_key(key)?; // Same as wrong but with clearer semantics: node content is bad, edges are fine. @@ -84,6 +88,7 @@ pub fn cmd_gap(description: &[String]) -> Result<(), String> { if description.is_empty() { return Err("gap requires a description".into()); } + super::check_dry_run(); let desc = description.join(" "); let mut store = store::Store::load()?; store.record_gap(&desc); @@ -146,6 +151,7 @@ pub fn cmd_node_delete(key: &[String]) -> Result<(), String> { if key.is_empty() { return Err("node-delete requires a key".into()); } + super::check_dry_run(); let key = key.join(" "); let mut store = store::Store::load()?; let resolved = store.resolve_key(&key)?; @@ -156,6 +162,8 @@ pub fn cmd_node_delete(key: &[String]) -> Result<(), String> { } pub fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<(), String> { + // args are positional, always valid if present + super::check_dry_run(); let mut store = store::Store::load()?; let old_resolved = store.resolve_key(old_key)?; store.rename_node(&old_resolved, new_key)?; @@ -286,6 +294,7 @@ pub fn cmd_write(key: &[String]) -> Result<(), String> { if content.trim().is_empty() { return Err("No content on stdin".into()); } + super::check_dry_run(); let mut store = store::Store::load()?; let key = store.resolve_key(&raw_key).unwrap_or(raw_key);