memory_lint Tool in ai-memory: Detecting Stale Pages and Dangling Cross-Project Links

The memory_lint tool built into ai-memory detects stale or wrong pages through front‑matter validation and content hash checks, while identifying dangling cross‑project links by resolving markdown targets against the SQLite page index.

The memory_lint linter—exposed as the ai-memory lint sub-command—safeguards the integrity of the ai-memory wiki by running static analysis over all stored markdown pages. According to the akitaonrails/ai-memory source code, this tool enforces data consistency for LLM‑driven knowledge retrieval and auto‑improvement pipelines.

How memory_lint Detects Stale and Wrong Pages

Stale pages undermine trust in the knowledge base. The linter catches three specific problems through metadata and content validation.

Missing or Malformed Front-Matter Metadata

Every wiki page requires YAML front‑matter with author, created_at, updated_at, project_id, and workspace_id fields. The linter in crates/ai-memory-consolidate/src/lint.rs parses these blocks and flags missing or unparsable entries as stale‑metadata warnings. Corrupted metadata causes incorrect attribution, broken search results, and failed permission checks.

Out‑of‑Sync Content Detection

The linter compares the stored content_hash in the database against a fresh hash of the on‑disk file. A mismatch indicates the file was edited outside the normal Wiki::write_page pathway, leaving the index stale. This check prevents the system from serving outdated knowledge to LLM recall pipelines.

Invalid Scope References

Pages referencing non‑existent project_id or workspace_id values are flagged when the ScopeResolver lookup against ai_memory_store tables fails. This catches orphaned pages that no longer belong to valid organizational scopes.

Cross‑project links enable knowledge reuse across workspaces. The linter ensures these links actually resolve.

The linter runs a regex over each markdown body to extract [text](target) links. It normalizes relative paths (e.g., ../other-project/page.md) to absolute paths using the source page's scope context.

Target Existence Verification

For each normalized target, the linter:

  • Calls ScopeResolver::lookup_existing_scope to validate the target's project/workspace exists
  • Queries the pages table for the target path
  • Reports dangling cross‑project links when either lookup fails

This catches both links to never‑indexed files and links to projects that were deleted or renamed.

Core Implementation Details

The linter architecture lives in crates/ai-memory-consolidate/src/lint.rs. The implementation streams every Page row from SQLite under single‑writer actor guarantees, ensuring a consistent snapshot without network I/O.

// Simplified core flow from lint.rs
pub enum LintResult {
    StaleMetadata { page_id: PageId, missing: Vec<String> },
    OutOfDateContent { page_id: PageId },
    DanglingLink { source: PageId, target: String },
}

Key validation functions include front‑matter YAML parsing, SHA‑256 content hashing, and ScopeResolver integration for cross‑project path resolution.

Running memory_lint

Command‑Line Usage


# From repository root

cargo run --bin ai-memory -- lint

Sample output:

[WARN] Stale metadata: page src/wiki/projectA/overview.md – missing author field
[WARN] Out‑of‑date content: page src/wiki/projectB/notes.md – file hash differs from index
[WARN] Dangling link: src/wiki/projectA/guide.md → ../projectC/missing.md (target not indexed)

Programmatic Integration

use ai_memory_consolidate::lint::{run_lint, LintResult};

fn main() -> anyhow::Result<()> {
    let results: Vec<LintResult> = run_lint(&store)?;
    
    // Filter for specific problem types
    let dangling: Vec<_> = results.into_iter()
        .filter(|r| matches!(r, LintResult::DanglingLink { .. }))
        .collect();
    
    println!("Found {} dangling cross‑project links", dangling.len());
    Ok(())
}

Key Source Files

File Purpose
crates/ai-memory-consolidate/src/lint.rs Core linter logic: page loading, metadata validation, hashing, link resolution
crates/ai-memory-cli/src/commands/lint.rs CLI argument parsing and formatted warning output
crates/ai-memory-store/src/schema.rs pages table definition with content_hash and metadata_json columns
crates/ai-memory-wiki/src/wiki.rs Wiki::write_page atomic write pathway that the linter cross‑checks
docs/ARCHITECTURE.md Single‑writer SQLite actor design for consistent snapshots

Summary

  • Stale metadata detection validates required front‑matter fields and flags missing or malformed YAML blocks.
  • Content freshness checks compare stored content_hash values against on‑disk files to catch unindexed edits.
  • Dangling link detection resolves cross‑project markdown links through ScopeResolver and verifies target existence in the database.
  • All checks run locally without network dependencies, making memory_lint suitable for CI/CD integration.

Frequently Asked Questions

What makes a page "stale" in memory_lint?

A page becomes stale when its front‑matter metadata is missing required fields, its content_hash no longer matches the file on disk, or it references a non‑existent project/workspace scope. The linter flags these conditions through StaleMetadata and OutOfDateContent result variants.

The linter normalizes relative paths using the source page's scope context, then resolves the absolute target through ScopeResolver::lookup_existing_scope. If the target project or page row is missing, it reports a DanglingLink warning with the source PageId and target path string.

Can memory_lint run without a running ai-memory server?

Yes. The linter operates entirely within the CLI process using a local SQLite snapshot. It requires no network I/O or running server, as implemented in crates/ai-memory-consolidate/src/lint.rs with single‑writer actor guarantees for data consistency.

Dangling links break navigation paths and corrupt the knowledge graph used for LLM‑driven recall and auto‑improvement. When an LLM retrieves context through these links, missing targets cause gaps in the retrieved knowledge, reducing response quality and hallucination resistance.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →