# How Pinned Pages in ai-memory Are Exempt from Memory Decay

> Discover how pinned pages in ai-memory bypass memory decay. Learn how the is_decayable function protects these crucial pages from the consolidation sweep.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: internals
- Published: 2026-08-22

---

**Pinned pages in ai-memory are exempt from the memory decay process because the `is_decayable` function in the consolidation sweep explicitly short-circuits when encountering the `pinned` flag or front-matter metadata, preventing decay evaluation regardless of the page's memory tier.**

The ai-memory system implements a forgetting mechanism to manage ephemeral information, but certain critical pages must remain permanent. These pinned pages bypass the automatic decay process through a dual-layer protection system implemented in the consolidation sweep. Understanding this exemption mechanism is essential for managing long-term versus short-term memory in AI applications.

## Two Methods for Pinning Pages in ai-memory

The repository identifies pinned pages through two distinct pathways, both of which set the `pinned` flag on the `PageMeta` struct to `true`.

### Automatic Pinning via the _slots/ Directory Hierarchy

Pages residing in the special `_slots/` hierarchy are automatically treated as pinned. When a page is written, the system checks `is_slot_path(&path)` during the write operation in [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs) (lines 1185-1187). If the path contains the `_slots/` prefix, the `pinned` field is automatically set to `true`.

```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?;

```

### Explicit Front-Matter Declaration

Users can manually pin any page by including `pinned: true` in the front-matter. During indexing, the `derive_index_metadata` function reads this field and sets `meta.pinned = true` (wiki.rs lines 84-86).

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

```

## The Decay Exemption Logic in the Forget-Sweep

The forget-sweep process begins by collecting candidate rows via `Reader::decay_candidates` in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs), which selects all pages where `is_latest = 1`. However, eligibility for actual decay is determined by the `is_decayable` function in [`crates/ai-memory-consolidate/src/sweep.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/sweep.rs) (lines 30-41).

This function implements a strict hierarchy of checks:

```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 immediately returns `false` (non-decayable) for any page where:
- The `c.pinned` boolean is `true`
- The `frontmatter_json` contains `"pinned": true`

This dual-check ensures that both automatically pinned slot pages and manually pinned pages are excluded from the decay pipeline.

## Technical Implementation of Decay Prevention

The decay process filters candidates through a pipeline that guarantees pinned page persistence. Only pages that pass all `is_decayable` checks proceed to eviction or TTL deletion:

```rust
let candidates = reader.decay_candidates(ws, proj).await?;
let decayable = candidates.into_iter().filter(is_decayable);
for cand in decayable {
    // eviction or TTL delete …
}

```

**Key implementation details:**
- **Tier restriction**: Only episodic tier pages can decay; semantic pages are excluded at the first conditional in `is_decayable`.
- **Flag precedence**: The `c.pinned` check occurs before front-matter parsing, providing a fast path for slot pages.
- **Persistence guarantee**: Because the sweep skips pinned pages entirely, they never undergo soft deletion or hard deletion operations defined in [`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs).

The integration tests in [`crates/ai-memory-consolidate/tests/slot_visibility.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/tests/slot_visibility.rs) confirm that slot pages are forced-pinned and remain visible across sweep cycles, verifying the decay-immunity mechanism.

## Summary

- **Pinned pages** are identified either by residing in `_slots/` or via explicit `pinned: true` front-matter, both setting the `PageMeta` flag in [`wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/wiki.rs).
- The **`is_decayable`** function in [`crates/ai-memory-consolidate/src/sweep.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/sweep.rs) immediately returns `false` for any page with the pinned flag or front-matter property.
- **Only episodic tier pages** that are not pinned are eligible for decay; semantic tier pages are excluded by tier regardless of pin status.
- This architecture ensures critical memory remains persistent while ephemeral, non-pinned content is properly managed through the forget-sweep cycle.

## Frequently Asked Questions

### What is the difference between slot pages and manually pinned pages in ai-memory?

Slot pages are automatically pinned when created in the `_slots/` directory hierarchy via the `is_slot_path` function during the write operation, while manually pinned pages require explicit `pinned: true` in their front-matter metadata. Both methods result in identical decay protection because both set the same `pinned` boolean on the `DecayCandidate` struct that `is_decayable` evaluates.

### Can semantic tier pages be pinned to prevent decay?

No, pinning is unnecessary for semantic tier pages because the decay process exclusively targets the episodic tier. The `is_decayable` function checks `c.tier != Tier::Episodic` as its first conditional, meaning semantic pages are already excluded from decay regardless of their pinned status. Pinning only provides protection within the episodic tier.

### Where does the forget-sweep check for pinned status in the codebase?

The primary check occurs 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 (lines 30-41). This function validates both the `c.pinned` boolean flag from the database row and parses the `frontmatter_json` field to detect the `"pinned"` key, ensuring comprehensive protection regardless of how the pin was originally set.

### How can I verify that my slot pages are immune to memory decay?

The repository includes integration tests in [`crates/ai-memory-consolidate/tests/slot_visibility.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/tests/slot_visibility.rs) that explicitly confirm slot pages are forced-pinned and excluded from decay cycles. You can also verify behavior by checking that the `decay_candidates` query in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs) filters out pages where `is_slot_path` returned true during the initial write operation in [`wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/wiki.rs).