fix(13-10/13-11): replay chat history to the model; unbreak general answers

Four defects found by on-device UAT, 2026-08-06.

1. D-08 persistence was WRITE-ONLY. chat() loaded the transcript only
   AFTER the loop, to append — the model was never shown any of it. The
   assistant answered "I don't have access to any previous conversation
   history" with its own transcript on disk, and "and is it healthy?"
   resolved to the node instead of the app just discussed. History now
   replays into every turn (text only: a stale tool result must not be
   re-presented as this turn's evidence), scoped by HistoryKey. The
   replayed prefix is excluded from the append, or each turn would
   re-persist the conversation and grow it geometrically.

2. The operator persona forbade the very answers the content surfaces
   render. 13-01's prompt refuses anything without a matching tool, so
   "recommend me 10 sci-fi films" was declined and the film/song/podcast
   grids from 13-11 could never populate — two plans in contradiction.
   The refusal rule now governs ACTIONS ON THE NODE; general questions
   and recommendations are answered from the model's own knowledge.
   (Whether the node should also SEARCH THE WEB depends on AIUI's
   web-search setting, which embedded mode never forwards — captured as
   a separate todo because it opens a new egress path.)

3. The content-surface loader labelled unrelated queries "Podcast
   recommendations": the classifier matched a bare "show", which is how
   operators phrase almost everything ("show me my files").

4. "Surfacing…" tracks at 0.2em and its final glyph collided with the
   close button; the header now spaces them properly.

assistant::history 9/9 green incl. replay_feeds_prior_turns_back_to_the_model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-06 13:24:41 -04:00
co-authored by Claude Fable 5
parent 857d9d4906
commit 82d1b60891
4 changed files with 132 additions and 7 deletions
@@ -14,8 +14,11 @@
:class="isDark ? 'text-white/70' : 'text-gray-600'">
{{ contextLabel }}
</p>
<div class="flex items-center gap-2 shrink-0">
<p class="text-xs font-mono uppercase tracking-[0.2em]"
<!-- gap-3 + mr-1 on the label: "Surfacing…" tracks at 0.2em, so its
last glyph's letter-spacing sits flush against the close button
and read as overlapping it. -->
<div class="flex items-center gap-3 shrink-0">
<p class="text-xs font-mono uppercase tracking-[0.2em] mr-1"
:class="isDark ? 'text-white/25' : 'text-gray-400'">
Surfacing…
</p>
+4 -1
View File
@@ -646,7 +646,10 @@ const loaderContextType = computed(() => {
if (/\b(tv show|tv series|series|television|binge|season)\b/.test(q)) return 'tvshow'
if (/\b(image|images|photo|photos|picture|pictures|screenshot|gallery|artwork|illustration)\b/.test(q)) return 'image'
if (/\b(restaurant|restaurants|place|places|food|eat|dining|cafe|bar|pub|brunch|lunch|dinner)\b/.test(q)) return 'film'
if (/\b(podcast|episode|show|listen to)\b/.test(q)) return 'podcast'
// NOT a bare `show`: "show me my files" / "show the logs" is the most
// common phrasing in an operator chat, and it was labelling every such
// query "Podcast recommendations" (reported on-device 2026-08-06).
if (/\b(podcast|episode|listen to)\b/.test(q)) return 'podcast'
if (/\b(news|latest|recent|current|what'?s happening|what are people saying)\b/.test(q)) return 'news'
if (/\b(bip|protocol|debate|sentiment|bearish|bull case|macro)\b/.test(q)) return 'magazine'
if (/\b(website|websites|where to check|best places|check online|resources?|sources?)\b/.test(q)) return 'websites'