# How Does Episodic Memory Tier Decay Work in ai-memory?

> Discover how ai-memory's episodic memory tier decay works. Learn about exponential time decay, access frequency, and actor breadth's role in evicting session pages.

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

---

**Episodic memory decay uses a retention-score formula combining exponential time decay, access frequency, and actor breadth to evict short-term session pages after ~30 days hot, ~180 days cold.**

The **Episodic** tier in `akitaonrails/ai-memory` represents short-term, per-session memory that naturally degrades over time. Understanding its decay mechanism is essential for tuning memory retention in production AI applications. This article examines the three-phase decay process implemented in the Rust codebase.

## The Three-Phase Episodic Decay Lifecycle

According to the architecture documentation, Episodic pages follow a strict temperature-based progression: **30 days hot**, **~180 days cold**, then **eviction** if retention scores fall below threshold.

### Phase 1: Retention Score Calculation

The core decay logic resides in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs). When a forget sweep runs, each Episodic page's score is recomputed using the `retention_score` and `retention_score_with_breadth` functions.

The formula combines three weighted factors:

```

score = salience·exp(‑λ·age_days)
        + σ·log(1 + access_count)·exp(‑μ·days_since_access)
        × (1 + breadth_weight·ln(1 + max(distinct_actors‑1,0)))

```

**Parameter breakdown:**

- `salience` — Page-specific importance (or default from `DecayParams`)
- `λ` (lambda) — Exponential decay rate for time elapsed
- `μ` (mu) — Decay rate for recency of access
- `age_days` — Days since last update
- `access_count` / `days_since_access` — Read frequency tracking
- `breadth_weight` — Multiplier for pages touched by many distinct actors

Default decay parameters (`λ ≈ 0.03`, `μ ≈ 0.1`) are configurable via `[decay]` section in [`ai_memory.toml`](https://github.com/akitaonrails/ai-memory/blob/main/ai_memory.toml).

### Phase 2: Decay Candidate Selection

The `Reader::decay_candidates` function in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs) identifies pages eligible for eviction. It filters for:

- Tier must be `Episodic`
- Page must **not** be pinned
- Either: older than hot period (30 days) with **zero** accesses, **or** retention score below configured threshold

This dual criteria prevents premature eviction of actively accessed pages while catching dormant ones.

### Phase 3: Tombstone Creation and Hard Deletion

Selected candidates undergo two-step removal:

1. **Soft delete** — `Writer::soft_delete_for_decay_if_latest` sets `superseded_at` timestamp, creating a decay tombstone
2. **Hard delete** — `Writer::hard_delete_decayed_page_chain` permanently removes tombstone and ancestry chain

Both operations reside in [`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs).

The `curator` component in [`crates/ai-memory-consolidate/src/curator.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/curator.rs) orchestrates this sweep, logging "cold episodic" pages and triggering retention-based decisions.

## Configuring Decay Parameters

Create or modify [`ai_memory.toml`](https://github.com/akitaonrails/ai-memory/blob/main/ai_memory.toml) to tune Episodic decay behavior:

```toml
[decay]
lambda = 0.03           # Time decay rate (higher = faster decay)

mu = 0.1                # Access recency decay rate

salience_default = 1.0  # Base importance for unmarked pages

breadth_weight = 0.5    # Boost for multi-actor pages

threshold = 0.1         # Score floor for eviction eligibility

```

## Practical Example: Simulating Episodic Decay

```rust
use ai_memory_core::Tier;
use ai_memory_store::Store;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut store = Store::open("memory.db").await?;
    
    // Create episodic session memory
    let page_id = store
        .write_page(
            "sessions/chat-2024-01-15.md",
            Tier::Episodic,
            "User asked about Rust async patterns...",
        )
        .await?;
    
    // Simulate access pattern
    store.record_access(page_id).await?;
    
    // Fast-forward 45 days (testing utility)
    store.advance_time_by_days(45).await?;
    
    // Trigger decay sweep (normally background scheduled)
    let decayed = store.run_decay_sweep().await?;
    println!("Evicted {} episodic pages", decayed.len());
    
    Ok(())
}

```

## Key Implementation Files

- **[`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs)** — `retention_score`, `DecayParams` struct, salience updates
- **[`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs)** — `Reader::decay_candidates` filter logic
- **[`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs)** — `soft_delete_for_decay_if_latest`, `hard_delete_decayed_page_chain`
- **[`crates/ai-memory-consolidate/src/curator.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/curator.rs)** — Sweep orchestration and logging
- **[`docs/ARCHITECTURE.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/ARCHITECTURE.md)** — High-level tier lifecycle specification

## Summary

- **Episodic decay** combines exponential time decay, access frequency, and actor breadth into a single retention score
- **Hot period** (30 days) protects recent pages; **cold period** (~180 days) allows recovery before final eviction
- **Two-phase deletion** (tombstone → hard delete) preserves auditability while reclaiming storage
- **All parameters configurable** via TOML without code changes

## Frequently Asked Questions

### How is the retention score different from simple TTL expiration?

TTL expires pages at fixed timestamps regardless of usage. The **retention score** dynamically adjusts based on access patterns—frequently-read pages survive longer, while untouched pages decay faster. The `log(1 + access_count)` and `exp(‑μ·days_since_access)` terms create this adaptive behavior.

### Can pinned Episodic pages ever be evicted?

**No.** The `decay_candidates` query explicitly excludes pinned pages. Pinning overrides the entire decay mechanism, making it suitable for preserving critical session context across arbitrary time periods.

### What happens to decayed page relationships and ancestry?

The `hard_delete_decayed_page_chain` function removes not just the tombstoned page but its **entire ancestry chain**. This prevents orphaned references and ensures referential integrity within the memory graph.

### How do I monitor which Episodic pages are being decayed?

The `curator` component emits structured logs for "cold episodic" pages at `INFO` level. Enable logging in your application and filter for `ai_memory_consolidate::curator` to observe sweep decisions in real time.