asks: prove the reviewed flow — the controller's root-free word asked before an approval, the desk on its own account, a tap only on the words shown, the question saying who asks and what each answer does, and a rehearsal at the terminal for the live acceptance
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery superseded: a newer head of the same pull request

This commit is contained in:
2026-10-09 12:44:21 +02:00
parent 569a9e7ae8
commit 01a541cadf
4 changed files with 126 additions and 48 deletions
+24 -3
View File
@@ -143,11 +143,32 @@ func (f *fakeTelegram) typed(from int64, chat int64, chatType, text string) {
"chat": map[string]any{"id": chat, "type": chatType}}})
}
// tapped is a button tapped on a message the bot sent, carrying the button's data as the service gives it.
// tapped is a button tapped on a message the bot sent, carrying the button's data and the message's words as
// the service gives them: the words the message shows now (its last send or edit), as on the phone.
func (f *fakeTelegram) tapped(from int64, chat int64, chatType string, onMessage int64, data string) {
f.tappedOn(from, chat, chatType, onMessage, data, f.textOf(onMessage))
}
// tappedOn is a tap on a message showing the words given: what a message changed under the operator's thumb,
// or forwarded and edited elsewhere, carries.
func (f *fakeTelegram) tappedOn(from int64, chat int64, chatType string, onMessage int64, data, text string) {
f.push(map[string]any{"callback_query": map[string]any{"id": fmt.Sprintf("cb-%d", f.nextID()), "data": data,
"from": map[string]any{"id": from, "is_bot": false, "first_name": fmt.Sprintf("Account %d", from)},
"message": map[string]any{"message_id": onMessage, "chat": map[string]any{"id": chat, "type": chatType}}}})
"from": map[string]any{"id": from, "is_bot": false, "first_name": fmt.Sprintf("Account %d", from)},
"message": map[string]any{"message_id": onMessage, "text": text,
"chat": map[string]any{"id": chat, "type": chatType}}}})
}
// textOf is what a message the bot sent shows now.
func (f *fakeTelegram) textOf(id int64) string {
f.mu.Lock()
defer f.mu.Unlock()
text := ""
for _, m := range f.sent {
if m.ID == id {
text = m.Text
}
}
return text
}
func (f *fakeTelegram) nextID() int64 {
+39 -6
View File
@@ -48,9 +48,12 @@ type lab struct {
mu sync.Mutex
conditions []map[string]any
desk []string // what the desk was shown
procs []*exec.Cmd
logs map[string]*bytes.Buffer
// notFree are the machines the controller's root-free verb answers as not root-free.
notFree map[string]string
rootAsked int
desk []string // what the desk was shown
procs []*exec.Cmd
logs map[string]*bytes.Buffer
}
// clones is where each core repository the proof builds is checked out: side by side in MESH_LAB_REPOS (beside
@@ -116,7 +119,9 @@ func (l *lab) run(dir string, env []string, name string, args ...string) string
// compose asks the controller at its commit for the bus it would compose, then (bus set) to raise it.
func (l *lab) compose(bus string) {
env := []string{"GOFLAGS=-mod=vendor", "GOPROXY=off", "MESH_LAB_ASKS_OUT=" + l.dir,
// The anchor composed as a machine the controller measured root-free: verified-sender reaches the router
// only then (ADR 0259 §8). The not-free case is played live, through the root-free verb, in the proof.
env := []string{"GOFLAGS=-mod=vendor", "GOPROXY=off", "MESH_LAB_ASKS_OUT=" + l.dir, "MESH_LAB_ASKS_ROOT_FREE=true",
"MESH_LAB_ASKS_CATALOGUE=" + filepath.Join(l.repos.catalogue, "modules"),
"MESH_LAB_ASKS_RUNTIME=" + filepath.Join(l.repos.tools, "node-tools", "module.json")}
if bus != "" {
@@ -343,9 +348,36 @@ func (w *lockedWriter) Write(p []byte) (int, error) {
return w.w.Write(p)
}
// playController answers the controller's `conditions` verb from the lab's list, as the router asks it.
// playController answers the controller's `conditions` verb from the lab's list, and its `root-free` verb from
// the lab's word on each machine (free unless notFree names it), as the router asks them. That a machine is
// root-free is the controller's own judgement, proven in its repository (probe_agent_root_test.go); here the
// lab says it, so the proof can show what the router does with each answer.
func (l *lab) playController() {
l.t.Helper()
free, err := l.observer.Subscribe("mesh.seat.mesh-controller.tool.root-free", func(m *nats.Msg) {
var args struct {
Machines []string `json:"machines"`
}
_ = json.Unmarshal(m.Data, &args)
var out []map[string]any
l.mu.Lock()
for _, name := range args.Machines {
why, not := l.notFree[name]
if !not {
why = "the lab says no agent can become root here"
}
out = append(out, map[string]any{"machine": name, "free": !not, "why": why,
"judged": time.Now().UTC().Format(time.RFC3339Nano)})
}
l.rootAsked++
l.mu.Unlock()
raw, _ := json.Marshal(map[string]any{"result": map[string]any{"machines": out}, "node": machine})
_ = m.Respond(raw)
})
if err != nil {
l.t.Fatal(err)
}
l.t.Cleanup(func() { _ = free.Unsubscribe() })
sub, err := l.observer.Subscribe("mesh.seat.mesh-controller.tool.conditions", func(m *nats.Msg) {
l.mu.Lock()
list := append([]map[string]any{}, l.conditions...)
@@ -409,7 +441,8 @@ func (l *lab) sayCondition(event string, c map[string]any) {
// carries the desk channel: it says it is ready, and keeps what the router shows it — the link's code.
func (l *lab) playDesk(ctx context.Context) {
l.t.Helper()
nc, _ := l.as(machine + ".node-tools")
// The desk channel runs as an account of its own (it carries a link's code), on its own credential.
nc, _ := l.as(machine + ".desk-channel")
stand := func() {
raw, _ := json.Marshal(map[string]any{"ready": true, "at": time.Now().UTC()})
js, _ := nc.JetStream()
+10 -10
View File
@@ -3,9 +3,9 @@
# terminal on the control node. The lab's proof (TestTheOperatorsAnswerEndToEnd) ran the same flow with a fake
# Telegram; this runs it once for real, with a question that changes nothing.
#
# sh live-acceptance.sh check, ask the drill, wait for the answer, read the record
# sh live-acceptance.sh check, ask the rehearsal, wait for the answer, read the record
#
# It reads and asks only: no setting, assignment or push. The one thing it starts is the drill, a question the
# It reads and asks only: no setting, assignment or push. The one thing it starts is a rehearsal, a question the
# operator answers on the phone; approving it performs nothing and is recorded as the operator's decision.
#
# It stops at the first check that fails and says what to do. Exit 0 means: the question reached the phone
@@ -31,11 +31,11 @@ ids=$($mc ask messenger messenger_identities) || stop "the router does not answe
echo "$ids"
echo "$ids" | grep -q '"telegram"' || stop "no Telegram account is linked: send /start to the bot, then the code from the desk"
echo "4. The drill: a question on your phone. Approve it there within 15 minutes."
said=$($mc drill --for 15m) || stop "the drill could not be asked"
echo "4. The rehearsal: a question on your phone. Approve it there within 15 minutes."
said=$($mc rehearse --for 15m) || stop "the rehearsal could not be asked"
echo "$said"
id=$(echo "$said" | sed -n 's/^drill \([A-Za-z0-9_-]*\) asked.*/\1/p')
[ -n "$id" ] || stop "the drill did not say its id"
id=$(echo "$said" | sed -n 's/^rehearsal \([A-Za-z0-9_-]*\) asked.*/\1/p')
[ -n "$id" ] || stop "the rehearsal did not say its id"
echo "5. Waiting for your answer in the hand-act log (every 10 s, at most 16 minutes)."
i=0
@@ -44,12 +44,12 @@ while [ $i -lt 96 ]; do
mine=$(echo "$acts" | tr -d '\n' | grep -o "{[^{}]*\"ask\": *\"$id\"[^{}]*}" || true)
if [ -n "$mine" ]; then
echo "$mine"
echo "$mine" | grep -q 'drill=decline' && stop "the drill was declined: run it again and choose Approve to see an approval recorded"
echo "$mine" | grep -q 'drill=approve' || stop "the drill's record names no approval"
echo "$mine" | grep -q 'rehearsal=decline' && stop "the rehearsal was declined: run it again and choose Approve to see an approval recorded"
echo "$mine" | grep -q 'rehearsal=approve' || stop "the rehearsal's record names no approval"
echo "$mine" | grep -q '"via": *"telegram (telegram), user id verified"' || stop "the record does not say the answer came through Telegram from a verified account"
echo "$mine" | grep -q '"P1"' || stop "the record does not carry the proof"
echo "$mine" | grep -q '"outcome": *"done"' || stop "the drill's act did not end done"
echo "ACCEPTED: the drill was approved on Telegram, acted on once, and recorded as your decision."
echo "$mine" | grep -q '"outcome": *"done"' || stop "the rehearsal's act did not end done"
echo "ACCEPTED: the rehearsal was approved on Telegram, acted on once, and recorded as your decision."
exit 0
fi
i=$((i + 1))
+53 -29
View File
@@ -109,13 +109,19 @@ func TestTheOperatorsAnswerEndToEnd(t *testing.T) {
first = a.ask("a1", "Flip the lab's switch?", time.Hour)
firstMessage = l.tg.waitFor("the first question with its buttons", operatorAccount, since, 90*time.Second,
func(m tgMessage) bool { return !m.Edited && button(m, "Approve") != "" && button(m, "Decline") != "" })
if !strings.Contains(firstMessage.Text, "Flip the lab's switch?") || !strings.Contains(firstMessage.Text, "approve or decline") {
t.Errorf("the question does not say what is asked and why: %q", firstMessage.Text)
for _, says := range []string{"Flip the lab's switch?", "approve or decline", "lab-asker",
"the switch is flipped, once", "nothing is flipped"} {
if !strings.Contains(firstMessage.Text, says) {
t.Errorf("the question does not say %q (what is asked, why, by whom, what each answer does, what "+
"no answer does): %q", says, firstMessage.Text)
}
}
tapped := time.Now()
l.tg.tapped(operatorAccount, operatorAccount, "private", firstMessage.ID, button(firstMessage, "Approve"))
l.tg.waitFor("the refusal of an approval inside the hour", operatorAccount, tapped, 90*time.Second,
func(m tgMessage) bool { return strings.Contains(m.Text, "can approve from") })
func(m tgMessage) bool {
return strings.Contains(m.Text, "That answer was not taken") && strings.Contains(m.Text, "linked at")
})
if !a.noWordOn("a1", 2*time.Second) || len(a.performed()) != 0 {
t.Fatalf("an approval inside the hour was a warrant: %v", a.performed())
}
@@ -152,7 +158,7 @@ func TestTheOperatorsAnswerEndToEnd(t *testing.T) {
if w.AskDigest != first.Digest() {
t.Errorf("the warrant names the digest %s, not the ask's %s", w.AskDigest, first.Digest())
}
if got := a.performed(); len(got) != 1 || got[0]["switch"] != "approve" {
if got := a.performed(); len(got) != 1 || got[0]["arg.switch"] != "approve" {
t.Fatalf("performed %v, want the one bound act once", got)
}
// Every copy says how it ended, its buttons gone.
@@ -197,20 +203,19 @@ func TestTheOperatorsAnswerEndToEnd(t *testing.T) {
m := l.tg.waitFor("the second question", operatorAccount, since, 90*time.Second,
func(m tgMessage) bool { return !m.Edited && button(m, "Approve") != "" })
// The buttons' data reached a stranger (a forwarded question), who taps in their own chat with the bot:
// refused, and the operator is told which account tried. The stranger is told nothing.
tapped := time.Now()
// the channel never showed that message there, so the tap goes nowhere, and the stranger is told
// nothing. (A stranger's answer that does reach the router is refused there and told to the operator by
// name: the router's own test.)
l.tg.tapped(strangerAccount, strangerAccount, "private", m.ID, button(m, "Approve"))
l.tg.waitFor("the operator told another account tried", operatorAccount, tapped, 90*time.Second,
func(m tgMessage) bool {
return strings.Contains(m.Text, "Another account tried to answer") && strings.Contains(m.Text, fmt.Sprint(strangerAccount))
})
if got := l.tg.since(strangerAccount, since); len(got) != 0 {
t.Errorf("the bot answered the stranger: %+v", got)
}
// The operator taps a message whose words are not the ones the channel put there.
l.tg.tappedOn(operatorAccount, operatorAccount, "private", m.ID, button(m, "Approve"), "Flip ALL the switches?")
// The operator taps in a group the bot is in: not their own chat, dropped by the channel.
l.tg.tapped(operatorAccount, groupChat, "supergroup", m.ID, button(m, "Approve"))
if !a.noWordOn("a2", 3*time.Second) {
t.Fatal("a tap from another account, or from a group, was a warrant")
t.Fatal("a tap from another account, on changed words, or from a group, was a warrant")
}
if got := l.tg.since(strangerAccount, since); len(got) != 0 {
t.Errorf("the bot answered the stranger: %+v", got)
}
if got := l.tg.since(groupChat, since); len(got) != 0 {
t.Errorf("the bot answered in a group: %+v", got)
@@ -278,23 +283,42 @@ func TestTheOperatorsAnswerEndToEnd(t *testing.T) {
})
// --- 7. While an agent can become root where the router runs, nothing proven there approves.
t.Run("while an agent can become root on the router's machine, approve is not offered", func(t *testing.T) {
key := "machine." + machine + ".agent-root"
l.raise(map[string]any{"key": key, "kind": "agent-root", "severity": "urgent",
"summary": "an agent can become root on anchor without a person",
"headline": "Root without you on anchor", "explanation": "A program working for you can become root.",
"subject": map[string]any{"scope": "machine", "id": machine, "machine": machine},
"raised": time.Now().UTC().Format(time.RFC3339Nano)})
time.Sleep(2 * time.Second)
t.Run("while the controller cannot say root is free where the router runs, nothing approves", func(t *testing.T) {
// An approval the operator already sees on the phone, then root stops being free.
since := time.Now()
a.ask("a3", "Flip it while root is open?", time.Hour)
m := l.tg.waitFor("the third question", operatorAccount, since, 90*time.Second,
func(m tgMessage) bool { return !m.Edited && button(m, "Decline") != "" })
if button(m, "Approve") != "" {
t.Errorf("approve was offered on Telegram while an agent can become root there: %v", labels(m))
a.ask("a5", "Flip it while root is checked?", time.Hour)
m := l.tg.waitFor("the question asked while root was free", operatorAccount, since, 90*time.Second,
func(m tgMessage) bool { return !m.Edited && button(m, "Approve") != "" })
l.mu.Lock()
l.notFree = map[string]string{machine: "an agent can become root without a person"}
asked := l.rootAsked
l.mu.Unlock()
tapped := time.Now()
l.tg.tapped(operatorAccount, operatorAccount, "private", m.ID, button(m, "Approve"))
l.tg.waitFor("the refusal of an approval while root is not free", operatorAccount, tapped, 90*time.Second,
func(m tgMessage) bool { return strings.Contains(m.Text, "That answer was not taken") })
l.mu.Lock()
askedAgain := l.rootAsked > asked
l.mu.Unlock()
if !askedAgain {
t.Error("the router judged an approval without asking the controller whether root is free")
}
l.clear(key)
if err := a.client.Cancel("a3"); err != nil {
if !a.noWordOn("a5", 2*time.Second) {
t.Fatal("an approval was a warrant while root was not free")
}
// A new ask that needs an approval is refused to its asker, in words: no channel can carry it now.
a.ask("a3", "Flip it while root is not free?", time.Hour)
w := a.warrantFor("a3", 30*time.Second)
if w.Outcome != asks.OutcomeRefused || w.By != nil {
t.Errorf("an approving ask while root is not free: %+v", w)
}
if got := a.performed(); len(got) != 1 {
t.Fatalf("performed %v while root was not free", got)
}
l.mu.Lock()
l.notFree = nil
l.mu.Unlock()
if err := a.client.Cancel("a5"); err != nil {
t.Errorf("taking the ask back: %v", err)
}
})