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

> Discover how ai-memory's decay policy excludes pinned pages using a dual-validation check in the is_decayable function. Learn how pinned content remains safe from the forget-sweep process.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: how-to-guide
- Published: 2026-09-10

---

**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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/wiki.rs), the system evaluates `is_slot_path(&path)` when constructing the page request:

```rust
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`](https://github.com/akitaonrails/ai-memory/blob/main/wiki.rs):

```markdown
---
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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/sweep.rs). The `is_decayable` function at lines 30-41 implements short-circuit evaluation that immediately excludes pinned candidates:

```rust
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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs).