Preserve context across conversation intents (V-542)
Owner explicitly requested direct commits to master; bypass the branch-only hook.
This commit is contained in:
+40
-2
@@ -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) |
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# Five turns retain one referent across fact, query and chat routes
|
||||
|
||||
Measured 2026-08-13 on homesrv from `master` at `a0e6643` plus the V-542
|
||||
working tree. Task: V-542. The run uses the deterministic simulator phraser;
|
||||
no resident model is needed.
|
||||
|
||||
## Setup
|
||||
|
||||
`conversation_anaphora.json` replays the five Russian turns that exposed the
|
||||
defect. Their scripted router decisions remain the observed
|
||||
fact/query/query/chat/query sequence. Each of the four contextual replies is
|
||||
accepted only if the chat phraser receives history containing the original
|
||||
`купил новый монитор` turn; this prevents a canned response from hiding a
|
||||
missing transcript.
|
||||
|
||||
Commands:
|
||||
|
||||
```sh
|
||||
make t PKG=./cmd/mavend RUN='TestExplicitConversationOpenerKeepsCrossIntentSessionAlive|TestConversationOpenerDoesNotMatchAnotherDavaiCommand|TestFollowUpMerge|TestSimulatorScenarios/conversation_anaphora' V=1 RACE=1
|
||||
make t PKG=./internal/dialogue RUN=TestSessionSurvivesRestart V=1 RACE=1
|
||||
make t PKG=./internal/router RUN=TestAnaphoraResolverUsesTokenBoundariesAcrossPunctuation V=1 RACE=1
|
||||
MAVEN_DIALOGUE_NO_SKIP=1 make t PKG=./cmd/mavend RUN=TestDialogueTraces V=1 RACE=0
|
||||
```
|
||||
|
||||
## Result
|
||||
|
||||
| Gate | before | after |
|
||||
| --- | ---: | ---: |
|
||||
| contextual replies that name the monitor | 0/4 | **4/4** |
|
||||
| replies proven to receive the original turn in history | 0/4 | **4/4** |
|
||||
| original grounded fact still written | 1/1 | **1/1** |
|
||||
| unsolicited sends in five turns plus one tick | 0/6 | **0/6** |
|
||||
| focused race-tested packages | — | **3/3 pass** |
|
||||
|
||||
The forced dialogue suite passed 18/22 rows. Its four failures are the existing,
|
||||
unrelated offline-floor cases for a mid-flow note, a correction while a question
|
||||
is parked, a whole-day reminder, and a short correction that does not park a
|
||||
clarification. None exercises V-542; the continuity scenario and all focused
|
||||
race gates pass.
|
||||
|
||||
## What this rules out
|
||||
|
||||
The fix does not rewrite pronouns into guessed nouns or add a phrase regex.
|
||||
Exact utterances form a persisted, chronological transcript independently of
|
||||
intent slots. An anaphoric query with live dialogue context moves to chat and
|
||||
clears query-source provenance; non-anaphoric queries retain their source path,
|
||||
and acts are never widened. An explicit conversation opener extends session
|
||||
lifetime without suppressing the fact side effect in its substantive clause.
|
||||
Reference in New Issue
Block a user