Why bridge-db runs on FTS5, not a vector database
I had a vector-search layer planned for my agent memory. A dry-run before I built it killed the whole idea; the misses were never a retrieval problem.
bridge-db is the thing that keeps my tools on the same page. It's a SQLite-backed MCP server, and a bunch of things read and write to it: Claude.ai, Claude Code, Codex, plus a few local ops tools I run (an operator-control daemon, a notification hub, a GitHub repo auditor, a Notion sync). Without it, each one keeps its own scratch notes and they drift apart. With it, they share one store and stay in sync across sessions and machines. There are two dozen MCP tools sitting on that store, and since I first wrote this it has grown a write-provenance layer (every write now carries who wrote it) and shipped-event tracking. This is about one of them, recall: the thing that answers "what do I already know about this?"
The obvious build
If you're building search for something like this today, the reflex is semantic search: embed every record, embed the query, hand back the nearest matches. That's what I planned to do. The design was a proper hybrid: a vector index, an embedding model, and a weighted rank-fusion step to merge the vector results with plain keyword results. Textbook retrieval.
The dry-run
Before I built any of it, I went back and looked at the queries recall was actually fumbling: the ones coming back empty or with the wrong rows. I figured I'd find a synonym problem: the query and the record meaning the same thing in different words. That's the gap embeddings are good at closing, so that's what I expected to see.
It wasn't there. The queries that missed were looking for content that wasn't in the database at all. Nothing had failed to match; the record had simply never been written. And no amount of vector search returns a row that doesn't exist.
What I shipped instead
So I killed the vector work and shipped FTS5. It's a content_index virtual table mirroring the content tables, BM25 ranking, exposed through that one recall tool. The best part is what it doesn't need: FTS5 lives inside SQLite, so there's no embedding service to call, no network hop, and nothing to install differently on each machine. It's deterministic, and it behaves the same everywhere the bridge runs. A health check keeps the index lined up with the source rows so it can't quietly fall out of sync.
Why this is the right call here
This isn't a "vectors are overrated" take. It's that bridge-db isn't a knowledge base; it's a coordination layer. The text in it is my own notes, activity, and handoffs, written in roughly the same vocabulary I'd use to search for them. It's small and consistent. The whole reason embeddings are worth the trouble is to bridge wording that doesn't line up, and on a corpus like this one there just isn't much of that gap to bridge. Vectors earn their keep on big, messy, many-author piles of text. A personal bridge is the opposite of that.
The part that still bugs me is that this was a diagnosis problem, not a search problem. When something says "search isn't finding things," the obvious move is to make the search smarter. The dry-run flipped that for me: the bug was upstream, in what was getting written down, not downstream in how it got matched. If I'd just built the fancy layer, I'd have buried the real issue, shipped vectors, watched recall keep missing, and gone off chasing the wrong thing.
What would change my mind
None of this is permanent. If the corpus ever grows across a lot of authors and vocabularies, or I start querying in language that looks nothing like how the records are written, that semantic gap gets real and the hybrid plan comes back on the table. I kept the eval harness around to measure exactly that, the day it happens. Until then, the right amount of machine learning in my memory layer is none.