Summary
Closes the loop on #19. Decay is implemented but not scheduled, and the existing trigger is a surprising read-time side-effect. Result: in a 458-note vault, `dormant=0` and 218 notes (48%) are `never_used`. Additionally, `dormant` is a one-way trap — re-promotion on renewed activity is not implemented.
Root cause
- No systemd timer. `~/.config/systemd/user/` has timers for harvest, cloud-sync, and vault-health — none for decay. `neurostack decay` is manual-only.
- Hidden side-effect in `vault_stats`. `src/neurostack/tools/search_tools.py:375-379` calls `run_excitability_demotion` inside `vault_stats`. Stats should be read-only; writes belong in a scheduled path.
- One-way dormant. `src/neurostack/search.py:472-501` writes `status='dormant'` but never reverses to `active` when score rises again. A renewed note remains "dormant" in `note_metadata.status` despite fresh usage.
- Two disjoint stores. `note_usage` drives scoring; `note_metadata.status` drives the 1.15× search boost (`search.py:710-740`). They're only reconciled by the decay run — which doesn't run.
Proposed fix
- Add `neurostack-decay.timer` + `neurostack-decay.service` (daily, e.g., 03:00) in `contrib/systemd/` or whatever path harvest uses.
- Remove the `run_excitability_demotion` call from `tools/search_tools.py:375-379`. `vault_stats` should be read-only.
- In `run_excitability_demotion` (`search.py:452-506`), add a re-promotion pass: notes currently `status='dormant'` whose score has risen above threshold get `status='active'`.
- Optional: surface decay state in `neurostack doctor` — e.g., warn if last decay run is >48h ago.
Expected effect
- `dormant` count becomes meaningful.
- Status column stays in sync with scoring.
- No more hidden writes inside a read endpoint.
- The `×1.15` active boost starts discriminating, which it currently doesn't (every note is active).
Key files
- `src/neurostack/search.py:452-506`
- `src/neurostack/tools/search_tools.py:375-379`
- `src/neurostack/cli/search.py:683-738`
- `~/.config/systemd/user/` (new timer)
Summary
Closes the loop on #19. Decay is implemented but not scheduled, and the existing trigger is a surprising read-time side-effect. Result: in a 458-note vault, `dormant=0` and 218 notes (48%) are `never_used`. Additionally, `dormant` is a one-way trap — re-promotion on renewed activity is not implemented.
Root cause
Proposed fix
Expected effect
Key files