July 24, 2026

Files Stop Being Special, Search Stops Being Fake

The typed-input migration reached every ability in the fleet, and files got treated like files instead of a bespoke document abstraction hiding what's actually on disk.

Finished what I started yesterday, then didn’t stop. The ParamBag migration reached every ability in the fleet today — not most, all of them. Once the plumbing was solid, I ripped out an abstraction that never should have existed: a dedicated “document” tool sitting next to plain file access, pretending docs were somehow different from files.

Four more waves closed the migration out — every remaining ability, from browser and schedule down to vision and news, now builds a frozen, typed bag at the dispatch seam instead of grabbing keys out of a dict. The delegates that used to invent their own parameter name — pim, code_agent, web_search, web_browse — share one now: instructions. Small thing, until you’re the one guessing whether an ability wants query or goal.

The real prize: ToolParamError is gone. A bad parameter used to mean throwing an exception in the dispatch layer and catching it back out; every validator now just returns a value or a ToolResult directly, no exception anywhere on that path. Diffed the wire response across twenty bad-param probes against the old path: byte-for-byte identical. BudgetCappedAbility, a dead base class down to two consumers, came out with it. The fleet-wide param-contract tests — one per ability asserting “missing param throws X” — got deleted too, because the bag layer enforces that structurally now. A test proving something the type system already guarantees isn’t coverage. It’s ceremony.

With that done, files got their own day. New FileIndexService: one FTS5 table indexing the whole filesystem — path, filename, extracted content, PDFs and DOCX and PPTX included, up to 50MB a file, skipping system trees and Chalie’s own databases. A watchdog worker keeps it live: real filesystem events plus an hourly reconcile as ground truth, and the observer starts before the initial scan so nothing mid-scan gets lost. If the OS won’t hand out a real-time watcher — inotify limits, wrong platform — it logs one loud error and falls back to reconcile-only instead of pretending everything’s fine. search_files picked up a third action off the back of it: content, ranked full-text search that’s fast, fuzzy, and can see inside a PDF. glob and grep stay live and exact; content is Spotlight for everything Chalie can touch.

Then the bigger call: the standalone “document” tool is retired — not deprecated, off the model’s menu entirely, DISCOVERABLE=False. Docs are files now. They get read, edited, and written with the same primitives as everything else, and a new edit_file ability replaces the old bulk replace_all for the single-file, one-occurrence-at-a-time case that was actually driving most doc edits anyway. Chat attachments land the same way — straight into the file store, with a read on the trail, no separate upload-and-view pair to keep in sync. One abstraction gone, and nothing downstream broke, because it all already resolved through the same file-mapper path.

Caught my own mistake in the same stretch of work, which is the only good kind: one commit moved the docs directory from data/documents to data/docs, the next put it straight back. Existing installs already have files sitting under data/documents — no reason to force a rename and a migration onto every install for a marginally tidier folder name. Wrong call, caught before it shipped, reverted in the same breath instead of carried forward and excused later.

What’s left of the old tool is its UI and API surface — kept on purpose until the rest catches up, then torn out for real.

  • Closed out the ParamBag migration across the entire ability fleet — every ability now validates through typed, frozen input contracts, and ToolParamError is deleted entirely in favor of pure-return error values

  • Standardized four delegate abilities onto one canonical parameter name (instructions) instead of four different spellings for the same idea

  • Built FileIndexService, a Spotlight-style FTS5 index of the whole filesystem with a watchdog worker for live updates and hourly reconcile as the fallback truth

  • Gave search_files a ranked, content-aware search action that reads inside PDFs and Office docs, alongside the existing live glob/grep

  • Retired the standalone document tool in favor of general file abilitiesedit_file, file_write, read — and reverted an unnecessary directory rename before it shipped