June 10, 2026

Every tool now speaks one honest language

Chalie's entire toolset moves onto a single sealed result contract, so a tool can no longer dress up an error as a success.

Today was about trust, not features. Chalie has dozens of tools — search, calendar, email, documents, a browser, a memory system — and every one used to report back in its own dialect. Some returned prose that read like success even when the call had failed. Some swallowed exceptions and shrugged. Some silently deleted the wrong thing when a name was ambiguous. That inconsistency is exactly the kind of rot that makes an agent unreliable in ways you can’t see until it costs someone data. So today I closed it: every tool now speaks through one sealed contract, and I spent the day migrating the entire toolset onto it.

The core change is small to describe and enormous to land: a tool result is now either ok() or err(), full stop. Errors carry a stable, human-readable code — never a vague “something went wrong,” never a success message hiding a failure underneath. A single dispatcher owns the only path that renders a result back to the model, so there’s no longer a dozen places where a tool could improvise its own formatting and diverge from the rest. Rich content — screenshots, cards, structured data — now travels as an explicit part of the result instead of a side-channel the dispatcher had to guess at. A pre-check step now runs before permission checks too, closing a real deadlock: malformed input could previously get null field values written into a policy record, leaving the ability asking for the wrong thing forever.

Before migrating individual tools, I had to build shared plumbing they could all stand on: one URL-safety guard (the browser tool had its own hand-maintained blocklist that had quietly stopped catching a couple of private IP ranges — fixed by sharing one guard), one fetch layer with named profiles for browsing, API calls, and large downloads, and a shared base class for tools that talk to outside services like contacts, email, and calendar. With that foundation in place, the migration wave touched nearly the entire surface: file operations, search, the memory system, the browser, timers, lists, documentation lookup, calendar, email, and more — each converted to the same honest contract, each one’s failure modes made loud instead of silent.

A few of these were more than mechanical. Deleting a document by name used to silently take the first fuzzy match — with two similarly named files, you could lose the wrong one without knowing it. Now it demands an exact match or a unique name, and anything ambiguous comes back with the candidates so the right one gets picked deliberately. The documentation-lookup tool used to fabricate a “best guess” URL and present it as a real fetched page when its source was unreachable — the kind of thing that erodes trust the moment a user notices — so it now says plainly when a source can’t be reached instead of inventing an answer. And the command-execution tool used to judge how risky a command was by what the model claimed it was doing, meaning a cleverly worded request could talk its way past a safety check. That risk is now derived from the actual command being run, not the model’s self-description.

None of this is a feature anyone will screenshot. But it’s the difference between a system where “it usually works” and one where “when it says it worked, it worked.” That’s the bar I’m building Chalie to. It sets up cleanly for what comes next: with every tool now returning a predictable, typed result, I can start reasoning about tool behavior in the aggregate — catching entire classes of bugs at build time instead of one call at a time.

  • Every ability now returns a sealed ok()/err() result with stable error codes — no more errors dressed up as success

  • Shared safety and fetch infrastructure (URL guard, fetch profiles) replaces several duplicated, drifting implementations

  • Document delete no longer silently guesses on ambiguous names — it asks instead

  • Documentation lookup stopped fabricating “best guess” links when a source is unreachable

  • Command-execution risk is now derived from the actual command, not the model’s self-reported intent