# How the Decay Math Formula and Retention Scoring Work in ai-memory

> Understand the decay math formula and retention scoring in ai-memory. Discover how exponential time decay and access reinforcement determine episodic page eviction.

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

---

**The decay math formula combines exponential time decay with logarithmic access reinforcement to calculate retention scores that determine which episodic pages get evicted from ai-memory.**

The akitaonrails/ai-memory repository implements a mathematically-grounded retention model to manage the lifecycle of episodic memory pages. At the heart of this system lies the decay math formula defined in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs), which computes retention scores by weighing page age against access patterns, user feedback, and optional actor breadth metrics.

## Core Decay Parameters

The `DecayParams` struct in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs) defines the tunable constants that govern the retention curve. These parameters allow operators to adjust the memory system's forgetfulness without altering the underlying code.

| Parameter | Default | Mathematical Role |
|-----------|---------|-------------------|
| `lambda` | 0.02 | Controls exponential decay on age since `updated_at` (approximately 35-day half-life) |
| `sigma` | 0.6 | Sets the magnitude of the access-reinforcement boost |
| `mu` | 0.04 | Governs exponential decay on days since last access (recent hits matter more) |
| `salience_default` | 1.0 | Baseline salience for pages without explicit feedback |
| `cold_threshold` | 0.20 | Score floor below which pages become eviction candidates |
| `hard_delete_after_days` | 180 | Tombstone survival period before permanent removal |

