Review: a path is read as a path in the whole words, which an ask's own message alone shows (issue 383)
mesh/merge-gate pass: builds claude-code, messenger, telegram → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did with…
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery-group group fix/383-a-proposal-reads-whole-on-the-phone ready: every member ready, and composed together they pass
mesh/delivery superseded: a newer head of the same pull request

The review of 2026-10-10: a mixed-case path with a digit ("/mnt/Photos_2024/Jochen") read as a
random string and was withheld from the whole words, the symptom again; the whole was judged on
the kind's issued capabilities instead of the controller's fresh word (Promises); a condition's
record could have taken an ask's whole words in place of its own; and a short instruction text
with a path line was said to be cut where it was shown whole.
This commit is contained in:
jochen
2026-10-10 15:46:54 +02:00
parent 96ba664cb4
commit 545e4319f9
5 changed files with 72 additions and 16 deletions
@@ -172,7 +172,7 @@ func (p Proposal) Act(option string) asks.Act {
func (p Proposal) Ask() asks.Ask {
verb := map[string]string{ActionAdd: "Add", ActionReplace: "Replace", ActionRemove: "Remove"}[p.Action]
shown, whole := excerpt(p.Text, shownLine, "a path, an address or a long word")
wholeShown, _ := excerpt(p.Text, wholeLine, "a long word or a secret's shape")
wholeShown, wholeIsAll := excerpt(p.Text, wholeLine, "a long word or a secret's shape")
part := ""
if p.Action != ActionRemove && !whole {
part = " Part shown"
@@ -181,7 +181,7 @@ func (p Proposal) Ask() asks.Ask {
if len([]rune(headline)) > asks.HeadlineLength {
headline = verb + " Claude instructions?" + part
}
compose := func(text string) string {
compose := func(text string, whole bool) string {
var b strings.Builder
switch p.Action {
case ActionAdd:
@@ -214,12 +214,14 @@ func (p Proposal) Ask() asks.Ask {
if p.Action == ActionRemove {
does = "the instructions are removed from those sessions"
}
a := asks.Ask{ID: p.ID, Headline: headline, Explanation: compose(shown), Who: asks.Operator,
a := asks.Ask{ID: p.ID, Headline: headline, Explanation: compose(shown, whole), Who: asks.Operator,
Expires: p.At.Add(ProposalLasts), OnExpiry: "the proposal is discarded; nothing changes",
About: "instructions." + p.Scope + "." + strings.Join(p.Nodes, "+") + "." + p.Name,
Details: d.String()}
if p.Action != ActionRemove && wholeShown != shown {
a.Whole = compose(wholeShown)
// Cut only when it is: a short text with a path line is whole here (the review of 2026-10-10). The
// headline's "Part shown" is one for every channel and follows the explanation.
a.Whole = compose(wholeShown, wholeIsAll)
}
if len(p.Nodes) == 0 {
a.About = "instructions." + p.Scope + "." + p.Name
@@ -442,6 +442,17 @@ func TestALongTextIsShownInPartAndReadWholeAtTheTerminal(t *testing.T) {
strings.Contains(a.Whole, "not shown here") {
t.Errorf("the whole words: %q", a.Whole)
}
// A short text with one path line: shown whole where the sender is proven, and said to be whole there.
short := "See /etc/claude-code for the managed files.\nA plain line of the hand-over.\n"
out, err = Propose(p, ProposeInput{Scope: ScopeMesh, Name: "short", Text: short}, store, r, t0.Add(time.Minute))
if err != nil {
t.Fatal(err)
}
sa := r.published[out["proposed"].(string)]
if !strings.Contains(sa.Explanation, "NOT SHOWN IN FULL") || !strings.Contains(sa.Explanation, "[1 line(s) not shown here") ||
strings.Contains(sa.Whole, "NOT SHOWN IN FULL") || !strings.Contains(sa.Whole, "?\n\n"+strings.TrimRight(short, "\n")+"\n\nProposed by") {
t.Errorf("the short text:\n%s\n--\n%s", sa.Explanation, sa.Whole)
}
// The whole words keep back what might be a secret's shape (Propose refuses such a text before it is asked;
// this holds the ask's own composition to it), and nothing else.
for line, may := range map[string]bool{
+10 -10
View File
@@ -24,7 +24,6 @@ import (
"encoding/json"
"errors"
"fmt"
"slices"
"sort"
"strings"
"time"
@@ -164,22 +163,23 @@ func (h *Holder) openAsk(r *Record) *AskRecord {
return &rec
}
// wholeOf is an ask's explanation with its values whole (asks.Ask.Whole), where a record's message on one
// kind shows it in place of the explanation (novox/hq issue 383): on a kind that proves who answers, as the
// controller issued it, 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 has no ask — then the explanation, held to the whole content rule, is shown. The ask's state does
// not matter: a message edited to say how the ask ended still shows what was approved.
// 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.Ask == "" || kind == "" || h.Bench == nil {
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 ""
}
info, held := h.Bench.Kind(kind)
if !held || !slices.Contains(info.Capabilities, "verified-sender") {
if !h.Bench.Promises(kind, "verified-sender") {
return ""
}
for _, tk := range e.rec.Tickets {
+18 -2
View File
@@ -91,12 +91,28 @@ func Check(text string) (Refusal, bool) {
// CheckSecret says whether a text carries a secret's shape — the one class that leaves the mesh nowhere. It is
// what an ask's whole words (asks.Ask.Whole, novox/hq issue 383) are held to: on a kind that proves who answers,
// a path or an address is what the operator approves and may be shown, a secret never.
// a path or an address is what the operator approves and may be shown, a secret never. A path is read as one:
// a run with a slash in it is judged piece by piece between the slashes, so "/mnt/Photos_2024/Jochen" is a
// path and not a random string (the review of 2026-10-10); the named shapes — a key block, a signed token, a
// bot token, a known token shape, a value given to a secret's name, a long hexadecimal string — hold whatever
// the run holds.
func CheckSecret(text string) (Refusal, bool) {
if refusal, ok := secretShape(text); !ok {
return refusal, false
}
return randomRun(text)
return randomRunOutsidePaths(text)
}
// randomRunOutsidePaths is randomRun with each run that holds a slash judged by its pieces between the slashes.
func randomRunOutsidePaths(text string) (Refusal, bool) {
for _, run := range reRun.FindAllString(text, -1) {
for _, piece := range strings.Split(run, "/") {
if len(piece) >= 20 && looksRandom(piece) {
return Refusal{"secret", "a long random-looking string"}, false
}
}
}
return Refusal{}, true
}
// secretShape is the secret shapes a pattern names.
@@ -111,6 +111,33 @@ func TestAnAskWithoutWholeWordsIsShownFromItsExplanationEverywhere(t *testing.T)
// A secret's shape leaves the mesh nowhere: an ask whose whole words carry one is refused as a whole, so the
// phone never sees it, with or without the proven sender.
func TestAnAskWhoseWholeWordsCarryASecretIsRefused(t *testing.T) {
// A path is read as a path, whatever its case and digits; a random run outside one is still a secret.
for text, may := range map[string]bool{
"+ shares: photos=/mnt/Photos_2024/Jochen": true,
"+ sources: games=smb://nas.lan/Recalbox_Games2024": true,
"+ library: /srv/media/Series_Archive/Season01": true,
"+ key: xK9mQ2vL8pR4tW7yB3nF6hJ1dG5sA0zC": false,
} {
if _, ok := CheckSecret(text); ok != may {
t.Errorf("CheckSecret(%q) lets it leave: %v", text, ok)
}
}
// A condition's record never shows an ask's whole words, whichever kind: its words are its own.
{
h, _, _, c, store, _ := newRouter(t)
link(h, store, c, "42", c.now().Add(-2*time.Hour))
k := cond("delivery.hq.held", Warning, "the delivery of hq is held")
k.Raised = c.now()
h.Condition(EventRaised, k)
settle(h, c)
a := proposalAsk(c, "s-5")
a.About = "delivery.hq.held"
submit(t, h, "mesh-controller", a)
settle(h, c)
if r := h.open["delivery.hq.held"]; r == nil || r.Ask != "mesh-controller.s-5" || h.wholeOf(r, "telegram") != "" {
t.Errorf("a condition's record is given an ask's whole words")
}
}
for _, secret := range []string{
"+ token: ghp_abcdefghijklmnopqrstuvwxyz0123456789",
"+ password=hunter2",