How We Deleted Our Retrieval Index and 5,246 Lines With It
For several months Atlarix carried a full retrieval stack: a SQLite FTS5 index, BM25 scoring, a ctags symbol tagger, a reranker, and a file-watcher keeping it all current. It was the architecture everyone recommends. On 2026-07-10 we deleted the entire thing in one release — a net of 5,246 lines — and replaced it with bundled ripgrep. The reason was not elegance. It was that the watcher was setting users' machines on fire.
The symptom nobody could reproduce on a small repo
The reports were vague in a familiar way: fans spinning up, the app getting warm, and eventually commands failing outright. None of it happened on the repositories we tested against day to day, which is precisely why it took a while to find. The failure needed scale to appear at all.
On a large repository the app would eventually start failing to spawn child processes altogether. Every shell command, every git call, every tool that needed a subprocess — refused, with an error that named nothing useful.
One file descriptor per watched file
The cause was chokidar, the file-watcher keeping the index fresh. On macOS it watches through fs.watch, which is kqueue, and kqueue costs one open file descriptor per watched file. Not per directory. Per file.
On a Jenkins-scale repository that worked out to roughly 12,365 open descriptors held permanently by the main process. Process limits are not that generous, so the next subprocess spawn hit the ceiling and returned spawn EBADF. The heat was separate and self-inflicted: FTS5 rebuilds were reading every file in the repository, repeatedly.
Why we removed the index instead of fixing the watcher
A polling watcher, a debounce, a smaller watch set — all of these were available, and all of them preserve the thing actually generating the cost, which is an index that has to be kept in sync with a moving tree. Every one of those fixes buys back some descriptors and keeps the synchronisation problem.
The question we asked instead was what the index was buying. And measured against ripgrep on the same queries, the answer was not much: the reranker mostly reordered results that were already correct, and the symbol tagger answered questions the model could answer by reading the file.
What actually shipped
Retrieval is now grep and glob, both running bundled ripgrep, shelled out per call with no state between calls. The header of that file says it plainly: there is no symbol index and no relevance rerank. Matches come back in ripgrep's own order.
Ignore handling survived the teardown and got stricter — .gitignore and .atlarixignore are passed explicitly, so they work even outside a git tree, while parent-directory and global git ignores are deliberately not applied. After the change, file-descriptor count stayed flat at around 113 under real load.
What we gave up, stated plainly
This is a real trade and it would be dishonest to present it as pure profit. A lexical search cannot find a concept you did not name: search for authentication and you will not be handed the function called verifyCredentials. An embedding index would have a better chance at that.
What we get in exchange is that there is nothing to build, nothing to invalidate, nothing to keep warm, and nothing to upload. The agent reads files the way you would. On code, where identifiers are shared vocabulary rather than prose, that trade has been worth making.
The rule we took from it
The index was not a bad idea, and the research behind it is published and stands. What made it a liability was that its cost lived somewhere nobody was measuring — descriptors held by a background watcher — while its benefit was assumed rather than tested.
So the rule is that a subsystem running continuously has to justify itself continuously. If we cannot measure what it buys against the simplest thing that could work, it is carrying weight nobody has priced. Less machinery, nothing to drift.
The teardown is the change we would point at first if asked what Atlarix learned in 2026. It removed more code than any feature we have added, it fixed a bug that had been quietly ruining large-repository sessions, and it made the retrieval story simple enough to describe in one sentence. Deleting it was the feature.