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
@@ -914,8 +914,8 @@ func Tools(k *KB, caller func(map[string]any) string) []stdio.Tool {
Description: "Propose a record whole under a reserved number: its title, topic, date and deciders; for a decision its " +
"clauses, each opening with a bold lead (\"**What is decided.** …\"), and its reasoning in sections (Context, " +
"Considered Options, Consequences, How it is checked, References); for a research effort its status and documents; its " +
"links (supersedes, changes \"0276 §4\", extends, became, cites), and whether it needs the operator. Refused, naming " +
"every reason at once and writing nothing, when it breaks one of the 29 rules that judge one record or its prose — a " +
"links (supersedes, changes \"0276 §4\", extends, became, cites), and whether it needs the operator, with the brief its ask shows. Refused, naming " +
"every reason at once and writing nothing, when it breaks one of the 30 rules that judge one record or its prose — a " +
"link to nothing, a clause without a bold lead, a superseded record cited alone, an unknown topic, a retired word. A " +
"proposal is revised by proposing it again; an accepted record changes only by a progressive insight (insight: what " +
"stood before). The knowledge base then reviews each version itself, in a judgement that sees only the record, the " +
@@ -937,13 +937,19 @@ func Tools(k *KB, caller func(map[string]any) string) []stdio.Tool {
"to": text("the target"), "from": integer("the clause of this record it goes out from")}, "required": []string{"kind", "to"}}},
"homes": texts("hq documents that cite it, such as a design's path; a decision needs one unless an accepted record links it"),
"needs-operator": flag("it holds only once the operator approves it"),
"status": map[string]any{"type": "string", "enum": ResearchStatuses, "description": "a research effort's status"},
"insight": text("for an accepted record: a progressive insight, saying what stood before"),
"why": text("one line: why this version"),
"brief": map[string]any{"type": "object", "description": "required with needs-operator: what the operator's phone shows " +
"in place of the clauses (issue 639) — a headline of at most 60 characters saying what it changes for the operator, " +
"not the title; a summary of two or three short sentences, at most 300 characters, on what changes, why and what it " +
"costs or risks; at most three points of at most 160 characters. No path, address, dotted name or long word",
"properties": map[string]any{"headline": text("what it changes for the operator"), "summary": text("two or three short sentences"),
"points": texts("at most three, only where they add something")}, "required": []string{"headline", "summary"}},
"status": map[string]any{"type": "string", "enum": ResearchStatuses, "description": "a research effort's status"},
"insight": text("for an accepted record: a progressive insight, saying what stood before"),
"why": text("one line: why this version"),
}),
Run: func(m map[string]any) (any, error) {
a, err := readArgs("propose", m, "kind", "number", "title", "topic", "date", "deciders", "clauses", "sections",
"links", "homes", "needs-operator", "status", "insight", "why")
"links", "homes", "needs-operator", "brief", "status", "insight", "why")
if err != nil {
return nil, err
}
@@ -977,6 +983,12 @@ func Tools(k *KB, caller func(map[string]any) string) []stdio.Tool {
if err := decode(m["links"], &p.Links, "links is a list of {kind, to, from}"); err != nil {
return nil, err
}
if m["brief"] != nil {
p.Brief = &Brief{}
if err := decode(m["brief"], p.Brief, "brief is {headline, summary, points}"); err != nil {
return nil, err
}
}
return k.Propose(caller(m), p)
}},
{Name: Seat + ".import",