schema · evergreen
The stack, and why
Milvus with built-in BM25, BGE-M3 locally, tree-sitter, a sha256 manifest, SQLite, MCP in-process.
One process, one collection, one database file. Each piece was picked against the four hard problems, not against a feature list.
MCP · /mcpsearch_code · read_code · list_repos, same process
FastAPI + typer/search /ask, webhooks, CLI
Retrieverroute → dense | BM25 → (RRF, rerank behind flags)
Milvus 2.6dense + BM25 sparse in one collection, repo_id partition key
BGE-M3 · tree-sitter · sha256 manifestembed · chunk · diff
SQLite + one workerrepos, files, jobs, enrichment cache
| Piece | Why | Rejected |
|---|---|---|
| Milvus 2.6 | BM25 is a built-in Function → no second index; partition key makes repo filters cheap | pgvector (no BM25), Qdrant (BM25 client-side), Elasticsearch (a second world) |
| BGE-M3, local, 1024d | multilingual: Turkish prose 0.684 where MiniLM had 0.04; code never leaves the machine | OpenAI / Voyage embeddings (code leaves; kept as a flag) |
| tree-sitter | chunk = code unit, citation = file:line — symbol | fixed windows, LLM chunking |
| sha256 manifest | rename, mode, submodule edge cases vanish; local dirs take the same path | git diff |
| webhook + poller | push-time freshness with a safety net | cron only, webhook only |
| SQLite + one worker | one file, no Redis, one pending job per repo | Postgres + Celery |
The whole thing talks to the world over HTTP and never writes into the repos it indexes. Personal access tokens travel as a git header and are redacted from every error message.