Ask one clarifying question, then finish the request #6

Closed
claude wants to merge 0 commits from overnight/clarify-wiring into master
Contributor

Stacked on #1 (the data layer) — review that one first. This is the half you can actually feel.

Before: router is not confident → Maven says "не разобрала" → your next sentence is parsed from scratch → the original request is gone.

After: she asks one specific question, remembers what she was doing, and finishes the job when you answer.

you:    напомни купить молоко
maven:  На когда напомнить?
you:    в семь вечера
maven:  (reminder created for 19:00)

The three questions, please check the Russian:

missing she asks
time На когда напомнить?
key Что записать?
fn Что сделать?
gave up Не разобрала — скажи целиком, пожалуйста.

All three questions are infinitive on purpose, so there is no gender agreement to get wrong. The feminine form sits in the give-up line ("разобрала"). Say the word if you would rather the questions carried it explicitly, e.g. Не разобрала — что сделать?.

She asks once. If your answer still does not resolve it, she drops the request and says so. MaxAttempts = 1, because DESIGN.md says she is not a nag. A failed answer always clears the state, so a stale question can never sit armed.

Templates, not the LLM. A 0.8B wanders, and a question that rewords itself every time is harder to answer than a blunt one that always reads the same.

note, query, chat and system get no question — they act on your raw words, so there is nothing identifiable to ask. That is also what makes pure noise fall through to the old canned reply with no special case.

Safety. Clarifying fills in an argument; it never grants authority. The completed request goes through applyAction like any other, so it gets the same allowlist match, the same destructive-act confirm and the same store write. Two tests pin it: an off-allowlist clarified act does not run and does not get enabled, and a clarified destructive act still parks a confirm first. This matters because the eval measured сделай это routing to act at 0.847 confidence with unresolved anaphora — that is the case this is here to catch.

One thing I let it do beyond the brief. The 25-line session-store block was already copy-pasted in two places and my path needed a third. It is now rememberTurn, called from all three. Net: voice.go is 43 lines shorter than before.

