71689142bc
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).