How Pinned Pages Are Handled in ai-memory's Decay Policy

Pinned pages in ai-memory are completely excluded from the forget-sweep decay process through a dual-validation check in the is_decayable function that inspects both the boolean pinned flag and front-matter metadata.

In the akitaonrails/ai-memory repository, the decay policy governs how episodic memories are phased out over time, but certain critical pages must remain permanently accessible. The system implements a pinning mechanism that marks specific pages as decay-immune, ensuring they survive routine consolidation sweeps regardless of their age or access patterns.

The Two Mechanisms for Pinning Pages

The system recognizes pinned status through two distinct pathways that both set the pinned flag on the PageMeta struct in crates/ai-memory-wiki/src/wiki.rs.

Automatic Pinning via Slot Paths

Pages residing within the special _slots/ hierarchy are automatically marked as pinned during the write operation. At lines 1185-1187 in wiki.rs, the system evaluates is_slot_path(&path) when constructing the page request:

let path = PagePath::new("_slots/personal/todo.md").unwrap();
let req = NewPage {
    // … other fields …
    pinned: is_slot_path(&path),   // true because of `_slots/` prefix
    ..Default::default()
};
wiki.write_page(req).await?;

This automatic assignment ensures that slot-based organizational pages never enter the decay evaluation pipeline.

Explicit Pinning via Front-Matter

Users can manually pin any page by including pinned: true in the YAML front-matter. When the page is indexed, derive_index_metadata parses this field and sets meta.pinned = true at lines 84-86 in wiki.rs:

---
title: "Important Fact"
tier: episodic
pinned: true
---
The fact should never decay.

This front-matter override provides granular control over persistence regardless of file location.

How the Decay Sweep Filters Pinned Content

The forget-sweep implementation resides in crates/ai-memory-consolidate/src/sweep.rs. The is_decayable function at lines 30-41 implements short-circuit evaluation that immediately excludes pinned candidates:

fn is_decayable(c: &DecayCandidate) -> bool {
    if c.tier != Tier::Episodic { return false; }
    if c.pinned { return false; }                       // <-- pinned flag blocks decay
    if let Ok(fm) = serde_json::from_str::<serde_json::Value>(&c.frontmatter_json)
        && fm.get("pinned").and_then(serde_json::Value::as_bool) == Some(true)
    {
        return false;                                   // <-- front-matter overrides
    }
    true
}

The sweep first queries Reader::decay_candidates from crates/ai-memory-store/src/reader.rs to collect rows where is_latest = 1. It then iterates through these candidates, calling is_decayable on each. The function returns false for any page where c.pinned is true or where the parsed front-matter contains "pinned": true, effectively removing them from the decayable set before any TTL deletion or eviction occurs.

Summary

  • Dual validation: The system checks both the struct's pinned boolean and the front-matter JSON to determine pinning status.
  • Automatic slot protection: Any page written to the _slots/ directory receives automatic pinned status via is_slot_path().
  • Explicit user control: Front-matter metadata allows pinning of arbitrary pages regardless of their storage location or tier.
  • Sweep exclusion: The is_decayable function in sweep.rs short-circuits and returns false immediately upon detecting a pinned flag, preventing eviction.
  • Tier independence: While only episodic pages are normally eligible for decay, pinning works across tiers but particularly protects episodic content from deletion.

Frequently Asked Questions

What happens to pinned pages during the forget-sweep?

Pinned pages are completely skipped during the forget-sweep process. When is_decayable encounters a candidate with c.pinned == true or front-matter containing "pinned": true, it returns false immediately, removing that page from the decay candidate pool. The sweep only processes episodic pages that pass all validation checks as non-pinned.

How do I pin a page in ai-memory?

You have two methods: place the page in the _slots/ directory hierarchy for automatic pinning, or add pinned: true to the YAML front-matter of any markdown file. The front-matter approach works regardless of the file path and is parsed during the indexing phase in crates/ai-memory-wiki/src/wiki.rs.

Are semantic tier pages affected by pinning?

Semantic tier pages are excluded from decay regardless of pinning status because is_decayable first checks if c.tier != Tier::Episodic { return false; }. However, pinning still functions as a secondary protection layer. If the tier classification were to change, the pinned status would prevent decay, ensuring the page remains protected under all circumstances.

Where is the decay logic implemented?

The core decay evaluation logic lives in crates/ai-memory-consolidate/src/sweep.rs within the is_decayable function. The candidate selection query is defined in crates/ai-memory-store/src/reader.rs as Reader::decay_candidates, while the pinning metadata is set during write operations in crates/ai-memory-wiki/src/wiki.rs.

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 →