How ai-memory Distinguishes Between TTL Expires-At, Pinned Pages, and Feedback Signals in Retention
ai-memory uses a strict three-tier hierarchy: TTL expiration triggers hard deletion first, pinned pages bypass all decay logic entirely, and feedback signals adjust salience scores only for non-pinned, non-expired pages.
The ai-memory system implements a deterministic retention pipeline that balances automatic cleanup, manual curation, and community feedback. Understanding how these three mechanisms interact is essential for managing knowledge bases that persist across sessions without growing indefinitely.
The Three Retention Mechanisms in ai-memory
ai-memory stores pages with three orthogonal concepts governing their lifespan. Each mechanism operates at a different layer of the retention decision, creating a predictable priority order.
TTL-Based Expires-At: The Hard Deadline
The expires_at field in PageMeta enforces absolute temporal boundaries. Parsed from Markdown front-matter in crates/ai-memory-wiki/src/wiki.rs【L1925-L1950】, this timestamp is evaluated by the reader with a WHERE expires_at IS NULL OR expires_at > now clause in crates/ai-memory-store/src/reader.rs【L51-L56】.
Pages with elapsed TTL receive hard deletion regardless of other signals. The admin delete path in crates/ai-memory-mcp/src/server.rs【L2390-L2393】 executes this removal during retention sweeps.
// TTL page – expires in 7 days
let ttl_req = wiki::WriteRequest {
path: "notes/todo.md".into(),
body: "Buy milk".into(),
meta: serde_json::json!({
"title": "Todo",
"expires_at": "2026-09-07"
}),
..Default::default()
};
wiki.write_page(ttl_req).await?;
Pinned Pages: Immunity from Decay
The pinned boolean flag, set via front-matter (pinned: true) or implicitly for slot pages (is_slot_path), grants complete eviction immunity. The flag is read and written in crates/ai-memory-wiki/src/wiki.rs【L560-L564】 and persists in the pages.pinned database column.
The retention formula completely ignores the decay term for pinned pages. In crates/ai-memory-store/src/reader.rs, the query includes pinned = 1 as a disjunctive clause ensuring pinned rows survive breadth and age predicates. The slot visibility tests in crates/ai-memory-store/tests/slot_visibility.rs【L40-L53】 verify this behavior.
// Pinned page via front-matter
let pinned_req = wiki::WriteRequest {
path: "notes/pinned.md".into(),
body: "Critical design decision".into(),
meta: serde_json::json!({
"title": "Design",
"pinned": true
}),
..Default::default()
};
wiki.write_page(pinned_req).await?;
Feedback Signals: Salience Adjustment
Community feedback mutates page salience without triggering deletion. Stored in the append-only page_feedback table, each row tracks kind (helpful, not_helpful, stale, wrong), optional reason, and author. The record_page_feedback function in crates/ai-memory-store/src/ops.rs【L1651-L1694】 handles insertion.
The decay::salience_after_feedback function in crates/ai-memory-store/src/decay.rs【L141-L152】 computes updated salience values. These feed into retention_score in crates/ai-memory-store/src/decay.rs【L48-L66】, where positive feedback elevates retention priority and negative feedback accelerates decay.
let feedback = Feedback {
page_path: "notes/pinned.md".into(),
kind: FeedbackKind::Helpful,
reason: Some("clarifies the architecture".into()),
author_id: user.id,
};
store.writer.record_page_feedback(feedback).await?;
The Retention Hierarchy: Priority Order
The ai-memory retention pipeline applies these mechanisms in strict sequence:
- Hard-delete check — TTL expiration takes precedence; elapsed timestamps trigger immediate removal
- Pinned guard — pinned pages bypass decay scoring entirely, becoming eviction-immune
- Feedback-adjusted salience — for remaining pages, retention_score =
f(age, access, salience, …)
This ordering ensures TTL → Pinned → Feedback-adjusted retention determinism. A page with negative feedback survives if pinned. A pinned page with expired TTL still gets deleted. Feedback modulates decay only within the non-pinned, non-expired subset.
Running the Retention Sweep
Execute the full four-pass sweep via CLI:
ai-memory retention
This command, implemented in crates/ai-memory-cli/src/cli.rs【L106-L108】, applies the TTL guard, pinned guard, feedback-adjusted salience calculation, and finally evicts low-score pages.
Key Source Files
| File | Responsibility |
|---|---|
crates/ai-memory-wiki/src/wiki.rs |
Parses expires_at and pinned from front-matter |
crates/ai-memory-store/src/reader.rs |
Filters by TTL and propagates pinned flag in queries |
crates/ai-memory-store/src/ops.rs |
Inserts feedback records and manages salience updates |
crates/ai-memory-store/src/decay.rs |
Computes salience_after_feedback and retention_score |
crates/ai-memory-cli/src/cli.rs |
Exposes the retention command |
crates/ai-memory-mcp/src/server.rs |
Admin TTL deletion and feedback API endpoints |
Summary
- TTL expires_at provides hard deadlines with absolute priority in eviction decisions
- Pinned pages receive complete immunity from decay-based retention scoring
- Feedback signals adjust salience scores bidirectionally but only affect non-pinned, non-expired pages
- The three-tier hierarchy guarantees predictable, deterministic cleanup behavior
Frequently Asked Questions
What happens if a page has both an expired TTL and is pinned?
The TTL check executes first in the retention pipeline. According to crates/ai-memory-mcp/src/server.rs【L2390-L2393】, expired timestamps trigger hard deletion regardless of pinned status. Pinning does not override temporal expiration.
Can feedback signals alone delete a page from ai-memory?
No. Feedback modifies salience through salience_after_feedback in crates/ai-memory-store/src/decay.rs【L141-L152】, which influences the retention score. Deletion requires the score to fall below threshold during the retention sweep, and pinned pages never reach this evaluation stage.
How do slot pages automatically become pinned?
Slot pages receive implicit pinning through the is_slot_path check in crates/ai-memory-wiki/src/wiki.rs【L560-L564】, setting pinned = true without explicit front-matter. This ensures slot-based organizational content persists indefinitely unless TTL-specified.
Does ai-memory support negative TTL or retroactive expiration?
The parser in crates/ai-memory-wiki/src/wiki.rs【L1925-L1950】 validates expires_at as a timestamp. Past dates are accepted and trigger immediate hard deletion on the next retention sweep, effectively enabling retroactive expiration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →