Merge suspend and resume (#219)
V-561, and the owner's acceptance transcript. A side query no longer kills the
flow it interrupted. The answer comes first and the parked question comes back
in the same reply.
напомни позвонить маме -> Когда?
какая сейчас погода в Риме? -> погода не настроена, на какое время
поставить напоминание?
в 21:00 -> хорошо, напомню сегодня в 21:00.
The owner rejected "Прошлую просьбу отпускаю." for this shape. It is kept for
new_request and cancel, where something really was dropped, and gone for
side_query.
clarifyResumedVariants is a new deck, one wording per slot, all infinitive so
there is no gender to get wrong. A resume spends no attempt, so it is not an
attempt ladder.
Resume is a deferred call in runTurn rather than a call at each exit. Eight
returns sit between the fall-through and the replier, and one that forgot would
park a request for ever.
askClarify pushes rather than puts when a side query needs clarifying of its
own. Put would replace the top, which is the flow the side query was allowed to
interrupt rather than kill.
TakeExpired returns a count, not a bool. The stack holds two and drops both when
the top times out, so the singular "прошлую просьбу" would have been a lie
about the number. clarifyExpiredPluralVariants covers it.
The contract row for a nested question is green and unskipped. The owner's own
transcript stays skipped, because StubDateTimeParser reads neither "на 9" nor
"на завтра". That is V-543 and V-562, and the skip reason now says so.
This commit is contained in:
@@ -207,23 +207,26 @@ func (s *ClarifyStore) Depth(id string) int {
|
||||
return len(s.stacks[id])
|
||||
}
|
||||
|
||||
// TakeExpired reports whether a question was parked here but its TTL ran out,
|
||||
// and drops it. Get drops such a question silently, which leaves the user
|
||||
// thinking his request is still alive — the caller uses this to tell him it is
|
||||
// gone before treating his words as a fresh utterance.
|
||||
// TakeExpired reports HOW MANY parked questions were dropped because the TTL
|
||||
// ran out, and drops them. 0 ⇒ nothing was parked, or what was parked is still
|
||||
// live. Get drops such a question silently, which leaves the user thinking his
|
||||
// request is still alive — the caller uses this to tell him it is gone before
|
||||
// treating his words as a fresh utterance.
|
||||
//
|
||||
// It looks at the top only, and drops the whole stack when that one is dead: one
|
||||
// notice is what a reply can carry, and anything parked under a question that
|
||||
// timed out has been waiting at least as long.
|
||||
func (s *ClarifyStore) TakeExpired(id string, now time.Time) bool {
|
||||
// It looks at the top only, and drops the whole stack when that one is dead:
|
||||
// anything parked under a question that timed out has been waiting at least as
|
||||
// long. The COUNT rather than a bool since V-561, because the stack can now
|
||||
// hold two — the flow and the side query that suspended it — and a notice
|
||||
// saying "прошлую просьбу" when two died is a lie about the count.
|
||||
func (s *ClarifyStore) TakeExpired(id string, now time.Time) int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
stack := s.stacks[id]
|
||||
if len(stack) == 0 || !stack[len(stack)-1].IsExpired(now) {
|
||||
return false
|
||||
return 0
|
||||
}
|
||||
delete(s.stacks, id)
|
||||
return true
|
||||
return len(stack)
|
||||
}
|
||||
|
||||
// Delete drops every question parked for this id. The old single-slot Delete
|
||||
|
||||
@@ -64,21 +64,21 @@ func TestClarifyStoreGetPutDelete(t *testing.T) {
|
||||
// whose TTL ran out.
|
||||
func TestClarifyStoreTakeExpired(t *testing.T) {
|
||||
s := NewClarifyStore(time.Minute)
|
||||
if s.TakeExpired("voice", base) {
|
||||
t.Fatal("nothing parked ⇒ nothing expired")
|
||||
if n := s.TakeExpired("voice", base); n != 0 {
|
||||
t.Fatalf("nothing parked ⇒ nothing expired, got %d", n)
|
||||
}
|
||||
s.Put("voice", &PendingQuestion{Missing: []Slot{SlotTime}, Asked: base, TTL: time.Minute})
|
||||
if s.TakeExpired("voice", base.Add(30*time.Second)) {
|
||||
t.Fatal("a live question must not report as expired")
|
||||
if n := s.TakeExpired("voice", base.Add(30*time.Second)); n != 0 {
|
||||
t.Fatalf("a live question must not report as expired, got %d", n)
|
||||
}
|
||||
if s.Get("voice", base.Add(30*time.Second)) == nil {
|
||||
t.Fatal("a live question must survive TakeExpired")
|
||||
}
|
||||
if !s.TakeExpired("voice", base.Add(2*time.Minute)) {
|
||||
t.Fatal("a stale question must report as expired")
|
||||
if n := s.TakeExpired("voice", base.Add(2*time.Minute)); n != 1 {
|
||||
t.Fatalf("a stale question must report as one expired, got %d", n)
|
||||
}
|
||||
if s.TakeExpired("voice", base.Add(2*time.Minute)) {
|
||||
t.Fatal("TakeExpired must drop the question, so the second call is false")
|
||||
if n := s.TakeExpired("voice", base.Add(2*time.Minute)); n != 0 {
|
||||
t.Fatalf("TakeExpired must drop the question, so the second call is 0, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,14 +116,16 @@ func TestStackExpiryDropsTheStackAndIsReported(t *testing.T) {
|
||||
|
||||
s.Push("voice", parked("напомни", pendingBase))
|
||||
s.Push("voice", parked("погода", pendingBase))
|
||||
if !s.TakeExpired("voice", late) {
|
||||
t.Error("TakeExpired did not report the timed-out exchange")
|
||||
// Two died, and the count says two: the notice that reports this has a
|
||||
// plural wording since V-561, and it is chosen off this number.
|
||||
if n := s.TakeExpired("voice", late); n != 2 {
|
||||
t.Errorf("TakeExpired reported %d timed-out questions, want 2", n)
|
||||
}
|
||||
if s.Depth("voice") != 0 {
|
||||
t.Error("TakeExpired left entries behind")
|
||||
}
|
||||
if s.TakeExpired("voice", late) {
|
||||
t.Error("TakeExpired reported twice")
|
||||
if n := s.TakeExpired("voice", late); n != 0 {
|
||||
t.Errorf("TakeExpired reported twice: %d", n)
|
||||
}
|
||||
// Pop of an expired top yields nothing rather than a dead action.
|
||||
s.Push("voice", parked("напомни", pendingBase))
|
||||
|
||||
Reference in New Issue
Block a user