# Adaptive Recall — Full Build Final Report **Build date:** 2026-07-12 **Sidecar:** `http://127.0.0.1:4380` — Phase 3 (3-channel RRF + ACT-R re-rank) --- ## What was built ### Phase 1 — Foundation (already shipped) - `src/server.js`, `src/qdrant.js`, `src/entities.js`, `src/rrf.js`, `src/embed.js` - 768/846 records in Qdrant `memories_ump` collection (90.6% backfill) - 2-channel RRF (UMP FTS5 + Qdrant cosine) with +50pp hit-rate lift over baseline ### Phase 1G — MCP wire-up (already shipped) - `src/ump-recall-mcp.js` — shim that routes `recall` to sidecar, falls back to canonical UMP - Both Hermes and Krystie configs point at the shim ### Phase 2 — Knowledge Graph Channel (shipped earlier this session) - `src/graph.js` (414 lines): Graph class with BFS neighbors, search entities, persistence - `scripts/build_graph.js`: 2592 nodes / 111 edges / 719 URNs in 553ms - `state/graph.json`: persisted graph artifact - `runGraphChannel()`: extracts query entities, BFS up to 2 hops, score=1/hops - Wired into `/recall` as 3rd channel in RRF **Eval:** 12 unique-to-3ch hits across 20 queries; zero regressions; +176ms latency ### Phase 3 — ACT-R Re-ranker (this session) - `src/actr.js`: pure `activation()` (Anderson 1983 formula) + `minMaxNormalize()` + `rerank()` - Formula: `A_i = -d·ln(age) + β·log1p(freq) + ε·conf` - Defaults: d=0.5, β=1.0, ε=1.0, α=0.3 (blend with RRF) - `lookupMeta()`: fetches UMP time+confidence, sums graph node frequencies - Wired into `/recall` between RRF fusion and hydration - Per-call cache for metadata (fresh per request) **Eval:** 4/20 queries had #1 changed (20%); 84% top-5 set retention; avg latency 1482ms ### Phase 4 — Memory Lifecycle Decay (shipped earlier this session) - `scripts/ump_decay.py`: pure `apply_decay()` with 6 per-kind rates - identity λ=0.0001, semantic 0.001, note 0.003, procedural 0.005, episodic 0.01, working 0.05 - Floor 0.05, never deletes - Atomic writes with timestamped backups - 20/20 tests pass - Cron `eaff2d9683fc` runs nightly at 3am ### Self-improvement framework (shipped earlier today) - 5 cron scripts: skill_gap_detector, spaced_repetition, forage, reflective_journal, self_measure - All wired and scheduled --- ## Test summary | Suite | Tests | Status | |---|---|---| | `test_graph.js` | 27 | PASS | | `test_actr.js` | 27 | PASS (new) | | `test_ump_decay.py` | 20 | PASS | | `test-mcp-shim.js` | 2 scenarios | PASS (sidecar + fallback) | | **Total** | **76** | **76/76 pass** | --- ## Eval summary | Channel combination | Hit-rate lift | Latency | |---|---|---| | Baseline UMP | 0% (reference) | 146ms | | 2-channel RRF (ump+vector) | +50pp recall@10 | 780ms | | 3-channel RRF (+graph) | +60pp, 12 unique wins | 903ms | | 3-channel + ACT-R re-rank | 4/20 #1 changes, 84% top-5 retention | 1482ms | --- ## Files created/modified this session **Created:** - `src/graph.js` (414 lines) - `src/actr.js` (130 lines) - `scripts/build_graph.js` (82 lines) - `scripts/ump_decay.py` (~600 lines) - `scripts/eval_3ch_vs_2ch.py` - `scripts/eval_channel_contribution.py` - `scripts/eval_actr.py` - `test/test_graph.js` - `test/test_actr.js` - `test/test_ump_decay.py` - `test/smoke_recall.py` - `test/smoke_phase3.py` - `state/SCHEMA.md` - `state/graph.json` (589KB, 2592 nodes) - `state/PHASE_2_AND_4_REPORT.md` - `state/PHASE_3_REPORT.md` (this file) **Modified:** - `src/server.js` (398 → 482 lines): graph channel + ACT-R + health/recall upgrades - `/root/.hermes/config.yaml` (patched via terminal): ump block points at MCP shim - `/root/.hermes/profiles/krystie/config.yaml` (already patched): same --- ## Live endpoints ``` GET /health — phase, upstreams, graph stats, ACT-R config POST /recall — 3-channel RRF + ACT-R re-rank POST /embed — Ollama → Qdrant upsert (existing, unchanged) ``` Config via env: - `PORT` (default 4380) - `GRAPH_ENABLED`, `GRAPH_FILE`, `GRAPH_DEPTH` (default 2) - `ACTR_ENABLED`, `ACTR_ALPHA` (default 0.3), `ACTR_D` (default 0.5) - `CHANNEL_TOP_N` (default 20), `RERANK_POOL` (auto: min(60, cap*3)) --- ## What's left (optional) 1. **Wire access_count tracking** — once retrieval bumps `access_count` and `last_accessed_at`, the ACT-R frequency term becomes real signal. Right now graph-node frequency is a proxy. 2. **Tune ACT-R d and α** — currently both fixed. Could A/B test 0.2 vs 0.5 vs 0.8 for d, and 0.1 vs 0.3 vs 0.5 for α. 3. **Co-occurrence graph layer** — supplement the typed-relation graph with edges for every pair of entities that co-occur in a record. Would significantly improve graph channel recall. 4. **Pre-existing MCP shim bug** — `remember`/`get`/`revise`/`forget`/`feedback` fail because shim skips the `initialize` step before proxying to canonical UMP subprocess. Out of scope here.