Skip to content

decay_cycle.py: ACT-R base-level path has no production caller, and its name collides with the live ADAPTIVE_DECAY mechanism #346

Description

@cdeust

Reported by an external reader (Claude Discord) who read mcp_server/core/decay_cycle.py and concluded that heat is ACT-R frequency-driven — B_i = ln(n) − d·ln(L), retrieval feeds heat, therefore ossification risk. That reading is wrong about live behaviour, but the file gives them no way to know it. Verified 2026-08-02.

Two distinct defects

1. Dead code. compute_decay_updates (decay_cycle.py:264) and the whole ACT-R block it dispatches to — compute_actr_base_level:134, actr_activation_to_heat:155, _compute_actr_decay:168 — have no production caller. The consolidation path imports only compute_entity_decay:

mcp_server/handlers/consolidation/decay.py:19:from mcp_server.core.decay_cycle import compute_entity_decay

The only remaining references are tests_py/core/test_decay_cycle.py and tests_py/benchmarks/benchmark_harness.py. This violates coding-standards.md §9: "Dead code … with no current caller. If it's built, it must be called."

2. Name collision with a live, published mechanism. The ADAPTIVE_DECAY reported in the LoCoMo v3 ablation table (ΔMRR −0.0163 / ΔR@10 −0.020, benchmarks/results/ablation/locomo_v3/summary.csv) is not this ACT-R code. The live mechanism is an adaptive rate on the Ebbinghaus path:

  • mcp_server/core/thermodynamics.py:286is_mechanism_disabled(Mechanism.ADAPTIVE_DECAY) inside compute_decay
  • mcp_server/core/pg_recall.py:253
  • mcp_server/infrastructure/memory_config.py:146-148ADAPTIVE_DECAY_ENABLED, ADAPTIVE_DECAY_MIN_RATE=0.90, ADAPTIVE_DECAY_MAX_RATE=0.999

A reader who goes from the file to the ablation table connects two unrelated things, and the published number gets attributed to code that never runs.

Why this matters beyond tidiness

The actual design property — retrieval never resets the decay clock, so a popularity-ossification loop cannot form — is real and currently invisible. update_memory_access (pg_store.py:855-861) writes only last_accessed and access_count; effective_heat() (pg_schema.py:1073-1130) decays from heat_base_set_at, stamped at write. The dead ACT-R block actively argues the opposite to anyone reading the source.

Proposed fix

  1. Delete compute_decay_updates + the three ACT-R functions + _ACT_R_DECAY_D / _ACT_R_NOISE_S, and the tests that exist only to cover them.
  2. If the ACT-R model is wanted later, it comes back with a caller, not before.
  3. Rename or document the surviving ADAPTIVE_DECAY so the ablation table and the code agree on what the name denotes.
  4. State the write-anchored/no-reheat property explicitly where a reader will hit it — decay_cycle.py module docstring and the effective_heat header.

Acceptance

  • grep -rn "compute_decay_updates\|compute_actr_base_level" mcp_server/ tests_py/ returns nothing.
  • The ADAPTIVE_DECAY name resolves to exactly one mechanism, and benchmarks/results/ablation/locomo_v3/summary.csv can be traced to it from the code without ambiguity.
  • No benchmark regression: LoCoMo MRR/R@10 unchanged within the ±0.002 per-row noise floor at n=1986 (the ACT-R path is uncalled, so the expected delta is exactly zero).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions