diff --git a/modules/messenger/cmd/messenger/answers_test.go b/modules/messenger/cmd/messenger/answers_test.go index 8d90d4ac..ce194024 100644 --- a/modules/messenger/cmd/messenger/answers_test.go +++ b/modules/messenger/cmd/messenger/answers_test.go @@ -839,3 +839,38 @@ func codeOf(t *testing.T, m Message) string { t.Fatalf("no code in %q", m.Body) return "" } + +// An ask's message goes alone even when only one of its answers can be given on a channel: folded into a burst's +// digest, its one button was lost (found by the lab's proof, mesh-lab asks/, with approval held by an open +// agent-root condition and that condition's message in the same burst). +func TestAnAsksMessageWithOneAnswerIsNotFoldedIntoADigest(t *testing.T) { + h, tg, _, c, store, _ := newRouter(t) + link(h, store, c, "42", c.now().Add(-2*time.Hour)) + settle(h, c) + // Something was just said on the channel, so what follows waits for the burst. + submit(t, h, "mesh-delivery", heldAsk(c, "d-0")) + settle(h, c) + h.mu.Lock() + h.open["machine.anchor.agent-root"] = &Record{Key: "machine.anchor.agent-root", Kind: KindAgentRoot, Severity: Urgent, + Origin: "condition", Raised: c.now()} + h.mu.Unlock() + k := cond("machine.anchor.disk", Urgent, "the disk on anchor is full") + k.Raised = c.now() + h.Condition(EventRaised, k) + before := len(tg.sends) + submit(t, h, "mesh-delivery", heldAsk(c, "d-1")) + settle(h, c) + var asked *Message + for i := range tg.sends[before:] { + m := tg.sends[before+i] + if ticketFor(m, "Silence for a week") != "" { + asked = &m + } + } + if asked == nil { + t.Fatalf("the ask's one answer was not offered on Telegram: %+v", tg.sends[before:]) + } + if ticketFor(*asked, "Release") != "" { + t.Errorf("an approval was offered while an agent can become root there: %q", labelsOf(*asked)) + } +} diff --git a/modules/messenger/cmd/messenger/outbox.go b/modules/messenger/cmd/messenger/outbox.go index 4ae846e1..d3c473a3 100644 --- a/modules/messenger/cmd/messenger/outbox.go +++ b/modules/messenger/cmd/messenger/outbox.go @@ -287,10 +287,12 @@ func (h *Holder) flushChannel(ch Channel, now time.Time, force bool) { return } if len(entries) > 1 { - // A message that asks the operator something goes alone, so its answers come with it. + // A message that asks the operator something goes alone, so its answers come with it: one with several + // answers, and one carrying an ask's answer even when only one can be given here (the lab's proof found + // an ask whose approval was not offered folded into a digest, and its one answer lost with it). var alone, rest []entry for _, e := range entries { - if len(e.msg.Actions) > 1 { + if len(e.msg.Actions) > 1 || (len(e.msg.Actions) > 0 && e.r != nil && e.r.Ask != "") { alone = append(alone, e) } else { rest = append(rest, e)