Known small gaps, both deliberate: a clarified fact loses a value that was in the original utterance but not the answer (rare; the answer's own extraction normally supplies it), and confidence reads 0 on a clarified decision because PendingQuestion has nowhere to keep the original. Nothing reads it today, but a trace view would show 0.

Full tree green.

Stacked on **#1** (the data layer) — review that one first. This is the half you can actually feel. **Before:** router is not confident → Maven says "не разобрала" → your next sentence is parsed from scratch → the original request is gone. **After:** she asks one specific question, remembers what she was doing, and finishes the job when you answer. ``` you: напомни купить молоко maven: На когда напомнить? you: в семь вечера maven: (reminder created for 19:00) ``` **The three questions, please check the Russian:** | missing | she asks | |---|---| | time | `На когда напомнить?` | | key | `Что записать?` | | fn | `Что сделать?` | | gave up | `Не разобрала — скажи целиком, пожалуйста.` | All three questions are infinitive on purpose, so there is no gender agreement to get wrong. The feminine form sits in the give-up line ("разобрала"). Say the word if you would rather the questions carried it explicitly, e.g. `Не разобрала — что сделать?`. **She asks once.** If your answer still does not resolve it, she drops the request and says so. `MaxAttempts = 1`, because DESIGN.md says she is not a nag. A failed answer always clears the state, so a stale question can never sit armed. **Templates, not the LLM.** A 0.8B wanders, and a question that rewords itself every time is harder to answer than a blunt one that always reads the same. **note, query, chat and system get no question** — they act on your raw words, so there is nothing identifiable to ask. That is also what makes pure noise fall through to the old canned reply with no special case. **Safety.** Clarifying fills in an argument; it never grants authority. The completed request goes through `applyAction` like any other, so it gets the same allowlist match, the same destructive-act confirm and the same store write. Two tests pin it: an off-allowlist clarified act does not run and does not get enabled, and a clarified destructive act still parks a confirm first. This matters because the eval measured `сделай это` routing to **act** at 0.847 confidence with unresolved anaphora — that is the case this is here to catch. **One thing I let it do beyond the brief.** The 25-line session-store block was already copy-pasted in two places and my path needed a third. It is now `rememberTurn`, called from all three. Net: `voice.go` is **43 lines shorter** than before. **Known small gaps, both deliberate:** a clarified fact loses a value that was in the *original* utterance but not the answer (rare; the answer's own extraction normally supplies it), and confidence reads 0 on a clarified decision because `PendingQuestion` has nowhere to keep the original. Nothing reads it today, but a trace view would show 0. Full tree green.
claude added 5 commits 2026-07-31 00:31:58 +02:00
router.Slots already carries the fact payload; the dialogue copy did not, so a clarifying answer had nowhere to put it. InheritSlots carries it like Key.
Reviewer: check the new inherit block does not overwrite a filled value.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
PendingQuestion plus ClarifyStore: same shape, locking and expiry as SessionStore. Answer fills only the missing slots and never overwrites a filled one. No wiring yet — TODOs mark the daemon hooks.
Reviewer: MaxAttempts is 1 on purpose (Maven asks once, she is not a nag).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
A table per intent (reminder needs a time, fact needs a key, act needs a fn)
plus one fixed Russian question per slot. Templates, not model output: a 0.8B
would wander and a question that rewords itself is harder to answer. Note,
query, chat and system get no question — for those a clarify decision keeps
the canned reply rather than inventing a question for noise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
On a clarify decision with one identifiable gap she now asks instead of saying
"не поняла", and parks the request. The next utterance is parsed as the answer
with the router's own extractor and the completed decision runs through
applyAction like any other — so a clarified act still needs the allowlist and
still hits the destructive confirm gate. An answer that does not fill the gap
drops the request; she never asks twice. Also pulls the session-store block
that HandlePushToTalk and handleText both had into rememberTurn, since the
clarify path needed a third copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
Covers: a reminder with no time is asked about and completes on the answer; the
same for a fact; an answer past the TTL falls through as a fresh utterance; a
second unclear answer drops the request with no second question; a clarified act
off the allowlist neither runs nor gets enabled; a clarified destructive act
still parks a confirm; noise keeps the canned reply. No model, no network.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
kami reviewed 2026-07-31 18:40:45 +02:00
@@ -0,0 +35,4 @@
// questions, so there is no gender agreement to get wrong; the feminine
// self-reference lives in the reply she gives when she drops the request.
var clarifyQuestions = map[dialogue.Slot]string{
dialogue.SlotTime: "На когда напомнить?",
Owner

"На какое время"

"На какое время"
kami reviewed 2026-07-31 18:42:16 +02:00
@@ -0,0 +56,4 @@
want string
asked bool
}{
{"reminder without a time", clarifyDec(router.IntentReminder, router.Slots{Text: "напомни позвонить маме"}, "напомни позвонить маме"), "На когда напомнить?", true},
Owner

just "Когда?" feels more natural.

just "Когда?" feels more natural.
kami reviewed 2026-07-31 18:42:47 +02:00
@@ -0,0 +59,4 @@
{"reminder without a time", clarifyDec(router.IntentReminder, router.Slots{Text: "напомни позвонить маме"}, "напомни позвонить маме"), "На когда напомнить?", true},
{"fact without a key", clarifyDec(router.IntentFact, router.Slots{Text: "запиши"}, "запиши"), "Что записать?", true},
{"act without a fn", clarifyDec(router.IntentAct, router.Slots{Text: "сделай это"}, "сделай это"), "Что сделать?", true},
{"reminder that already has a time", clarifyDec(router.IntentReminder, router.Slots{HasTime: true}, "напомни в 11"), "", false},
Owner

reminder to do what?

reminder to do what?
Owner

make sure this one goes in to #1-#4 (any of them) instead of master, btw.

make sure this one goes in to #1-#4 (any of them) instead of master, btw.
Owner

Superseded by #47, which landed this whole stack on master as one reviewed integration merge. This PR head is an ancestor of master — its commits are in, nothing here is lost. Closing as merged-by-proxy rather than merged, since the merge came in through #47.

Review threads on this PR were answered or acted on before the merge; the Russian wording fixes went in as #48.

Superseded by #47, which landed this whole stack on master as one reviewed integration merge. This PR head is an ancestor of master — its commits are in, nothing here is lost. Closing as merged-by-proxy rather than merged, since the merge came in through #47. Review threads on this PR were answered or acted on before the merge; the Russian wording fixes went in as #48.
kami closed this pull request 2026-07-31 20:21:37 +02:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kami/Maven#6