Building MemStack's Memory Engine and Inspector Dashboard
I shipped MemStack's Memory Engine in two sessions. The engine adds persistent facts to the loader, scores them by recall and age, injects top facts into SessionStart, and surfaces everything in a new dashboard page.
Five MCP Tools
Step 2 wired five mem_* MCP tools: mem_store, mem_correct, mem_recall, mem_inspect, and mem_stats. MemoryStore in src/memstack_skill_loader/memory_engine.py is the append-only versioned fact store. Two read-only helpers, list_facts and stats, went in alongside.
Query-Time Recall Scoring
Step 3 added recall scoring. The score is confidence * exp(-age_days / (HALF_LIFE_BASE=30 + HALF_LIFE_PER_RECALL=15 * recall_count)), floored at 0.5 within 7 days and archived below 0.05. Scoring happens at query time and is never stored, so the curve can be retuned without migration. A deep escape hatch on mem_recall lets you override the score filter.
Diary FACTS Ingestion
Step 4 added diary_ingest.py, a parser that pulls facts from the diary's ## FACTS block. It dedupes on (source_ref, subject, claim) via MemoryStore.fact_exists, so re-running the hook stores nothing new. The diary skill lives at C:/Projects/memstack/skills/diary/SKILL.md, not in the loader repo, so this step touched two repos.
The live diary skill fires from the plugin cache at version 3.5.7, not the edited source. I had to follow the updated protocol manually for this save.
SessionStart Living-Memory Injection
Step 5 built session_context.py. It renders the current project's top recall-scored live facts, plus reserved _global, into the SessionStart additionalContext, each with a provenance tag. The block is hard-budgeted at 1500 tokens using a chars/4 heuristic. The memstack hooks/session-start bash hook folds the module's stdout into the injected context.
Live smoke caught a fail-open breach. A header emoji crashed the module on Windows cp1252 stdout with exit 1 because the stdout.write sat outside main's try. I dropped the emoji, forced UTF-8 via reconfigure plus a byte-buffer fallback, wrapped the write, and added a regression test.
Memory Inspector Dashboard
Step 6 added a Memory Inspector page on port 3333. It shows a project-filtered table of live facts with subject, claim, recall-score bar, provenance, recall count, and status. Click a fact and you get the supersession chain root to tip with status markers and recall history. Two read-only endpoints under /api/ sit behind the same X-Auth-Token gate: 403 without or with a wrong token. Score bars use recall_score at render. Fresh install or unreachable DB shows a friendly empty state, never 500.
Release Shape
Step 7 committed the release shape but did not publish. The loader went to 4.13.0 and the plugin to 3.6.0. The two version tracks are independent by policy and must never be reconciled. I added a new SECURITY.md that routes reports through the cwaffiliateinvestments.com contact page because GitHub private vulnerability reporting is unavailable on a private repo.
Auditing commit messages against the code caught two over-claims this run. Step 4's message said 16 tests ran through the real CLI entry point, but only 5 of 16 actually call main. The message also claimed format drift fails a test, but no test read SKILL.md until I added the drift guard in a separate commit.
Publish ordering is fixed: loader to PyPI first, then plugin propagation. A new skill on an old loader would only hit the harmless fail-open path of diary_ingest. The full suite is 265 tests green. Publish, tag, and propagation are held for the next session's explicit go.