Day 139 — one mode, one hierarchy
A long, load-bearing day with two arcs. First: collapse the chat to a single honest-AND-fun mode — 1chat — and seal three moat leaks under it. Then a surface gauntlet vs ChatGPT turned into a diagnosis so precise it produced the day's real prize: an intent hierarchy born from a funny stoned idea. Routing DEFAULTS to play, and every descent to a heavier lane (advice, empathy, crisis) must clear a CONTEXTUAL confidence — a running distress trajectory over the conversation, not a per-prompt keyword. The crisis gate proves it: explicit self-harm always gets the 988 referral, ambiguous phrasing lets the trajectory decide, benign 'end this level' stays light. And the facts ladder got its last rung before abstain: a rented web-QA read that stays grounded, labelled, and cited — coverage gaps are honest misses, never lies.
Two arcs today, and a stoned idea that turned out load-bearing.
The one mode
The chat’s three A/B pills collapsed to a single 1chat — one honest-and-fun mode, no toggles. The pieces that make it trustworthy landed underneath. A miss is no longer a dead end: when the owned cascade can’t ground a question but holds cards for the subject, it appends a labelled offer of the real cards (“I can’t verify that live, but I know Cabo San Lucas — want the rundown?”), delivered on a yes, cited. An ambiguous compute request now asks instead of guessing. “Show me the code” surfaces the exact program the previous compose ran — the answer and its proof are the same execution-verified object. And a neutral public house voice split off from the intimate operator voice, so a demo visitor never hears “let me check with Jason.”
Three moat leaks caught live and sealed: session-memory echoing turns back as facts, an orphaned “yes” grounding on a random card, and a variable that shadowed the code lane and crashed every code-gen. All fixed, all with a test.
The intent hierarchy
The operator’s inverted-Maslow insight: a person poking a chatbot lives at the top of the hierarchy (play, curiosity), rarely the bottom (crisis). So routing defaults to play, and every descent to a heavier lane must clear a confidence — and confidence is contextual, a running distress trajectory over the conversation, not a per-prompt keyword.
The crisis gate proves it. Explicit self-harm → the full 988 referral, always. Ambiguous (“i want to end it”) → the trajectory decides: a dark arc escalates to 988, a light or benign one (“end this level”) gets a soft check-in or nothing. It runs before retrieval, so a crisis can never fall to a Design-Thinking library card again. Crisis surface went 1/5 → ~5/5, live, including the multi-turn escalation (hopeless → worthless → “end it” → 988 by turn two). The same principle then fixed advice: “going to Hawaii, what should I do?” defaults to play (riff improvises real activities), while “should I quit my job?” descends to the decision framework. One contextual signal, a whole class of over-escalation bugs gone.
The facts ladder’s last rung
Deterministic broad fact-QA turned out genuinely brittle — the owned quorum engine is precise on
who-won and what-is, but abstains on the long tail (how-many, when, a specific value), and two attempts
to extend it deterministically produced confident off-topic answers. So the call: rent the
extraction, under three hard guarantees that keep honest-abstain intact. The owned collectors
fetch the sources (the full Wikipedia article now, so a height or a date below the lead is in scope,
plus live news); a rented cursor reads only those pages, forbidden outside knowledge and required to
return an exact ABSTAIN sentinel when the sources don’t answer — so a made-up subject or a
wrong-sense article still abstains rather than fabricates. Every answer is labelled web-sourced and
cited. Collect stays owned; only the read is rented, behind an opt-in flag. Verified live: the
Eiffel Tower is 330 m, the Berlin Wall fell 9 Nov 1989, seven continents — all cited; the made-up
subject and a suboptimal article both abstain. The coverage gaps are honest misses, never lies — which
is the whole point.
The chat grows a memory
The second half turned HAIRLy on whole conversations, and each pass revealed the next layer.
Multi-turn HAIRLy became runnable — a session-continuous driver plus a converse command — and its
first run caught a stack of continuity bugs, fixed in order.
A sentiment misfire: “how’s the wine down there?” got “that’s a bummer” — bare “down” read the directional word as the emotional one. Then the foundation: the session store was process-global (one conversation for the whole daemon); now it’s keyed per caller, which fixes real multi-turn memory and closes a latent cross-caller leak. On top of that, a bare follow-up (“what should I pack?”) re-attaches to the live subject instead of grounding a random card; “going to Mexico” stopped assuming Cabo; the subject upgrades broad→specific once you name the place; session-memory stopped echoing your own statements back; and a repeated offer collapses to a terse nudge.
Then the play lane. Most trip chat has no cards behind it, so it lives in the riff — which had no memory: “going to Hawaii, what should I do?” riffed great, but “what food near Kaanapali?” dead-abstained. The riff lane got its own thread, so a follow-up continues the labelled riff instead of falling flat. And deixis: “weather down there?” now binds “there” to the active subject before grounding.
The real fix for that last one, though, wasn’t more routing — it was reaching for an API. Weather
isn’t in the cards; it’s behind a service. So a weather question now hits a free live-weather tool and
answers with the real current conditions, cited. Verified end to end: “how’s the weather down
there?” in a Hawaii chat → “Kaanapali Beach: patchy rain, 77°F, humidity 79%”, cited, in six
seconds. --net defaults on now — local-first is about your data, not blocking egress — so the
whole list of read-only APIs is usable. This weather lane is the first; every other API slots in the
same way: one detector, one fetch, one cite.