diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4e9ad21..05cb668 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -79,7 +79,7 @@ cd apps/dashboard && pnpm build
# Debug build
cargo build -p vestige-mcp
-# Release build (22MB binary with embedded dashboard)
+# Release build (23MB binary with embedded dashboard)
cargo build --release -p vestige-mcp
# The release binary is at target/release/vestige-mcp
diff --git a/README.md b/README.md
index 625f944..bf6b6ab 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ So that's what Vestige is. Everyone else built a memory that **remembers**. I tr
It's one Rust binary. It runs entirely on your machine. It never phones home. And there's a 60-second start right below.
-> ๐๏ธ **The 60-second version** of this whole story, the one I give in person, lives in [`demo/PITCH-v2-causebench.md`](demo/PITCH-v2-causebench.md). If you've got a minute, read that first. It's the clearest way to *get* why this matters.
+> ๐๏ธ **Want the 60-second version?** Run the [Postdict demo](demo/README.md): one command, a throwaway DB, and you watch Vestige reach backward through time to a root cause a vector search can't find. It's the clearest way to *get* why this matters.
---
@@ -272,7 +272,7 @@ Registering the server exposes the tools; a short instruction tells the agent *w
| | |
|---|---|
-| **Language** | Rust 2024 (MSRV 1.91), **86,000+ lines** |
+| **Language** | Rust 2024 (MSRV 1.91), **92,000+ lines** |
| **Binary** | ~23MB, single file |
| **Embeddings** | Nomic Embed Text v1.5 (768dโ256d Matryoshka, 8192 ctx); Qwen3 optional |
| **Vector search** | USearch HNSW (โ20ร faster than FAISS) |
@@ -299,7 +299,7 @@ Registering the server exposes the tools; a short instruction tells the agent *w
### If your agent should remember what you taught it yesterday, star it. โญ
-86,000+ lines of Rust ยท 13 tools ยท 30 cognitive modules ยท 130 years of memory research ยท one 23MB binary that never phones home.
+92,000+ lines of Rust ยท 13 tools ยท 30 cognitive modules ยท 130 years of memory research ยท one 23MB binary that never phones home.Built by @samvallad33 ยท AGPL-3.0 ยท 100% local, 100% yours
diff --git a/crates/vestige-mcp/README.md b/crates/vestige-mcp/README.md
index 2547e42..ee65658 100644
--- a/crates/vestige-mcp/README.md
+++ b/crates/vestige-mcp/README.md
@@ -54,17 +54,30 @@ HTTP and dashboard bearer tokens are generated locally; see
## Current Tool Surface
-The server exposes the current unified MCP tools from
-[`src/server.rs`](src/server.rs), including:
+As of v2.2, the server advertises **13 consolidated tools** from
+[`src/server.rs`](src/server.rs):
-- `session_context`
-- `search`, `smart_ingest`, `memory`, `codebase`, `intention`
-- `deep_reference`, `cross_reference`, `contradictions`
-- `dream`, `explore_connections`, `predict`
-- `memory_health`, `memory_graph`, `composed_graph`, `system_status`
-- `importance_score`, `find_duplicates`
-- `consolidate`, `memory_timeline`, `memory_changelog`
-- `backup`, `export`, `restore`, `gc`, `suppress`
+- `recall` โ unified retrieval (folds `search` + `deep_reference` +
+ `cross_reference` + `contradictions`); modes `lookup`/`reason`/`contradictions`
+- `smart_ingest` โ Prediction-Error-gated ingestion (single + batch)
+- `memory` โ get / edit / promote / demote / state / purge
+- `graph` โ chains, associations, bridges, predictions, force-directed export
+ (folds `explore_connections` + `predict` + `memory_graph` + `composed_graph`)
+- `maintain` โ consolidate / dream / gc / importance_score / backup / export / restore
+- `dedup` โ scan / plan_merge / plan_supersede / apply / undo / protect / policy
+ (folds `find_duplicates` + the 7 Phase-3 merge tools)
+- `memory_status` โ health / retention / timeline / changelog
+ (folds `system_status` + `memory_health` + `memory_timeline` + `memory_changelog`)
+- `suppress` โ top-down active forgetting (Anderson 2025 + Davis Rac1)
+- `backfill` โ Retroactive Salience Backfill (Cai 2024 Nature), the flagship
+- `codebase` โ per-project patterns and decisions
+- `intention` โ set / check / update / list future triggers
+- `source_sync` โ external-source connectors (GitHub, Redmine)
+- `session_start` โ one-call session initialization (renamed from `session_context`)
+
+The ~22 pre-consolidation names (`search`, `deep_reference`, `dream`,
+`consolidate`, `memory_graph`, โฆ) still work as **hidden back-compat aliases**
+dispatched in `handle_tools_call`, so existing configs keep working.
See the root [`README.md`](../../README.md) and
[`docs/AGENT-MEMORY-PROTOCOL.md`](../../docs/AGENT-MEMORY-PROTOCOL.md) for
diff --git a/crates/vestige-mcp/src/server.rs b/crates/vestige-mcp/src/server.rs
index 571e2c6..7f86bcd 100644
--- a/crates/vestige-mcp/src/server.rs
+++ b/crates/vestige-mcp/src/server.rs
@@ -240,8 +240,11 @@ impl McpServer {
/// Handle tools/list request
async fn handle_tools_list(&self) -> Result {
- // v2.2: 12 advertised tools after Layer-1 Tool Consolidation
- // (verified by `tools.len() == 12` in test_tools_list_returns_all_tools).
+ // v2.2: 13 advertised tools after Layer-1 Tool Consolidation
+ // (12 consolidated: dedup + memory_status + graph + maintain + recall,
+ // session_context renamed; plus the flagship `backfill`, a distinct
+ // cognitive primitive kept separate from `maintain`).
+ // Verified by `tools.len() == 13` in test_tools_list_returns_all_tools.
// 22 deprecated/folded names still work as hidden redirects in
// handle_tools_call. See docs/launch/tool-consolidation-v2.2.0.md.
let mut tools = vec![
diff --git a/demo/PITCH-FINAL-spoken.md b/demo/PITCH-FINAL-spoken.md
deleted file mode 100644
index 70173cc..0000000
--- a/demo/PITCH-FINAL-spoken.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Vestige โ THE PITCH THAT CHANGED EVERYTHING (final, condensed cut)
-
-Sam's stage pitch โ ~45-50s spoken, for a multi-pitch event with a separate
-networking hour afterward. Solo founder, first person, no slides/terminal on
-stage (the demo happens 1:1 at networking on Sam's pre-loaded laptop). Two seams
-closed from the draft: opener "hours"->"days" (matches "days ago"), and "AI
-agents that touch" (plural). NAME = Vestige.
-
----
-
-## THE PITCH
-
-> Your bug was born days before it crashed โ but you just can't remember where.
->
-> When production breaks, you lose days hunting a bug. The cause is usually a
-> quiet change you made days ago that looks nothing like the error it created.
->
-> Today's memory systems fail here, because they look for what *looks like* the
-> problem. But a root cause never looks like the bug it creates.
->
-> **[beat]**
->
-> It's like blaming a goalie for a 90th-minute World Cup goal โ when the real
-> failure was a silent midfield turnover fifteen minutes earlier. They're
-> searching the goal line. Vestige traces the match backward.
->
-> A 2024 Nature paper proved the human brain rewinds time to link a sudden shock
-> to the quiet memory that caused it. Vestige does the exact same thing for
-> software.
->
-> Everyone else built a memory that remembers. I built the first one that
-> *realizes.* Millions of AI agents that touch production will hit this wall.
-> That's not a feature โ that's the next layer of the dev stack.
->
-> I'm Sam. I created Vestige from my tiny Chicago apartment โ the first memory
-> that finds the cause, not the lookalike.
->
-> Come find me after. I'll show you in thirty seconds.
-
----
-
-## THE LINES THAT WIN THE ROOM
-- **Opener (pattern interrupt):** "Your bug was born days before it crashed โ but you just can't remember where."
-- **The detonation (THE line):** "A root cause never looks like the bug it creates." โ beat of silence after.
-- **The soccer proof (the secret weapon โ makes ANYONE get it):** "...blaming the goalie for a 90th-minute goal when the real failure was a midfield turnover fifteen minutes earlier. They're searching the goal line. Vestige traces the match backward."
-- **The category (the quotable):** "Everyone else built a memory that remembers. I built the first one that realizes."
-- **The TAM (the money line):** "That's not a feature โ that's the next layer of the dev stack."
-- **The closer (fits the networking format):** "Come find me after. I'll show you in thirty seconds."
-
-## STAGE RULES
-1. NEVER open with "Hi, I'm Sam, thanks." Walk out, hold the room one beat, THEN
- the opener. Your name comes near the END.
-2. The detonation line โ say it, then a real beat of silence before the soccer line.
-3. The soccer analogy is your weapon โ deliver it conversationally, like you're
- explaining it to a friend. It's what makes a NON-engineer get it instantly.
-4. "Built it alone, from a Chicago apartment" is a flex. Own it.
-5. End on "I'll show you in thirty seconds." Don't say thank you. Let it hang.
-
-## NETWORKING (the demo happens HERE, not on stage)
-- The stage pitch's only job: be the MOST memorable founder so they seek you out
- in the networking hour, out of all the other pitches.
-- Have the seeded demo (`./demo/postdict-demo.sh`) ALREADY RUNNING / pre-warmed on
- your laptop so when someone walks up you show it in ~10 seconds, no boot fumble.
-- Demo from STRENGTH: show YOUR seeded scenario (instant, reproducible). Do NOT
- promise to debug their codebase live โ Vestige has to be recording their history
- first. If asked "does it work on mine?": "it has to record your history first โ
- give me your repo for a day and I'll show you it catch a real one." (That's the
- pilot hook, said 1:1, never from stage.)
-
-## DELIVERY MAP
-- "born days before it crashed" โ land "born" with weight.
-- "a root cause never looks like the bug it creates" โ then a real beat of silence.
-- soccer line โ conversational, build to "Vestige traces the match backward."
-- "It runs locally" energy on "Vestige does the exact same thing for software."
-- "the first one that realizes" โ let it land.
-- "the next layer of the dev stack" โ slow, final. Mic-drop.
-- "I'll show you in thirty seconds." โ quiet, confident, then stop.
diff --git a/demo/funding-demo-script.md b/demo/funding-demo-script.md
deleted file mode 100644
index 0b52bc3..0000000
--- a/demo/funding-demo-script.md
+++ /dev/null
@@ -1,129 +0,0 @@
-# Postdict โ the 60-second funding demo
-
-**Audience:** investors. **Goal:** they see a category, a moat, and a market โ not a feature.
-**The thesis, in four words:** *relevance does not equal resemblance.*
-
-Format: live terminal, one take, you talking over it. ~60s. Punchy. No slides.
-**Rule:** the monologue earns ~20 seconds. The rest is the terminal *doing the impossible* on screen.
-
----
-
-## THE SCRIPT (what's on screen ยท what you say)
-
-### [0:00โ0:15] โ THE FLAWED AXIOM (your cold open, verbatim)
-
-**On screen:** a clean dark terminal, one line:
-`relevance โ resemblance`
-
-**You say:**
-> "Every major AI memory framework on Earth โ every VC-backed startup, every
-> native platform layer โ is built on one flawed assumption: that **relevance
-> equals resemblance.** They turn your text into vector embeddings, and when
-> something breaks, they search for memories that *look similar* to the problem.
->
-> Here's what blows that foundation to pieces: **a root cause never looks like
-> the bug it creates.** So the entire industry is searching in the one place the
-> answer can never be."
-
-*(Stop. Let it sit one beat. "Relevance equals resemblance" is the line they repeat to their partners.)*
-
----
-
-### [0:15โ0:28] โ MAKE IT CONCRETE (type it live)
-
-**On screen:**
-```
-$ vestige ingest "Set API_TIMEOUT=2 in the deploy env" --ago-days 3 # the quiet cause
-$ vestige ingest "500 error in the billing service" --ago-days 20 # an old lookalike
-$ vestige ingest "Service crashed: 500 on the auth endpoint" # today's crash
-```
-
-**You say:**
-> "Watch. A one-line config change three days ago โ forgotten. An old, unrelated
-> 500 error weeks back. And today, the auth service crashes. Now ask: which past
-> memory *caused* today's crash? A vector database ranks by resemblance โ so to
-> it, today's crash looks most like that old billing 500. **The thing that looks
-> similar is never the thing that caused it.**"
-
----
-
-### [0:28โ0:45] โ THE PROOF (the money shot ยท slow down here)
-
-**On screen:** `$ vestige backfill --contrast` โ
-```
-โโ 1. SIMILARITY SEARCH ยท keyword (BM25) โโ
- 1. 500 error in the billing service โ top match (WRONG)
- โ ranked by RESEMBLANCE. its top hit is a lookalike, not the cause.
-
-โโ 2. POSTDICT (reach backward for the CAUSE) โโ
- #1 Set API_TIMEOUT=2 in the deploy env
- โฉ reached back 3.0 days before the failure
- ๐ causal join: api_timeout (RIGHT)
-```
-
-**You say (let the `โฉ reached back 3.0 days` line hold in silence for a full beat):**
-> "Same database. Same query. Similarity search returns the lookalike โ confident,
-> and wrong. Postdict reaches **backward three days** and finds the actual cause.
-> Not because it's similar โ because it's **causally upstream.** This is memory
-> with hindsight: the 'ohhh, *that's* why' moment, automatic."
-
----
-
-### [0:45โ0:55] โ THE MOAT (kill the "can't they just add this?" objection)
-
-**You say:**
-> "Two reasons this is defensible. One: it's a faithful port of a 2024 *Nature*
-> result โ the brain reaches *backward* in time to find causes, and it's
-> backward-*only*, which is exactly right, because a root cause is always in the
-> past. We didn't invent this; we ported the algorithm evolution already
-> perfected. Two: the incumbents can't bolt this on. Their entire architecture
-> **is** the flawed axiom. To do this you rebuild memory from the cognitive
-> science up โ which we already did, and it's running locally, today."
-
----
-
-### [0:55โ1:00] โ THE MARKET + THE ASK
-
-**On screen:** `the first memory that finds the cause, not the lookalike.`
-
-**You say:**
-> "Every AI agent that writes code, runs infrastructure, or touches production
-> hits root causes it can't explain. That's the entire agentic market, and it's
-> on fire. We're not a better memory โ we're the first memory that **reasons
-> backward.** Local-first, reproducible, running now. We're raising [X] to make
-> every agent debug like a senior engineer. The seed's in the repo โ run it
-> yourself."
-
----
-
-## WHY THIS OPENING IS STRONGER (and how to deliver it)
-
-Your framing beats "category error" because it names the **mechanism** of the
-error, not just that one exists:
-
-- **"Relevance equals resemblance"** is a *diagnosis* โ it tells the investor
- precisely what's broken (the axiom) in four words. "Category error" only says
- *that* something's broken.
-- **The one-two punch:** state the flawed axiom โ detonate it with the fact
- ("a root cause never looks like the bug it creates"). The investor *feels* the
- foundation crack. That's the moment they lean in.
-
-**Delivery rules:**
-1. **The monologue is 20 seconds, max.** Investors fund what they *see* work, not
- what they hear claimed. Get to the terminal fast; let the contrast carry the
- weight.
-2. **Memorize two sentences.** The axiom: *"They believe relevance equals
- resemblance."* The detonation: *"A root cause never looks like the bug it
- creates."* Everything else can be loose.
-3. **Silence is the tool at 0:28โ0:45.** When `โฉ reached back 3.0 days` hits the
- screen, say nothing for a full second. The image does the selling.
-4. **The moat answer is non-optional.** Every investor thinks "can't Mem0 add
- this?" Answer it *before* they ask โ their architecture is the axiom. That's
- what converts "neat" into "fundable."
-5. **End on the market, not the demo.** "Every agent that touches production" is
- the TAM. The demo earns the right to say it; don't bury it under the feature.
-
-## THE THREE LINES THAT DO THE WORK
-- **The axiom (the hook):** "Every AI memory framework believes relevance equals resemblance."
-- **The detonation (the thesis):** "A root cause never looks like the bug it creates."
-- **The category (the close):** "We're not a better memory. We're the first memory that reasons backward."
diff --git a/demo/funding-demo-v2-blow-it-open.md b/demo/funding-demo-v2-blow-it-open.md
deleted file mode 100644
index cfb421f..0000000
--- a/demo/funding-demo-v2-blow-it-open.md
+++ /dev/null
@@ -1,117 +0,0 @@
-# Postdict โ the 60-second SPOKEN pitch (solo founder, first person)
-
-**This is a SPEECH.** You, on stage, looking at 50-100 engineers / founders /
-investors. No terminal. No slides. No screen. Just your voice and the room.
-
-**First person. "I built this." One person.** For a solo founder that's a
-*strength* โ it tells investors you can ship alone, which is exactly what they
-bet on. Never say "we."
-
-Built for the *ear*: short sentences, hard stops, one line they'll repeat in the
-hallway. Read the **[beats]** as pauses โ silence is your loudest move on stage.
-
----
-
-## THE PITCH (โ60 seconds, spoken)
-
-> Every engineer in this room has lost a day to the same thing.
->
-> Production breaks. You burn hours hunting. And the cause turns out to be one
-> line you changed three days ago โ and forgot.
->
-> **[pause]**
->
-> The bug looked nothing like that change. So you never connected them. You just
-> suffered until you stumbled onto it.
->
-> **[pause]**
->
-> Now we're handing that exact job โ debugging โ to AI agents. And every AI
-> memory system on Earth is about to fail at it. Mem0, Zep, all of them. Because
-> they're built on one assumption: that *relevance equals resemblance.* They
-> search your memory for whatever *looks like* the problem.
->
-> **[slow down โ this is the line]**
->
-> But a root cause never looks like the bug it creates.
->
-> **[full stop. let it hang.]**
->
-> So the entire industry is searching in the one place the answer can never be.
->
-> **[pause]**
->
-> So I built the opposite. When your agent hits a failure, my memory reaches
-> *backward in time* โ and finds the quiet change, days earlier, that actually
-> caused it. The one no similarity search will ever surface, because it isn't
-> *similar* โ it's *upstream.*
->
-> I didn't invent this. I ported it from your brain. There's a 2024 *Nature*
-> paper: when something goes wrong, the brain reaches backward to find the cause
-> โ backward only, because a cause is always in the past. I turned that into
-> software. It runs locally. Today.
->
-> **[pause]**
->
-> And the incumbents can't copy it โ their whole architecture *is* the wrong
-> assumption. To do this, you rebuild memory from the brain up. I already did.
->
-> **[land it]**
->
-> Everyone else built a memory that *remembers.* I built the first one that
-> *realizes.* Every AI agent that touches production needs it โ that's the whole
-> market, and it's on fire.
->
-> I'm Sam. This is Postdict. The first memory that finds the *cause*, not the
-> lookalike. Come find me โ I'll prove it on a laptop in thirty seconds.
-
----
-
-## HOW TO DELIVER IT (this is 80% of the win on a stage)
-
-1. **Memorize three sentences. Improvise the rest.** If you only nail three,
- nail these:
- - **The wound:** *"The cause turns out to be one line you changed three days ago โ and forgot."*
- - **The detonation:** *"A root cause never looks like the bug it creates."*
- - **The category:** *"Everyone else built a memory that remembers. I built the first one that realizes."*
-
-2. **The detonation line is the whole pitch.** Walk *toward* the audience as you
- say it. Then STOP. Say nothing for two full seconds. That silence is you
- letting 100 people independently realize you're right. Do not rush it.
-
-3. **Open about THEM, not you.** The first 15 seconds is their pain, not your
- product. When heads nod because they've lived it, the room is yours โ you
- haven't even said what you do yet.
-
-4. **"I built this" is a flex, not a weakness.** Solo means you can ship without
- a team โ investors fund that. Say "I built," "I ported," "I already did it."
- Own it.
-
-5. **Short sentences. Hard stops.** On a stage, a long sentence loses the back
- row. Every sentence above is built to be said in one breath. Trust the
- periods.
-
-6. **Don't demo on stage โ *promise* the demo.** "Come find me, I'll prove it on
- a laptop in 30 seconds" is stronger than fumbling a terminal in front of 100
- people. It pulls the serious ones to you afterward, one-on-one, where deals
- actually start.
-
-7. **End on your name + the one-liner.** "I'm Sam. This is Postdict. The first
- memory that finds the cause, not the lookalike." That's what they Google in
- the parking lot.
-
----
-
-## THE 30-SECOND VERSION (if you only get a hallway / elevator)
-
-> "Every AI memory tool searches for what your bug *looks like.* But a root cause
-> never looks like the bug it creates โ it's a config change from three days ago
-> that looks nothing like the crash. So they all miss it. I built the first
-> memory that reaches *backward in time* to find the actual cause. Ported it
-> straight from a *Nature* paper on how the brain does it. Every agent that
-> touches production needs it. I'll show you on a laptop right now."
-
-## THE ONE LINE (if you get five seconds)
-
-> "I built the first AI memory that finds the *cause* of a bug, not the lookalike
-> โ because a cause never looks like the bug it creates."
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
index 450cb71..9e8da00 100644
--- a/docs/CONFIGURATION.md
+++ b/docs/CONFIGURATION.md
@@ -310,7 +310,7 @@ vestige update --sandwich-companion
**Pin to specific version:**
```bash
-vestige update --version v2.1.21
+vestige update --version v2.2.0
```
**Manage the optional Cognitive Sandwich layer without updating binaries:**
diff --git a/docs/VESTIGE_STATE_AND_PLAN.md b/docs/VESTIGE_STATE_AND_PLAN.md
index 5f22469..2ab044b 100644
--- a/docs/VESTIGE_STATE_AND_PLAN.md
+++ b/docs/VESTIGE_STATE_AND_PLAN.md
@@ -15,14 +15,29 @@ For current user-facing release information, use:
## Current Release Shape
-Vestige v2.1.21 is the "Agent-Neutral Hardening" release. Its public scope is:
+Vestige v2.2.0 is the "Retroactive Salience + Tool Consolidation" release. Its
+public scope is:
+
+- Retroactive Salience Backfill (Cai 2024 *Nature*): on a recorded failure,
+ reach backward in time to promote the causally-upstream root cause. Auto-fires
+ in the consolidation pass; also exposed as the `backfill` MCP tool and
+ `vestige backfill` CLI
+- MCP tool consolidation: 34 tools collapse to 13 advertised
+ (`recall`, `smart_ingest`, `memory`, `graph`, `maintain`, `dedup`,
+ `memory_status`, `suppress`, `backfill`, `codebase`, `intention`,
+ `source_sync`, `session_start`); the pre-consolidation names remain
+ dispatchable as hidden back-compat aliases
+- `deep_reference`/`recall` retrieval engine upgrades: F32 embeddings (was I8),
+ Reciprocal Rank Fusion, and claim-vs-memory contradiction checks
+
+It carries forward the local-first hardening baseline from prior releases:
- stdio MCP as the default agent transport, with HTTP MCP opt-in only
- binary-only `vestige update` by default
- delete and purge confirmation parity for destructive memory removal
-- portable sync fixes for purge tombstones, UPSERT merge, and vector index
+- portable sync handling for purge tombstones, UPSERT merge, and vector index
reloads
-- safer release packaging with dashboard freshness checks and checksums
+- release packaging with dashboard freshness checks and checksums
- agent-neutral memory instructions for any MCP-compatible client
The release keeps the local-first baseline intact. Heavy model hooks, local
@@ -69,7 +84,14 @@ Vestige is organized as:
- `packages/vestige-init`: installer helper
- `docs`: user and integration documentation
-## v2.1.21 Implementation Notes
+## v2.2.0 Implementation Notes
+
+The advertised MCP surface is 13 tools (see `handle_tools_list` in
+`crates/vestige-mcp/src/server.rs`, verified by `test_tools_list_returns_all_tools`).
+Consolidation is a facade: the ~22 folded/renamed names still dispatch through
+`handle_tools_call` as hidden aliases, so existing client configs keep working.
+`backfill` is deliberately advertised as its own tool rather than folded into
+`maintain`, because backward causal promotion is a distinct cognitive primitive.
HTTP MCP is disabled unless the user passes `--http`, passes `--http-port`, or
sets `VESTIGE_HTTP_ENABLED=1`. The stdio MCP server remains the portable default
diff --git a/docs/adr/0001-pluggable-storage-and-network-access.md b/docs/adr/0001-pluggable-storage-and-network-access.md
deleted file mode 100644
index c150c70..0000000
--- a/docs/adr/0001-pluggable-storage-and-network-access.md
+++ /dev/null
@@ -1,303 +0,0 @@
-# ADR 0001: Pluggable Storage Backend, Network Access, and Emergent Domains
-
-**Status**: Accepted
-**Date**: 2026-04-21
-**Related**: [docs/prd/001-getting-centralized-vestige.md](../prd/001-getting-centralized-vestige.md)
-
----
-
-## Context
-
-Vestige v2.x runs as a per-machine local process: stdio MCP transport, SQLite +
-FTS5 + USearch HNSW in `~/.vestige/`, fastembed locally for embeddings. This is
-ideal for single-machine single-agent use but blocks three real needs:
-
-- **Multi-machine access** -- same memory brain from laptop, desktop, server
-- **Multi-agent access** -- multiple AI clients against one store concurrently
-- **Future federation** -- syncing memory between decentralized nodes (MOS /
- Threefold grid)
-
-SQLite's single-writer model and lack of a native network protocol make it
-unsuitable as a centralized server. PostgreSQL + pgvector collapses our three
-storage layers (SQLite, FTS5, USearch) into one engine with MVCC concurrency,
-auth, and replication.
-
-Separately, Vestige today has no notion of domain or project scope -- all memories
-share one namespace. For a multi-machine brain, users want soft topical
-boundaries ("dev", "infra", "home") without manual tenanting. HDBSCAN clustering
-on embeddings produces these boundaries from the data itself.
-
-The PRD at `docs/prd/001-getting-centralized-vestige.md` sketches the full design.
-This ADR records the architectural decisions and resolves the open questions from
-that document.
-
----
-
-## Decision
-
-Introduce two new trait boundaries, a network transport layer, and a domain
-classification module. All four changes ship in parallel phases.
-
-**Trait boundaries:**
-
-1. `MemoryStore` -- single trait covering CRUD, hybrid search, FSRS scheduling,
- graph edges, and domains. One big trait, not four.
-2. `Embedder` -- separate trait for text-to-vector encoding. Storage never calls
- fastembed directly. Callers (cognitive engine locally, HTTP server remotely)
- compute embeddings and pass them into the store.
-
-**Backends:**
-
-- `SqliteMemoryStore` -- existing code refactored behind the trait, no behavior
- change.
-- `PgMemoryStore` -- new, using sqlx + pgvector + tsvector. Selectable at runtime
- via `vestige.toml`.
-
-**Network:**
-
-- MCP over Streamable HTTP on the existing Axum server.
-- API key auth middleware (blake3-hashed, stored in `api_keys` table).
-- Dashboard uses the same API keys for login, then signed session cookies for
- subsequent requests.
-
-**Domain classification:**
-
-- HDBSCAN clustering over embeddings to discover domains automatically.
-- Soft multi-domain assignment -- raw similarity scores stored per memory, every
- domain above a threshold is assigned.
-- Conservative drift handling -- propose splits/merges, never auto-apply.
-
----
-
-## Architecture Overview
-
-### Component Breakdown
-
-1. **`Embedder` trait** (new module `crates/vestige-core/src/embedder/`)
- - `async fn embed(&self, text: &str) -> Result>`
- - `fn model_name(&self) -> &str`
- - `fn dimension(&self) -> usize`
- - Impls: `FastembedEmbedder` (local ONNX, today), future `JinaEmbedder`,
- `OpenAiEmbedder`, etc.
- - Stays pluggable forever -- no lock-in to fastembed or to nomic-embed-text.
-
-2. **`MemoryStore` trait** (new module `crates/vestige-core/src/storage/trait.rs`)
- - One trait, ~25 methods across CRUD, search, FSRS, graph, domain sections.
- - Uses `trait_variant::make` to generate a `Send`-bound variant for
- `Arc` in Axum/tokio contexts.
- - The 29 cognitive modules operate exclusively through this trait. No direct
- SQLite or Postgres access from the modules.
-
-3. **`SqliteMemoryStore`** (refactor of existing `crates/vestige-core/src/storage/sqlite.rs`)
- - Existing rusqlite + FTS5 + USearch code, wrapped behind the trait.
- - Add `domains TEXT[]` equivalent (JSON-encoded array column in SQLite).
- - Add `domain_scores` JSON column.
- - No behavioral change for current users.
-
-4. **`PgMemoryStore`** (new `crates/vestige-core/src/storage/postgres.rs`)
- - `sqlx::PgPool` with compile-time checked queries.
- - pgvector HNSW index for vector search, tsvector + GIN for FTS.
- - Native array columns for `domains`, JSONB for `domain_scores` and `metadata`.
- - Hybrid search via RRF (Reciprocal Rank Fusion) in a single SQL query.
-
-5. **Model registry**
- - Per-database table `embedding_model` with `(name, dimension, hash, created_at)`.
- - Both backends refuse writes from an embedder whose signature doesn't match
- the registered row.
- - Model swap = `vestige migrate --reembed --model=`, O(n) cost, explicit.
-
-6. **`DomainClassifier` cognitive module** (new `crates/vestige-core/src/neuroscience/domain_classifier.rs`)
- - Owns the HDBSCAN discovery pass (using the `hdbscan` crate).
- - Computes soft-assignment scores for every memory against every centroid.
- - Stores raw `domain_scores: HashMap` per memory; thresholds into
- the `domains` array using `assign_threshold` (default 0.65).
- - Runs discovery on demand (`vestige domains discover`) or during dream
- consolidation passes.
-
-7. **HTTP MCP transport** (extension of existing Axum server in `crates/vestige-mcp/src/`)
- - New route `POST /mcp` for Streamable HTTP JSON-RPC.
- - New route `GET /mcp` for SSE (for long-running operations).
- - REST API under `/api/v1/` for direct HTTP clients (non-MCP integrations).
- - Auth middleware validates `Authorization: Bearer ...` or `X-API-Key`, plus
- signed session cookies for dashboard.
-
-8. **Key management** (new `crates/vestige-mcp/src/auth/`)
- - `api_keys` table -- blake3-hashed keys, scopes, optional domain filter,
- last-used timestamp.
- - CLI: `vestige keys create|list|revoke`.
-
-9. **FSRS review event log** (future-proofing for federation)
- - New table `review_events` -- append-only `(memory_id, timestamp, rating,
- prior_state, new_state)`.
- - Current `scheduling` table becomes a materialized view over the event log
- (reconstructible from events).
- - Phase 5 federation merges event logs, not derived state. Zero cost today,
- avoids lock-in tomorrow.
-
-### Data Flow
-
-**Local mode (stdio MCP, unchanged UX):**
-```
-stdio client -> McpServer -> CognitiveEngine -> FastembedEmbedder -> MemoryStore (SQLite)
-```
-
-**Server mode (HTTP MCP, new):**
-```
-Remote client -> Axum HTTP -> auth middleware -> CognitiveEngine
- -> FastembedEmbedder (server-side) -> MemoryStore (Postgres)
-```
-
-The cognitive engine is backend-agnostic. The embedder and the store are both
-swappable. The 7-stage search pipeline (overfetch -> cross-encoder rerank ->
-temporal -> accessibility -> context match -> competition -> spreading activation)
-sits *above* the `MemoryStore` trait and works identically against either backend.
-
-### Orthogonality of HDBSCAN and Reranking
-
-HDBSCAN and the cross-encoder reranker solve different problems and both stay:
-
-- **HDBSCAN** discovers domains by clustering embeddings. Runs once per discovery
- pass. Produces centroids. Used to *filter* search candidates, not to rank them.
-- **Cross-encoder reranker** (Jina Reranker v1 Turbo) scores query-document pairs
- at search time. Runs on every search. Produces ranked results.
-
-Domain membership is a filter applied before or during overfetch; reranking runs
-on whatever candidate set survives the filter.
-
----
-
-## Alternatives Considered
-
-| Alternative | Pros | Cons | Why Not |
-|-------------|------|------|---------|
-| Split into 4 traits (`MemoryStore + SchedulingStore + GraphStore + DomainStore`) | Cleaner interface segregation | Every module holds 4 trait objects, coordinates transactions across them | One trait is fine in Rust; extract sub-traits later if a genuine need appears |
-| Embedding computed inside the backend | Simpler call sites for callers | Backend becomes aware of embedding models; can't support remote clients without local fastembed | Keep storage pure; separate `Embedder` trait handles pluggability |
-| Unconstrained pgvector `vector` (no dimension) | Flexible for model swaps | HNSW still needs fixed dims at index creation; hides a meaningful migration as "silent" | Fixed dimension per install, explicit `--reembed` migration |
-| Dashboard separate auth (cookies only, no keys) | Simpler dashboard UX | Two auth systems to maintain | Shared API keys with session cookie layer on top |
-| Auto-tuned `assign_threshold` targeting an unclassified ratio | Adapts to corpus | Hard to debug ("why did this memory change domain?"); magical | Static 0.65 default, config-tunable, dashboard shows `domain_scores` for manual retuning |
-| Aggressive drift (auto-reassign memories whose scores drifted) | Always up-to-date domains | Breaks user muscle memory; silent reshuffling | Conservative: always propose, user accepts |
-| CRDTs for federation state | Mathematically clean merges | Massive complexity, performance cost, overkill | Defer; design FSRS as event log now so any future sync model works |
-
----
-
-## Consequences
-
-### Positive
-
-- Single memory brain accessible from every machine.
-- Multi-agent concurrent access via Postgres MVCC.
-- Natural topical scoping emerges from data, not manual tenants.
-- Future embedding model swaps are a config + migration, not a rewrite.
-- Federation has a clean on-ramp (event log merge) without committing now.
-- The `Embedder` / `MemoryStore` split unlocks other storage backends later
- (Redis, Qdrant, Iroh-backed blob store, etc.) with minimal work.
-
-### Negative
-
-- Operating a Postgres instance is more work than managing a SQLite file.
-- Users who stay on SQLite gain nothing from this ADR (but lose nothing either).
-- Migration (`vestige migrate --from sqlite --to postgres`) is a sensitive
- operation for users with months of memories -- needs strong testing.
-- HDBSCAN + re-soft-assignment runs in O(n) over all embeddings. At 100k+
- memories this starts to matter; manageable but not free.
-
-### Risks
-
-- **Trait abstraction leaks**: a cognitive module might need backend-specific
- behavior (e.g., Postgres triggers for tsvector). Mitigation: keep such logic
- inside the backend impl; the trait stays pure.
- Escalation: if a module genuinely cannot express what it needs through the
- trait, the trait grows, not the module bypasses.
-- **Embedding model drift**: users on older fastembed versions silently
- producing slightly different vectors after a fastembed upgrade. Mitigation:
- model hash in the registry, refuse mismatched writes, surface a clear error.
-- **Auth misconfiguration**: a user binds to `0.0.0.0` without setting
- `auth.enabled = true`. Mitigation: refuse to start with non-localhost bind
- and auth disabled. Hard error, not a warning.
-- **Re-clustering feedback loop**: dream consolidation proposes re-clusters,
- which the user accepts, which changes classifications, which affects future
- retrievals, which affect future dreams. Mitigation: cap re-cluster frequency
- (every 5th dream by default), require explicit user acceptance of proposals.
-- **Cross-domain spreading activation weight (0.5 default)**: arbitrary choice;
- could be too aggressive or too lax. Mitigation: config-tunable; instrument
- retrieval quality metrics in the dashboard so the user sees impact.
-
----
-
-## Resolved Decisions (from Q&A)
-
-| # | Question | Resolution |
-|---|----------|------------|
-| 1 | Trait granularity | Single `MemoryStore` trait |
-| 2 | Embedding on insert | Caller provides; separate `Embedder` trait for pluggability |
-| 3 | pgvector dimension | Fixed per install, derived from `Embedder::dimension()` at schema init |
-| 4 | Federation sync | Defer algorithm; store FSRS reviews as append-only event log now |
-| 5 | Dashboard auth | Shared API keys + signed session cookie |
-| 6 | HDBSCAN `min_cluster_size` | Default 10; user reruns with `--min-cluster-size N`; no auto-sweep |
-| 7 | Domain drift | Conservative -- always propose splits/merges, never auto-apply |
-| 8 | Cross-domain spreading activation | Follow with decay factor 0.5 (tunable) |
-| 9 | Assignment threshold | Static 0.65 default, config-tunable, raw `domain_scores` stored for introspection |
-
----
-
-## Implementation Plan
-
-Five phases, each independently shippable.
-
-### Phase 1: Storage trait extraction
-- Define `MemoryStore` and `Embedder` traits in `vestige-core`.
-- Refactor `SqliteMemoryStore` to implement `MemoryStore`; no behavior change.
-- Refactor `FastembedEmbedder` to implement `Embedder`.
-- Add `embedding_model` registry table; enforce consistency on write.
-- Add `domains TEXT[]`-equivalent and `domain_scores` JSON columns to SQLite
- (empty for all existing rows).
-- Convert all 29 cognitive modules to operate via the traits.
-- **Acceptance**: existing test suite passes unchanged. Zero warnings.
-
-### Phase 2: PostgreSQL backend
-- `PgMemoryStore` with sqlx, pgvector, tsvector.
-- sqlx migrations (`crates/vestige-core/migrations/postgres/`).
-- Backend selection via `vestige.toml` `[storage]` section.
-- `vestige migrate --from sqlite --to postgres` command.
-- `vestige migrate --reembed` command for model swaps.
-- **Acceptance**: full test suite runs green against Postgres with a testcontainer.
-
-### Phase 3: Network access
-- Streamable HTTP MCP route on Axum (`POST /mcp`, `GET /mcp` for SSE).
-- REST API under `/api/v1/`.
-- API key table + blake3 hashing + `vestige keys create|list|revoke`.
-- Auth middleware (Bearer, X-API-Key, session cookie).
-- Refuse non-localhost bind without auth enabled.
-- **Acceptance**: MCP client over HTTP works from a second machine; dashboard
- login flow works; unauth requests return 401.
-
-### Phase 4: Emergent domain classification
-- `DomainClassifier` module using the `hdbscan` crate.
-- `vestige domains discover|list|rename|merge` CLI.
-- Automatic soft-assignment pipeline (compute `domain_scores` on ingest, threshold
- into `domains`).
-- Re-cluster every Nth dream consolidation (default 5); surface proposals in the
- dashboard.
-- Context signals (git repo, IDE) as soft priors on classification.
-- Cross-domain spreading activation with 0.5 decay.
-- **Acceptance**: on a corpus of 500+ mixed memories, discover produces sensible
- clusters; search scoped to a domain returns tightly relevant results.
-
-### Phase 5: Federation (future, explicitly out of scope for this ADR's
-acceptance)
-- Node discovery (Mycelium / mDNS).
-- Memory sync protocol over append-only review events and LWW-per-UUID for
- memory records.
-- Explicit follow-up ADR before any code.
-
----
-
-## Open Questions
-
-None at ADR acceptance time. Follow-up items that are *implementation choices*,
-not architectural:
-
-- Precise cross-domain decay weight (start at 0.5, instrument, tune)
-- Dashboard histogram of `domain_scores` (UX design detail)
-- Whether to gate Postgres behind a Cargo feature flag (`postgres-backend`) or
- always compile it in (lean toward feature flag to keep SQLite-only builds small)
diff --git a/docs/adr/0002-phase-2-execution.md b/docs/adr/0002-phase-2-execution.md
deleted file mode 100644
index 5d590b4..0000000
--- a/docs/adr/0002-phase-2-execution.md
+++ /dev/null
@@ -1,545 +0,0 @@
-# ADR 0002: Phase 2 Execution -- Postgres Backend Integration, Phase 1 Amendment
-
-**Status**: Accepted
-**Date**: 2026-05-26
-**Related**: [docs/adr/0001-pluggable-storage-and-network-access.md](0001-pluggable-storage-and-network-access.md), [docs/plans/0002-phase-2-postgres-backend.md](../plans/0002-phase-2-postgres-backend.md)
-
----
-
-## Context
-
-ADR 0001 set the architectural direction: introduce `MemoryStore` and `Embedder`
-traits, ship a Postgres backend behind a feature flag, and reach a single shared
-memory brain across machines. Phase 1 (storage trait extraction) shipped on
-`feat/storage-trait-phase1` (790c0c8). The Phase 2 master plan at
-`docs/plans/0002-phase-2-postgres-backend.md` was drafted before Phase 1 was
-frozen.
-
-Starting Phase 2 surfaces a small set of execution-level decisions that ADR 0001
-did not cover and that the master plan now disagrees with the live code on.
-These decisions are too big to silently absorb into a per-step plan and too
-small to amend ADR 0001. They live here.
-
-Three concrete realities driving this ADR:
-
-1. **Trait shape mismatch.** Master plan 0002 assumed `trait_variant::make`
- produced distinct `MemoryStore` (Send-bound) and `LocalMemoryStore`
- (non-Send) variants, and that errors were `StoreError`. Phase 1 froze on
- `#[async_trait::async_trait]` with `pub use MemoryStore as LocalMemoryStore`
- and an error type called `MemoryStoreError`. The Postgres backend has to
- follow Phase 1, not the master plan -- but we should record that explicitly.
-2. **`SqliteMemoryStore` is monolithic.**
- `crates/vestige-core/src/storage/sqlite.rs` is ~8200 lines. Phase 1 appended
- the trait impl block at the bottom of the same file. Adding a similarly
- large `postgres.rs` perpetuates the pattern; this is the natural moment to
- decide whether the SQLite file gets split.
-3. **Constructor surface drift.** Master plan 0002 specifies
- `PgMemoryStore::connect(url, max_connections, &dyn Embedder)`. The Phase 1
- `SqliteMemoryStore` constructor takes no embedder -- registry consistency
- runs through `registered_model()` / `register_model()` on the trait,
- invoked by the caller. The two backends should look the same to a caller;
- right now they would not.
-4. **Multi-tenancy is a one-way door.** The Postgres schema is the place to
- reserve user/group/visibility columns *now*, even though Phase 3 is the
- phase that wires the auth filter using them. Adding `owner_user_id` and
- GIN indexes to a populated, HNSW-indexed `knowledge_nodes` table later is an
- expensive online migration; reserving NULL-defaulted columns at schema
- creation is ~10 lines of SQL. The same logic applies to per-memory
- context capture (codebase, MCP caller, session) -- promoting `codebase`
- to a first-class column now keeps the door open for context-aware
- sharing rules in Phase 4 without touching `knowledge_nodes`. See D7 and D8.
-
-This ADR is also the umbrella under which Phase 2 sub-plans (`0002a-...`,
-`0002b-...`, etc.) sit. The intent is: ADR + sub-plans land as one PR for
-review; the implementation lands as a second PR with many commits inside.
-
----
-
-## Already Decided (carried in by reference)
-
-These are settled by ADR 0001 or by explicit agreement during this session.
-Listed here so the discussion frame is clear; not re-litigated below.
-
-- Postgres backend ships behind a `postgres-backend` Cargo feature, default
- OFF. Mutually compilable with SQLite. (ADR 0001.)
-- Single big `MemoryStore` trait. `PgMemoryStore` implements the same surface
- as `SqliteMemoryStore`. (ADR 0001.)
-- pgvector HNSW + tsvector + GIN + RRF hybrid search in one SQL statement.
- (Master plan 0002, D4-D5.)
-- sqlx 0.8 + pgvector 0.4 + compile-time-checked queries + offline `.sqlx/`
- cache committed. (Master plan 0002.)
-- Two sqlx migration files: `0001_init` (extensions, tables, non-vector
- indexes) and `0002_hnsw` (HNSW separated for re-embed drop/recreate).
- (Master plan 0002, D4.)
-- `vestige migrate --from sqlite --to postgres` and
- `vestige migrate --reembed --model=` CLI subcommands. (ADR 0001 +
- master plan 0002, D8-D10.)
-- PR cadence: PR #1 carries this ADR plus all sub-plans; PR #2 carries the
- implementation as many commits.
-- Sub-plans use `0002a-`, `0002b-`, ... suffixes off `0002-`.
-- `PgMemoryStore::connect` lands as `todo!()` in the skeleton; real body
- comes later.
-
----
-
-## Decisions
-
-### D1. Sunset async_trait across the Phase 1 traits
-
-Phase 1 froze with `#[async_trait::async_trait]` on both the `MemoryStore`
-trait (`storage/memory_store.rs:194`) and the `Embedder` trait
-(`embedder/mod.rs:27`), plus their SQLite and Fastembed impl blocks. async_trait
-boxes every async fn into `Pin>` -- one heap allocation
-per call inside the hottest code path. We are amending Phase 1 to remove
-async_trait entirely and replace it with `trait_variant::make`, so each trait
-becomes two real generated variants (`MemoryStore` / `LocalMemoryStore`,
-`Embedder` / `LocalEmbedder`) with `Send` bounds on the outer variant.
-
-Scope split across three Phase 1 amendment sub-plans:
-
-- **`0001a-trait-rewrite.md`** -- Rewrite `MemoryStore` only. Touches
- `storage/memory_store.rs` (trait declaration) and `storage/sqlite.rs`
- (impl block attribute). Leaves async_trait in place on the embedder side
- so the diff stays focused.
-- **`0001b-sqlite-split.md`** -- Pure code motion. Splits the
- ~8200-line `sqlite.rs` into a `sqlite/` directory. Independent of D1; can
- land in either order relative to `0001a`.
-- **`0001c-async-trait-sunset.md`** -- Rewrite `Embedder` the same way, then
- remove `async-trait = "0.1"` from `crates/vestige-core/Cargo.toml`. Final
- amendment commit removes the dependency entirely. After this lands, the
- workspace contains zero references to `async_trait`.
-
-All three sub-plans land on the existing `feat/storage-trait-phase1` branch
-(790c0c8 has not been opened upstream yet; amend in place, no force-push to a
-public PR).
-
-### D2. PgMemoryStore::connect mirrors SqliteMemoryStore::new
-
-```rust
-impl PgMemoryStore {
- pub async fn connect(url: &str, max_connections: u32) -> MemoryStoreResult;
- pub async fn from_pool(pool: PgPool) -> MemoryStoreResult;
-}
-```
-
-No `Embedder` in the constructor. The pgvector-specific
-`ALTER TABLE knowledge_nodes ALTER COLUMN embedding TYPE vector($N)` DDL lives
-inside the trait method `register_model(&ModelSignature)`. That method is
-called by the caller (cognitive engine bootstrap, migrate CLI, tests) after
-construction, exactly as it is for `SqliteMemoryStore`.
-
-`MemoryStoreError` gains two variants behind the feature flag (added during
-the Postgres impl, not during the Phase 1 amendment):
-```rust
-#[cfg(feature = "postgres-backend")]
-#[error("postgres error: {0}")]
-Postgres(#[from] sqlx::Error),
-
-#[cfg(feature = "postgres-backend")]
-#[error("postgres migration error: {0}")]
-Migrate(#[from] sqlx::migrate::MigrateError),
-```
-
-### D3. Split sqlite.rs into a sqlite/ directory as Phase 1 amendment
-
-Pure code motion, no behavioural change. Target layout:
-```
-crates/vestige-core/src/storage/sqlite/
- mod.rs -- SqliteMemoryStore struct, new(), reader/writer locks
- crud.rs -- insert/get/update/delete
- search.rs -- fts_search, vector_search, hybrid search
- scheduling.rs -- FSRS state methods
- graph.rs -- edges, neighbors
- domain.rs -- domain CRUD, classify stub
- registry.rs -- embedding_model table + register_model
- portable_sync.rs -- portable archive backend bridge
- trait_impl.rs -- impl LocalMemoryStore for SqliteMemoryStore
-```
-
-Cognitive-module imports stay on `crate::storage::SqliteMemoryStore` and
-related re-exports from `storage/mod.rs`; the split is private to the
-module. Each motion commit must keep `cargo test -p vestige-core` green for
-bisectability.
-
-This lands in the Phase 1 amendment PR alongside D1 (separate commit, same
-branch).
-
-### D4. Postgres backend as a directory from day one
-
-```
-crates/vestige-core/src/storage/postgres/
- mod.rs -- PgMemoryStore struct, connect, from_pool, trait impl
- pool.rs -- PgPool construction from PostgresConfig
- migrations.rs -- sqlx::migrate! wrapper
- registry.rs -- ensure_registry, ALTER COLUMN TYPE vector(N)
- search.rs -- RRF query + row mapping
- migrate_cli.rs -- SQLite -> Postgres streaming copy
- reembed.rs -- O(n) re-encode + HNSW rebuild
-```
-
-D1+D2 of the master plan land first as a skeleton in `mod.rs` with `todo!()`
-bodies; later sub-plans fill in the other files.
-
-### D5. Sub-plan layout: two phases worth of sub-plans
-
-Phase 1 amendment sub-plans (under `docs/plans/`):
-- `0001a-trait-rewrite.md` -- MemoryStore async_trait -> trait_variant, call-site audit
-- `0001b-sqlite-split.md` -- sqlite.rs -> sqlite/ directory, commit-by-commit
-- `0001c-async-trait-sunset.md` -- Embedder rewrite + drop async-trait dep from Cargo.toml
-
-Phase 2 sub-plans (under `docs/plans/`):
-- `0002a-skeleton-and-feature-gate.md` -- master plan D1 + D2 (todo!() bodies)
-- `0002b-pool-and-config.md` -- master plan D3 + D7
-- `0002c-migrations.md` -- master plan D4
-- `0002d-store-impl-bodies.md` -- master plan D2 real bodies + D6 registry
-- `0002e-hybrid-search.md` -- master plan D5
-- `0002f-migrate-cli.md` -- master plan D8 + D10
-- `0002g-reembed.md` -- master plan D9
-- `0002h-testing-and-benches.md` -- master plan D14 + D15
-- `0002i-runbook.md` -- master plan D16
-
-Each sub-plan is a self-contained brief sized to fit one focused
-implementation session (handed to Claude Code as a `/goal` instruction
-without requiring the agent to load the master plan).
-
-### D6. SQLite split does not get its own ADR
-
-The split is pure code motion; no public types, behaviour, or paths change.
-`0001b-sqlite-split.md` is enough.
-
-### D7. Multi-tenancy schema reservation (L1-L3)
-
-Phase 2 reserves the columns and tables needed for future per-user / per-group
-visibility, so Phase 3 (auth) does not require a column-add migration over a
-populated, HNSW-indexed `knowledge_nodes` table. Single-user behaviour is unchanged
-in both backends.
-
-New tables in `0001_init.up.sql`:
-
-```sql
-CREATE TABLE users (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- handle TEXT NOT NULL UNIQUE,
- display_name TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
- metadata JSONB NOT NULL DEFAULT '{}'::jsonb
-);
-
-CREATE TABLE groups (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- handle TEXT NOT NULL UNIQUE,
- display_name TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
- metadata JSONB NOT NULL DEFAULT '{}'::jsonb
-);
-
-CREATE TABLE group_memberships (
- user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
- group_id UUID NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
- role TEXT NOT NULL DEFAULT 'member', -- 'member' | 'admin'
- joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),
- PRIMARY KEY (user_id, group_id)
-);
-
-INSERT INTO users (id, handle, display_name)
- VALUES ('00000000-0000-0000-0000-000000000001', 'local', 'Local User');
-```
-
-New columns on `knowledge_nodes`:
-
-```sql
-owner_user_id UUID NOT NULL DEFAULT '00000000-0000-0000-0000-000000000001'
- REFERENCES users(id),
-visibility TEXT NOT NULL DEFAULT 'private', -- 'private' | 'group' | 'public'
-shared_with_groups UUID[] NOT NULL DEFAULT '{}'
-
-CREATE INDEX idx_knowledge_nodes_owner ON knowledge_nodes (owner_user_id);
-CREATE INDEX idx_knowledge_nodes_shared_groups ON knowledge_nodes USING GIN (shared_with_groups);
-```
-
-Phase 3 visibility filter (declared here for reference; implemented in Phase 3):
-
-```sql
-WHERE
- (visibility = 'private' AND owner_user_id = $me)
- OR (visibility = 'group'
- AND (owner_user_id = $me OR shared_with_groups && $my_group_ids))
- OR visibility = 'public'
-```
-
-Why tri-state enum and not just `shared_with_groups[] + is_public`: the
-explicit `visibility` field documents intent at the row level. A `'private'`
-row with a non-empty `shared_with_groups` is detectable inconsistency
-(a CHECK constraint can enforce it later) rather than silent data.
-
-SQLite parity: same tables and columns with identical defaults.
-`shared_with_groups` is a JSON `'[]'` text encoding (no array type).
-Single-user mode never changes any of these values; the trait surface ignores
-the visibility filter for SQLite because there is exactly one user.
-
-Sharing automation (matching by domain, tag, repo, MCP caller, ...) is
-explicitly **not** in Phase 2. See D8 for context capture, and the Follow-ups
-section for the Phase 4 `sharing_rules` design sketch.
-
-RLS policies are not declared in Phase 2. Phase 3 decides whether to add
-RLS as defense-in-depth on top of the app-layer filter.
-
-### D8. Context-aware ingest
-
-Every memory carries its ingest context, so future automation (sharing rules,
-domain scoping, audit) can match on it without a schema migration. Most of
-this is already happening in the Phase 1 ingest pipeline; D8 promotes it to
-ADR-level commitment so Phase 2 cannot drop it on the way to Postgres.
-
-Context dimensions and where they live:
-
-- **`codebase`** -- promoted to a first-class indexed column on `knowledge_nodes`.
- High-frequency query path (`SELECT ... WHERE codebase = 'vestige'`) for
- both human exploration and Phase 4 HDBSCAN scoping. Direct B-tree index
- beats JSONB extraction.
- ```sql
- codebase TEXT, -- nullable; populated from ingest context
- CREATE INDEX idx_knowledge_nodes_codebase ON knowledge_nodes (codebase) WHERE codebase IS NOT NULL;
- ```
- `MemoryRecord` gains `pub codebase: Option`.
-
-- **`mcp_client_id`** -- which MCP caller created this. Persistent identity
- once Phase 3 API keys exist. Lives in `metadata.mcp_client_id` (JSONB).
- Not query-hot enough to deserve a column.
-
-- **`session_id`** -- ephemeral; identifies the calling session for runtime
- override scoping. Lives in `metadata.session_id` (JSONB). Sessions die
- fast; storing them as rows or indexed columns is waste.
-
-- **`file` / `topics`** -- existing optional context already accepted by the
- ingest pipeline. Stay in metadata JSONB.
-
-Phase 2's job for D8 is operational, not architectural: audit the ingest
-path from MCP request to row write to ensure none of these fields gets
-dropped when crossing the SQLite -> Postgres backend boundary.
-
----
-
-## PR Cadence
-
-Two work streams, three PRs total:
-
-1. **PR A: Phase 1 amendment**
- - Branch: `feat/storage-trait-phase1` (existing, amended in place)
- - Commits: MemoryStore trait rewrite (0001a) + sqlite split (0001b, multiple
- motion commits) + Embedder rewrite & async-trait dep removal (0001c).
- - Sub-plans `0001a-`, `0001b-`, `0001c-` are committed on this branch.
-
-2. **PR B: ADR 0002 + Phase 2 sub-plans (this document + the 9 sub-plans)**
- - New branch off PR A's tip once that is reviewed.
- - No code; docs only.
-
-3. **PR C: Phase 2 implementation**
- - New branch off PR B's tip.
- - One PR with many commits clustered by sub-plan.
-
-PR B is the "let's discuss execution before writing code" gate. PR C is the
-"now we write code" gate. If PR A is itself sizable enough that it needs the
-amendments reviewed in stages, the three sub-plans (`0001a`, `0001b`, `0001c`)
-can split into separate PRs; that's a tactical call at PR time.
-
----
-
-## Architecture Overview
-
-Final layout after the Phase 1 amendment (PR A) and Phase 2 implementation
-(PR C):
-
-```
-crates/vestige-core/src/storage/
- mod.rs -- re-exports, Storage alias for BC
- memory_store.rs -- trait_variant-generated MemoryStore + LocalMemoryStore, types, error
- migrations.rs -- SQLite migration registry (Phase 1, unchanged)
- portable.rs -- portable archive format (Phase 1, unchanged)
- sqlite/ -- was sqlite.rs (D3, Phase 1 amendment)
- mod.rs -- SqliteMemoryStore struct, new(), reader/writer locks
- crud.rs -- insert/get/update/delete
- search.rs -- fts/vector/hybrid
- scheduling.rs -- FSRS state
- graph.rs -- edges, neighbors
- domain.rs -- domain CRUD, classify stub
- registry.rs -- embedding_model table + register_model
- portable_sync.rs -- portable backend bridge
- trait_impl.rs -- impl LocalMemoryStore for SqliteMemoryStore
- postgres/ -- D4, Phase 2
- mod.rs -- PgMemoryStore struct, connect, from_pool, trait impl
- pool.rs -- PgPool construction from config
- migrations.rs -- sqlx::migrate! wrapper
- registry.rs -- register_model body, ALTER COLUMN TYPE vector(N)
- search.rs -- RRF query + row mapping
- migrate_cli.rs -- SQLite -> Postgres streaming copy
- reembed.rs -- O(n) re-encode + HNSW rebuild
-
-crates/vestige-core/migrations/
- sqlite/ -- Phase 1, with V15 migration for D7+D8 columns/tables
- postgres/ -- Phase 2
- 0001_init.up.sql -- includes D7 tables + columns, D8 codebase column
- 0001_init.down.sql
- 0002_hnsw.up.sql
- 0002_hnsw.down.sql
-```
-
-Tables in the Postgres schema after migration 0001:
-
-| Table | Purpose | Phase that populates |
-|-------|---------|----------------------|
-| `embedding_model` | One-row registry of name/dim/hash | Phase 2 (first connect) |
-| `knowledge_nodes` | Core records + owner/visibility/codebase | Phase 2 ingest; Phase 4 fills `domains` |
-| `scheduling` | FSRS state | Phase 2 |
-| `edges` | Spreading activation graph | Phase 2 |
-| `review_events` | Append-only FSRS review log | Phase 2; Phase 5 federation reads |
-| `domains` | Phase 4 cluster centroids | Phase 4 |
-| `users` | L1 identities (D7) | Phase 3 |
-| `groups` | L3 groups (D7) | Phase 3 |
-| `group_memberships` | L3 user-group links (D7) | Phase 3 |
-
-`sharing_rules` (Phase 4) and `api_keys` (Phase 3) are added later by their
-own migrations.
-
----
-
-## Alternatives Considered
-
-| Alternative | Why not |
-|-------------|---------|
-| Keep async_trait on the Phase 1 trait | One heap allocation per trait call inside the hottest code path in Vestige. Boxing every future also obscures the actual return type, which makes lifetimes and Send-ness harder to reason about. The Phase 1 PR is not opened upstream yet, so amending is free. |
-| Take `&dyn Embedder` into `connect` | Couples constructor to embedder; breaks ADR 0001's separation; can't be used by callers that don't have an embedder yet (tests, migrate CLI). |
-| Defer SQLite split | Postgres lands alongside an 8K-line peer; the pattern compounds; future readers see "backends are huge here". |
-| Single `postgres.rs` | Master plan calls out 7 sub-files; we know it's getting split; doing it twice is waste. |
-| Per-deliverable sub-plans (16 docs) | Review fatigue; many sub-plans would be 3-5 lines of Cargo or one migration each. Logical groups cluster naturally with PR commits. |
-| One rolling sub-plan with checkboxes | Moving target; doesn't serve as a `/goal` brief for a fresh Claude Code session. |
-| Separate ADR for the SQLite split | Pure code motion with no public-surface change; doesn't constrain future decisions. ADRs are for decisions that bind. |
-| Punt multi-tenancy schema entirely to Phase 3 | Adding `owner_user_id` and indexes to a populated, HNSW-indexed `knowledge_nodes` table later is an expensive online migration. Reserving NULL-defaulted columns now is ~10 lines of SQL. |
-| `shared_with_groups[] + is_public` instead of tri-state visibility enum | More compact but `visibility = 'private'` documents intent at the row level; a CHECK constraint can later enforce array/enum consistency. Two columns conveying one fact is fine when both are referenced often. |
-| Add `shared_with_users[]` for direct user-to-user sharing | A "group of one" subsumes it without an extra column and GIN index. Phase 3 CLI can auto-create singleton groups if a user requests direct shares. |
-| Bake per-domain or per-tag sharing defaults into Phase 2 schema | Sharing automation needs real usage data before committing to fuzzy (domain centroids) vs crisp (tags) vs context (codebase / MCP caller). Phase 4 designs a generic `sharing_rules` table that matches on any context dimension; deferring costs nothing because rules live in a new table, not new columns. |
-| `codebase` stays in JSONB metadata | High-frequency query path (HDBSCAN scoping, codebase-wide searches, future `sharing_rules` match). B-tree on a real column beats GIN on a JSONB key for this access pattern. Cost is one nullable TEXT column. |
-
----
-
-## Consequences
-
-### Positive
-- Phase 1 trait stops boxing futures on every call. Lifetimes and Send-ness
- become inspectable instead of hidden inside an `async_trait` macro expansion.
-- `connect` stays backend-agnostic; tests and CLI tools stand up either backend
- without an `Embedder` in scope.
-- Cognitive module imports never change paths -- the SQLite split is private
- to `storage/sqlite/`, public re-exports through `storage/mod.rs` unchanged.
-- Postgres backend lands already-modular; future SQL changes touch one of
- seven small files, not one of eight thousand lines.
-- Phase 2 master plan stays archival; ADR 0002 + sub-plans are the live source
- of truth for execution.
-- Multi-tenancy columns reserved now means Phase 3 auth is purely additive --
- no online migration over a populated, HNSW-indexed `knowledge_nodes` table.
-- Context-aware ingest (D8) keeps the door open for repo / session /
- MCP-caller-scoped sharing rules in Phase 4 without changing `knowledge_nodes`.
-
-### Negative
-- The Phase 1 amendment expands a "finished" branch. It is a real cost: the
- trait rewrite touches every cognitive module that holds a store handle.
-- SQLite split is a pure-motion diff. Annoying to review even when safe.
-- Three PRs (amendment, ADR+plans, implementation) instead of one or two.
- Discipline tax in exchange for reviewability.
-- Multi-tenancy reservation adds three never-queried tables and three
- always-default columns to the SQLite schema. Real but small storage cost in
- single-user mode (a single bootstrap row + empty tables + NULL/empty
- defaults per memory).
-
-### Risks
-- **Trait rewrite breaks a cognitive module's Send-ness expectation.**
- Mitigation: `cargo test --workspace` runs after each call-site edit;
- trait_variant-generated `MemoryStore` is the Send variant and matches the
- current `Arc` usage everywhere except thread-local impls (none
- exist today).
-- **SQLite motion commit introduces a silent semantic change.** Mitigation:
- each commit keeps `cargo test -p vestige-core` green; reviewer can bisect.
-- **Sub-plan boundaries don't match how implementation wants to commit.**
- Mitigation: sub-plans are advisory; the implementation PR clusters commits
- however it ends up needing to.
-- **Reserved columns get used in Phase 3 in a way that mismatches Phase 2
- defaults.** Mitigation: Phase 3 owns the auth filter; Phase 2 defaults
- (`owner_user_id = local`, `visibility = 'private'`) are intentionally the
- "no access for anyone but the owner" worst-case; widening at Phase 3 is
- safe, narrowing would be the dangerous direction.
-- **Memory: PR A amendment invalidates the locally-deployed Phase 1 binary's
- ABI.** Not a real risk -- the trait change is purely source-level Rust; the
- on-disk DB schema is unchanged. The rebuilt binary slots in over the
- current one without DB migration.
-
----
-
-## Resolved Decisions
-
-| # | Question | Resolution |
-|---|----------|------------|
-| Q1 | Phase 1 trait shape | Rewrite with trait_variant::make. Amend Phase 1 PR. |
-| Q2 | PgMemoryStore::connect signature | Mirror SqliteMemoryStore::new; no Embedder. register_model does the pgvector typmod stamp. |
-| Q3 | Split sqlite.rs | Yes, as Phase 1 amendment. sqlite.rs -> sqlite/ directory; pure code motion. |
-| Q4 | Postgres module layout | Directory from day one. |
-| Q5 | Sub-plan granularity | Logical groups, ~9 docs for Phase 2 plus 2 for the Phase 1 amendment. |
-| Q6 | ADR for SQLite split | No. Sub-plan `0001b-sqlite-split.md` is sufficient. |
-| Q7 | Multi-tenancy schema | Reserve users / groups / group_memberships tables and owner_user_id / visibility / shared_with_groups columns on knowledge_nodes in Phase 2. Single-user defaults; Phase 3 fills in real values. |
-| Q8 | Visibility encoding | Tri-state enum `'private' \| 'group' \| 'public'` plus `shared_with_groups[]`. No `shared_with_users[]`; no RLS in Phase 2. |
-| Q9 | Sharing automation grain | Per-memory only in Phase 2. Phase 4 ships a generic `sharing_rules` table matching on codebase / tag / node_type / mcp_client_id. |
-| Q10 | Context capture on ingest | `codebase` promoted to a first-class indexed column; `mcp_client_id` and `session_id` stay in metadata JSONB. |
-
----
-
-## Follow-ups
-
-- Phase 1 amendment sub-plans drafted: `0001a-trait-rewrite.md`,
- `0001b-sqlite-split.md`, `0001c-async-trait-sunset.md`. Ready to execute on
- `feat/storage-trait-phase1`.
-- Phase 2 sub-plans drafted: `0002a-` through `0002i-` against the accepted
- decisions above. Ready to execute on a new branch off PR A's tip.
-- Decide branch placement for this ADR before it gets committed -- it cannot
- live on `feat/storage-trait-phase1` (that branch is now PR A's code-only
- amendment branch). Likely a new branch off PR A's tip for PR B (docs only).
-- Validate local Postgres dev cluster before PR C work begins. Recipe at
- `docs/plans/local-dev-postgres-setup.md` is correct but needs to be applied
- on this machine (delandtj-home): cluster is not initdb'd, pgvector is not
- installed. Containerized `pgvector/pgvector:pg18` is a viable alternative
- if pgvector packaging is friction. See open discussion thread.
-
-### Phase 4 sketch: `sharing_rules` and the precedence chain
-
-Recorded here so the Phase 4 author does not have to rediscover the design.
-Phase 2 does **not** implement any of this; it only ensures the schema and
-ingest context capture make this possible without a `knowledge_nodes` migration.
-
-```sql
--- Phase 4 migration (not Phase 2)
-CREATE TABLE sharing_rules (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- owner_user_id UUID NOT NULL REFERENCES users(id),
- -- Match: any subset; all set fields must match conjunctively
- match_codebase TEXT,
- match_tag TEXT,
- match_node_type TEXT,
- match_api_key_id UUID REFERENCES api_keys(id), -- MCP caller identity
- -- Policy
- visibility TEXT NOT NULL,
- shared_with_groups UUID[] NOT NULL DEFAULT '{}',
- -- Conflict resolution
- priority INTEGER NOT NULL DEFAULT 0,
- created_at TIMESTAMPTZ NOT NULL DEFAULT now()
-);
-```
-
-Precedence on ingest, first match wins:
-
-1. Caller-explicit visibility in the MCP request
-2. Active session override held by the MCP server (per-session, in-memory,
- not persisted; matched by `session_id`)
-3. Highest-priority `sharing_rules` row whose match fields all hold
-4. User's `default_visibility` (typically `'private'`)
-
-Per-session overrides do not persist; storing ephemeral session IDs as DB
-rows is waste. Per-codebase / per-MCP-caller rules do persist as
-`sharing_rules` rows.
diff --git a/docs/launch/blog-post.md b/docs/launch/blog-post.md
deleted file mode 100644
index 886bb5a..0000000
--- a/docs/launch/blog-post.md
+++ /dev/null
@@ -1,395 +0,0 @@
-# Building a Cognitive Memory System with FSRS-6 and Three.js -- What 130 Years of Neuroscience Taught Us About AI Memory
-
-Your AI assistant does not remember anything.
-
-Every conversation starts from zero. You explain your project structure, your preferences, the bug you fixed last Tuesday, the architectural decision you made last month. The context window is a goldfish bowl -- 200K tokens of short-term memory, then nothing. RAG systems bolt on a vector database and call it "memory," but what they actually build is a search engine. Search is not memory. Memory is a living system that decays, strengthens, connects, and dreams.
-
-Vestige is an open-source Rust MCP server that gives AI agents persistent memory modeled on real neuroscience. Not metaphorical neuroscience. Actual published algorithms from Ebbinghaus (1885), Collins & Loftus (1975), Bjork & Bjork (1992), Frey & Morris (1997), and the FSRS-6 spaced repetition scheduler trained on 700 million Anki reviews.
-
-77,840+ lines of Rust. 29 cognitive modules. 734 tests. Single binary deployment with an embedded SvelteKit dashboard. AGPL-3.0 licensed.
-
-Here is how we built it.
-
----
-
-## The Problem: Session Boundaries are Amnesia
-
-Every AI conversation today has the same failure mode. The model is stateless. Context windows are large but finite, and they reset between sessions. The industry's answer has been Retrieval-Augmented Generation -- embed documents, stuff them into the prompt, let the model figure it out.
-
-RAG works for document Q&A. It does not work for memory, and here is why:
-
-1. **No forgetting curve.** Every chunk in a vector database has equal weight forever. A configuration snippet from six months ago has the same retrieval priority as the bug fix from yesterday.
-2. **No consolidation.** Memories are never merged, connected, or synthesized. You get back isolated chunks, not understanding.
-3. **No retroactive importance.** If you flag something as important today, RAG cannot go back and strengthen the memories from last week that suddenly matter.
-4. **No surprise detection.** Every insert is treated the same. Duplicate information bloats the database. Contradictions pile up silently.
-
-We wanted something different. We wanted memory that behaves like a brain -- where memories compete, strengthen through use, decay through neglect, and form connections during idle periods.
-
----
-
-## The Solution: Treat Memory Like a Brain, Not a Database
-
-Vestige implements a cognitive architecture with three core principles:
-
-1. **Memories have a lifecycle.** They are born, they strengthen through retrieval, they decay over time, they can be revived, and they eventually fade below the retrieval threshold. This is FSRS-6.
-2. **Storage is gated by novelty.** Not everything deserves to be remembered. Prediction Error Gating compares new information against existing memories and decides whether to create, update, merge, or supersede. This is the hippocampal bouncer.
-3. **Retrieval changes memory.** Every search strengthens the memories it finds (the Testing Effect) and weakens competitors (retrieval-induced forgetting). Memory is not a read-only operation.
-
-### Architecture Overview
-
-```
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-โ AI Agent (Claude, GPT, etc.) โ
-โ โ JSON-RPC over stdio (MCP protocol) โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ vestige-mcp 19 MCP tools โ
-โ โโโ Axum HTTP server (dashboard + WebSocket) โ
-โ โโโ CognitiveEngine (29 stateful modules) โ
-โ โโโ Tool handlers (one file per tool) โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
-โ vestige-core cognitive algorithms โ
-โ โโโ fsrs/ FSRS-6 spaced repetition โ
-โ โโโ neuroscience/ 10 modules (STC, spreading โ
-โ โ activation, hippocampal โ
-โ โ index, importance signals...) โ
-โ โโโ search/ hybrid, HyDE, reranker, โ
-โ โ keyword, vector, temporal โ
-โ โโโ advanced/ 11 modules (dreams, PE gate, โ
-โ โ chains, compression, โ
-โ โ cross-project learning...) โ
-โ โโโ embeddings/ fastembed (Nomic v1.5, 768d) โ
-โ โโโ storage/ SQLite + FTS5 + USearch HNSW โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-```
-
-The entire system compiles to a single binary. The SvelteKit dashboard is embedded at compile time using Rust's `include_dir!` macro. No external services. No cloud dependencies. Your memories live on your machine.
-
----
-
-## Deep Dive: FSRS-6 -- A Power Law Forgetting Curve
-
-The Free Spaced Repetition Scheduler (FSRS-6) is the mathematical backbone of Vestige. Where traditional systems store memories with static priority scores, every memory in Vestige has two dynamic properties: **stability** (how deeply encoded it is) and **difficulty** (how hard it is to retain). These evolve over time according to a 21-parameter model trained on 700 million real-world Anki reviews.
-
-The core formula is the power forgetting curve:
-
-```
-R(t, S) = (1 + factor * t / S) ^ (-w20)
-
-where factor = 0.9 ^ (-1 / w20) - 1
-```
-
-- `R` is retrievability -- the probability you can recall this memory right now
-- `t` is elapsed time since last access (in days)
-- `S` is stability -- the number of days for R to drop to 90%
-- `w20` is the personalizable decay parameter (default 0.1542)
-
-Why power law instead of exponential? Because Ebbinghaus was right in 1885, and the data from 700 million reviews confirms it: human forgetting follows a power curve, not an exponential one. Power law decay has a heavier tail -- memories hang around longer than exponential models predict, which matches real behavior.
-
-Here is the actual Rust implementation:
-
-```rust
-pub fn retrievability_with_decay(stability: f64, elapsed_days: f64, w20: f64) -> f64 {
- if stability <= 0.0 { return 0.0; }
- if elapsed_days <= 0.0 { return 1.0; }
-
- let factor = 0.9_f64.powf(-1.0 / w20) - 1.0;
- let r = (1.0 + factor * elapsed_days / stability).powf(-w20);
- r.clamp(0.0, 1.0)
-}
-```
-
-FSRS-6 also introduced three new parameters (w17, w18, w19) for **same-day reviews** -- a gap in earlier versions that caused instability when memories were accessed multiple times within 24 hours. In an AI agent context where the same memory might be retrieved dozens of times in one session, this matters enormously.
-
-Each memory exists in one of four states based on its accessibility score:
-
-| State | Accessibility | Behavior |
-|-------|--------------|----------|
-| **Active** | >= 70% | Immediately retrievable, surfaces in searches |
-| **Dormant** | 40-70% | Retrievable with effort, lower search priority |
-| **Silent** | 10-40% | Rarely surfaces, needs direct access to revive |
-| **Unavailable** | < 10% | Below retrieval threshold, candidate for GC |
-
-Memories are never hard-deleted. They fade. And any access -- even a search that returns them as a secondary result -- strengthens them back toward Active.
-
----
-
-## Deep Dive: Prediction Error Gating -- The Hippocampal Bouncer
-
-The brain does not store everything it perceives. The hippocampus acts as a novelty filter, comparing incoming information against existing memories and only consolidating what is genuinely new. This is prediction error: the gap between what you expected and what you got.
-
-Vestige implements this as `PredictionErrorGate`. When `smart_ingest` is called with new content:
-
-1. The content is embedded into a 768-dimensional vector (Nomic Embed v1.5)
-2. Existing memories are searched for candidates above a similarity threshold
-3. Cosine similarity determines the prediction error: `PE = 1.0 - similarity`
-4. A decision is made:
-
-```rust
-pub enum GateDecision {
- Create { reason, prediction_error, related_memory_ids },
- Update { target_id, similarity, update_type, prediction_error },
- Supersede { old_memory_id, similarity, supersede_reason, prediction_error },
- Merge { memory_ids, avg_similarity, strategy },
-}
-```
-
-The thresholds:
-
-| Similarity | Decision | Rationale |
-|-----------|----------|-----------|
-| > 0.92 | **Reinforce** | Near-identical content. Strengthen existing memory. |
-| > 0.75 | **Update/Merge** | Related content. Merge information into existing memory. |
-| 0.70-0.75 + contradiction detected | **Supersede** | Correction. New content replaces outdated memory. |
-| < 0.75 | **Create** | Novel content. Store as new memory. |
-
-Contradiction detection uses heuristic NLP -- looking for negation patterns ("don't" vs. "do", "avoid" vs. "use") and correction phrases ("actually", "the right way", "should be"). This catches the common case where a user corrects earlier advice.
-
-The result: you can call `smart_ingest` aggressively without worrying about duplicates. The gate handles deduplication, merging, and conflict resolution automatically. The cost of a false positive (saving something redundant) is near zero because the gate will catch it. The cost of a false negative (losing knowledge) is permanent.
-
----
-
-## Deep Dive: HyDE Search -- Query Expansion Without an LLM
-
-Hypothetical Document Embeddings (HyDE) is a technique from Gao et al. (2022) where you use an LLM to generate a hypothetical answer to a query, embed that hypothetical answer, and use it for vector search. The intuition: a hypothetical answer is closer in embedding space to the real answer than the raw question is.
-
-Full HyDE requires an LLM call at search time. That is too slow for a local-first system with sub-50ms search targets. Vestige implements a zero-latency approximation:
-
-1. **Intent classification.** The raw query is classified into one of six intents: Definition, HowTo, Reasoning, Temporal, Lookup, or Technical.
-
-2. **Template expansion.** Based on the intent, 3-5 variant queries are generated:
-
-```rust
-QueryIntent::Definition => {
- variants.push(format!("{clean} is a concept that involves"));
- variants.push(format!("The definition of {clean} in the context of"));
- variants.push(format!("{clean} refers to a type of"));
-}
-```
-
-3. **Centroid embedding.** All variants are embedded, and the centroid (average) of the embedding vectors is computed and L2-normalized.
-
-4. **Broadened search.** The centroid embedding captures a wider semantic space than any single query, improving recall for conceptual and question-style queries.
-
-This gives approximately 60% of full HyDE quality improvement with zero latency overhead. The embedding model (Nomic v1.5 running locally via fastembed) generates all variant embeddings in a single batch.
-
-The search pipeline then runs seven stages:
-
-1. **Overfetch** -- Pull 3x results from hybrid search (BM25 keyword + semantic vector)
-2. **Rerank** -- Re-score by relevance using a cross-encoder-style reranker
-3. **Temporal boost** -- Recent memories get a recency bonus
-4. **Accessibility filter** -- FSRS-6 retention threshold gates results (Ebbinghaus curve)
-5. **Context match** -- Tulving's encoding specificity (1973): match current context to encoding context
-6. **Competition** -- Anderson's retrieval-induced forgetting (1994): winners strengthen, competitors weaken
-7. **Spreading activation** -- Collins & Loftus (1975): activate related memories as a side effect
-
-That last stage is the critical differentiator. Every search does not just return results -- it reshapes the memory landscape.
-
----
-
-## Deep Dive: Synaptic Tagging and Capture -- Retroactive Importance
-
-This is the feature that no other AI memory system has.
-
-In 1997, Frey and Morris published a landmark paper in Nature describing Synaptic Tagging and Capture (STC). The finding: weak stimulation creates a temporary "synaptic tag" at a synapse. If a strong stimulation occurs within a temporal window (up to 9 hours), Plasticity-Related Products (PRPs) are produced that can be "captured" by the tagged synapses, consolidating them to long-term storage.
-
-Translation for AI: **memories can become important retroactively.**
-
-You have a conversation with a coworker about their vacation plans. Trivial. Three hours later, you learn they are leaving the company. Suddenly that vacation conversation is important context. In a traditional memory system, the vacation memory has already been classified as low-priority and buried. With STC, the "leaving the company" event triggers a backward sweep that captures and promotes the vacation conversation.
-
-Vestige implements this with a 9-hour backward window and a 2-hour forward window:
-
-```rust
-const DEFAULT_BACKWARD_HOURS: f64 = 9.0;
-const DEFAULT_FORWARD_HOURS: f64 = 2.0;
-```
-
-When an importance event occurs (user explicitly flags something, a novelty spike is detected, or repeated access patterns emerge), the STC system sweeps for tagged memories within the capture window. Capture probability decays with temporal distance using one of four configurable decay functions (exponential, linear, power law, or logarithmic).
-
-Different event types have different capture characteristics:
-
-| Event Type | Base Strength | Capture Radius | Use Case |
-|-----------|--------------|----------------|----------|
-| UserFlag | 1.0 | 1.0x | "Remember this" |
-| NoveltySpike | 0.9 | 0.7x (narrow) | High prediction error |
-| EmotionalContent | 0.8 | 1.5x (wide) | Sentiment detection |
-| RepeatedAccess | 0.75 | 1.2x | Pattern of retrieval |
-
-Captured memories are grouped into **importance clusters** -- temporal neighborhoods of memories that collectively provide context around a significant moment. This models how biological memory works: you do not remember isolated facts, you remember episodes.
-
----
-
-## Deep Dive: Memory Dreaming -- Offline Consolidation
-
-During sleep, the hippocampus replays recent experiences and transfers consolidated memories to the neocortex. This process discovers hidden connections between memories, strengthens important patterns, and prunes weak connections.
-
-Vestige simulates this with a 5-stage dream cycle:
-
-```
-Stage 1 - Replay: Replay recent memories in chronological order
-Stage 2 - Cross-reference: Compare all memory pairs for hidden connections
-Stage 3 - Strengthen: Reinforce connections that co-activate
-Stage 4 - Prune: Decay weak connections, remove below threshold
-Stage 5 - Transfer: Identify memories ready for semantic storage
-```
-
-The dreaming system maintains a `ConnectionGraph` -- a weighted bidirectional graph where edges represent discovered relationships between memories. Edges have strength (0.0 to 2.0) and decay over time (factor 0.95 per consolidation cycle). Connections below 0.1 strength are pruned.
-
-During Phase 2, the system evaluates memory pairs and discovers connections via multiple signals:
-
-```rust
-pub enum DiscoveredConnectionType {
- Semantic, // High embedding similarity (> 0.8)
- SharedConcept, // 2+ shared tags
- Temporal, // Created within 24 hours + similarity > 0.6
- Complementary, // Moderate similarity, different angles
- CausalChain, // Cause-effect relationship detected
-}
-```
-
-Phase 3 generates synthesized insights -- new knowledge that emerges from combining existing memories:
-
-```rust
-pub enum InsightType {
- HiddenConnection, // "X and Y are related in ways you didn't notice"
- RecurringPattern, // "You keep encountering this theme"
- Generalization, // "These specific cases suggest a general rule"
- Contradiction, // "These two memories conflict"
- KnowledgeGap, // "You know X and Z but not Y"
- TemporalTrend, // "This topic has evolved over the past month"
- Synthesis, // "Combining A + B + C yields new understanding"
-}
-```
-
-Dreams are triggered automatically: at session start if more than 24 hours have passed since the last dream, or after every 50 memory saves. The consolidation scheduler also monitors activity patterns and runs during detected idle periods (30+ minutes of inactivity).
-
----
-
-## The Dashboard: Three.js Makes Memory Visible
-
-Memory is invisible by default. You cannot debug what you cannot see. Vestige includes an embedded dashboard that renders the memory graph as a 3D force-directed visualization powered by Three.js with WebGL bloom post-processing.
-
-Every memory is a glowing sphere. Size maps to retention strength. Color maps to node type (fact, concept, decision, pattern, event). Opacity fades as memories decay. Edges represent discovered connections, with opacity proportional to connection weight.
-
-The visualization is event-driven via WebSocket. The Axum HTTP server runs alongside the MCP stdio transport, broadcasting `VestigeEvent` variants to all connected dashboard clients:
-
-```rust
-pub enum VestigeEvent {
- MemoryCreated { id, content_preview, node_type, tags, timestamp },
- SearchPerformed { query, result_count, result_ids, duration_ms, timestamp },
- DreamStarted { memory_count, timestamp },
- DreamProgress { phase, memory_id, progress_pct, timestamp },
- ConnectionDiscovered { source_id, target_id, connection_type, weight, timestamp },
- RetentionDecayed { id, old_retention, new_retention, timestamp },
- Heartbeat { uptime_secs, memory_count, avg_retention, timestamp },
- // ... 13 event types total
-}
-```
-
-On the frontend, each event type triggers a distinct visual effect:
-
-- **MemoryCreated:** Particle spawn burst (60 particles expanding outward) + expanding shockwave ring
-- **SearchPerformed:** Blue pulse ripple across all nodes
-- **DreamStarted:** Purple wash, bloom intensity increases to 1.5, rotation slows
-- **DreamProgress:** Individual memories light up as they are "replayed"
-- **ConnectionDiscovered:** Golden flash line between two nodes
-- **RetentionDecayed:** Red pulse on the decaying node
-- **ConsolidationCompleted:** Golden shimmer across all nodes
-
-The force-directed layout uses a Fibonacci sphere distribution for initial positions with repulsion-attraction dynamics: nodes repel each other (Coulomb's law), edges attract connected nodes (spring force), and a centering force prevents drift. The simulation runs for 300 frames then settles.
-
-The entire dashboard ships inside the Vestige binary. No separate frontend deployment. No CDN. `include_dir!` embeds the SvelteKit build output at compile time, and Axum serves it with proper MIME types and cache headers:
-
-```rust
-static DASHBOARD_DIR: Dir<'_> =
- include_dir!("$CARGO_MANIFEST_DIR/../../apps/dashboard/build");
-```
-
----
-
-## Architecture: Why Rust, SQLite, and Local Embeddings
-
-### Rust
-
-Memory is infrastructure. It runs on every interaction, on every search, on every save. Latency matters. We need sub-50ms search over thousands of memories, with embedding generation, FSRS calculations, and seven-stage pipeline execution. Rust gives us zero-cost abstractions, fearless concurrency (the `CognitiveEngine` is `Arc>` shared across async handlers), and compile-time guarantees that the 29 stateful cognitive modules do not have data races.
-
-### SQLite + FTS5 + USearch
-
-SQLite is the most deployed database in the world for a reason. WAL mode gives us concurrent reads alongside writes. FTS5 gives us BM25 keyword search with zero operational overhead. USearch provides a Rust-native HNSW index for approximate nearest-neighbor vector search. The entire memory store is a single file at `~/.vestige/vestige.db`.
-
-### fastembed (Nomic Embed v1.5)
-
-All embeddings run locally. The Nomic Embed v1.5 model produces 768-dimensional vectors, runs via ONNX Runtime, and is competitive with OpenAI's ada-002. The model is cached in the platform user cache directory after first download (~130MB), with `./.fastembed_cache` as a fallback. No API keys. No network calls during operation. Your memories never leave your machine.
-
-### Performance
-
-| Memories | Search Time | Memory Usage |
-|----------|-------------|--------------|
-| 100 | < 10ms | ~50MB |
-| 1,000 | < 50ms | ~100MB |
-| 10,000 | < 200ms | ~300MB |
-
----
-
-## Results: 734 Tests and What They Cover
-
-Vestige has 734 tests across the workspace (313 in vestige-core, 338 in vestige-mcp, plus e2e tests). Every cognitive module has dedicated test coverage:
-
-- **FSRS-6 algorithm:** Retrievability monotonic decay, round-trip interval calculation, sentiment boost, same-day review stability, difficulty mean reversion, fuzzing determinism
-- **Prediction Error Gating:** Empty candidates, near-identical reinforcement, demoted memory supersession, orthogonal content creation, contradiction detection, force-create/force-update intents
-- **Synaptic Tagging:** Tag creation and capture, PRP triggering, weak event rejection, clustering, tag decay and cleanup, batch operations, capture window probability
-- **Spreading Activation:** Network creation, edge addition, BFS propagation with decay, activation thresholds, edge reinforcement
-- **Memory Dreaming:** Full dream cycle, tag similarity, connection graph CRUD, consolidation scheduling, activity tracking
-
-### Comparison with Existing Approaches
-
-| Feature | RAG (Pinecone/Chroma) | mem0 | Vestige |
-|---------|----------------------|------|---------|
-| Forgetting curve | No | No | FSRS-6 (21-param power law) |
-| Duplicate detection | Manual | Basic | Prediction Error Gating |
-| Retroactive importance | No | No | Synaptic Tagging & Capture |
-| Retrieval strengthening | No | No | Testing Effect + spreading activation |
-| Dream consolidation | No | No | 5-stage sleep model |
-| Query expansion | No | No | HyDE (template-based) |
-| 3D visualization | No | No | Three.js + WebSocket |
-| Local embeddings | Optional | Cloud | Always local (Nomic v1.5) |
-| Single binary | No | No | include_dir! embedded dashboard |
-| License | Proprietary/OSS | OSS | AGPL-3.0 |
-
----
-
-## What We Learned
-
-**Neuroscience is an engineering goldmine.** The literature on human memory is vast, detailed, and largely untapped by the AI systems community. Papers from the 1970s through 2000s describe algorithms that directly translate into code -- Collins & Loftus's spreading activation is literally a BFS with weighted edges and decay. FSRS-6 is a parameterized forgetting curve. STC is a temporal window query with capture probability.
-
-**The Testing Effect changes everything.** Making search a write operation (not just read) transforms the memory dynamics. Frequently accessed memories get stronger. Competitors get weaker. The system self-organizes toward surfacing what matters most.
-
-**Prediction Error Gating eliminates the "save or not" problem.** The single hardest UX question in AI memory is: what should be saved? The answer from neuroscience is: whatever is surprising. PE Gating compares against existing knowledge and only stores what is genuinely novel. This eliminates both the "save everything" bloat and the "save nothing" amnesia.
-
-**Dreams are not a gimmick.** Offline consolidation consistently discovers connections that real-time search misses. When you replay 50 memories and compare all pairs, patterns emerge that individual searches would never find. The insight generation is simple (tag overlap + temporal proximity + embedding similarity), but the results are surprisingly useful.
-
----
-
-## What's Next
-
-Vestige v1.9 is targeting autonomic features: a retention target system that automatically adjusts consolidation frequency, adaptive embedding model selection based on content type, and a proactive suggestion engine that surfaces relevant memories before you search for them.
-
-Further out: emotional memory tagging via sentiment analysis (the amygdala module), multi-agent memory sharing (let your coding agent share memories with your research agent), and a training loop that personalizes the FSRS-6 weights to your individual forgetting curve.
-
-Memory is the missing layer between context windows and persistent knowledge. We think treating it as a cognitive system -- not a database -- is the right approach.
-
----
-
-Vestige is open source under AGPL-3.0 at [github.com/samvallad33/vestige](https://github.com/samvallad33/vestige).
-
-### References
-
-- Ebbinghaus, H. (1885). *Uber das Gedachtnis*. Duncker & Humblot.
-- Collins, A. M., & Loftus, E. F. (1975). A spreading-activation theory of semantic processing. *Psychological Review*, 82(6), 407-428.
-- Bjork, R. A., & Bjork, E. L. (1992). A new theory of disuse and an old theory of stimulus fluctuation. In *From learning processes to cognitive processes: Essays in honor of William K. Estes* (Vol. 2, pp. 35-67).
-- Tulving, E., & Thomson, D. M. (1973). Encoding specificity and retrieval processes in episodic memory. *Psychological Review*, 80(5), 352-373.
-- Frey, U., & Morris, R. G. M. (1997). Synaptic tagging and long-term potentiation. *Nature*, 385, 533-536.
-- Roediger, H. L., & Karpicke, J. D. (2006). Test-enhanced learning: Taking memory tests improves long-term retention. *Psychological Science*, 17(3), 249-255.
-- Anderson, M. C., Bjork, R. A., & Bjork, E. L. (1994). Remembering can cause forgetting: Retrieval dynamics in long-term memory. *Journal of Experimental Psychology: Learning, Memory, and Cognition*, 20(5), 1063-1087.
-- Redondo, R. L., & Morris, R. G. M. (2011). Making memories last: the synaptic tagging and capture hypothesis. *Nature Reviews Neuroscience*, 12(1), 17-30.
-- Gao, L., et al. (2022). Precise Zero-Shot Dense Retrieval without Relevance Labels. *arXiv:2212.10496*.
-- Ye, J., et al. (2024). FSRS-6: A spaced repetition algorithm based on free recall. *github.com/open-spaced-repetition*.
diff --git a/docs/launch/demo-script.md b/docs/launch/demo-script.md
deleted file mode 100644
index 4740dc4..0000000
--- a/docs/launch/demo-script.md
+++ /dev/null
@@ -1,484 +0,0 @@
-# Vestige v2.0 "Cognitive Leap" โ MCP Dev Summit NYC Demo Script
-
-**Event:** MCP Dev Summit NYC, April 1-3, 2026
-**Presenter:** Sam Valladares
-**Project:** Vestige โ The cognitive engine that gives AI a brain.
-**Tagline:** 130 years of memory research. One Rust binary. Zero cloud.
-
----
-
-## Pre-Demo Checklist (Do This Before the Conference)
-
-### Hardware
-- [ ] MacBook charged, charger packed
-- [ ] USB-C to HDMI adapter tested with venue projector (ask AV team for resolution)
-- [ ] Phone hotspot configured as backup (embedding model already cached = no network needed)
-
-### Software
-- [ ] Vestige v2.0 binary installed: `vestige-mcp --version` shows `2.0.0`
-- [ ] Claude Code installed and authenticated
-- [ ] Terminal font size: 18pt minimum (audience readability)
-- [ ] Browser zoom: 150% for dashboard views
-
-### Pre-Load for Offline Demo
-```bash
-# 1. Ensure embedding model is cached (130MB, downloads on first use)
-# Run any search to trigger download:
-vestige health
-
-# 2. Verify cache exists:
-ls ~/.fastembed_cache/
-# Should show: nomic-ai--nomic-embed-text-v1.5/
-
-# 3. Pre-load ~20 diverse memories so the graph looks alive:
-vestige ingest "TypeScript is my preferred language for frontend development" --tags preference,typescript
-vestige ingest "React Server Components eliminate client-side data fetching waterfalls" --tags pattern,react
-vestige ingest "Decided to use Axum over Actix-web for Vestige's HTTP layer because of tower middleware ecosystem" --tags decision,architecture
-vestige ingest "BUG FIX: SQLite WAL mode requires separate reader/writer connections for concurrent access" --tags bug-fix,sqlite
-vestige ingest "FSRS-6 uses 21 parameters trained on 700M+ Anki reviews, achieving 30% better efficiency than SM-2" --tags fsrs,science
-vestige ingest "Prediction Error Gating: similarity > 0.92 = reinforce, > 0.75 = update, < 0.75 = create new" --tags pe-gating,science
-vestige ingest "Synaptic Tagging (Frey & Morris 1997): memories become important retroactively when a significant event occurs within 9 hours" --tags science,synaptic-tagging
-vestige ingest "Three.js InstancedMesh renders 1000+ nodes at 60fps using GPU instancing" --tags pattern,threejs
-vestige ingest "MCP protocol uses JSON-RPC 2.0 over stdio โ no HTTP overhead, native tool integration" --tags mcp,architecture
-vestige ingest "Bjork dual-strength model: storage strength never decreases, retrieval strength decays with time" --tags science,bjork
-vestige ingest "HyDE query expansion classifies intent into 6 types and generates 3-5 hypothetical document variants" --tags hyde,search
-vestige ingest "Ebbinghaus forgetting curve: R = e^(-t/S) where R=retrievability, t=time, S=stability" --tags science,ebbinghaus
-vestige ingest "USearch HNSW index is 20x faster than FAISS for nearest neighbor search" --tags performance,search
-vestige ingest "Reconsolidation (Nader 2000): retrieved memories enter a labile state for 24-48 hours where they can be modified" --tags science,reconsolidation
-vestige ingest "Anderson 1994 retrieval-induced forgetting: retrieving one memory suppresses competing memories" --tags science,competition
-vestige ingest "Einstein & McDaniel 1990 prospective memory: remember to do X when Y happens, with time/context/event triggers" --tags science,prospective-memory
-vestige ingest "Vestige search pipeline: overfetch -> rerank -> temporal boost -> accessibility filter -> context match -> competition -> spreading activation" --tags architecture,search-pipeline
-vestige ingest "Vestige has 734 tests, 77,840 lines of Rust, 29 cognitive modules, and ships as a 22MB binary" --tags vestige,stats
-vestige ingest "The difference between Vestige and every other AI memory tool: we implemented the actual neuroscience, not just a vector database with timestamps" --tags vestige,philosophy
-vestige ingest "Spreading activation: when you search for one thing, related memories light up automatically, like how thinking of 'doctor' primes 'nurse'" --tags science,spreading-activation
-
-# 4. Run a dream cycle to create connections between memories:
-# (Do this through Claude Code so the CognitiveEngine processes it)
-# In Claude Code, say: "Dream about my recent memories"
-
-# 5. Verify dashboard is running:
-open http://localhost:3927/dashboard
-# Should see 3D graph with ~20 nodes, connections from the dream
-
-# 6. Test the full demo flow once (time it):
-# - Open terminal, open browser side by side
-# - Run through the 3-minute version
-# - Target: under 3 minutes with natural pacing
-```
-
-### Browser Tabs to Pre-Open
-1. `http://localhost:3927/dashboard` (3D Graph view)
-2. `http://localhost:3927/dashboard/feed` (Real-time event feed)
-3. `http://localhost:3927/dashboard/stats` (Stats with retention histogram)
-4. GitHub repo: `https://github.com/samvallad33/vestige`
-
-### Terminal Setup
-- Split terminal: left pane for Claude Code, right pane for commands
-- Dark background, high contrast
-- Cursor blink off (less distracting on projector)
-
----
-
-## VERSION 1: 30-Second Elevator Pitch
-
-**Use this:** Hallway conversations, after-party, meeting someone at the coffee line, anyone who asks "what are you working on?"
-
-### The Script
-
-> Your AI forgets everything between sessions. Every conversation starts from zero.
->
-> I built Vestige โ a single Rust binary that gives AI persistent memory based on real cognitive science. Not a vector database with a timestamp column. Actual FSRS-6 spaced repetition trained on 700 million reviews. Prediction error gating. Synaptic tagging. Memory dreaming. The same algorithms your brain uses.
->
-> It runs as an MCP server. One command to install, one command to connect. Your AI remembers your preferences, your decisions, your bug fixes. And memories decay on the Ebbinghaus curve unless they're used โ just like yours do.
->
-> Twenty-two megabyte binary. Seven hundred thirty-four tests. Zero cloud dependencies. I'm at the summit if you want to see the 3D brain visualization.
-
-### Key Points to Hit
-- "Real neuroscience, not just embeddings" โ this is the differentiator
-- "Single Rust binary" โ simplicity, performance
-- "MCP server" โ relevant to this audience specifically
-- "Zero cloud" โ privacy, local-first resonates
-- End with an invitation to see the dashboard โ creates a follow-up
-
-### If They Ask One Follow-Up Question
-**"How is this different from Mem0?"**
-> Mem0 is a cloud memory API. Great product, well-funded. But it's fundamentally a vector store with categories. Vestige implements the actual cognitive science โ memories decay on the Ebbinghaus curve, get strengthened by retrieval, get consolidated in dream cycles, compete for activation. It's the difference between a filing cabinet and a brain.
-
-**"What's the MCP integration like?"**
-> One command: `claude mcp add vestige vestige-mcp -s user`. That's it. Twenty-one tools, but they're organized into five subsystems that Claude uses automatically. You don't even think about it โ your AI just starts remembering.
-
-**"Is it open source?"**
-> AGPL-3.0. Fully open. The neuroscience is the moat, not the code.
-
----
-
-## VERSION 2: 3-Minute Demo โ "Watch Me Think"
-
-**Use this:** Lightning talk slot, booth demo, small group gathered around your laptop.
-
-**Tone:** Fast. Visual. Punchy. Let the 3D graph do the talking.
-
-**Setup:** Terminal on left half of screen. Browser with dashboard on right half. Dashboard open to the Graph page.
-
-### [0:00-0:20] The Hook
-
-> *[Point at 3D graph on screen, nodes floating and pulsing]*
->
-> This is a brain. Not a metaphor โ an actual implementation of how memory works. Every node is a memory. Every connection was discovered by a dream cycle. That glow you see? That's retention decaying on the Ebbinghaus curve in real time.
->
-> This is Vestige. Let me show you what happens when I talk to Claude.
-
-### [0:20-0:50] The Live Memory
-
-*[Switch to terminal with Claude Code]*
-
-```
-You: Remember that I'm presenting at MCP Dev Summit NYC and my talk is about cognitive memory systems
-```
-
-*[While Claude processes, switch to browser โ point at the Feed tab]*
-
-> Watch the feed. See that? "MemoryCreated" event just fired over WebSocket. And if you look at the graph...
->
-> *[Switch to Graph tab โ a new node appears with a burst animation]*
->
-> There. A new neuron, literally being born in real time.
-
-### [0:50-1:30] The Search
-
-*[Back to terminal]*
-
-```
-You: What do you know about how I'm using memory science in my work?
-```
-
-> Now watch what happens during a search.
->
-> *[Switch to Feed โ show SearchPerformed event]*
->
-> Seven-stage pipeline just fired. It overfetched 3x results, ran them through a cross-encoder reranker, applied temporal boost, checked FSRS retention, matched context using Tulving's encoding specificity principle, ran competition โ Anderson's retrieval-induced forgetting โ and spread activation to related memories.
->
-> *[Point at Claude's response showing recalled context]*
->
-> And it just pulled back exactly the right context. Not because of keyword matching โ because of cognitive science.
-
-### [1:30-2:15] The Dream
-
-*[Back to terminal]*
-
-```
-You: Dream about my recent memories
-```
-
-> Now the wild part. This triggers a dream cycle โ inspired by how your hippocampus replays memories during sleep to find hidden connections.
->
-> *[Switch to Graph โ show dream mode: purple ambient, nodes pulsing sequentially]*
->
-> Watch the nodes light up one by one. It's replaying memories, looking for patterns it missed. See those golden lines appearing? Those are connections it just discovered between memories that were stored at completely different times.
->
-> *[Switch to Stats tab, point at retention histogram]*
->
-> And here's the retention distribution. FSRS-6 โ trained on 700 million Anki reviews. Every memory has a predicted decay curve. Memories that aren't accessed fade. Memories that get used get stronger. Exactly like your brain.
-
-### [2:15-2:50] The Punchline
-
-> *[Switch to terminal, run:]*
-
-```bash
-vestige-mcp --version
-# โ vestige-mcp 2.0.0
-```
-
-```bash
-wc -l $(find /path/to/vestige/crates -name "*.rs") | tail -1
-# โ 77,840 total
-```
-
-> Seventy-eight thousand lines of Rust. Seven hundred thirty-four tests. Twenty-two megabyte binary. Ships with the dashboard embedded. Install is one npm command:
-
-```bash
-npm install -g vestige-mcp-server
-claude mcp add vestige vestige-mcp -s user
-```
-
-> Two lines. Your AI now has a brain.
-
-### [2:50-3:00] Close
-
-> Vestige v2.0, "Cognitive Leap." Open source, AGPL-3.0. The repo is `samvallad33/vestige`. Come talk to me if you want to see the neuroscience under the hood.
-
----
-
-## VERSION 3: 10-Minute Deep Dive โ Full Walkthrough
-
-**Use this:** Breakout session, workshop demo, recorded talk, anyone who gives you a stage.
-
-**Tone:** Authoritative. Technical depth but accessible. Build from simple to mind-blowing.
-
-**Setup:** Terminal fullscreen. Browser in separate space (swipe to switch). Have the GitHub repo open as a backup.
-
----
-
-### ACT 1: The Problem [0:00-1:30]
-
-> I want to start with a question. How many of you use Claude, or GPT, or Cursor every day?
->
-> *[Hands go up]*
->
-> And how many of you have had this experience: you spent two hours debugging a problem with your AI, you finally fixed it, and a week later you hit the exact same bug and your AI has absolutely no memory of the solution?
->
-> *[Nods]*
->
-> That's because every major AI assistant has the memory of a goldfish. Every session starts from absolute zero. Claude literally tells you this: "I don't have the ability to remember previous conversations."
->
-> Now here's what's strange. We've known how to build memory systems for over a century. Hermann Ebbinghaus published the forgetting curve in 1885. Bjork and Bjork formalized the dual-strength model in 1992. FSRS-6 โ the state of the art in spaced repetition โ was trained on 700 million reviews and published in 2024. The science exists.
->
-> Nobody implemented it for AI. So I did.
-
-### ACT 2: Install and First Memory [1:30-3:30]
-
-> Let me show you how fast this is. I'm starting from scratch.
-
-```bash
-# Install (macOS Apple Silicon)
-npm install -g vestige-mcp-server
-```
-
-> Three binaries. The MCP server, the CLI admin tool, and a restore utility. Twenty-two megabytes total. No Docker. No Python. No node_modules. No cloud API key.
-
-```bash
-# Connect to Claude Code
-claude mcp add vestige vestige-mcp -s user
-```
-
-> That's it. One command connects Vestige to Claude as a user-scoped MCP server. Let me restart Claude Code and show you what happens.
-
-*[Open Claude Code]*
-
-```
-You: Remember that I prefer Rust over Go for systems programming, and TypeScript for frontend work.
-```
-
-> Watch what Claude does. It calls `smart_ingest` โ Vestige's primary storage tool. But this isn't just shoving text into a database. Let me walk you through what just happened under the hood:
->
-> 1. **Prediction Error Gating** checked if this memory already exists. It compared the new content against all stored memories using embedding similarity. Since it's novel โ similarity below 0.75 โ it creates a new memory.
-> 2. **Importance scoring** ran four channels: novelty (is this new?), arousal (is this emotionally significant?), reward (will this be useful?), attention (is the user focused?). User preferences score high on reward.
-> 3. **Intent detection** classified this as a "preference" statement.
-> 4. **Synaptic tagging** marked this memory for potential retroactive strengthening if something related happens in the next 9 hours.
-> 5. **Hippocampal indexing** created a fast-lookup pointer.
->
-> All of that in under 50 milliseconds. In a single Rust binary. No API calls.
-
-*[New Claude Code session]*
-
-```
-You: What programming languages do I prefer?
-```
-
-> New session. Clean context. But Vestige remembers.
->
-> *[Show Claude responding with the saved preference]*
->
-> And this search just ran a 7-stage cognitive pipeline. Let me show you what's happening visually.
-
-### ACT 3: The Dashboard [3:30-5:30]
-
-> *[Switch to browser: `localhost:3927/dashboard`]*
->
-> This is the Vestige dashboard. SvelteKit 2, Three.js, WebSocket connection to the running MCP server.
-
-*[Graph page โ nodes floating in 3D space with bloom post-processing]*
-
-> Every node is a memory. The brightness represents retention strength โ how accessible this memory is right now. That's driven by FSRS-6, the same algorithm that powers modern Anki. Brighter means recently accessed or frequently used. Dimmer means it's fading.
->
-> The connections between nodes? Those were discovered during dream cycles โ offline consolidation where Vestige replays memories and finds hidden relationships, just like your hippocampus does during sleep.
->
-> I can click any node to see its details โ content, type, tags, retention strength, when it was created, when it was last accessed, its predicted decay curve.
-
-*[Click a node, show detail panel with retention curve]*
-
-> See this curve? That's `R(t) = (1 + FACTOR * t / S)^(-w20)` โ the FSRS-6 forgetting curve. Right now this memory has 94% retention. In 7 days without access, it drops to 71%. In 30 days, 43%. But every time it's retrieved, the stability increases and the curve flattens. The more you use a memory, the harder it is to forget. Exactly like your brain.
-
-*[Switch to Feed page]*
-
-> This is the real-time event feed. Every cognitive operation โ memory creation, search, dreaming, consolidation โ fires a WebSocket event. Let me show you.
-
-*[Switch back to terminal, run a search in Claude Code]*
-
-```
-You: How does FSRS-6 work?
-```
-
-*[Switch to Feed โ show SearchPerformed event with details]*
-
-> There โ `SearchPerformed`. Query: "How does FSRS-6 work?" Results: 3 memories returned. And on the graph, watch the nodes involved pulse.
->
-> *[Switch to Graph โ show nodes pulsing from the search]*
->
-> The blue pulse you see is spreading activation. When you search for FSRS, it doesn't just find memories about FSRS โ it activates related memories about spaced repetition, Ebbinghaus, Bjork. Like how thinking about "doctor" primes "nurse" in your mind.
-
-### ACT 4: The Dream [5:30-7:30]
-
-> Now let me show you the feature I'm most proud of. Memory dreaming.
->
-> Your brain doesn't just store memories and retrieve them. During sleep, your hippocampus replays the day's experiences, compresses them, finds connections you missed while awake, and consolidates the important ones into long-term storage. This is the science of memory consolidation โ Diekelmann and Born, 2010.
->
-> Vestige does the same thing.
-
-*[In Claude Code:]*
-
-```
-You: Dream about my recent memories
-```
-
-*[Switch to Dashboard Graph โ show dream mode activate]*
-
-> Watch this. Purple ambient wash โ we're entering dream mode. The graph slows down. And now watch the nodes light up one at a time.
->
-> *[Point as nodes pulse sequentially]*
->
-> It's replaying each memory, computing similarity to every other memory, looking for connections that weren't obvious at ingest time. See that golden line that just appeared? It just discovered that two memories stored days apart are semantically related.
-
-*[Switch to Feed โ show DreamStarted, then DreamCompleted events]*
-
-> The dream cycle produces insights โ natural language descriptions of the connections it found. And here's the critical part: the memories that get replayed during dreaming are strengthened. Their FSRS stability increases. Memories that aren't replayed continue to decay. Over time, the system naturally retains what matters and forgets what doesn't.
->
-> This is not a cron job. This is not garbage collection. This is the actual computational equivalent of memory consolidation during sleep.
-
-### ACT 5: HyDE and the Search Pipeline [7:30-9:00]
-
-> One more thing I want to show you. The search isn't just keyword matching plus cosine similarity.
->
-> Vestige v2.0 added HyDE โ Hypothetical Document Embeddings. When you search for something conceptual, the system first classifies your intent: are you asking a definition question? A how-to? Reasoning about a problem? Looking up a specific fact?
->
-> Based on that classification, it generates 3 to 5 hypothetical documents โ what a perfect answer might look like โ and creates a centroid embedding from all of them. That centroid is what gets compared against your stored memories.
->
-> The result: dramatically better recall on conceptual queries. If you search "how does memory decay work," you don't just get memories that contain those words. You get memories about Ebbinghaus, FSRS, retention curves, Bjork's dual-strength model โ because the hypothetical documents capture the concept, not just the keywords.
->
-> This runs on top of the 7-stage pipeline:
->
-> 1. Overfetch 3x results from hybrid search (BM25 keyword + semantic embedding)
-> 2. Cross-encoder reranker re-scores by deep relevance
-> 3. Temporal boost for recent memories
-> 4. FSRS-6 retention filter โ memories below threshold are inaccessible
-> 5. Context matching โ Tulving 1973, encoding specificity
-> 6. Competition โ Anderson 1994, retrieval-induced forgetting
-> 7. Spreading activation โ activate related memories, update predictive model
->
-> And critically: searching strengthens the memories you find. This is called the Testing Effect โ retrieval practice is the single most effective way to consolidate memory. Every search is a workout for the memories involved.
-
-### ACT 6: The Numbers and the Close [9:00-10:00]
-
-> Let me leave you with the numbers.
-
-*[Terminal:]*
-
-```bash
-vestige-mcp --version
-# โ vestige-mcp 2.0.0
-
-# Stats
-# 77,840 lines of Rust
-# 734 tests, zero failures
-# 29 cognitive modules
-# 22MB release binary with embedded dashboard
-# 21 MCP tools across 5 subsystems
-# 12 published neuroscience principles implemented
-# <50ms typical ingest latency
-# <300ns cosine similarity (benchmarked with Criterion)
-# Zero cloud dependencies
-# Zero API keys required
-# One command to install
-```
-
-> This is what I've been building for the past three months. I'm one person, I'm twenty-one years old, and I believe this is how AI memory should work โ grounded in real science, running locally, open source.
->
-> Vestige v2.0, "Cognitive Leap." The repo is `github.com/samvallad33/vestige`. The dashboard is running at `localhost:3927`. I'll be around all three days โ come find me if you want to talk about FSRS, or synaptic tagging, or why I think every AI assistant on the planet should have a forgetting curve.
->
-> Thank you.
-
----
-
-## Anticipated Audience Questions and Answers
-
-### Technical Questions
-
-**Q: How does this compare to RAG? Isn't this just RAG with extra steps?**
-> RAG is retrieval-augmented generation โ you search a corpus and inject results into the prompt. Vestige does that, but with a cognitive layer on top. RAG doesn't have retention decay. RAG doesn't have memory consolidation. RAG doesn't have prediction error gating to prevent duplicates. RAG doesn't suppress competing memories on retrieval. Vestige is to RAG what human memory is to a filing cabinet โ the retrieval mechanism is similar, but the memory lifecycle is completely different.
-
-**Q: What embedding model do you use?**
-> Nomic Embed Text v1.5 by default โ 768 dimensions truncated to 256 via Matryoshka representation learning. All local via ONNX through fastembed. v2.0 also supports Nomic v2 MoE (475M params, 8 experts) as an opt-in feature. The reranker is Jina v1 Turbo, with Qwen3-Reranker-0.6B available as opt-in.
-
-**Q: What's the storage backend?**
-> SQLite with WAL mode. FTS5 for keyword search with Porter stemming. USearch HNSW for vector search โ 20x faster than FAISS. Separate reader/writer connections for concurrent access. Single file database. I8 vector quantization for 2x storage savings with under 1% recall loss.
-
-**Q: How does FSRS-6 actually work? What are the 21 parameters?**
-> FSRS models memory as a power-law forgetting curve: `R(t) = (1 + FACTOR * t / S)^(-w20)` where S is stability and w20 is the decay parameter. The 21 parameters were trained on 700 million Anki reviews using machine learning. They encode how difficulty changes with repeated reviews, how stability grows based on review quality (Again/Hard/Good/Easy), and how same-day reviews affect long-term retention. It's 30% more efficient than SM-2, which is what Anki has used for decades.
-
-**Q: Does it work with Claude Desktop? Other AI clients?**
-> Yes. It speaks MCP โ the Model Context Protocol. One config change and it works with Claude Desktop, Cursor, VS Code Copilot, JetBrains, Windsurf, Xcode 26.3. Anything that speaks MCP.
-
-**Q: What about multi-user or team memory?**
-> The current Pro plan is more pragmatic: prove portable sync/storage first, then ship Solo and Team workflows around managed sync, backups, onboarding, and support. The open-source core stays local-first; paid team features should stay in the separate Pro/commercial boundary.
-
-**Q: How does Prediction Error Gating prevent duplicate memories?**
-> When you ingest a new memory, it computes embedding similarity against all existing memories. If similarity is above 0.92, it reinforces the existing memory (bumps FSRS stability). Between 0.75 and 0.92, it updates/merges. Below 0.75, it creates a new memory. The thresholds come from computational neuroscience research on prediction error signals โ the brain stores what's surprising, reinforces what's familiar, and updates what's partially known. Same principle.
-
-**Q: What's the performance like at scale? How many memories can it handle?**
-> I'm currently running with 700+ memories and everything is under 50ms. HNSW scales logarithmically, so 10x the memories doesn't mean 10x the latency. SQLite handles millions of rows. The practical limit is probably in the hundreds of thousands before you'd want to shard โ but for personal AI memory, that's years of heavy use.
-
-### Business / Community Questions
-
-**Q: Why AGPL and not MIT?**
-> Because I don't want AWS or Google hosting Vestige as a service without contributing back. AGPL means if you serve it over a network, you must open-source your modifications. Local use is completely free and unrestricted. Cloud and team features are proprietary โ the MongoDB/HashiCorp playbook.
-
-**Q: How do I contribute?**
-> GitHub: `samvallad33/vestige`. The codebase is well-tested โ 734 tests, zero warnings. Good first issues are labeled. The science is documented in `docs/SCIENCE.md`. PRs welcome, especially for new cognitive modules.
-
-**Q: Are you funded? Is this a company?**
-> Not yet. This is a solo project. I built it because I believe AI memory should work like human memory โ not because a VC told me to. If the right opportunity comes along, I'm open to it. But the open source core isn't going anywhere.
-
-### Skeptical Questions
-
-**Q: Isn't this over-engineered? Do you really need 29 cognitive modules?**
-> Fair question. You could build a memory system with just embeddings and timestamps, and it would work for simple cases. But it would miss things. It wouldn't know that a memory stored last week is related to one stored last month unless you searched for both. It wouldn't automatically forget outdated information. It wouldn't retroactively strengthen a memory when something important happens later. Each module solves a specific problem that real users hit. The 29 modules aren't bloat โ they're the necessary completeness of a system that actually models cognition.
-
-**Q: How do I know the neuroscience is real and not just marketing?**
-> Every principle I've implemented has a citation. Ebbinghaus 1885. Bjork and Bjork 1992. Frey and Morris 1997. Anderson 1994. Tulving 1973. Einstein and McDaniel 1990. Nader 2000. Diekelmann and Born 2010. FSRS was published with full methodology and trained on public Anki data. I didn't make any of this up โ I translated peer-reviewed cognitive science into Rust.
-
-**Q: Claude Code just added built-in memory. Doesn't that make Vestige obsolete?**
-> Claude's built-in memory is a flat text file. No spaced repetition, no decay, no dreaming, no cognitive search pipeline, no visualization. It's a good first step โ it validates that persistent memory matters. But it's the difference between a notepad and a brain. Vestige doesn't compete with Claude's memory โ it replaces and extends it.
-
----
-
-## Recovery Plans
-
-### If the Dashboard Doesn't Load
-- Vestige-mcp must be running (it auto-starts the dashboard on port 3927)
-- Fall back to terminal-only demo: `vestige health` and `vestige stats` show system state
-- Say: "The dashboard is SvelteKit embedded in the binary โ it starts automatically. Let me show you the data from the terminal instead."
-
-### If the Embedding Model Downloads Mid-Demo
-- This only happens on first-ever use. The pre-demo checklist prevents this.
-- If it happens: "The embedding model is 130MB and downloads once on first use. After that, Vestige is fully offline. Let me switch to the dashboard while it downloads."
-
-### If Claude Code Takes Too Long to Respond
-- Have a second terminal tab with pre-typed commands ready
-- Switch to showing the dashboard and explaining the architecture
-- Say: "While Claude processes that, let me show you what's happening in the visualization."
-
-### If Someone Asks Something You Don't Know
-- "That's a great question and I honestly don't have a good answer for it yet. Find me after the talk and let's dig into it."
-- Never bluff. The audience is technical. Authenticity wins.
-
----
-
-## Stage Presence Notes
-
-- **Start from the dashboard.** The 3D graph is the hook. It's visual, it's unusual, it makes people lean in.
-- **Don't rush the dream sequence.** The purple wash and sequential node pulses are the most visually impressive moment. Let it breathe for 3-4 seconds.
-- **Say the scientists' names.** "Ebbinghaus," "Bjork," "Frey and Morris" โ this signals that you've done the reading. The MCP Dev Summit audience respects depth.
-- **Make eye contact during the punchline.** "One command. Your AI now has a brain." Look at the audience, not the screen.
-- **Own your age.** Twenty-one, solo developer, zero funding. This is an asset, not a liability. You built something that the well-funded competitors haven't.
-- **The dashboard is your co-presenter.** Every time Claude does something, the dashboard should be showing the corresponding event. Practice the terminal-to-browser switch until it's seamless.
-- **Don't apologize.** Not for bugs, not for the AGPL, not for being solo. Confident but not arrogant. The work speaks.
diff --git a/docs/launch/opencode-adoption.md b/docs/launch/opencode-adoption.md
deleted file mode 100644
index bb46a44..0000000
--- a/docs/launch/opencode-adoption.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# OpenCode Adoption Plan
-
-Status: Vestige was tested with OpenCode `1.16.2` on June 8, 2026. The working config uses OpenCode's top-level `mcp.vestige` schema, not `mcpServers`.
-
-Public promotion started:
-
-- Vestige PR #70: `https://github.com/samvallad33/vestige/pull/70`
-- OpenCode issue: `https://github.com/anomalyco/opencode/issues/31402`
-- OpenCode docs/ecosystem PR: `https://github.com/anomalyco/opencode/pull/31405`
-- awesome-opencode PR: `https://github.com/awesome-opencode/awesome-opencode/pull/418`
-- opencode.cafe listing request: `https://github.com/R44VC0RP/opencode.cafe/issues/6`
-- OpenCode persistent memory comment: `https://github.com/anomalyco/opencode/issues/16077#issuecomment-4652064625`
-
-## Release Gate
-
-- PR #67 is merged upstream and should be treated as the contributor-driven starting point.
-- Ship the corrected OpenCode config docs and `@vestige/init` migration from stale `mcpServers.vestige` to `mcp.vestige`.
-- Ship the background embedding initialization fix before making direct `npx` the main OpenCode install path. A cold published `2.1.23` package can still time out while OpenCode waits for tools.
-- After release, verify all three OpenCode paths again:
- - installed binary: `command: ["vestige-mcp"]`
- - project memory: `command: ["vestige-mcp", "--data-dir", "./.vestige"]`
- - direct npm: `command: ["npx", "-y", "-p", "vestige-mcp-server@latest", "vestige-mcp"]` with `timeout: 60000`
-
-## Official OpenCode PR
-
-Target repo: `https://github.com/anomalyco/opencode`
-
-Files:
-
-- `packages/web/src/content/docs/mcp-servers.mdx`
-- `packages/web/src/content/docs/ecosystem.mdx`
-
-MCP docs snippet:
-
-```json
-{
- "$schema": "https://opencode.ai/config.json",
- "mcp": {
- "vestige": {
- "type": "local",
- "command": ["npx", "-y", "-p", "vestige-mcp-server@latest", "vestige-mcp"],
- "enabled": true,
- "timeout": 60000
- }
- }
-}
-```
-
-Ecosystem row:
-
-```md
-| [Vestige](https://github.com/samvallad33/vestige) | Local MCP memory server for OpenCode that remembers project decisions, preferences, and previous fixes across sessions |
-```
-
-Positioning: local, inspectable MCP memory for OpenCode. Avoid claiming Vestige fixes OpenCode's process memory or session resume behavior.
-
-## Awesome OpenCode
-
-Target repo: `https://github.com/awesome-opencode/awesome-opencode`
-
-Suggested entry, with category to confirm against maintainer preference (`data/projects/vestige.yaml` or `data/resources/vestige.yaml`):
-
-```yaml
-name: Vestige
-repo: https://github.com/samvallad33/vestige
-tagline: Local persistent memory for OpenCode
-description: Local MCP server that lets OpenCode remember project decisions, preferences, architecture context, and previous fixes across sessions.
-scope:
- - global
- - project
-tags:
- - mcp
- - memory
- - local-first
- - sqlite
- - opencode
-min_version: 1.16.2
-homepage: https://github.com/samvallad33/vestige/blob/main/docs/integrations/opencode.md
-installation: |
- npm install -g vestige-mcp-server@latest
- npx @vestige/init
-```
-
-## MCP Directories
-
-Current state:
-
-- Official MCP Registry already lists `io.github.samvallad33/vestige` at `https://registry.modelcontextprotocol.io/v0/servers?search=vestige`.
-- Smithery already lists Vestige and indexes 25 tools: `https://smithery.ai/server/@samvallad33/vestige`.
-- Glama already lists Vestige, but the listing needs a refresh/fix if it shows no tools: `https://glama.ai/mcp/servers/samvallad33/vestige`.
-- `mcp.so` does not show Vestige under the expected slugs yet; submit manually at `https://mcp.so/submit`.
-
-Priority order:
-
-1. Official MCP Registry: `https://github.com/modelcontextprotocol/registry`
-2. Awesome MCP Servers: `https://github.com/punkpeye/awesome-mcp-servers`
-3. Glama MCP directory: `https://glama.ai/mcp/servers`
-4. Smithery: `https://smithery.ai`
-5. PulseMCP: `https://www.pulsemcp.com`
-
-Registry metadata is mostly ready: `server.json` exists and `packages/vestige-mcp-npm/package.json` has `mcpName: "io.github.samvallad33/vestige"`. Publish only when the package version and `server.json` version match the released npm package.
-
-## Community Launch
-
-Use tested technical copy, not hype:
-
-> Vestige now works with OpenCode as a local MCP memory server. It gives OpenCode persistent memory for project decisions, preferences, architecture context, and previous fixes across sessions. Install with `npm install -g vestige-mcp-server@latest`, run `npx @vestige/init`, then verify with `opencode mcp list`.
-
-High-signal channels after release:
-
-- OpenCode Discord: `https://opencode.ai/discord`
-- opencode.cafe MCP Server listing: `https://opencode.cafe`
-- OpenCode memory-related GitHub issues, only where directly relevant
-- Hacker News and Lobsters with a technical post about the tested OpenCode integration and failure modes
-- npm keyword/discovery after the next package release includes `opencode`
-
-## Proof Checklist
-
-- `opencode debug config` accepts `mcp.vestige`.
-- `opencode mcp list` shows `vestige connected`.
-- Stale `mcpServers.vestige` examples fail in OpenCode and are migrated by `@vestige/init`.
-- OpenCode tools are prefixed as `vestige_search`, `vestige_smart_ingest`, `vestige_session_context`, and `vestige_deep_reference`.
-- The OpenCode guide says `timeout: 60000` for direct `npx` and `timeout: 10000` for installed binaries.
diff --git a/docs/launch/reddit-cross-reference.md b/docs/launch/reddit-cross-reference.md
deleted file mode 100644
index eae7aaf..0000000
--- a/docs/launch/reddit-cross-reference.md
+++ /dev/null
@@ -1,233 +0,0 @@
-# Reddit Launch Posts โ cross_reference Tool
-
-## Post 1: r/ClaudeAI (Primary)
-
-**Title:** `I built a tool that catches when Claude's memories contradict each other before it answers. It's been wrong 0 times since.`
-
----
-
-I've been building Vestige โ an MCP memory server that gives Claude persistent memory across sessions. FSRS-6 spaced repetition (the Anki algorithm), 29 neuroscience-inspired modules, single Rust binary. 1,111 memories stored. It works.
-
-But last week it almost cost me hours of debugging.
-
-Claude confidently told me a benchmark notebook should use `--enable-prefix-caching` with vLLM. I trusted it. The notebook crashed. Burned a daily submission.
-
-The problem? I had TWO memories:
-- **January**: "prefix caching crashes with our vLLM build"
-- **March**: "prefix caching works with the newer vLLM build"
-
-Claude found both. Picked the wrong one. Gave me a confident wrong answer based on the January memory. The March memory was correct โ but Claude had no way to know they conflicted.
-
-So I built `cross_reference`.
-
-Now before answering any factual question, Claude calls:
-
-```
-cross_reference({ query: "should I use --enable-prefix-caching with vLLM?" })
-```
-
-And gets back:
-
-```json
-{
- "status": "contradictions_found",
- "confidence": 0.35,
- "guidance": "WARNING: 1 contradiction detected across 12 memories.
- The newer memory is likely more accurate.",
- "contradictions": [
- {
- "newer": {
- "date": "2026-03-18",
- "preview": "Switched to a newer vLLM build that supports --enable-prefix-caching..."
- },
- "older": {
- "date": "2026-01-15",
- "preview": "prefix caching crashed with our older local vLLM build..."
- },
- "recommendation": "Trust the newer memory. Consider demoting the older one."
- }
- ],
- "timeline": [ /* 12 memories sorted newest-first */ ]
-}
-```
-
-Claude sees the contradiction BEFORE answering. Trusts the March memory. Gets it right.
-
-**I've been using it for 2 days. It's caught contradictions I didn't even know I had.** Old project decisions that got reversed. Config values that changed. Library versions that got upgraded. All sitting in memory, waiting to give me wrong answers.
-
-### How it works under the hood:
-
-1. **Broad retrieval** โ pulls up to 50 memories related to the query
-2. **Cross-encoder reranking** โ filters for quality (Jina Reranker v1 Turbo)
-3. **Pairwise contradiction detection** โ every memory pair checked for negation patterns ("don't" vs "do", "deprecated" vs "recommended") and correction signals ("now uses", "switched to", "replaced by")
-4. **Topic gating** โ only compares memories that share significant words (prevents false positives between unrelated memories)
-5. **Confidence scoring** โ agreements boost confidence, contradictions tank it, recency helps
-6. **Timeline** โ everything sorted newest-first so Claude sees the evolution
-7. **Superseded list** โ explicitly identifies which old memories should be demoted
-
-### The bigger picture:
-
-Context windows are now 1M tokens (Claude Opus 4.6, GPT-5.4). But bigger context doesn't fix this problem. The "Lost in the Middle" research shows accuracy drops from 92% to 78% at 1M tokens. More context = more chances for contradictions to slip through.
-
-Memory systems need to be SMARTER, not just bigger. That's what Vestige does โ 29 cognitive modules implementing real neuroscience:
-
-- **FSRS-6** โ memories naturally decay when unused, strengthen when accessed (21 parameters trained on 700M+ reviews)
-- **Prediction Error Gating** โ only stores what's genuinely new (no duplicates)
-- **Dream consolidation** โ replays memories to discover hidden connections (yes, like sleep)
-- **Spreading activation** โ search for "auth bug" and find the related JWT update from last week
-- **cross_reference** โ the new tool that catches contradictions before they become wrong answers
-
-### Stats:
-- 22 MCP tools
-- 746 tests, 0 failures
-- Zero `unsafe` code
-- Clean security audit (0 findings โ AgentAudit verified)
-- Single 22MB Rust binary โ no Docker, no PostgreSQL, no cloud
-- Works with Claude Code, Cursor, VS Code, Xcode 26.3, JetBrains, Windsurf
-
-### Install (30 seconds):
-```bash
-# macOS Apple Silicon
-npm install -g vestige-mcp-server
-sudo mv vestige-mcp /usr/local/bin/
-claude mcp add vestige vestige-mcp -s user
-```
-
-Then add to your CLAUDE.md:
-```
-Before answering factual questions, call cross_reference({ query: "the topic" })
-to verify your memories don't contradict each other.
-```
-
-That's it. Your AI now fact-checks itself.
-
-**GitHub:** https://github.com/samvallad33/vestige
-
-I searched every memory MCP server out there โ Mem0 (47K stars), Hindsight, Zep, Letta, OMEGA, Solitaire, MuninnDB. Some detect contradictions at ingest time (when you save). Nobody else gives the AI an on-demand tool to verify its own memories before answering, with confidence scoring and guidance on which memory to trust.
-
-Happy to answer questions about the neuroscience, the architecture, or how to set it up.
-
----
-
-## Post 2: r/LocalLLaMA (Cross-post, 2h later)
-
-**Title:** `Your AI has 1000 memories. Some contradict each other. It doesn't know. I built the fix โ 100% local, single Rust binary, zero cloud.`
-
----
-
-The problem nobody talks about with AI memory:
-
-You use a memory system. Over months, you accumulate 1000+ memories. Your project config changed 3 times. A library got deprecated. A decision got reversed. All those old memories are still there.
-
-When your AI searches memory, it finds 5 results. Two of them disagree. The AI picks one โ maybe the wrong one โ and gives you a confident answer based on outdated information.
-
-I built `cross_reference` to fix this. It's tool #22 in Vestige, my cognitive memory MCP server.
-
-```
-cross_reference({ query: "what database does the project use?" })
-```
-
-Returns:
-```json
-{
- "status": "contradictions_found",
- "confidence": 0.35,
- "contradictions": [{
- "newer": { "date": "2026-03", "preview": "Migrated to PostgreSQL..." },
- "older": { "date": "2026-01", "preview": "Using SQLite for the backend..." }
- }],
- "guidance": "WARNING: Trust the newer memory."
-}
-```
-
-The AI sees the conflict. Picks the right one. Every time.
-
-**What makes this different from RAG/vector search:**
-
-| | RAG | Vestige |
-|---|---|---|
-| Storage | Store everything | Prediction Error Gating โ only stores what's new |
-| Retrieval | Nearest neighbor | 7-stage cognitive pipeline with reranking |
-| Contradictions | Returns both, hopes for the best | **Detects and flags before answering** |
-| Decay | Nothing expires | FSRS-6 โ memories fade naturally |
-| Duplicates | Manual dedup | Self-healing via semantic comparison |
-
-**The stack:**
-- Rust 2024 edition. Single 22MB binary. No Python, no Docker, no cloud.
-- FSRS-6 spaced repetition (21 parameters, 700M+ reviews)
-- 29 cognitive modules (spreading activation, synaptic tagging, dream consolidation, prediction error gating)
-- 3D neural dashboard at localhost:3927 (Three.js, real-time WebSocket)
-- MCP server โ works with any AI that speaks MCP
-
-**100% local. Your data never leaves your machine.**
-
-```bash
-npm install -g vestige-mcp-server
-sudo mv vestige-mcp /usr/local/bin/
-claude mcp add vestige vestige-mcp -s user
-```
-
-746 tests. Zero unsafe code. Clean security audit. AGPL-3.0.
-
-GitHub: https://github.com/samvallad33/vestige
-
----
-
-## Post 3: r/rust (Optional, technical audience)
-
-**Title:** `I built a 22MB Rust binary that gives AI agents a brain โ FSRS-6, 29 cognitive modules, 3D dashboard, and a new contradiction detection tool. 746 tests, zero unsafe.`
-
----
-
-Vestige is a cognitive memory engine for AI agents. MCP server (stdio JSON-RPC + HTTP transport), SQLite WAL backend, USearch HNSW vector search, Nomic Embed v1.5 via fastembed (local ONNX, no API).
-
-The latest addition: `cross_reference` โ pairwise contradiction detection across memories at retrieval time. The AI calls it before answering factual questions to verify its memories don't disagree.
-
-**Why Rust:**
-- Single static binary (22MB with embedded SvelteKit dashboard via `include_dir!`)
-- No runtime, no GC pauses during real-time search
-- `tokio::sync::Mutex` for the cognitive engine, `std::sync::Mutex` for SQLite reader/writer split
-- Zero `unsafe` blocks in the entire codebase
-- `cargo test` runs 746 tests in 11 seconds
-
-**Architecture:**
-```
-Axum 0.8 (dashboard + HTTP transport)
- โ WebSocket event bus (tokio::broadcast, 1024 capacity)
-MCP Server (stdio JSON-RPC)
- โ 22 tools dispatched via match on tool name
- โ Arc + Arc>
-SQLite WAL + FTS5 + USearch HNSW
- โ fastembed 5.11 (Nomic Embed v1.5, 768D โ 256D Matryoshka)
- โ Jina Reranker v1 Turbo (cross-encoder, 38M params)
-```
-
-**Key crates:** rusqlite 0.38, axum 0.8, tokio 1, fastembed 5.11, usearch 2, chrono 0.4, serde 1, uuid 1, include_dir 0.7
-
-**What I learned building it:**
-- `OnceLock, String>>` for lazy model initialization โ the embedding model downloads ~130MB on first run, `OnceLock` ensures it only happens once and caches the error if it fails
-- `floor_char_boundary()` saved me from a UTF-8 panic (content truncation with multi-byte chars)
-- SQLite `PRAGMA journal_mode = WAL` + `synchronous = NORMAL` + `mmap_size = 268435456` gives surprisingly good concurrent read performance
-- `try_lock()` pattern for cognitive features in search โ if the lock is held (by dream consolidation), search falls back to simpler scoring instead of blocking
-
-Clean security audit. Parameterized SQL everywhere. CSP headers on the dashboard. Constant-time auth comparison (`subtle::ConstantTimeEq`). File permissions 0o600/0o700.
-
-GitHub: https://github.com/samvallad33/vestige
-AGPL-3.0 | 746 tests | 79K+ LOC
-
----
-
-## Posting Strategy
-
-| Subreddit | When | Expected Engagement |
-|-----------|------|-------------------|
-| r/ClaudeAI | First โ 12-14 UTC (US morning) | High โ direct audience for MCP tools |
-| r/LocalLLaMA | 2h after r/ClaudeAI | High โ local-first angle resonates here |
-| r/rust | Same day, evening UTC | Medium โ technical deep dive audience |
-| r/MachineLearning | Next day if first two do well | Lower but prestigious |
-
-**Title formula that works on Reddit:** Personal story + specific problem + "nobody else has this"
-
-**DO NOT do:** Product-name-first titles, marketing speak, "introducing X", "check out my project"
-
-**DO:** Lead with the PAIN ("my AI gave me wrong answers"), show the FIX (the JSON output), then reveal the tool.
diff --git a/docs/launch/show-hn.md b/docs/launch/show-hn.md
deleted file mode 100644
index 8cc5a95..0000000
--- a/docs/launch/show-hn.md
+++ /dev/null
@@ -1,547 +0,0 @@
-# Vestige v2.0 Launch โ Show HN + Cross-Posts
-
----
-
-## 1. Hacker News โ Show HN
-
-### Title (76 chars)
-
-```
-Show HN: Vestige โ FSRS-6 spaced repetition as long-term memory for AI agents
-```
-
-### Body (first comment)
-
-```
-Hi HN,
-
-I built Vestige because every AI conversation starts from zero. Your AI has no
-memory of yesterday. I wanted to fix that with real science, not just a vector
-database with a wrapper.
-
-**What it is:** A memory server for AI agents (MCP protocol). It sits between
-you and your AI โ Claude, Cursor, VS Code Copilot, etc. โ and gives it genuine
-long-term memory with cognitive-science-backed forgetting, strengthening, and
-retrieval. Written in Rust, 100% local, single 22MB binary.
-
-**The neuroscience stack:**
-
-- **FSRS-6 spaced repetition** โ the same 21-parameter power-law forgetting
- model behind Anki's best algorithm, trained on 700M+ reviews. Memories decay
- along empirically-validated curves instead of living forever with equal weight.
-
-- **Prediction Error Gating** โ on ingest, new content is compared against all
- existing memories. If similarity >92%, it reinforces. 75-92%, it merges.
- <75%, it creates. This is inspired by how the brain decides what's worth
- encoding vs. what's redundant.
-
-- **Dual-strength model** (Bjork & Bjork, 1992) โ each memory tracks storage
- strength (how well-encoded, only increases) and retrieval strength (how
- accessible right now, decays over time). A memory can be well-stored but hard
- to retrieve, like a name on the tip of your tongue.
-
-- **Testing Effect** โ every search automatically strengthens matching memories.
- Using memory makes it stronger. This is one of the most robust findings in
- cognitive psychology (Roediger & Karpicke, 2006).
-
-- **Synaptic Tagging** (Frey & Morris, 1997) โ when something important happens,
- it retroactively strengthens memories from the surrounding time window (default:
- 9 hours back, 2 hours forward). This models how the brain consolidates memories
- during waking hours.
-
-- **Spreading Activation** (Collins & Loftus, 1975) โ searching for "React hooks"
- surfaces "useEffect" memories through semantic similarity, even without keyword
- overlap.
-
-- **Memory Dreaming** โ offline consolidation that replays recent memories to
- discover hidden connections and synthesize insights. Inspired by hippocampal
- replay during sleep.
-
-**v2.0 adds:**
-
-- 3D neural visualization dashboard (SvelteKit + Three.js) โ watch memories
- pulse when accessed, burst particles on creation, golden flash lines when
- connections form. GPU instanced rendering handles 1000+ nodes at 60fps.
-
-- WebSocket event bus โ every cognitive operation (search, dream, consolidation,
- decay) broadcasts real-time events to the dashboard.
-
-- HyDE query expansion โ template-based Hypothetical Document Embeddings that
- classify query intent into 6 types, expand into 3-5 variants, and average
- the embedding vectors. Dramatically improves conceptual search.
-
-- Everything compiles into a single 22MB binary. The SvelteKit dashboard is
- embedded via Rust's `include_dir!` macro. No Docker, no Node runtime, no
- external services.
-
-**Numbers:** 77,840 lines of Rust, 734 tests, 29 cognitive modules, 21 MCP
-tools, search under 50ms for 1000 memories (SQLite FTS5 + USearch HNSW).
-
-**What it is NOT:** This is not RAG. RAG treats memory as a static database โ
-chunk everything, embed it, top-k retrieve. Vestige treats memory as a dynamic
-cognitive system. Memories decay. Using them makes them stronger. Important
-events retroactively strengthen recent memories. Irrelevant memories fade. The
-system evolves.
-
-The embedding model (Nomic Embed Text v1.5) runs locally via ONNX. After the
-first-run model download (~130MB), there are zero network requests. No
-telemetry, no analytics, no phoning home.
-
-I've been using this daily for 2 months and the experience is genuinely different.
-Claude remembers my coding patterns, my architectural decisions, my preferences.
-New sessions start with context instead of a blank slate.
-
-Source: https://github.com/samvallad33/vestige
-
-Happy to answer any questions about the cognitive science, the Rust architecture,
-or MCP in general.
-```
-
----
-
-## 2. Prepared FAQ for HN Comments
-
-### Q: "How is this different from just shoving everything into a vector database?"
-
-```
-The core difference: a vector database gives everything equal weight forever.
-Vestige applies forgetting.
-
-In a vector DB, a note from 6 months ago you never referenced sits alongside
-critical context from yesterday, both equally retrievable. Over time your
-retrieval quality degrades because the signal-to-noise ratio gets worse.
-
-Vestige uses FSRS-6 (the same algorithm as Anki's best spaced repetition mode)
-to model forgetting curves. Memories you use get stronger (Testing Effect).
-Memories you ignore fade (power-law decay). Important events retroactively
-strengthen nearby memories (Synaptic Tagging).
-
-The result is a system where retrieval quality improves over time instead of
-degrading. The AI surfaces what's actually relevant, not just what's
-semantically closest.
-
-It also deduplicates on ingest (Prediction Error Gating) โ if you try to store
-something 92%+ similar to existing memory, it reinforces instead of creating a
-duplicate. This keeps the knowledge base clean without manual maintenance.
-```
-
-### Q: "Why not just use Claude's built-in memory / ChatGPT memory?"
-
-```
-Two reasons: control and science.
-
-Control: Native AI memory is a black box on someone else's servers. You can't
-see what was stored, how it decays, or export it. Vestige stores everything in
-a local SQLite database you own. You can query it directly, back it up, export
-as JSON, or delete it entirely.
-
-Science: Native memory implementations are proprietary. We have no idea what
-algorithm they use for retention or retrieval. Vestige uses published research
-โ FSRS-6 (power-law forgetting, 21 parameters, trained on 700M Anki reviews),
-dual-strength model (Bjork & Bjork 1992), encoding specificity (Tulving 1973).
-These are well-studied, empirically-validated models.
-
-They also work simultaneously โ Claude's native memory handles general context,
-Vestige handles structured knowledge with explicit cognitive science.
-```
-
-### Q: "77K lines of Rust seems like a lot for a memory system. What's in there?"
-
-```
-Fair question. Roughly:
-
-- ~22K: fastembed (vendored fork of the embedding library, ONNX inference)
-- ~15K: 29 cognitive modules (FSRS-6, prediction error gating, synaptic
- tagging, spreading activation, dreaming, hippocampal index, etc.)
-- ~12K: MCP server + 21 tool implementations
-- ~8K: Storage layer (SQLite, FTS5, HNSW vector index, migrations)
-- ~7K: SvelteKit dashboard (TypeScript/Svelte, embedded in binary)
-- ~6K: Tests (734 tests across core + mcp + e2e + doctests)
-- ~5K: Search pipeline (hybrid BM25+semantic, RRF fusion, HyDE, reranker)
-- ~3K: Dashboard backend (Axum, WebSocket, REST API, event system)
-
-Is it over-engineered? Maybe. But each cognitive module implements a specific
-finding from memory research. The complexity comes from faithfully modeling how
-memory actually works, not from unnecessary abstraction.
-```
-
-### Q: "Does FSRS-6 actually make a difference, or is it just a gimmick?"
-
-```
-FSRS-6 is the state of the art in spaced repetition. It was developed by the
-open-spaced-repetition group, trained on 700M+ Anki reviews, and benchmarks
-30% more efficient than SM-2 (the algorithm most SRS apps use, which dates
-to 1987).
-
-The key insight is the forgetting model. SM-2 uses exponential decay, which
-doesn't match empirical data. FSRS-6 uses a power-law curve:
-
- R(t, S) = (1 + factor * t / S)^(-w20)
-
-Power-law forgetting has been consistently demonstrated in memory research
-since Wixted & Ebbesen (1991). The difference matters practically โ exponential
-decay predicts memories fall off a cliff, while power-law decay predicts a long
-tail where old memories can still be retrieved.
-
-For an AI memory system, this means old but important memories don't vanish.
-They fade slowly and can be revived by accessing them (the Testing Effect).
-```
-
-### Q: "Does this work with models other than Claude?"
-
-```
-Yes. Vestige speaks MCP (Model Context Protocol), which is supported by Claude,
-Cursor, VS Code Copilot, JetBrains, Windsurf, and others. Any MCP-compatible
-client can use it.
-
-The CLAUDE.md configuration in the repo tells the AI when and how to use the
-memory tools, but the underlying server is model-agnostic. You could write
-equivalent instructions for any model that supports MCP tool calling.
-```
-
-### Q: "Why AGPL-3.0?"
-
-```
-To prevent cloud providers from hosting Vestige as a competing service without
-contributing back. AGPL requires that if you serve the software over a network,
-you must open-source your modifications.
-
-The core memory system is fully open source and always will be. If you run it
-locally (which is the intended use case), AGPL is functionally identical to GPL.
-```
-
-### Q: "What's the performance like? SQLite seems like it would be slow for vectors."
-
-```
-SQLite handles the keyword search (FTS5 โ very fast). Vector search uses USearch
-HNSW with int8 quantization, which is separate from SQLite.
-
-Benchmarks on an M1 MacBook Pro:
-
- 100 memories: <10ms search
- 1,000 memories: <50ms search
- 10,000 memories: <200ms search
- 100,000 memories: <1s search
-
- cosine_similarity: 296ns
- RRF fusion: 17ยตs
- Embedding generation: ~100ms per memory (only on ingest)
-
-For personal use (hundreds to a few thousand memories), search is essentially
-instant. The bottleneck is embedding generation, which only happens when storing
-new memories.
-```
-
-### Q: "How does the 3D dashboard work? Is it practical or just eye candy?"
-
-```
-Both, honestly. The 3D force-directed graph is genuinely useful for seeing
-clusters of related knowledge, discovering memories you forgot about, and
-watching the dreaming process in real-time (memories pulse purple as they're
-replayed).
-
-But the primary UX for interacting with memories is through your AI. The
-dashboard is a monitoring and exploration tool, not the main interface. You
-talk to Claude/Cursor/etc normally, and Vestige handles memory in the
-background via MCP tool calls.
-
-The dashboard is embedded in the binary via include_dir! โ no separate server.
-It's served on localhost:3927/dashboard alongside the MCP stdio server.
-```
-
-### Q: "Is the 'dreaming' feature actually doing anything useful?"
-
-```
-It does three things:
-
-1. Replays recent memories and computes pairwise semantic similarity to
- discover connections you didn't explicitly create.
-
-2. Identifies memories that should be linked based on content overlap,
- creating an associative network.
-
-3. Synthesizes short insights from clusters of related memories.
-
-Is it as sophisticated as hippocampal replay during NREM sleep? No. It's an
-engineering approximation that runs the same kind of "offline processing" โ
-reviewing, connecting, consolidating โ that biological memory consolidation
-does during sleep. The connections it discovers are sometimes genuinely
-surprising and useful.
-```
-
----
-
-## 3. Reddit Cross-Posts
-
-### r/rust
-
-**Title:** `Vestige v2.0 โ 77K LOC Rust memory system with FSRS-6, HNSW, Axum WebSockets, and an embedded SvelteKit dashboard in a 22MB binary`
-
-**Body:**
-
-```markdown
-I've been building Vestige for the past few months and just shipped v2.0. It's
-a cognitive memory system for AI agents that implements neuroscience-backed
-memory algorithms in pure Rust.
-
-**Rust-specific highlights:**
-
-- **Single binary deployment**: SvelteKit dashboard compiled to static files,
- then embedded into the Rust binary via `include_dir!`. The entire system โ
- MCP server, HTTP dashboard, WebSocket event bus, embedding inference โ ships
- as one 22MB binary.
-
-- **fastembed vendored fork**: We vendor a fork of the fastembed-rs crate for
- ONNX embedding inference (Nomic Embed Text v1.5, 768-dim vectors truncated
- to 256 via Matryoshka). Feature flags for Nomic v2 MoE and Metal GPU
- acceleration.
-
-- **Axum 0.8 + tokio broadcast**: Dashboard runs on Axum with WebSocket
- upgrade at `/ws`. A single `tokio::broadcast::channel(1024)` propagates
- events from the MCP stdio server to all connected dashboard clients.
-
-- **USearch HNSW**: Vector similarity search via USearch with int8
- quantization (M=16, efConstruction=128, efSearch=64). Criterion benchmarks:
- cosine_similarity at 296ns, RRF fusion at 17ยตs.
-
-- **SQLite + FTS5**: rusqlite 0.38 with WAL mode, reader/writer connection
- split, FTS5 porter tokenizer for keyword search. Interior mutability via
- `Mutex` โ all Storage methods take `&self`.
-
-- **Rust 2024 edition**: Using `use<'_>` captures in RPITIT and the latest
- edition features. MSRV 1.85.
-
-- **Release profile**: `lto = true`, `codegen-units = 1`, `opt-level = "z"`,
- `strip = true` gets the binary down to 22MB including embedded assets.
-
-- **734 tests**: 352 core + 378 mcp + 4 doctests. Zero warnings.
-
-**Architecture:**
-
-```
-MCP Client (Claude/Cursor/etc)
- | stdio JSON-RPC
- v
-McpServer (rmcp 0.14)
- |
- +---> Arc (SQLite + HNSW)
- |
- +---> Arc> (29 modules)
- |
- +---> broadcast::Sender
- |
- v
- Axum Dashboard (port 3927)
- |
- +---> /ws (WebSocket)
- +---> /api/* (REST)
- +---> /dashboard/* (SvelteKit static)
-```
-
-The cognitive engine implements FSRS-6 spaced repetition, prediction error
-gating, synaptic tagging, spreading activation, and memory dreaming. Each
-module is a stateful struct initialized once at startup and shared via Arc.
-
-**What I'd do differently:** The fastembed vendoring is the ugliest part of the
-codebase. ONNX Runtime bindings are notoriously painful in Rust, and I spent
-more time fighting `ort` than any other dependency. If I started over, I might
-explore `candle` as the primary backend instead of ORT.
-
-Source: https://github.com/samvallad33/vestige
-License: AGPL-3.0
-
-Happy to discuss any of the Rust architecture decisions.
-```
-
----
-
-### r/ClaudeAI
-
-**Title:** `Vestige v2.0 "Cognitive Leap" โ give Claude real long-term memory with neuroscience-backed forgetting, a 3D dashboard, and 21 MCP tools`
-
-**Body:**
-
-```markdown
-Vestige gives Claude persistent memory across sessions using real cognitive
-science instead of just dumping everything into a database.
-
-**The problem it solves:** Every Claude conversation starts from zero. Even with
-native memory, you have no control over what's stored, how it decays, or where
-it lives. Vestige gives Claude a proper long-term memory system that runs 100%
-locally on your machine.
-
-**What makes it different from other MCP memory servers:**
-
-- **Memories decay like real memories.** FSRS-6 spaced repetition (the same
- algorithm Anki uses) models forgetting curves. Memories you use get stronger.
- Memories you ignore fade. Important events retroactively strengthen recent
- memories.
-
-- **Smart deduplication.** When Claude tries to save something similar to what
- it already knows, Prediction Error Gating decides whether to create, merge,
- or just reinforce the existing memory. No manual cleanup needed.
-
-- **29 cognitive modules** implementing findings from memory research: dual-
- strength model, testing effect, synaptic tagging, spreading activation,
- context-dependent retrieval, memory dreaming.
-
-**v2.0 new features:**
-
-- **3D Memory Dashboard** at localhost:3927/dashboard โ watch Claude's mind in
- real-time. Memories pulse when accessed, burst particles on creation, golden
- lines when connections form. SvelteKit + Three.js with bloom post-processing.
-
-- **Real-time event bus** โ every cognitive operation (search, dream,
- consolidation) broadcasts WebSocket events to the dashboard.
-
-- **HyDE query expansion** โ dramatically better search for conceptual queries.
-
-- **Single 22MB binary** โ everything embedded, no Docker, no Node, no cloud.
-
-**Setup (2 minutes):**
-
-```bash
-npm install -g vestige-mcp-server
-claude mcp add vestige vestige-mcp -s user
-```
-
-Then add the CLAUDE.md instructions from the repo to tell Claude how to use
-memory tools automatically.
-
-**What it's like in practice:** After 2 months of daily use, Claude remembers
-my coding patterns, my architectural decisions, my preferences across every
-project. New sessions start with context instead of a blank slate. It knows I
-prefer Rust over Go, that I use Tailwind, and that my last debugging session
-on Project X ended with a tricky race condition in the WebSocket handler.
-
-It's the difference between talking to someone with amnesia vs. someone who
-actually knows you.
-
-21 MCP tools. 77,840 lines of Rust. 734 tests. Works with Claude Code, Claude
-Desktop, Cursor, VS Code Copilot, JetBrains, Windsurf, and Xcode.
-
-Source: https://github.com/samvallad33/vestige
-
-Happy to answer questions or help with setup.
-```
-
----
-
-### r/LocalLLaMA
-
-**Title:** `Vestige v2.0 โ local-first AI memory server with FSRS-6 spaced repetition, ONNX embeddings, and zero cloud dependency (77K LOC Rust, 22MB binary)`
-
-**Body:**
-
-```markdown
-Vestige is a memory system for AI agents that runs entirely on your machine.
-No cloud, no API keys, no telemetry. After the first-run embedding model
-download (~130MB), it makes zero network requests.
-
-**Why this matters for local LLM setups:**
-
-Most memory/RAG solutions assume cloud embeddings (OpenAI, Cohere, etc.).
-Vestige embeds locally via ONNX (Nomic Embed Text v1.5, 768-dim vectors) and
-stores everything in a local SQLite database. If you're already running local
-models, your memory system should be local too.
-
-**The cognitive science angle:**
-
-This isn't just another vector database wrapper. It implements real memory
-algorithms:
-
-- **FSRS-6**: The state-of-the-art spaced repetition algorithm (power-law
- forgetting, 21 parameters, trained on 700M+ Anki reviews). Memories decay
- naturally instead of living forever.
-
-- **Prediction Error Gating**: On ingest, compares new content against existing
- memories. Creates/merges/reinforces based on novelty. Prevents bloat.
-
-- **Testing Effect**: Searching for a memory automatically strengthens it.
- Memory improves through use.
-
-- **Dual-strength model**: Storage strength (how well-encoded, never decreases)
- vs. retrieval strength (how accessible now, decays over time). Mimics the
- tip-of-tongue phenomenon.
-
-- **Memory Dreaming**: Offline consolidation that replays memories to discover
- connections. Inspired by hippocampal replay during sleep.
-
-**Technical stack:**
-
-- Rust, single 22MB binary
-- Nomic Embed Text v1.5 via ONNX (local, ~130MB model)
-- Optional: Nomic v2 MoE (475M params, feature flag), Metal GPU (Apple Silicon)
-- SQLite FTS5 for keyword search + USearch HNSW for vector search
-- MCP protocol (works with Claude, Cursor, VS Code Copilot, etc.)
-- Axum HTTP + WebSocket for dashboard
-- SvelteKit + Three.js 3D visualization (embedded in binary)
-
-**v2.0 highlights:**
-
-- 3D force-directed memory graph with real-time WebSocket events
-- HyDE query expansion (template-based hypothetical document embeddings)
-- FSRS decay visualization with retention curves
-- 734 tests, 29 cognitive modules, 21 tools
-- fastembed 5.11 with feature flags for Nomic v2 MoE + Qwen3 reranker
-
-**Performance:**
-
-- Search: <50ms for 1K memories, <200ms for 10K
-- Embedding: ~100ms per memory (ingest only)
-- cosine_similarity: 296ns (Criterion benchmark)
-- Memory: ~100MB for 1K memories, ~300MB for 10K
-
-**For local model users specifically:** Vestige speaks MCP (Model Context
-Protocol). If your local model setup supports MCP tool calling, it can use
-Vestige directly. The CLAUDE.md instructions in the repo tell the model when
-and how to use memory tools โ you can adapt these for any model.
-
-The embedding model downloads from Hugging Face on first run and caches
-locally. After that, fully air-gapped.
-
-Source: https://github.com/samvallad33/vestige
-License: AGPL-3.0 (use freely for local/personal use, cloud service requires
-source disclosure)
-
-This is a solo project โ feedback, issues, and contributions are very welcome.
-```
-
----
-
-## 4. Posting Strategy
-
-### Timing
-
-- **Show HN**: Tuesday or Wednesday, 8-10 AM EST (peak engagement window)
-- **r/rust**: Same day, 1-2 hours after HN post goes up
-- **r/ClaudeAI**: Same day, stagger by 1 hour
-- **r/LocalLLaMA**: Same day or next morning
-
-### Rules to Follow
-
-- **HN**: Title is the post. First comment is the body above. Respond to every
- comment within 30 minutes for the first 3 hours. Be humble, technical,
- transparent about limitations. Never say "AI-powered" or "game-changer."
-- **Reddit**: Each subreddit gets a tailored post emphasizing what that
- community cares about. No cross-linking between posts. Engage authentically.
-- **General**: Lead with the science, not the product. Let the tech speak.
- Acknowledge competitors honestly. Never disparage alternatives.
-
-### Key Messaging Points
-
-1. This is a solo project built on published research, not a startup pitch
-2. The neuroscience is real but honestly described (some modules are faithful
- implementations, some are engineering heuristics inspired by research)
-3. 100% local, zero cloud โ this is a feature, not a limitation
-4. The 3D dashboard is a genuine exploration tool, not just eye candy
-5. FSRS-6 is the differentiator โ no other AI memory system uses real spaced
- repetition
-
-### What NOT to Say
-
-- "Revolutionary" / "game-changing" / "paradigm shift"
-- "AI-powered" (it IS AI infrastructure, don't label it that way)
-- Anything negative about Mem0, Cognee, or other competitors
-- Claims about being "the best" at anything
-- Marketing language of any kind
diff --git a/docs/launch/tool-consolidation-v2.2.0.md b/docs/launch/tool-consolidation-v2.2.0.md
index 3e7190c..839644e 100644
--- a/docs/launch/tool-consolidation-v2.2.0.md
+++ b/docs/launch/tool-consolidation-v2.2.0.md
@@ -4,6 +4,11 @@
> tool, then make the few always-on tools deterministic. Two layers: Layer 1
> (this release) collapses 34 advertised tools to 12; Layer 2 (follow-up) shrinks
> the *default* surface and enforces the memory loop with hooks.
+>
+> Note: v2.2.0 also ships one **net-new** tool from a separate value stream โ the
+> flagship `backfill` (Retroactive Salience, Cai 2024 *Nature*). It is not part of
+> this fold, so the shipped advertised surface is **13** (12 from consolidation +
+> `backfill`), as asserted by `test_tools_list_returns_all_tools`.
## Why (frontier evidence)
@@ -21,7 +26,7 @@ ignores make the 5 it uses harder to choose:
Vestige had **34** advertised tools. This is the correction.
-## Layer 1 โ Count reduction (THIS RELEASE): 34 โ 12 advertised
+## Layer 1 โ Count reduction (THIS RELEASE): 34 โ 12 (fold result)
Principle: **one consolidation per commit, one change per submission.** Each
consolidation is its own commit, landed in a safe order with the hot retrieval
@@ -42,9 +47,12 @@ habits keep working) and is removed in **v2.3.0**.
`recall` is committed **last** because it is the hot path.
-### Final advertised surface (12)
+### Final advertised surface (13)
-| Standalone (6) | Consolidated (6) |
+The fold itself lands at 12; the release adds the net-new flagship `backfill`,
+for **13 advertised**.
+
+| Standalone (7) | Consolidated (6) |
|---|---|
| `smart_ingest` | `recall` |
| `memory` | `dedup` |
@@ -52,6 +60,7 @@ habits keep working) and is removed in **v2.3.0**.
| `intention` | `graph` |
| `source_sync` | `maintain` |
| `suppress` | `session_start` |
+| `backfill` | |
### Action / mode / view maps
@@ -66,8 +75,9 @@ habits keep working) and is removed in **v2.3.0**.
- **`search` is folded, not kept standalone.** `recall` with no `mode` (the
default) *is* search โ a zero-overhead pass-through to `search_unified`. Keeping
both `search` and `recall` advertised would be the exact RAG-MCP anti-pattern.
- Final count is a clean **12**, leaving 2 slots of headroom toward a future
- always-on `save` surface rather than spending them on a redundant verb.
+ The fold lands at a clean **12**; adding the flagship `backfill` brings the
+ shipped surface to **13**, still well inside the ~30-tool safe zone and leaving
+ headroom toward a future always-on `save` surface.
- **`graph` actions are flat peers, not nested.** `explore`'s `chain` /
`associations` / `bridges` sit alongside `predict` / `memory_graph` /
`composed_graph` actions in a single `action` enum โ matching the existing
diff --git a/docs/plans/0001-phase-1-storage-trait-extraction.md b/docs/plans/0001-phase-1-storage-trait-extraction.md
deleted file mode 100644
index 9960462..0000000
--- a/docs/plans/0001-phase-1-storage-trait-extraction.md
+++ /dev/null
@@ -1,1026 +0,0 @@
-# Phase 1 Plan: Storage Trait Extraction
-
-**Status**: Draft
-**Depends on**: none
-**Related**: docs/adr/0001-pluggable-storage-and-network-access.md (Phase 1)
-
----
-
-## Scope
-
-### In scope
-
-- Introduce a new module `crates/vestige-core/src/storage/memory_store.rs` defining:
- - `LocalMemoryStore` base trait (Sync + 'static)
- - `MemoryStore` Send-bound alias generated via `#[trait_variant::make(MemoryStore: Send)]`
- - Supporting data types referenced by the trait: `MemoryRecord`, `SchedulingState`, `SearchQuery`, `SearchResult`, `MemoryEdge`, `Domain`, `ClassificationResult`, `StoreStats`, `HealthStatus`, `MemoryStoreError`.
-- Introduce a new module `crates/vestige-core/src/embedder/` defining:
- - `Embedder` async trait with `embed`, `model_name`, `dimension` plus `model_hash` (for the registry) and optional `embed_batch` with a default implementation.
- - Move/adapt the existing `EmbeddingService` impl into a new struct `FastembedEmbedder` that implements `Embedder`.
-- Refactor `Storage` (existing `crates/vestige-core/src/storage/sqlite.rs`) into `SqliteMemoryStore`:
- - Keep the struct, the `writer`/`reader` `Mutex` pair, the `FSRSScheduler`, and the USearch `VectorIndex`.
- - Rename the type alias `Storage` to `SqliteMemoryStore` with a `pub type Storage = SqliteMemoryStore;` alias for backward source compatibility during the transition. (The trait method surface is the new public contract.)
- - Implement `LocalMemoryStore` by wrapping existing synchronous `rusqlite` methods inside `async fn` bodies that call a small `spawn_blocking`-or-inline adapter. Bodies MAY block; the `async fn` signature exists because `LocalMemoryStore` is async.
-- Add a `schema_version = 12` migration that introduces two schema additions:
- 1. `embedding_model` registry table (one-row constraint enforced in code).
- 2. Two new TEXT columns on `knowledge_nodes`: `domains TEXT NOT NULL DEFAULT '[]'` and `domain_scores TEXT NOT NULL DEFAULT '{}'` (both JSON-encoded).
-- Enforce model registry on every write path: on the first non-empty embedding write the model signature is recorded; subsequent writes whose `Embedder::model_name()` / `dimension()` / `model_hash()` disagree must fail with `MemoryStoreError::ModelMismatch` before touching the DB.
-- Audit all 29 cognitive modules under `crates/vestige-core/src/neuroscience/` and `crates/vestige-core/src/advanced/` to confirm they hold no direct `rusqlite::Connection` references, no `Storage` struct field, and no SQL strings. Any that do get refactored to take `&dyn LocalMemoryStore` (local-only modules) or `&Arc` (modules crossing `await` points).
-- Add unit tests alongside each new trait method and integration tests in `tests/phase_1/`.
-
-### Out of scope
-
-- Implementing `PgMemoryStore` on sqlx + pgvector -- that is Phase 2.
-- `vestige migrate --from sqlite --to postgres` and `vestige migrate --reembed` -- Phase 2.
-- MCP over Streamable HTTP, API key middleware, `api_keys` table, `vestige keys create|list|revoke` -- Phase 3.
-- `DomainClassifier` module, HDBSCAN clustering, `vestige domains discover|list|rename|merge` CLI, incremental soft-assignment, cross-domain spreading activation decay -- Phase 4.
-- Federation, mycelium/mDNS node discovery, review event log table -- Phase 5.
-- Removing the `pub type Storage = SqliteMemoryStore;` compatibility alias -- that cleanup happens at the end of Phase 4 when no consumers still spell the old name.
-
-## Prerequisites
-
-### Current code state
-
-- Single concrete type `Storage` in `crates/vestige-core/src/storage/sqlite.rs` (4592 lines, 216 public symbols on the impl blocks, approximately 85 public methods) is the only storage surface the crate exposes.
-- `EmbeddingService` in `crates/vestige-core/src/embeddings/local.rs` holds the fastembed singleton. No trait exists; callers type-erase via `&EmbeddingService`.
-- Migrations live in `crates/vestige-core/src/storage/migrations.rs`; the current head is v11.
-- All cognitive modules in `neuroscience/` and `advanced/` are pure (verified by `grep rusqlite|Connection::|execute\(|prepare\(` returning no matches in those trees). They operate on `KnowledgeNode`, `Vec`, `ConnectionRecord`, etc. passed in by the caller.
-- `vestige-mcp` consumes `Arc` in `crates/vestige-mcp/src/server.rs` and every tool under `crates/vestige-mcp/src/tools/`. These call sites will type-check unchanged after the alias is introduced because the trait methods preserve the exact signatures of the existing `pub fn` on `Storage`.
-- Test count reported in `CLAUDE.md`: 758 tests (406 mcp + 352 core). This is the no-regression target.
-
-### Required crates (add via `cargo add` under `crates/vestige-core`)
-
-| Crate | Version | Why |
-|-------|---------|-----|
-| `trait-variant` | `0.1` | Generates the `Send`-bound `MemoryStore` alias from `LocalMemoryStore` so `Arc` works under tokio/axum without hand-writing two traits. Listed in PRD section "Crate Dependencies (new)" under Phase 1. |
-| `blake3` | `1` | `Embedder::model_hash() -> [u8; 32]` uses blake3 to stabilise the "model signature" stored in the `embedding_model` registry. Already slated for Phase 3 auth; pulling it forward costs nothing and avoids a second migration to add a hash column. |
-| `async-trait` | `0.1` | Not strictly required with `trait-variant` on MSRV 1.91 (RPITIT is stable), but used for one utility trait (`EmbedderExt`) that carries a default `embed_batch` body. OPTIONAL; see Open Implementation Questions below. |
-
-No changes to `vestige-mcp/Cargo.toml` are required for Phase 1 -- the new trait lives in `vestige-core` and the mcp crate continues to depend on the `SqliteMemoryStore` concrete type (via the `Storage` alias) until Phase 2 introduces backend selection.
-
-## Deliverables
-
-1. `crates/vestige-core/src/storage/memory_store.rs` -- `LocalMemoryStore` + `MemoryStore` traits and supporting types.
-2. `crates/vestige-core/src/storage/mod.rs` -- updated exports and module wiring.
-3. `crates/vestige-core/src/storage/sqlite.rs` -- `Storage` renamed to `SqliteMemoryStore`, `impl LocalMemoryStore for SqliteMemoryStore` block, enforcement hooks for the model registry, serde of `domains` / `domain_scores` columns.
-4. `crates/vestige-core/src/storage/migrations.rs` -- `MIGRATION_V12_UP` adding `embedding_model` table and `domains`, `domain_scores` columns.
-5. `crates/vestige-core/src/embedder/mod.rs` -- `Embedder` trait and re-exports.
-6. `crates/vestige-core/src/embedder/fastembed.rs` -- `FastembedEmbedder` implementation.
-7. `crates/vestige-core/src/embeddings/local.rs` -- retained; `EmbeddingService` kept as the underlying fastembed holder; `FastembedEmbedder` wraps it.
-8. `crates/vestige-core/src/lib.rs` -- new `pub mod embedder;` + re-exports for `MemoryStore`, `LocalMemoryStore`, `Embedder`, `FastembedEmbedder`, and the data types.
-9. `tests/phase_1/trait_round_trip.rs` -- integration test: round-trip of every trait method through `SqliteMemoryStore`.
-10. `tests/phase_1/embedding_model_registry.rs` -- integration test: first-write registers, mismatch refuses, dimension mismatch refuses.
-11. `tests/phase_1/domain_column_migration.rs` -- integration test: a v11 DB upgraded to v12 reads `domains=[]` and `domain_scores={}` for all existing rows.
-12. `tests/phase_1/cognitive_module_isolation.rs` -- integration test: every cognitive module compiles and executes against an `Arc` without touching `SqliteMemoryStore` concretely.
-13. `tests/phase_1/send_bound_variant.rs` -- integration test: an `Arc` can be moved across `tokio::spawn`.
-14. Updated `tests/phase_1/mod.rs` (if the dir already uses a module layout) or individual `[[test]]` entries in `tests/e2e/Cargo.toml` as needed -- see "Test Plan" for the exact layout.
-
-## Detailed Task Breakdown
-
-### D1. Trait + supporting types (`memory_store.rs`)
-
-- **File**: `crates/vestige-core/src/storage/memory_store.rs` (new).
-- **Depends on**: `trait-variant` crate added under vestige-core, `chrono`, `serde_json`, `uuid`, `thiserror` (all already in Cargo.toml).
-- **Signatures**:
-
-```rust
-//! Backend-agnostic memory store trait.
-//!
-//! This is the single abstraction every cognitive module sits above. It is
-//! intentionally flat: one trait, ~25 methods, no sub-traits.
-
-use std::collections::HashMap;
-
-use chrono::{DateTime, Utc};
-use serde::{Deserialize, Serialize};
-use uuid::Uuid;
-
-// ----------------------------------------------------------------------------
-// ERROR
-// ----------------------------------------------------------------------------
-
-/// Error returned by every `LocalMemoryStore` / `MemoryStore` method.
-#[non_exhaustive]
-#[derive(Debug, thiserror::Error)]
-pub enum MemoryStoreError {
- #[error("not found: {0}")]
- NotFound(String),
-
- #[error("backend error: {0}")]
- Backend(String),
-
- #[error(
- "embedding model mismatch: store registered {registered_name} (dim {registered_dim}, \
- hash {registered_hash}), embedder is {actual_name} (dim {actual_dim}, hash {actual_hash})"
- )]
- ModelMismatch {
- registered_name: String,
- registered_dim: usize,
- registered_hash: String,
- actual_name: String,
- actual_dim: usize,
- actual_hash: String,
- },
-
- #[error("invalid input: {0}")]
- InvalidInput(String),
-
- #[error("initialization error: {0}")]
- Init(String),
-}
-
-impl From for MemoryStoreError {
- fn from(e: crate::storage::StorageError) -> Self {
- use crate::storage::StorageError as S;
- match e {
- S::NotFound(s) => MemoryStoreError::NotFound(s),
- S::Database(e) => MemoryStoreError::Backend(e.to_string()),
- S::Io(e) => MemoryStoreError::Backend(e.to_string()),
- S::InvalidTimestamp(s) => MemoryStoreError::Backend(format!("invalid timestamp: {s}")),
- S::Init(s) => MemoryStoreError::Init(s),
- }
- }
-}
-
-pub type MemoryStoreResult = std::result::Result;
-
-// ----------------------------------------------------------------------------
-// DATA TYPES
-// ----------------------------------------------------------------------------
-
-/// Backend-agnostic memory record.
-///
-/// Phase 1 intentionally keeps this type independent of `KnowledgeNode` to
-/// avoid dragging 30+ legacy fields through the trait surface. The SQLite
-/// backend converts between `MemoryRecord` and `KnowledgeNode` at the
-/// boundary.
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct MemoryRecord {
- pub id: Uuid,
- /// Empty = unclassified. Populated in Phase 4.
- pub domains: Vec,
- /// Raw similarity per domain centroid. Empty until Phase 4 runs clustering.
- pub domain_scores: HashMap,
- pub content: String,
- pub node_type: String,
- pub tags: Vec,
- pub embedding: Option>,
- pub created_at: DateTime,
- pub updated_at: DateTime,
- pub metadata: serde_json::Value,
-}
-
-/// FSRS-6 scheduling state, one row per memory.
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct SchedulingState {
- pub memory_id: Uuid,
- pub stability: f64,
- pub difficulty: f64,
- pub retrievability: f64,
- pub last_review: Option>,
- pub next_review: Option>,
- pub reps: u32,
- pub lapses: u32,
-}
-
-/// Hybrid search request.
-#[derive(Debug, Clone, Default)]
-pub struct SearchQuery {
- pub domains: Option>,
- pub text: Option,
- pub embedding: Option>,
- pub tags: Option>,
- pub node_types: Option>,
- pub limit: usize,
- pub min_retrievability: Option,
-}
-
-#[derive(Debug, Clone)]
-pub struct SearchResult {
- pub record: MemoryRecord,
- pub score: f64,
- pub fts_score: Option,
- pub vector_score: Option,
-}
-
-/// Edge in the spreading-activation graph.
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct MemoryEdge {
- pub source_id: Uuid,
- pub target_id: Uuid,
- pub edge_type: String,
- pub weight: f64,
- pub created_at: DateTime,
-}
-
-/// A topical domain (populated in Phase 4). Phase 1 only needs the type to
-/// shape the trait surface; discover/classify are Phase 4 work.
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct Domain {
- pub id: String,
- pub label: String,
- pub centroid: Vec,
- pub top_terms: Vec,
- pub memory_count: usize,
- pub created_at: DateTime,
-}
-
-/// Result of classifying one vector against all known domains.
-#[derive(Debug, Clone)]
-pub struct ClassificationResult {
- pub scores: HashMap,
- pub domains: Vec,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
-pub struct StoreStats {
- pub total_memories: usize,
- pub memories_with_embeddings: usize,
- pub total_edges: usize,
- pub total_domains: usize,
- pub registered_model_name: Option,
- pub registered_model_dim: Option,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub enum HealthStatus {
- Healthy,
- Degraded { reason: String },
- Unavailable { reason: String },
-}
-
-// ----------------------------------------------------------------------------
-// EMBEDDING MODEL SIGNATURE
-// ----------------------------------------------------------------------------
-
-/// Snapshot of the embedding model that was used to write vectors into the
-/// store. Persisted in the `embedding_model` table; compared on every write
-/// before the vector is accepted.
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct ModelSignature {
- pub name: String,
- pub dimension: usize,
- /// Lowercase hex-encoded blake3 hash, 64 chars.
- pub hash: String,
-}
-
-// ----------------------------------------------------------------------------
-// TRAIT
-// ----------------------------------------------------------------------------
-
-/// The single storage abstraction. `trait_variant::make` auto-generates a
-/// `MemoryStore` alias with `Send`-bound return futures so `Arc`
-/// works in tokio/axum contexts.
-#[trait_variant::make(MemoryStore: Send)]
-pub trait LocalMemoryStore: Sync + 'static {
- // --- Lifecycle ---
- async fn init(&self) -> MemoryStoreResult<()>;
- async fn health_check(&self) -> MemoryStoreResult;
-
- // --- Embedding model registry ---
- async fn registered_model(&self) -> MemoryStoreResult