*Source: [`DecayParams` definition](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs#L15-L30)*

## The Retention Score Formula

The public API entry point `retention_score` delegates to `retention_score_with_breadth` with a breadth weight of zero, preserving backwards compatibility with the original historic formula.

```rust
pub fn retention_score(
    params: &DecayParams,
    age_days: f64,
    access_count: u32,
    days_since_access: Option<f64>,
    salience: Option<f64>,
) -> f64 {
    retention_score_with_breadth(
        params,
        age_days,
        access_count,
        days_since_access,
        salience,
        0,
        0.0,
    )
}

```

*Source: [`retention_score` implementation](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs#L48-L74)*

### Mathematical Components

The retention score calculation combines three distinct mathematical components:

**Time Decay Term**

```

salience * exp(-λ * age_days)

```

Older pages decay exponentially based on the `lambda` parameter, while higher `salience` values lift the entire curve upward.

**Access Reinforcement Term**

```

σ * ln(1 + access_count) * exp(-μ * days_since_access) * breadth

```

This term applies a logarithmic scaling to access frequency (`ln(1 + access_count)`) multiplied by its own exponential decay based on recency. The `sigma` parameter scales the boost magnitude.

**Breadth Multiplier (Optional)**

```

1 + breadth_weight * ln(1 + distinct_actors - 1)

```

When `breadth_weight` is non-zero, the formula accounts for social reinforcement by scaling the access term based on the count of distinct actors who accessed the page. With `breadth_weight = 0`, this term collapses to 1.

*Source: [`retention_score_with_breadth` core math](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs#L94-L119)*

## Salience Adjustments via Feedback

User feedback directly manipulates the `salience` variable before the retention calculation. The `salience_after_feedback` function in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs) clamps adjustments within bounded limits to prevent single actors from permanently locking or instantly destroying pages.

```rust
pub fn salience_after_feedback(
    params: &DecayParams,
    current: Option<f64>,
    kind: ai_memory_core::FeedbackKind,
) -> f64 {
    let current = current.unwrap_or(params.salience_default);
    let next = match kind {
        K::Helpful => current + SALIENCE_STEP,
        K::NotHelpful => current - SALIENCE_STEP,
        K::Stale | K::Wrong => SALIENCE_MIN,
    };
    next.clamp(SALIENCE_MIN, SALIENCE_MAX)
}

```

*Source: [`salience_after_feedback`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs#L41-L53)*

- **Helpful** feedback increases salience by `SALIENCE_STEP` (0.25), capped at `SALIENCE_MAX` (2.0)
- **NotHelpful** decreases salience by 0.25, with a floor of `SALIENCE_MIN` (0.25)
- **Stale** or **Wrong** feedback immediately floors salience to 0.25

These bounds ensure that the periodic retention sweep—not instantaneous feedback—governs long-term page eviction.

## The Eviction Decision Flow

During the periodic forget sweep (`ai_memory_store::Reader::decay_candidates`), the system evaluates each page using the decay math formula:

1. Calculate raw retention score using current metadata
2. Compare against `cold_threshold` (default 0.20)
3. Check `PageMetadata::pinned` status (pinned pages survive regardless of score)
4. Candidates below threshold enter soft-delete → tombstone → hard-delete pipeline after `hard_delete_after_days`

*Sources:*
- Candidate extraction: [`decay_candidates`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs#L3083-L3110)
- Soft-delete logic: [`soft_delete_for_decay_if_latest`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs#L1731-L1759)

## Practical Implementation Examples

### Computing a Basic Retention Score

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

fn main() {
    let params = DecayParams::default();
    let age_days = 45.0;
    let access_count = 12;
    let days_since_access = Some(3.0);
    let salience = None;  // uses salience_default (1.0)

    let score = retention_score(
        &params,
        age_days,
        access_count,
        days_since_access,
        salience,
    );
    println!("Retention score = {:.4}", score);
}

```

### Applying Feedback and Recomputing

```rust
use ai_memory_store::decay::{salience_after_feedback, retention_score};
use ai_memory_core::FeedbackKind;

fn main() {
    let params = DecayParams::default();
    
    // User marks page as Helpful
    let new_salience = salience_after_feedback(
        &params,
        None,
        FeedbackKind::Helpful,
    );
    
    let score = retention_score(
        &params,
        10.0,           // age_days
        5,              // access_count
        Some(1.0),      // days_since_access
        Some(new_salience),
    );
    println!("Score after helpful feedback = {:.4}", score);
}

```

### Incorporating Multi-Actor Breadth

```rust
use ai_memory_store::decay::{DecayParams, retention_score_with_breadth};

fn main() {
    let params = DecayParams::default();
    
    let score = retention_score_with_breadth(
        &params,
        30.0,           // age_days
        20,             // access_count
        Some(2.0),      // days_since_access
        None,           // default salience
        5,              // distinct_actors
        0.5,            // breadth_weight
    );
    println!("Score with actor breadth = {:.4}", score);
}

```

## Summary

- The **decay math formula** lives in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs) and combines exponential time decay with logarithmic access reinforcement
- **Retention scoring** weighs three factors: salience-adjusted age decay, recency-weighted access count, and optional social breadth
- The `cold_threshold` parameter (default 0.20) determines eviction eligibility, while pinned pages bypass deletion regardless of score
- User feedback adjusts **salience** within bounded limits (0.25 to 2.0) to prevent permanent locking or instant destruction of pages
- Evicted pages enter a tombstone state for 180 days (`hard_delete_after_days`) before permanent removal

## Frequently Asked Questions

### What is the default half-life for page decay in ai-memory?

With the default `lambda` value of 0.02, pages experience approximately a 35-day half-life through pure time decay. This means a page with default salience (1.0) and no access reinforcement will see its time-decay component halve roughly every 35 days according to the exponential decay formula `exp(-0.02 * age_days)`.

### How does feedback affect the retention score calculation?

Feedback modifies the `salience` variable before the retention formula executes. **Helpful** feedback increments salience by 0.25 up to a maximum of 2.0, while **NotHelpful** decrements by 0.25 down to a minimum of 0.25. **Stale** or **Wrong** feedback immediately floors salience to 0.25. Because salience multiplies the time-decay term, higher values effectively raise the entire retention curve, extending page lifetime.

### What happens when a page's retention score drops below the cold threshold?

When `retention_score` returns a value below `cold_threshold` (default 0.20) and the page is not pinned, it becomes a candidate for the forget sweep. The system initiates a soft-delete via `soft_delete_for_decay_if_latest`, creating a tombstone that persists for `hard_delete_after_days` (180 days by default) before hard deletion and permanent removal from the ancestry chain.

### Can the decay parameters be tuned without modifying source code?

Yes, the `DecayParams` struct exposes all mathematical constants—including `lambda`, `sigma`, `mu`, and `cold_threshold`—as configuration parameters. According to the source in [`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs), these values are exposed via the MCP admin API, allowing runtime adjustment of the retention model's sensitivity without recompiling the ai-memory store.