# How Pinned Pages Are Handled in ai‑memory's Decay System

> Discover how ai-memory's decay system protects pinned pages from eviction. Learn how the pinned flag and front-matter ensure your important content remains accessible.

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

---

**ai-memory excludes pinned pages from decay by checking the `pinned` flag on the `PageMeta` struct and the front‑matter `pinned` field during the forget‑sweep, rendering both slot pages and user‑pinned content immune to eviction.**

ai-memory implements a forget‑sweep mechanism to manage episodic memory decay, yet certain knowledge must remain permanent. The system treats pages as *pinned* when they reside in the special `_slots/` hierarchy or when their front‑matter explicitly sets `pinned: true`, ensuring these entries never enter the decay evaluation pipeline according to the source code in `akitaonrails/ai-memory`.

## What Makes a Page Pinned in ai-memory

The repository recognizes two distinct mechanisms for pinning a page, both ultimately setting the `pinned` field on the `PageMeta` struct to `true` at [`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.

### Automatic Pinning via the Slots Directory

Any page written to a path prefixed with `_slots/` is automatically considered a slot page. During the write operation, the `is_slot_path(&path)` helper returns `true`, which forces the `pinned` flag to `true` regardless of other metadata.

### 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, the `derive_index_metadata` function reads this field and sets `meta.pinned = true` according to the implementation at [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs) lines 84‑86.

## The Decay Exclusion Logic in sweep.rs

The core protection logic resides in the `is_decayable` function within **crates/ai-memory-consolidate/src/sweep.rs**. This function short‑circuits decay eligibility through multiple guard clauses:

```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
}

```

As shown at lines 30‑41, the function immediately returns `false` if `c.pinned` is `true`, preventing the page from being marked for eviction. It also performs a secondary check on the raw front‑matter JSON to catch pinned status that might not have been propagated to the struct flag.

## How the Forget-Sweep Processes Candidates

The decay pipeline begins in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs), where `Reader::decay_candidates` collects all rows with `is_latest = 1`. These candidates are then filtered through `is_decayable` during the sweep execution.

Only pages that satisfy all three conditions—**episodic tier**, **not struct‑pinned**, and **not front‑matter‑pinned**—proceed to eviction or TTL deletion. Semantic tier pages are excluded earlier by the tier check, while pinned episodic pages are filtered out by the pinning logic before reaching [`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs).

## Practical Code Examples

### Creating a Pinned Slot Page (Auto‑Pinned)

When writing to a `_slots/` path, the system automatically pins the page:

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

```

### Explicitly Pinning via Front-Matter

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

```

When indexed, this front‑matter ensures `meta.pinned = true`, protecting the page during subsequent sweeps.

### Running the Forget-Sweep

The sweep logic filters candidates before 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 …
}

```

Only non‑pinned episodic pages flow through the filter, ensuring pinned content remains untouched.

## Summary

- **Two pinning methods**: Automatic for `_slots/` paths, manual via front‑matter `pinned: true`.
- **Protection mechanism**: The `is_decayable` function in [`sweep.rs`](https://github.com/akitaonrails/ai-memory/blob/main/sweep.rs) checks both the `PageMeta` struct flag and the front‑matter JSON.
- **Tier restriction**: Only **episodic** pages are candidates for decay; semantic pages are inherently excluded.
- **File locations**: Logic resides in [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs) (writing) and [`crates/ai-memory-consolidate/src/sweep.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/sweep.rs) (sweeping).

## Frequently Asked Questions

### What is the difference between slot pages and user-pinned pages?

Slot pages reside in the `_slots/` directory and are automatically pinned by the write path in [`wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/wiki.rs). User‑pinned pages can exist anywhere in the hierarchy and rely on explicit `pinned: true` front‑matter. Both receive identical decay protection once the `pinned` flag is set.

### Can semantic tier pages be pinned?

Yes, but pinning is redundant for semantic tier pages. The `is_decayable` function returns `false` immediately for any page where `c.tier != Tier::Episodic`, meaning semantic pages never enter the decay pipeline regardless of their pinned status.

### Where does the decay sweep physically delete pages?

The actual deletion operations—soft delete and hard delete—are implemented in [`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs). However, these functions only receive candidates that have already passed the `is_decayable` filter in [`sweep.rs`](https://github.com/akitaonrails/ai-memory/blob/main/sweep.rs), ensuring pinned pages never reach the deletion stage.

### How can I verify a page is pinned before it gets swept?

Inspect the `pinned` field on the `PageMeta` struct stored in the database, or query the front‑matter JSON directly. If either shows `pinned` as `true`, the page will be excluded when `Reader::decay_candidates` feeds rows into the sweep pipeline defined in [`crates/ai-memory-consolidate/src/sweep.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/sweep.rs).