Preserve context across conversation intents (V-542)

Owner explicitly requested direct commits to master; bypass the branch-only hook.
This commit is contained in:
2026-08-13 02:14:46 +04:00
parent a0e6643465
commit da9114b623
15 changed files with 499 additions and 56 deletions
+40 -2
View File
@@ -1,6 +1,6 @@
# Maven — Design
*Last verified: 2026-08-07 @ beb093a. Living doc: correct it in place, do not append.*
*Last verified: 2026-08-13 @ a0e6643 + V-542 working tree. Living doc: correct it in place, do not append.*
> Folded 2026-07-30 from `SPEC.md` (north star, 2026-07-03), `maven.md`
> (consolidated decisions, 2026-06-30) and `ROADMAP.md` (execution plan,
@@ -338,6 +338,44 @@ one run-on thought:
> вот что я нашла: вайфай пароль лежит в ящике стола, на какое время поставить
> напоминание?
#### Conversation context is independent of intent
Decided 2026-08-13 (V-542). A conversation is a sequence of turns, not a run of
one route label. The utterance "давай поболтаем: я купил новый монитор" may
correctly produce a grounded fact, and the next question may correctly route as
query. Neither decision is permission to discard the words that make "он" in
the next turn mean the monitor.
`dialogue.Session.Utterance` therefore stores the exact user turn separately
from `Slots.Text`. Slots are intent payloads: a fact may normalize them, a
stage-0 route may leave them empty, and a continuation may deliberately carry
an older topic. None of those is a transcript. `Session.History` holds up to
four prior turns in speaking order and is persisted with the session; blobs
written by older binaries fall back to their old `Slots.Text` field until they
expire.
`Session.Conversational` is orthogonal state too. A chat route sets it, as does
an explicit cooperative opener such as "давай поговорим" even when the
substantive clause routes fact. The opener is recognised from the closed marker
plus `lexicon.ConversationVerbs`, with Russian forms compared by `morph`; there
is no route regex or substring carve-out. Conversational state carries across
later intents and uses the existing 15-minute chat TTL instead of expiring the
anchor after two minutes.
After routing, `followUpMerge` may use state the router cannot see. A routed
query containing an anaphoric pronoun and a live prior transcript becomes chat,
with query-only source provenance cleared. `PhraseChat` then receives the prior
turns and the current utterance exactly once. This is narrower than adding the
transcript to every query source: a non-anaphoric calendar, recall or world
question still walks its evidence chain unchanged. Acts are never widened by
this rule; an unresolved "выключи его" still has no executable function and
must fail closed.
An explicit conversational opener does not suppress a substantive side effect.
The monitor statement remains a fact and also becomes the dialogue anchor.
Conversation state and save-where are orthogonal, so making the first route
chat would merely lose a true fact to work around a session defect.
### save-where — the two-memory routing axis
One discriminator: **does the loop evaluate a predicate against it?**
@@ -896,7 +934,7 @@ Condensed from `ROADMAP.md` (2026-07-06). The live queue is the Vikunja board
| 1.3 | desk_active presence script on desk PC | P1 | **not done** — operator action on `linux` (systemd user timer + hypridle listener); 0 facts ever written, presence runs on `page_heartbeat` alone |
| 2.1 | Cold-start unlock (passkey → L3 key seam) | P2 | code done `b0932a1`+`15fe7bb`, **tests missing** — wrap/unwrap round-trip, wrong-cred unwrap fails, locked-mode IPC rejects non-unlock methods |
| 3.1 | Always-on listening | P3 | MVP `e57647c` (energy-VAD only); remaining: wake-word model in `vad.go` |
| 3.2 | Conversation depth (multi-turn) | P3 | done `05236ad` — anaphora resolver + cross-intent `followUpMerge` + `Session.History` |
| 3.2 | Conversation depth (multi-turn) | P3 | repaired V-542 on 2026-08-13 — intent-independent utterance history; anaphoric queries reach chat context across fact/query/chat boundaries |
| 3.3 | Latency / streaming (streaming STT/TTS, barge-in) | P3 | not started; recommended path is WebSocket voice, keeping TCP for non-browser clients |
| 4.1 | Routing quality (dev embedder) | P4 | done `b7eb53a``make download-embedder`, configurable `voice.query_min_score` |
| 4.2 | Act surface broadening | P4 | not a code item (operator config) |