Files
mesh-catalog/modules/messenger/cmd/messenger/glance_test.go
T
jochen ea1dfdce5b
mesh/delivery-group group fix/383-an-ask-reads-at-a-glance checking: composed check out of date: novox/mesh-catalog@ea1dfdce5bc7 was judged against trunk 5f491…
mesh/merge-gate out of date: judged against trunk 5f491e0c097d, the trunk is now 4d15a155e634; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk 5f491e0c097d, the trunk is now 4d15a155e634; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request
An ask reads at a glance on the phone: a headline, a summary and its points, the rest in a note (issues 383 and 639)
The operator would not read the Telegram ask for ADR 0343: thirty plain lines, every clause on one, no bold,
no summary. A settings proposal was the same wall of text, its values masked so the approval was blind.

- messenger: an ask's message is the asker's headline and explanation alone; who asked and when, what each
  answer does, the expiry, where else it is answered and the asker's Details travel as the message's note
  (asks.Message.Note). Answered, the message is one line, "You chose Approve at 16:06 on Telegram ·
  <headline>". An explanation that does not read at a glance (asks.Glance) is shown and logged. On a kind
  that proves who answers, an ask's values are shown whole (asks.Ask.Whole), held to the secret rule alone
  (from the earlier work on issue 383, #178).
- telegram: every message in HTML, never reworded: the title bold, a key line monospace, the note apart in
  a collapsed blockquote; markup escaped. A tap is compared by its words, whitespace aside, and a message
  shown before is still compared by the digest it was kept under.
- desk-channel: the note follows the body.
- mesh-knowledge: a record that needs the operator carries a brief (headline, summary, at most three
  points), the 30th per-record rule; its ask shows the brief and never the clauses. An ask made before is
  composed again as it was asked (form), so its warrant still holds.
- claude-code: an instructions proposal's explanation is one summary line (the proposer's, or the
  module's), its text in the Details; a question's context is at most 300 characters and its recommendation
  one point. Older proposals and questions keep their form.

The SDK is pinned at mesh-sdk 9588871 (fix/639-an-ask-reads-at-a-glance), which adds Message.Note and
asks.Glance.
2026-10-13 16:33:45 +02:00

91 lines
4.4 KiB
Go

package main
import (
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/asks"
)
// novox/hq issue 639: an ask's message reads at a glance on the phone. Its body is the asker's summary and points
// alone; who asked and when, what each answer does, the expiry and the asker's Details are its note; once answered
// it is one line with the headline.
// decisionAsk is the ask for ADR 0343 as the knowledge base composes it now (its glance form).
func decisionAsk(c *clock) asks.Ask {
return asks.Ask{ID: "kb-decision-343-0d1e2f3a", Headline: "Your agents' memory follows you to every machine",
Explanation: "What an agent learns in a project's memory is copied to every machine that runs the agent, so a session " +
"anywhere starts with it. A file changed on two machines keeps both versions and warns you; secrets in memory files " +
"are screened, though not perfectly.\n" +
"• Only memory folders travel: links and files over 128 KiB stay where they are\n" +
"• A conflict reaches you as a warning that needs you\n" +
"• A deleted file is marked, and a newer change elsewhere outlives the deletion",
Who: asks.Operator, Expires: c.now().Add(23*time.Hour + 50*time.Minute), OnExpiry: "the record stays proposed; nothing of it is in force",
About: "knowledge.decision.343",
Details: "ADR 0343, proposed by fix-367@g14; the knowledge base's own review approved it. Fingerprint a59f 361b f60e cb5e d237 a837.\n" +
"Read its 13 clauses with the knowledge base's get, number 343.",
Options: []asks.Option{
{ID: "approve", Label: "Approve", Does: "the record is accepted and its clauses are in force", Level: asks.Approve, Binds: bound("approve")},
{ID: "decline", Label: "Decline", Does: "the record stays proposed; nothing of it is in force", Level: asks.Approve, Binds: bound("decline")},
}}
}
func TestAnAsksMessageIsItsSummaryWithTheRestInItsNote(t *testing.T) {
h, tg, _, c, store, _ := newRouter(t)
link(h, store, c, "42", c.now().Add(-2*time.Hour))
a := decisionAsk(c)
submit(t, h, "mesh-knowledge", a)
settle(h, c)
if len(tg.sends) != 1 {
t.Fatalf("telegram got %d messages", len(tg.sends))
}
m := tg.sends[0]
if m.Title != a.Headline || m.Body != a.Explanation {
t.Errorf("the body is not the asker's summary and points alone:\n%s\n%s", m.Title, m.Body)
}
note := strings.Split(m.Note, "\n")
asked := c.now().In(time.UTC).Format("15:04")
want := []string{
"Asked by mesh-knowledge at " + asked + ". Unanswered by tomorrow " + a.Expires.In(time.UTC).Format("15:04") +
": the record stays proposed; nothing of it is in force.",
"Approve: the record is accepted and its clauses are in force. Decline: the record stays proposed; nothing of it is in force.",
"ADR 0343, proposed by fix-367@g14; the knowledge base's own review approved it. Fingerprint a59f 361b f60e cb5e d237 a837.",
"Read its 13 clauses with the knowledge base's get, number 343.",
}
if strings.Join(note, "\n") != strings.Join(want, "\n") {
t.Errorf("the note:\n%s\nwant:\n%s", m.Note, strings.Join(want, "\n"))
}
if n := len(strings.Split(m.Text(), "\n")); n > 9 || len(m.Actions) != 2 {
t.Errorf("the message is %d lines offering %s:\n%s", n, labelsOf(m), m.Text())
}
// Answered, it is one line with the headline; the body is not said again.
if out, err := h.Chosen("telegram", choiceOf(ticketFor(m, "Approve"), "42", c.now())); err != nil || out != "chosen" {
t.Fatalf("Approve: %q %v", out, err)
}
var edited Message
for _, e := range tg.edits {
edited = e
}
if edited.Title != "You chose Approve at "+asked+" on Telegram · "+a.Headline || edited.Body != "" ||
strings.Contains(edited.Note, "What an agent learns") || len(edited.Actions) != 0 {
t.Errorf("the answered copy: %q\n%q\n%q", edited.Title, edited.Body, edited.Note)
}
}
// An ask that does not read at a glance is shown all the same, and said: refusing it would leave the operator
// unasked, and the bound is its asker's to keep.
func TestAnAskThatIsAWallOfTextIsShownAndSaid(t *testing.T) {
h, tg, _, c, store, _ := newRouter(t)
link(h, store, c, "42", c.now().Add(-2*time.Hour))
var said []string
h.Logf = func(f string, a ...any) { said = append(said, f) }
a := decisionAsk(c)
a.Explanation += "\nA fourth point\nA fifth point"
submit(t, h, "mesh-knowledge", a)
settle(h, c)
if len(tg.sends) != 1 || !strings.Contains(strings.Join(said, "\n"), "is a wall of text on the phone") {
t.Errorf("sent %d, said %v", len(tg.sends), said)
}
}