write: validate inline references on write
Warn when content contains render artifacts (poc-memory render key embedded in prose — should be just `key`) or malformed → references. Soft warnings on stderr, doesn't block the write. Catches agent output that accidentally includes render-decorated links, preventing content growth from round-trip artifacts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
601a072cfd
commit
5ce1d4ed24
1 changed files with 50 additions and 0 deletions
|
|
@ -246,6 +246,48 @@ pub fn cmd_render(key: &[String]) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Check content for common inline reference problems:
|
||||
/// - `poc-memory render key` embedded in content (render artifact, should be just `key`)
|
||||
/// - `→ something` where something doesn't parse as a valid key
|
||||
/// - `key` referencing a node that doesn't exist
|
||||
fn validate_inline_refs(content: &str, store: &store::Store) -> Vec<String> {
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
for line in content.lines() {
|
||||
// Check for render commands embedded in content
|
||||
if line.contains("poc-memory render ") && !line.starts_with(" ") {
|
||||
// Skip lines that look like CLI documentation/examples
|
||||
if !line.contains("CLI") && !line.contains("equivalent") && !line.contains("tool") {
|
||||
warnings.push(format!(
|
||||
"render command in content (should be just `key`): {}",
|
||||
line.chars().take(80).collect::<String>(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Check → references
|
||||
if let Some(rest) = line.trim().strip_prefix("→ ") {
|
||||
// Extract the key (may be backtick-quoted)
|
||||
let key = rest.trim().trim_matches('`').trim();
|
||||
if !key.is_empty() && !store.nodes.contains_key(key) {
|
||||
// Might be a poc-memory render artifact
|
||||
if let Some(k) = key.strip_prefix("poc-memory render ") {
|
||||
warnings.push(format!(
|
||||
"render artifact in → reference (use `{}` not `poc-memory render {}`)", k, k,
|
||||
));
|
||||
} else if key.contains(' ') {
|
||||
warnings.push(format!(
|
||||
"→ reference doesn't look like a key: → {}", key,
|
||||
));
|
||||
}
|
||||
// Don't warn about missing keys — the target might be created later
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
warnings
|
||||
}
|
||||
|
||||
pub fn cmd_history(key: &[String], full: bool) -> Result<(), String> {
|
||||
if key.is_empty() {
|
||||
return Err("history requires a key".into());
|
||||
|
|
@ -332,6 +374,14 @@ pub fn cmd_write(key: &[String]) -> Result<(), String> {
|
|||
|
||||
let mut store = store::Store::load()?;
|
||||
let key = store.resolve_key(&raw_key).unwrap_or(raw_key);
|
||||
|
||||
// Validate inline references: warn about render commands embedded
|
||||
// in content (should be just `key`) and broken references.
|
||||
let warnings = validate_inline_refs(&content, &store);
|
||||
for w in &warnings {
|
||||
eprintln!("warning: {}", w);
|
||||
}
|
||||
|
||||
let result = store.upsert(&key, &content)?;
|
||||
match result {
|
||||
"unchanged" => println!("No change: '{}'", key),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue