From 71689142bcf21101bdb9434fc99c72c72ebd5ba8 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Sat, 1 Aug 2026 18:55:58 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20Donut=20voiced-interview=20slice-3=20?= =?UTF-8?q?=E2=80=94=20retire-ready=20KB-recall=20bridge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grounds the interview character in the ingested corpus while she stays in-voice. Tier-3 agents are tool-less by design in v1, so this is the consumer-side workaround (DEC-6, worldtree-dev ruling): per opted-in interview turn, ratatoskr consults Mimir out-of-band, extracts the passages, and pins them as memory_context on the character's turn. She frames the pinned corpus as her own memory. - src/ratatoskr/kb_bridge.py (new, RETIRE-READY): pin_kb_context β€” THE single seam (INV-KB-1). Allowlist-gated (INV-KB-4: ratatoskr:donut only), hard-timeout-bounded, degrades to [] on any failure/timeout/empty (INV-KB-3, never raises; CancelledError propagates). Imports nothing from the SDK-adapter / TTS core. aclosing() closes the SDK stream deterministically on the DoneEvent break. - wt.stream_turn: memory_context passthrough (defaults None β€” inert for every other caller and for the bridge's own retirement). Seam-review catch: the contract's original touch list undercounted wt.py by one file (recorded in the contract). - web/server.py: TurnHandle.agent_id + the single pin_kb_context call-site on the turn path; the browser now sends agent_id so the allowlist can gate. - web/static/index.html: the turn POST carries agent_id. Consult prompt tuned live: "search_library EXACTLY ONCE, no read_note" converges Mimir in ~3-15s (the softer "do one search" phrasing looped past 25s on conversational questions). TDD: 12 kb_bridge unit tests + wt memory_context forwarding + 2 server wiring tests (531 green). Live-smoked on :8081/b128: pin_kb_context grounds in the DCC corpus (real excerpts, <20s) and Donut answers in-voice; degrades cleanly on a slow consult. KNOWN LIMIT surfaced (not a bridge defect): DCC's fiction index is weak (failed backfill, a worldtree-dev item), so grounding is opportunistic β€” the bridge's real payoff is a corpus the model does not already know. Per docs/contracts/donut_voiced_interview.contract.md (slice 3 of 3). --- .../donut_voiced_interview.contract.md | 26 +++- src/ratatoskr/kb_bridge.py | 113 ++++++++++++++ src/ratatoskr/web/server.py | 24 ++- src/ratatoskr/web/static/index.html | 2 +- src/ratatoskr/wt.py | 15 +- tests/test_kb_bridge.py | 143 ++++++++++++++++++ tests/test_web_server.py | 55 +++++++ tests/test_wt.py | 10 +- 8 files changed, 375 insertions(+), 13 deletions(-) create mode 100644 src/ratatoskr/kb_bridge.py create mode 100644 tests/test_kb_bridge.py diff --git a/docs/contracts/donut_voiced_interview.contract.md b/docs/contracts/donut_voiced_interview.contract.md index 650e52c..5fd506f 100644 --- a/docs/contracts/donut_voiced_interview.contract.md +++ b/docs/contracts/donut_voiced_interview.contract.md @@ -19,9 +19,11 @@ scope: > consult (an existing agent turn). touches: - src/ratatoskr/web/server.py # /api/tts route + the retrieval-pinning seam on the turn path - - src/ratatoskr/web/static/index.html # speak-on-done playback, πŸ”Š toggle,