{"record":{"id":"15c06b542f9e17da","repo":"Hmbown/CodeWhale","slug":"memory-edits-require-non-empty-evidence","errorCode":null,"errorMessage":"memory edits require non-empty evidence","messagePattern":"memory edits require non-empty evidence","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/native_memory.rs","lineNumber":876,"sourceCode":"            several.len()\n        ),\n    }\n}\n\n/// The note body of a Markdown bullet, if the line is one.\nfn bullet_text(line: &str) -> Option<&str> {\n    let trimmed = line.trim();\n    trimmed\n        .strip_prefix(\"- \")\n        .or_else(|| trimmed.strip_prefix(\"* \"))\n        .map(str::trim)\n}\n\n/// Evidence is required on every in-place edit and held to the same bounds\n/// as a note. An unexplained rewrite of durable context is the thing the\n/// journal exists to prevent.\nfn normalize_evidence(evidence: &str) -> Result<String> {\n    normalize_note(evidence).map_err(|_| anyhow!(\"memory edits require non-empty evidence\"))\n}\n\nfn normalize_note(note: &str) -> Result<String> {\n    let note = note.replace(\"\\r\\n\", \"\\n\").replace('\\r', \"\\n\");\n    let note = note\n        .lines()\n        .map(str::trim)\n        .filter(|line| !line.is_empty())\n        .collect::<Vec<_>>()\n        .join(\" \");\n    if note.is_empty() {\n        bail!(\"memory note is empty\");\n    }\n    if note.len() > MAX_NOTE_BYTES {\n        bail!(\"memory note exceeds {MAX_NOTE_BYTES} bytes\");\n    }\n    Ok(note.trim_start_matches('-').trim().to_string())\n}","sourceCodeStart":858,"sourceCodeEnd":894,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/native_memory.rs#L858-L894","documentation":"normalize_evidence requires every in-place memory edit (revise/retire) to carry non-empty evidence that passes the same normalization/bounds rules as a note. Empty, whitespace-only, or over-limit evidence is rejected: an unexplained rewrite of durable context is exactly what the journal is designed to prevent.","triggerScenarios":"Calling revise or retire with evidence = \"\" or whitespace, or evidence that fails normalize_note (e.g. exceeds size/line bounds after \\r\\n normalization); normalize_note returns Err and it is mapped to this message.","commonSituations":"Automated tooling calling retire without a reason; a UI that leaves the evidence field blank; programmatic cleanup scripts editing memories without justification.","solutions":["Provide a meaningful, non-empty evidence string explaining why the memory is being revised or retired.","Keep evidence within the same limits as notes (line count/size) — trim verbose justification.","At the tool/UI layer, require an evidence field before submitting the edit."],"exampleFix":"// before\nmemory.retire(scope, Some(ws), id, \"\")?;\n// after\nmemory.retire(scope, Some(ws), id, \"superseded by entry 42; fact no longer holds after refactor\")?;","handlingStrategy":"validation","validationCode":"fn validate_evidence(evidence: &str) -> Result<()> {\n    let t = evidence.trim();\n    if t.is_empty() { bail!(\"evidence required for memory edits\"); }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match memory.retire(scope, ws, id, evidence) {\n    Err(e) if e.to_string().contains(\"non-empty evidence\") => {\n        eprintln!(\"Provide a reason: why is this memory being retired?\");\n    }\n    other => other?,\n}","preventionTips":["Make evidence a mandatory field in any UI/tool that calls revise or retire.","Reject blank/whitespace input at the form layer before submission.","Keep evidence within note bounds — brief, factual justification.","Journal edits always with a reason; it is the audit trail for durable context changes."],"tags":["memory","validation","empty-input"],"backgroundTag":"empty-required-field","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}