July 25, 2026
Boot Stops Lying About What's Installed
A dependency nobody declared was quietly starving every request's context budget by a third, and voice went from press-and-wait to already-spoken by the time you reach for it.
Found a bug today that’s been running since god knows when: a dependency that was never installed, failing silently into a fallback that lied about its accuracy. Fixed it. Then made the whole class of bug impossible.
tiktoken measures how big a request actually is before it hits the model — the number the compaction gate watches to decide when to trim history before a request blows the window. Never declared in any manifest. The import sat inside a try/except ImportError, so on every install where it silently failed to land, the measurement fell back to a word-count heuristic that under-measures a real request by about a third and drops tool definitions entirely. The gate was watching a number that was quietly wrong — it never fired when it should have, and context ran past the window it was supposed to protect. That kind of bug never announces itself. It just makes things slightly worse in a way nobody traces back to a missing package.
The fix isn’t just declaring the dependency. It’s a boot-time preflight that reads every dependency out of the manifest and checks it against what’s installed. Miss one and boot doesn’t proceed: a critical line to stderr, the holding page names exactly what’s missing, the health check fails with no retry-after, because no retry is coming. A backend with a hole in its dependencies used to boot anyway and hope. Now it refuses.
The rest of the day’s context work was the same instinct: get the real number, stop guessing. Two exceptions used to cover “request doesn’t fit the window” — one for the pre-flight check, one for the provider rejecting it mid-inference — and they’d drifted into two recovery paths for one problem. Collapsed into a single exception that owns the recovery: log it, compact the turn’s history, let the model read back what got folded away, retry smaller instead of resending the same request. The window itself stopped being a hardcoded guess too — providers now carry their own context window, capped at 200k, sized against what the model actually supports.
Voice got the bigger feature. Speech for a reply used to generate when you pressed the speaker button — cold model start, retry ladder, all of it while you sat there waiting. Now it’s done before you’d think to press anything: the moment a turn settles, synthesis kicks off in the background, and the button just reads whatever got made. Press play, get audio. No ladder, nothing left to wait out.
Yesterday’s unfinished business got finished too. The Documents panel — the UI I said I was keeping “until the rest catches up, then torn out for real” — is gone, with the database tables, the response DTOs, and every policy row that went with it. Docs are files now, full stop. The privacy endpoints — export, delete-all, data summary — got deleted alongside it: compliance scaffolding for a regime that was never going to apply, since Chalie runs single-user, on the owner’s own machine, against the owner’s own data. No second party to serve a request to.
Smaller cleanup rode along: routes stop being declared by whichever controller owns them. One table now owns every path, and an unmounted controller fails loud instead of quietly not showing up.
What’s next is whatever real usage does to today’s bets — a compaction gate firing on the right number, and speech that’s already there when you reach for it, only prove out under load.
-
Fixed a silently-missing dependency that under-measured context by roughly a third and defeated the compaction gate; boot now refuses to start on any missing dependency instead of degrading quietly
-
Collapsed two divergent “request doesn’t fit” exceptions into one path that compacts and retries smaller instead of resending the same request
-
Gave providers a real, capped context window (200k ceiling) instead of sizing every request against a guess
-
Shipped a voice pre-synthesis pipeline — speech generates the moment a turn settles, not on speaker press, killing the old retry ladder
-
Finished the docs-as-files migration — dropped the legacy document tables, DTOs, and UI panel — and deleted the privacy endpoints as scaffolding for a regime that never applied to a single-user system