0009 — Retire decay-driven block archival trigger¶
Status: Accepted Date: 2026-08-08 Deciders: elf (curator), Ben
Context¶
curate() has always run _archive_decayed_blocks(): it computes each active
block's recency = exp(-decay_lambda * hours_since_reinforced) from its
DecayTier, and archives (reason "decayed") any block whose recency falls
below PRUNE_THRESHOLD (0.05), except high-weighted-degree "bridge" nodes.
docs/plans/plan_v2_substrate_reevaluation.md bundled this together with
pairwise contradiction detection under "step 7: retire pairwise contradiction
+ decay" as expensive, low-value machinery.
Grounding against the real self-hosted elfmem database
(~/.elfmem/databases/elfmem.db, months of active use) found:
The archival trigger has never fired in production. Meanwhile step 6a
(review_corpus() / elfmem review corpus, shipped ahead of this ADR)
already covers the same underlying need — surfacing blocks that are
long-unused, rarely reinforced, and never confirmed by an outcome — as a
deterministic, zero-LLM-cost, human-gated proposal instead of a silent
automatic archive.
Further grounding found decay_lambda / DecayTier / compute_recency are
not solely used for archival. They are also live inputs to:
memory/retrieval.py::_stage_4_composite_score—recencyis a scoring term on everyframe()/recall()call.curate.py::_prune_decayed_edges— a separate edge-decay mechanism viacompute_lambda_edge, unrelated to block archival.curate.py::_reinforce_top_blocks— the same recency term feeds curate's own top-N reinforcement composite score.operations/mind.py— setsdecay_lambdaon Theory-of-Mind blocks.
ADR 0001 established the project's precedent for scoring-mechanism changes: don't touch what you haven't measured. The archival trigger has direct evidence of inertness; the tier/lambda system as a retrieval-ranking signal does not — deleting the whole system would violate that same precedent for the parts nobody measured.
Alternatives considered¶
- Retire the whole
DecayTier/decay_lambdasystem, matching the plan doc's original "retire decay" framing. Rejected: would silently degrade live retrieval ranking with zero evidence gathered, and would also break edge-decay pruning and curate's own reinforcement scoring — neither of which showed any sign of being inert. - Keep
_archive_decayed_blocks()but tune thresholds (raiseprune_threshold, widen bridge protection). Rejected: doesn't address the actual finding — the mechanism doesn't archive too aggressively, it doesn't archive at all — and 6a already ships a demonstrably-usable, human-gated replacement for the same underlying need. - Retire only the archival trigger, leave decay-tier/lambda machinery fully intact everywhere else. Chosen.
Decision¶
Remove _archive_decayed_blocks(), PRUNE_THRESHOLD, and
BRIDGE_PROTECTION_QUANTILE from operations/curate.py. Remove
MemoryConfig.prune_threshold. curate() is now two phases (edge
pruning/decay, top-N reinforcement) instead of three.
DecayTier, decay_lambda_for_tier(), and compute_recency() are
untouched — they remain load-bearing for live retrieval scoring, edge
decay pruning, and curate's own reinforcement scoring.
Consequences¶
CurateResult.archivedfield removed (a permanently-0 field is worse than no field). Breaking change for any caller reading.archived/to_dict()["archived"]. Migration: usereview_corpus()/elfmem review corpusfor staleness detection, andforget(reason=ArchiveReason.DECAYED)to act on accepted proposals.curate()'s docstring / CLI help / MCP tool description updated to "prune weak/decayed edges, reinforce top knowledge" — archival dropped from the description.MemoryConfig.prune_thresholdremoved. A deployment YAML that still setsmemory.prune_thresholdhas the key silently ignored (Pydantic's defaultextra="ignore"), not an error.src/elfmem/viz/data.pystill renders aprune_threshold/ "archive cliff" line on its decay-curve dashboard chart via its own independent local constant (_PRUNE_THRESHOLD) — now describing a thresholdcurate()no longer enforces. Left untouched; out of scope for this step, flagged as a residual follow-up for whoever next touchesviz/.
Trigger to revisit¶
If a future need for automatic (non-human-gated) archival re-emerges with
real evidence that review_corpus() + human review is insufficient.
References¶
- Original framing:
docs/plans/plan_v2_substrate_reevaluation.md - ADR 0001 — precedent for evidence-gated changes to scoring/decay mechanisms
src/elfmem/operations/corpus_review.py— step 6a, the shipped replacementsrc/elfmem/memory/retrieval.py::_stage_4_composite_score— the live retrieval-scoring consumer ofdecay_lambdathat was not touched- Companion decision: ADR 0010