# How ai-memory Handles Memory Decay Across Tiers

> Discover how ai-memory manages memory decay across tiers. Learn about its nightly retention sweep, scoring, and tier-specific rules for optimal data management.

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

---

**ai-memory applies a nightly retention sweep that calculates a composite score combining exponential time decay and access frequency, then applies tier-specific rules: working memory stays pinned indefinitely, episodic content faces eviction when scores drop below 0.20, semantic knowledge persists longer through elevated salience, and procedural patterns remain indefinite until frequency thresholds fail.**

The `ai-memory` project implements a sophisticated forgetting mechanism that mimics human memory by organizing content into four distinct tiers. This article examines how the system calculates **memory decay** through its retention scoring algorithm and applies different survival rules based on whether content lives in working, episodic, semantic, or procedural storage.

## The Retention Score Formula

At the heart of the decay system lies the `retention_score` function defined in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs). This pure function computes a value between 0 and 1 determining whether a page survives the nightly M8 retention sweep.

The calculation combines two distinct psychological principles:

### Exponential Time Decay

The **time term** applies exponential decay based on page age: `salience × exp(−λ × age_days)`. With the default `lambda` (λ) of `0.02` defined in the `DecayParams` struct, content exhibits approximately a 35-day half-life unless other factors intervene.

### Access Frequency Boost

The **access term** rewards recent interactions: `σ × log(1 + access_count) × exp(−μ × days_since_access) × breadth`. This logarithmic scaling prevents highly accessed pages from dominating indefinitely while still offering protection against decay. The optional **breadth** factor rewards pages read by many distinct operators.

## Tier-Specific Decay Policies

The `Tier` enum defined in [`crates/ai-memory-core/src/page.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/page.rs) establishes four distinct survival strategies. The system checks the `tier` column during the sweep to apply these rules:

### Working Tier: Pinned Indefinitely

Pages in the **working** tier possess a `pinned = true` flag in the database. According to the reader implementation in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs) (around line 1116), the sweep explicitly excludes pinned pages, and tests in the wiki module verify this with `assert!(candidates[0].pinned)`. Working memory represents the active session context and never decays.

### Episodic Tier: Eviction on Cold Threshold

**Episodic** memory stores recent session logs and operational history. After calculating the retention score, the system compares it against `cold_threshold` (default `0.20` from `DecayParams`). Pages scoring below this threshold receive a decay-tombstone—setting `superseded_at` to a non-null timestamp—marking them as eviction candidates without immediate deletion.

### Semantic Tier: Extended Longevity

**Semantic** pages contain long-term knowledge and benefit from higher default salience values. As documented in [`docs/design-decisions.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/design-decisions.md), semantic content follows a "30 days hot, 180 days cold" policy. The elevated salience multiplier in the time decay formula allows semantic memories to persist well beyond episodic content before facing eviction.

### Procedural Tier: Indefinite Retention

**Procedural** memory stores extracted patterns from repeated episodic content. Unlike other tiers, procedural pages are **indefinite** and ignore the cold threshold entirely. According to the design documentation, these entries persist until a separate *frequency-decay* mechanism removes them only after remaining unobserved for a configurable duration.

## The Decay Sweep Process

The M8 retention sweep orchestrates the actual forgetting mechanism through coordinated database operations in [`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs).

### Soft Deletion via Tombstones

When a page's score falls below its tier threshold, the sweep calls `soft_delete_for_decay_if_latest`. This creates a decay-tombstone row that preserves the content for `hard_delete_after_days` (default 180 days) while marking it as superseded.

### Permanent Hard Deletion

After the grace period expires, the background job `hard_delete_decayed_page_chain` permanently removes the tombstone and all ancestry records. This prevents unbounded database growth while maintaining a short-term audit trail of forgotten content.

## Computing Individual Retention Scores

Before the nightly sweep runs, you can manually calculate retention scores using the pure function exported from [`decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/decay.rs):

```rust
use ai_memory_store::{DecayParams, retention_score};

let params = DecayParams::default(); // λ=0.02, σ=0.6, μ=0.04
let score = retention_score(
    &params,
    90.0,            // age_days
    12,              // access_count
    Some(3.0),       // days_since_access
    None,            // salience (uses default 1.0)
);
println!("Retention score = {}", score);

```

## Configuring Decay Parameters

Developers can customize decay behavior through the `DecayParams` struct. The following example demonstrates tuning for aggressive forgetting:

```rust
use ai_memory_store::DecayParams;
use ai_memory_mcp::Server;

let mut params = DecayParams::default();
params.lambda = 0.04;               // ~17-day half-life
params.cold_threshold = 0.15;       // Evict earlier
params.hard_delete_after_days = 90; // Shorter grace period

let server = Server::new().with_decay_params(params);

```

For collaborative environments, enable **breadth weighting** to slow decay for pages accessed by multiple distinct operators using the `retention_score_with_breadth` function:

```rust
// access_term × (1 + breadth_weight × ln(distinct_actors + 1))
let mut params = DecayParams::default();
params.breadth_weight = 0.5;  // Reward multi-user pages

```

## Querying Decay Candidates

The reader module exposes methods to preview which pages face eviction before the sweep executes:

```rust
// Returns DecayCandidate structs containing path, tier, and current score
let candidates = store.reader.decay_candidates(ws, proj).await?;
for c in candidates {
    println!("{} – tier: {} – score: {}", c.path, c.tier, c.score);
}

```

## Summary

- **ai-memory** calculates memory decay using a composite score combining exponential age decay and logarithmic access frequency in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs).
- **Working tier** pages remain pinned indefinitely and bypass the retention sweep entirely.
- **Episodic tier** content faces eviction when retention scores drop below the `cold_threshold` (default 0.20), creating tombstoned records for 180 days.
- **Semantic tier** knowledge persists longer through elevated salience values, following documented hot/cold period policies.
- **Procedural tier** patterns enjoy indefinite retention until explicit frequency-decay thresholds trigger removal.
- Hard deletion occurs via `hard_delete_decayed_page_chain` in [`ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/ops.rs) after the configurable grace period expires.

## Frequently Asked Questions

### How is the retention score calculated for a specific page?

The `retention_score` function in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs) computes a value based on two terms: a time decay component (`salience × exp(−λ × age_days)`) and an access frequency component (`σ × log(1 + access_count) × exp(−μ × days_since_access)`). These combine to produce a score between 0 and 1 that determines eviction eligibility during the nightly sweep.

### Can working memory ever be deleted by the decay process?

No. Working tier pages carry a `pinned = true` flag in the database that explicitly excludes them from the retention sweep, as enforced in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs). This ensures active session context remains available regardless of age or access patterns.

### What happens to episodic memories that fall below the cold threshold?

When episodic pages score below `cold_threshold` (default 0.20), the system creates a decay-tombstone by setting `superseded_at` to a timestamp via `soft_delete_for_decay_if_latest` in [`ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/ops.rs). The content remains queryable for `hard_delete_after_days` (default 180 days) before `hard_delete_decayed_page_chain` permanently removes it.

### How does procedural memory differ from semantic memory in decay behavior?

Procedural memory remains **indefinite** and ignores the cold threshold eviction rule, persisting until frequency-decay detects extended unobserved periods. Semantic memory, while using the same scoring formula, typically carries higher salience values that delay decay, following documented retention windows (30 days hot, 180 days cold) before potential eviction.