Commit Graph

5 Commits

Author SHA1 Message Date
claude 92eb6cf6e1 dialogue: the clarify store holds a bounded stack (V-559)
One parked question per dialogue id meant a side query could only destroy
the flow it interrupted. The store now keeps a stack per id, newest last,
with Push, Peek, Pop, Depth and Delete as drop-all. MaxStackDepth is 2:
one flow plus the thing he interrupted it with, because spoken
conversation does not nest deeper, and because every level she keeps is a
level she has to be able to speak when it dies.

Behaviour is unchanged. Put still replaces the top rather than growing the
stack — a re-ask is another question about the same action — and nothing
calls Push yet, so the daemon runs at depth one exactly as before. Get is
Peek under the name the callers already use. An expired top takes the
stack with it and TakeExpired reports it, so no parked action dies without
a word; Push returns the entry the depth bound forced out for the same
reason.

PendingQuestion.IsExpired and CanAsk now answer through PendingAction, so
the TTL and attempt-cap rules have one copy and the widening cannot drift.
2026-08-06 00:37:01 +04:00
claude 5b622389c5 dialogue: a restart expires the parked question (V-385)
The decision, not a behaviour change: ClarifyStore stays in memory, and she
does not announce the loss either.

The TTL and the attempt count measure a pause in one conversation. A restart
is a gap of unknown length, so a restored question is either dead already or
lying about its age, and the request behind it is one he has likely given up
on. Announcing it would mean storing a marker that outlives the thing it
describes, to say one sentence in the rare window where he speaks within 90s
of a restart. His next words route fresh, which is right either way.

Written down in docs/design.md, pinned at both ends by a comment, and held by
a test that builds a second handler over the same store.
2026-08-04 03:33:19 +04:00
kami 214a4032cf Tell him when an expired clarify question is dropped
Vikunja #382. A parked clarifying question past its TTL was discarded
silently on read; now she says the old request is gone and the newly
spoken words are still routed as a fresh utterance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
2026-07-31 12:51:47 +04:00
kami 62d320f93a Let her ask three times, and let a restated answer win
MaxAttempts was 1, justified as "not a nag". Wrong reading: "not a nag" is about
interrupting unprompted, and a clarifying question is part of a conversation he
started. Now three, configurable via voice.clarify_max_attempts (default 3).
Three, because after that the likely problem is she misheard the whole request,
not one slot.

Answer used to keep the parked value, so "в три" then "нет, в пять" threw the
five away. Now a value the answer carries wins for the slot she asked about.
Only for the clarify answer — a correction in a fresh turn is followUpMerge.

The eight-field chained assertion in the Answer test is one DeepEqual now, so a
new field in Slots is covered without touching the test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
2026-07-31 12:31:46 +04:00
kami 39d83a33e8 Add the pending-question data layer for slot clarification
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
2026-07-31 02:13:48 +04:00