A settings proposal carries every key with its whole value in its Details (issue 383)

The review of #304: with more than three keys the ask showed only the names of the rest, a long value was
cut without a word, and the Details held no key, so Approve bound values the operator never saw. The
Details now list every key uncut — masked, and whole in WholeDetails for a channel that proves who
answers — before who proposed it and the fingerprint; the glance says what it cut. Vendors mesh-sdk at
f474544 and captures its conformance events there.
This commit is contained in:
jochen
2026-10-13 20:45:25 +02:00
parent c58e0a571e
commit 4644f25edd
7 changed files with 114 additions and 27 deletions
+36 -21
View File
@@ -164,29 +164,38 @@ func (p settingsProposal) ask(id string, machines []string) (asks.Ask, map[strin
headline = verb + " settings?"
}
// The message reads at a glance (issues 383 and 639): a summary of what changes where, then at most three
// points, a key each; who proposed it, the fingerprint and how to read it whole are its Details, which the
// router notes after the answers.
shown, shownWhole := p.change(machines, false)
whole, _ := p.change(machines, true)
// points, a key each; every key with its whole value, who proposed it, the fingerprint and how to read it at the
// terminal are its Details, which the router notes after the answers.
shown, shownAll, shownWhole := p.change(machines, false)
whole, wholeAll, _ := p.change(machines, true)
summary := p.summary()
compose := func(points []string) string {
return strings.Join(append([]string{summary}, points...), "\n")
}
var d strings.Builder
fmt.Fprintf(&d, "Proposed by %s at %s. Fingerprint %s.\n", sayable(p.From, machines), p.At.Local().Format("15:04 on 2 Jan"),
fingerprint(p.Digest))
if !shownWhole {
d.WriteString("Where who answers is not proven, its values are shown as ‹address›, ‹path› or ‹withheld›.\n")
// The Details: every setting it sets, one line each, uncut — whole where the sender is proven (WholeDetails),
// masked elsewhere — then who proposed it, the fingerprint and how to read it at the terminal.
details := func(lines []string) string {
var d strings.Builder
d.WriteString(strings.Join(lines, "\n") + "\n")
fmt.Fprintf(&d, "Proposed by %s at %s. Fingerprint %s.\n", sayable(p.From, machines), p.At.Local().Format("15:04 on 2 Jan"),
fingerprint(p.Digest))
if !shownWhole {
d.WriteString("Where who answers is not proven, its values are shown as ‹address›, ‹path› or ‹withheld›.\n")
}
fmt.Fprintf(&d, "Read it whole, with this fingerprint: settings proposals %s at the controller's terminal, or the "+
"controller's settings verb with proposal %s through the mesh MCP server.", id, id)
return d.String()
}
fmt.Fprintf(&d, "Read it whole, with this fingerprint: settings proposals %s at the controller's terminal, or the "+
"controller's settings verb with proposal %s through the mesh MCP server.", id, id)
q := asks.Ask{ID: id, Headline: headline, Explanation: compose(shown), Who: asks.Operator,
Expires: p.At.Add(askApproveFor), OnExpiry: "the proposal is discarded; nothing changes",
About: p.about(), Details: d.String()}
About: p.about(), Details: details(shownAll)}
// Only where a value was masked: an ask whose values all pass the content rule shows the same everywhere.
if w := compose(whole); w != q.Explanation {
// Only where a value was masked: an ask whose values all pass the content rule shows the same everywhere.
q.Whole = w
}
if w := details(wholeAll); w != q.Details {
q.WholeDetails = w
}
options := map[string]int{}
for i, act := range p.actions(id) {
binds, _ := asks.ActDigest(boundAct(act))
@@ -233,12 +242,14 @@ func (p settingsProposal) summary() string {
n, p.Module, p.where(), stay, takes)
}
// change is what changes, as at most asks.MostPoints points, and whether every value was shown whole: "+ key: value"
// change is what changes: as at most asks.MostPoints points for the glance, as every line whole for the note, and
// whether every value was shown exactly: "+ key: value"
// added, "~ key: value (was: old)" changed, "- key (was: old)" removed. Each value is shown under the content rule
// (sayableValue), or — exact, for a channel that proves who answers — as it is, withheld only when shaped like a
// secret (wholeValue). More changes than points: the first two, and the keys of the rest; a point longer than a
// phone shows at a glance is cut. Either is read whole with `settings proposals`.
func (p settingsProposal) change(machines []string, exact bool) ([]string, bool) {
// phone shows at a glance is cut and says so. The note carries every line uncut (the review of #304: Approve sets
// exactly what the operator can read).
func (p settingsProposal) change(machines []string, exact bool) ([]string, []string, bool) {
whole := true
say := func(v any) string {
s, w := sayableValue(v, machines)
@@ -268,6 +279,10 @@ func (p settingsProposal) change(machines []string, exact bool) ([]string, bool)
lines = append(lines, line{k, fmt.Sprintf("- %s (was: %s)", k, say(was[k]))})
}
}
all := make([]string, len(lines))
for i, l := range lines {
all[i] = l.text
}
var out []string
for i, l := range lines {
if len(lines) > asks.MostPoints && i == asks.MostPoints-1 {
@@ -275,19 +290,19 @@ func (p settingsProposal) change(machines []string, exact bool) ([]string, bool)
for _, r := range lines[i:] {
rest = append(rest, r.key)
}
out = append(out, fmt.Sprintf("and %d more: %s", len(rest), strings.Join(rest, ", ")))
whole = false
out = append(out, fmt.Sprintf("and %d more, each whole in the note: %s", len(rest), strings.Join(rest, ", ")))
break
}
out = append(out, l.text)
}
// A point too long for a glance is cut, and says so: its whole value is the note's.
const cut = "… (cut: whole in the note)"
for i, l := range out {
if r := []rune(l); len(r) > asks.PointLength {
out[i] = string(r[:asks.PointLength-1]) + "…"
whole = false
out[i] = string(r[:asks.PointLength-len([]rune(cut))]) + cut
}
}
return out, whole
return out, all, whole
}
func layerKeys(m map[string]any) []string {