May 3, 2026
Rich-media cards go physical, not promptable
Weather, timer, and article cards ship end to end while the rich-media contract is rebuilt as a dispatcher-level gate instead of a prompt instruction.
The goal for the day was to take rich-media chat cards from a working weather pilot to a real feature: multiple card types, a contract robust enough the model can’t quietly opt out of it, and a light/dark theme pass so the interface looks intentional rather than assembled. All three landed, and the middle taught the sharpest lesson of the day.
Rich-media cards started as a good idea with a weak enforcement mechanism: a tool would bake an instruction into its result telling the model to wrap its answer in a <span id="weather_1"> tag, trusting the model to comply. It mostly did — until a subagent got involved. Round one fixed trailer compliance and how tool-call rows get matched back to the right turn. Round two tried teaching the parent model to preserve spans already in a subagent’s output via an “inheritance” clause in the prompt. Testing showed a near-zero pass rate, and the longer prompt weakened compliance on the direct-call path. Round three dropped the idea that this should be a prompt-following problem at all: the ordinal that turns a tool result into a card is now injected only when the dispatch channel is user-facing, so a subagent call never sees it and cannot emit a card tag, and anything a subagent returns is scrubbed of stray tags before reaching the parent. The contract stopped being something the model must remember and became something the code enforces — wiring over prompting, now baked into every future card type.
Two new cards shipped on top of it. The timer ability is a pure ephemeral countdown — pause, stop, a real alarm — deliberately excluded from the wall clock the model can see: it gets a title and duration, and the frontend grafts the actual start timestamp from the database row, so the model can’t fabricate or edit a timestamp it never had. The article card (search results and news) went from a plain link to something with a real photo: an image-candidate service shortlists up to three thumbnails per result, runs them through local OCR, and hands the model a candidate list to pick from — or none. One mid-flight fix: OCR text on news photos is usually empty, and a blank signal was making the model default to “no image.” Swapping the primary matching signal to the source title fixed that without touching OCR itself.
The other thread was interface: a full light/dark theme with persistent toggle, warm-paper light background, and a token system replacing hardcoded dark-mode colors — hunting down every surface that assumed black (audio player, slide-out panel, moment-recall overlay, reasoning status text) and re-pointing it at theme variables. Chat bubbles got a redesign too: assistant turns went full-bleed with a header row (glyph + timestamp) instead of a floating badge, user turns dropped their meta row for bold text, and every drop-shadow suited to dark mode but a bruise in light got removed. A same-day production incident also closed here: a subagent ability’s input schema declared a field named type, colliding with the dispatcher’s own envelope key and silently breaking every subagent call that specified an agent type explicitly. Fixed by renaming the field and flipping the merge order so the envelope wins, plus a systemic test scanning every ability for that collision class.
What this unlocks: rich-media cards are no longer a one-off demo, they’re a pattern — a channel-gated contract plus a per-ability enrichment hook, with three card types (weather, timer, article) proving it holds across very different data shapes. The next ones should be cheap.
-
Rich-media contract rebuilt as a physical dispatcher gate on the user-facing channel, not a prompt instruction, closing the subagent-leak class for good
-
Timer card ships: ephemeral countdown with pause/stop/alarm, wall-clock timestamp kept server-side and invisible to the model
-
Article cards get OCR-shortlisted image candidates so search and news results can carry a real photo the model picked
-
Full light/dark theme with persistent toggle, warm-paper palette, and a bubble redesign removing every dark-mode-only shadow
-
Production incident closed: a colliding schema field was silently breaking explicit agent-type calls; fixed, plus a registry-wide regression latch