Commit Graph
2 Commits
Author SHA1 Message Date
archipelagoandClaude Fable 5 82d1b60891 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>
2026-08-06 13:24:41 -04:00
archipelagoandClaude Fable 5 3da9928cc9 feat(13-10): node-side chat history, scoped by caller, compacted not truncated
history.rs persists the ChatMessage transcript under data_dir (D-08),
keyed by a HistoryKey derived from CallerScope so an operator's AIUI
session and a mesh peer's transcript are structurally distinct files, not
two rows a filter could forget. Writes are atomic (temp sibling + rename,
matching music/index.rs::save_atomic's precedent) and 0600, following
grants.rs's convention.

Tool results longer than MAX_TOOL_RESULT_CHARS are truncated with a
visible marker before entering history -- a new, assistant-scoped
constant, never assist.rs's LoRa-airtime-tuned reply cap. Once the
transcript exceeds KEEP_VERBATIM_TURNS, older turns fold into a running
summary extended incrementally as turns age out, never regenerated from
the full transcript. Wallet/files-category tool-call arguments are never
persisted (AI-SPEC §7b's field policy applied to storage, not only
tracing) -- categories are resolved by the caller from the same tools
registry execute_tool uses, so history.rs never re-derives a second,
driftable category list. Nothing reachable from confirm.rs's pending-
confirmation state has a parameter path into this module at all (S-09
stays true structurally).

assistant.history / assistant.clear-history route through 13-01's
existing assistant.* dispatcher arm (dispatcher.rs untouched), each
scoped to the calling session's own HistoryKey.

run_loop (loop_.rs) now returns (answer, full_history) instead of just
the answer string -- structurally necessary so chat() (mod.rs) can
persist the tool-call/tool-result messages the loop built internally, not
only the user question and final answer (Rule 3, mirroring 13-05's
precedent of touching a file outside its own plan's files_modified list
when the plan's own intent requires it). chat() persists this turn after
run_loop returns; it does not yet feed prior persisted turns back into
live model context -- a documented, deliberately scoped follow-up (see
mod.rs's chat() doc comment and the plan SUMMARY).

8 new tests under assistant::history::tests::, including
operator_and_mesh_transcripts_are_separate and
wallet_tool_arguments_never_reach_the_transcript (asserted against both
the deserialized struct and the raw on-disk bytes). Full assistant::
suite: 50/50 (42 baseline-after-Task-1 + 8 new); confirm::tests::
restart_drops_pending_not_executes still passes -- S-09 not weakened.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 18:03:55 -04:00