messenger: send an ask's message alone even when only one answer can be given there
mesh/merge-gate fail: builds messenger, sudo → ace, g14, novox, shanks; no bus step; a manifest the change touches fails the module check: modules/messenge…
mesh/repo-check fail: its merge-check.sh failed: files asking for a setting without saying whether it is trusted: 20
mesh/delivery-group group feat/asks-answered-on-any-channel rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request

The lab's proof showed an ask whose approval was held back (an agent could become root where the
channel runs) folded into a burst's digest with a condition, and its one remaining answer, Decline,
lost with it. A message carrying an ask's answer now always goes alone.
This commit is contained in:
2026-10-09 11:03:35 +02:00
parent 8ee75ec1bd
commit 7b0b02fa32
2 changed files with 39 additions and 2 deletions
@@ -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))
}
}
+4 -2
View File
@@ -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)