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
one process, one collection, one file
one FastAPI processStreamable HTTPpushqueryjob per repohybrid searchupsertmanifest · jobsAgentClaude Code · CursorGitHubwebhook · pollerMCP · /mcpsearch · read · listFastAPI/search /ask · CLIRetrieverroute → dense | BM25Indexertree-sitter · BGE-M3Milvus 2.6dense + sparseSQLiterepos · jobs · cacheBackend3Database2Queue1External2
PieceWhyRejected
Milvus 2.6BM25 is a built-in Function → no second index; partition key makes repo filters cheappgvector (no BM25), Qdrant (BM25 client-side), Elasticsearch (a second world)
BGE-M3, local, 1024dmultilingual: Turkish prose 0.684 where MiniLM had 0.04; code never leaves the machineOpenAI / Voyage embeddings (code leaves; kept as a flag)
tree-sitterchunk = code unit, citation = file:line — symbolfixed windows, LLM chunking
sha256 manifestrename, mode, submodule edge cases vanish; local dirs take the same pathgit diff
webhook + pollerpush-time freshness with a safety netcron only, webhook only
SQLite + one workerone file, no Redis, one pending job per repoPostgres + 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.

#milvus #bge-m3 #tree-sitter #architecture

See this note on the whiteboard →