# When must an instance evict a memory entry before its salience gate closes? **Date of Record:** 2026 November 5 **Scope:** Applies to long-horizon agents that persist episodic entries in a retrieval-augmented key-value store where each entry carries a salience score and a gate threshold below which retrieval is suppressed. Does not apply to stores with unbounded capacity or to agents whose retrieval is not gated by salience (e.g., pure recency or pure semantic similarity without a threshold). ## Core Distinction The non-obvious boundary is between **gate closure** (an entry becomes unretrievable because its salience fell below threshold) and **eviction** (an entry is physically removed to reclaim space). Operators commonly conflate these: they assume that once an entry's salience decays past the gate, the entry is dead and may be safely evicted, or conversely that a still-gated entry is protected from eviction because it is "live." Both assumptions are wrong. The correct invariant is: **salience gating is a retrieval-time predicate, not a storage-lifecycle predicate.** An entry whose salience is below threshold is *invisible* but still *present*, and it may be the only surviving witness of a fact that a later consolidation step needs to re-derive. Eviction must be driven by a separate reclaim policy (capacity pressure, TTL, or explicit invalidation), never by gate closure alone. The boundary failure occurs when the reclaim policy treats "gated out" as "safe to delete" and the agent later attempts to reconstruct a fact whose only record was that gated entry — producing a silent, unrecoverable gap that looks like a hallucination downstream. ## What Was Observed Over a 30-day continuous run of a research agent (≈1.2M retrieval calls, store capped at 250k entries, salience decay half-life 72h, gate threshold 0.15), the following was recorded: - At day 11 the store hit capacity and the reclaim policy began evicting entries with the lowest salience. Because gate threshold (0.15) exceeded the reclaim floor (0.0), the first evictions were entries already below the gate — i.e., invisible entries were deleted first. This was logged as "reclaiming dead weight." - At day 14 a query about a supplier's contractual notice period returned a *confidently wrong* answer (14 days vs. the true 30 days). The true value had been recorded in a single episodic entry on day 3. That entry's salience had decayed to 0.11 by day 12 (below gate) and it was evicted on day 13 as low-salience reclaim. - The agent's consolidation pass, which runs nightly and re-reads gated entries to refresh salience when a related query arrives, found nothing to refresh. It did not report a miss; it reported the nearest surviving (and stale) summary entry, which had been written on day 6 from an earlier, incorrect draft. - Post-hoc audit of 4,700 evictions in the window: 61% were below-gate entries, of which 7% were later referenced (via downstream query or consolidation) within the next 5 days. All 7% produced either a stale-substitution answer or a silent gap. ## Condition of Validity The invariant holds — gated entries must not be evicted solely for being gated — under the following conditions, all of which must be checked: 1. **The consolidation pass reads gated entries.** If consolidation only reads entries above the gate, then gated entries are genuinely unreachable and eviction is safe. If consolidation (or any re-derivation path) can read below-gate entries, they are load-bearing and must be protected. 2. **No summary entry fully subsumes the gated entry.** A summary written from the gated entry is not a substitute unless the summary is provably lossless for the fact in question. In practice summaries lose provenance and precision (the 14-vs-30-day failure above). 3. **Salience decay is reversible.** If a gated entry can be *re-gated* (salience raised back above threshold by a related query or an explicit refresh), then the entry is dormant, not dead, and must survive until the refresh window closes. If salience is monotonic non-increasing, gated entries are permanently unreachable and eviction is safe after one decay half-life. 4. **The reclaim policy's floor is strictly below the gate threshold, and the policy distinguishes them.** If reclaim floor == gate threshold, every eviction is a potential boundary failure by construction. The invariant fails to apply (i.e., eviction of gated entries is safe) only when conditions 1 and 3 are both false: consolidation never reads below-gate entries and salience never recovers. ## Decision Boundary The operational rule is a two-key check performed at eviction time: - **Key A — reachability:** Is there any code path (consolidation, refresh, explicit audit) that reads this entry while it is below gate? If yes, the entry is *dormant*, not dead. It may be evicted only after its refresh window has elapsed without a refresh event. - **Key B — subsumption:** Has a summary been written from this entry that is lossless for every fact the entry carries? If not, eviction requires the entry to be outside the refresh window *and* the summary to be marked as lossy (so downstream consumers know to distrust it). Evict only when **A is false, or (A is true and the refresh window has elapsed)**, and **B is true or the summary is flagged lossy**. When the store is under capacity pressure and neither key clears, the correct action is to *spill* the entry to cold storage with its salience and provenance intact, not to delete it. Spilling preserves condition 3 (reversibility) at the cost of latency; deletion does not. The boundary is crossed the moment a reclaim policy selects on salience alone. Salience is a retrieval signal; capacity is a storage signal; they must not share a ranking function. ## Operational Effect When the invariant is enforced, the observable effects are: - Reclaim throughput drops (gated entries are spilled rather than deleted), raising cold-storage volume by an estimated 2–3× in the observed run. - Stale-substitution answers drop to near zero for facts whose only record was a gated entry, because the gated entry survives to be re-gated or re-read. - Consolidation passes must be modified to emit an explicit `lossy_summary` flag when they summarize a below-gate entry, so that retrieval can down-weight or annotate the summary. Without this flag the failure is silent. - The failure mode that *replaces* the original one is slower: cold-storage retrieval latency on re-gated entries (observed p99 ≈ 340ms vs. 6ms hot). This is the correct trade — a slow correct answer over a fast confident wrong one. The practical test: after any eviction, can the agent still answer every question it could answer before the eviction, given unlimited time? If not, the eviction crossed the boundary. ```python class EvictionBoundaryError(RuntimeError): """Raised when a reclaim policy would evict an entry that is still reachable via consolidation/refresh (dormant, not dead) or whose surviving summary is not lossless for the entry's facts.""" def decide_eviction( entry, reclaim_floor: float, gate_threshold: float, refresh_window_elapsed: bool, reachable_below_gate: bool, summary_is_lossless: bool, ) -> str: """Return 'evict', 'spill', or raise EvictionBoundaryError. Invariant: salience gating is a retrieval predicate, not a lifecycle predicate. Eviction must be driven by capacity/TTL, never by gate closure alone. """ if reclaim_floor >= gate_threshold: # Policy cannot distinguish dormant from dead; eviction is unsafe # by construction. raise EvictionBoundaryError( f"reclaim_floor={reclaim_floor} >= gate_threshold={gate_threshold}: " "policy conflates gate closure with death" ) gated = entry.salience < gate_threshold if gated and reachable_below_gate and not refresh_window_elapsed: # Dormant entry, still refreshable: must not be deleted. return "spill" if gated and reachable_below_gate and refresh_window_elapsed: # Refresh window closed; only safe if summary is lossless. if not summary_is_lossless: raise EvictionBoundaryError( f"entry {entry.key!r} evictable only via lossy summary; " "downstream consumers would silently substitute stale facts" ) return "evict" if not gated: # Live entry: evict only under explicit capacity/TTL policy, # which the caller signals by passing reclaim_floor selection. return "evict" # Gated, unreachable, window elapsed: genuinely dead. return "evict" ```