July 27, 2026
A Success Response Was Lying By Omission
An empty search coming back as status=success was quietly teaching the model to make things up, and the fix meant auditing every read action in the fleet for the same lie.
Found the same bug shape everywhere today, once I knew to look for it: a read action that finds nothing was answering status=success with an empty result. To a strong model, that’s obviously “nothing here, move on.” To a weaker one, success reads as “the call worked” — and it settles on fabricated content instead of admitting it doesn’t know. That’s a worse failure than an error, because it doesn’t look like one.
Went through the fleet action by action — search, news, image search, file search, email, schedule, contacts, skills, MCP tools, calendar, smart-home device lists, the transcript and tool-call review windows — and converted every zero-row read into a loud status=error code=no-results. Left the neighboring cases alone on purpose: a target you already found with genuinely empty contents isn’t a miss, a partial hit isn’t a miss, and an infrastructure failure (provider unreachable, not connected, query failed) is a different error and shouldn’t get relabeled into this one. A no-results contract that also swallows real outages just moves the lying somewhere else.
Memory recall got the same treatment plus one more problem. The turn-0 seed recall — the automatic memory pull that opens every conversation — was dumping up to nineteen weak-match rows into context before the model had asked for anything, most of them low-confidence noise drowning out whatever the model actually needed to look up live. Capped low-relevance rows at three, kept medium and high uncapped, and collapsed two separate recall hints into one standing rule that rides along on every hit or miss: memory is context, not ground truth — live state gets double-checked with a tool regardless of what memory says. The seed recall also got pulled out of the act trail’s normal tool-call rendering into its own background_memory block, so the model stops reading “every turn starts with a memory call” as the pattern to imitate.
The other fix was testing debt, not a feature: the strict typing gate had been red with nine errors, all missing or too-narrow annotations, and four telemetry tests were failing on a stale premise — both provider test doubles answered with empty text and tripped a since-added retry-on-blank-completion guard, tripling the ledger rows the tests expected. Fixed the annotations, gave the doubles real text to return, scrubbed ten leftover references to a typing doc that was deliberately deleted. Proof wasn’t a feeling — mypy clean across all 683 files, full suite at 1164 passed.
Last thing: every ability now carries its tool name as a single typed constant instead of a get_name() method, and the registry throws at boot on anything missing, empty, or duplicated. A rename now propagates everywhere it’s referenced instead of leaving stale prose sitting in a skill file nobody re-read. Diffed all 42 ability descriptors and 22 config prompt values against the previous tree to confirm the only actual change was one intentional reword — same discipline as yesterday’s dead-reference sweep, smaller in scope, caught before it could rot.
Next: watching whether the no-results contract actually changes model behavior on a real miss, not just in the test suite.
-
Converted every zero-row read action across the fleet — search, files, email, schedule, contacts, skills, MCP, calendar, smart-home — from a quiet
status=successto a loudstatus=error code=no-results -
Capped the turn-0 memory seed at three low-relevance rows and collapsed two recall hints into one rule: memory is context, live state still needs a tool check
-
Re-rendered the seed recall as its own context block instead of a normal tool-call row, so it stops teaching memory-first habits
-
Greened the strict typing gate (9 errors, all missing annotations) and fixed four telemetry tests that were failing on a stale test-double assumption
-
Made each ability’s
NAMEconstant the single source of truth for its tool name, with a boot-time check that fails loud on anything missing or duplicated