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.
This commit is contained in:
jochen
2026-10-13 17:16:50 +02:00
parent 9c166398f2
commit eadf301f9f
46 changed files with 1276 additions and 162 deletions
+41 -2
View File
@@ -163,6 +163,33 @@ func (h *Holder) openAsk(r *Record) *AskRecord {
return &rec
}
// wholeOf is an ask's explanation with its values whole (asks.Ask.Whole), where an ask's own message on one
// kind shows it in place of the explanation (novox/hq issue 383): on a kind that proves who answers by the
// controller's fresh word (Promises, as every other verified-sender judgement here), and that the router gave
// the ask's answers (a ticket there, so the kind was linked and root-free when the ask was taken). Empty where
// the kind proves nothing, the ask has no whole words, or the record is a condition's — a condition's words
// stay under the whole content rule, whatever an ask about it carries — then the explanation, held to that
// rule, is shown. The ask's state does not matter: a message edited to say how the ask ended still shows what
// was approved.
func (h *Holder) wholeOf(r *Record, kind string) string {
if r == nil || r.Origin != "ask" || r.Ask == "" || kind == "" || h.Bench == nil {
return ""
}
e := h.askOf(r.Ask)
if e == nil || e.rec.Ask.Whole == "" {
return ""
}
if !h.Bench.Promises(kind, "verified-sender") {
return ""
}
for _, tk := range e.rec.Tickets {
if tk.Kind == kind {
return e.rec.Ask.Whole
}
}
return ""
}
// carriers are the kinds that can carry an answer of a level now: ready, offering a choice, and for
// approve verifying their sender with an account of the operator's linked, where the controller says now
// that no agent can become root (rootFree, asked live).
@@ -439,6 +466,18 @@ func (h *Holder) TakeAsk(verb, asker string, body json.RawMessage) error {
h.refuseAsk(asker, a, "its words carry "+refusal.String()+", which may not leave the mesh: move it into a reference or say it in words")
return nil
}
// An ask that does not read at a glance is said, and shown all the same: the bound is its asker's to keep
// (asks.Glance, issue 639), and refusing it would leave the operator unasked.
if err := asks.Glance(a.Explanation); err != nil {
h.Logf("[messenger] %s's ask %q is a wall of text on the phone: %v", asker, a.Headline, err)
}
// The whole words are shown only where the sender is proven (wholeOf), so a path or an address may stand
// in them; a secret's shape leaves the mesh nowhere, and an ask carrying one is refused as a whole, never
// shown with its explanation alone (novox/hq issue 383).
if refusal, ok := CheckSecret(a.Whole); !ok {
h.refuseAsk(asker, a, "its whole words carry "+refusal.String()+", which may not leave the mesh on any channel: withhold it")
return nil
}
open, replaced := 0, ""
h.mu.Lock()
for k, e := range h.asks {
@@ -1040,7 +1079,7 @@ func (h *Holder) showOnHandle(kind string, m Message) (string, error) {
if h.Publish == nil {
return "", errors.New("this router cannot reach the bus")
}
out := asks.Message{Handle: newHandle(), Title: m.Title, Body: m.Body, Urgent: m.Urgent, Silent: m.Quiet, Reply: m.Reply, To: m.To,
out := asks.Message{Handle: newHandle(), Title: m.Title, Body: m.Body, Note: m.Note, Urgent: m.Urgent, Silent: m.Quiet, Reply: m.Reply, To: m.To,
Secret: m.Secret}
if out.To == "" && h.Bench != nil && h.Bench.To != nil {
out.To = h.Bench.To(kind)
@@ -1065,7 +1104,7 @@ func (h *Holder) showOn(kind string, m Message) error {
if h.Publish == nil {
return errors.New("this router cannot reach the bus")
}
out := asks.Message{Handle: newHandle(), Title: m.Title, Body: m.Body, Urgent: m.Urgent, Silent: m.Quiet, Reply: m.Reply}
out := asks.Message{Handle: newHandle(), Title: m.Title, Body: m.Body, Note: m.Note, Urgent: m.Urgent, Silent: m.Quiet, Reply: m.Reply}
if h.Bench != nil && h.Bench.To != nil {
out.To = h.Bench.To(kind)
}