Let observers' findings and a carried holder's seat events reach the bus (issue 1952)
mesh/delivery merged: source.main moved to novox/mesh-sdk@5ae69df8ccde
mesh/delivery-group group fix/l4-quick merged: every member is merged
mesh/check pass cbbfbf3d2d55 on fea48f19cea5 | unit 2729 run, 4570 kept, 16 skipped | integration 2975 run, 4493 kept, 25 skipped | over bound: unit, …

A finding's field `token` is named like a credential, so the tool runner
refused every finding that carried it; it is `ref` now, and a finding
written under the old name is still read. A module the tool runner
carries got no grant for its own held seat's events, so the self-check
could not say `checked`; a held plain seat's events are now in the
holder's seat traffic, its membership and its runtime's grants.

Quick vs thorough: quick, these fixes for L4's take-over now; the
thorough fix (research 032 / ADR 0240's health view extension) is
decided later.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-10-31 21:30:31 +01:00
parent fea48f19ce
commit cbbfbf3d2d
4 changed files with 156 additions and 6 deletions
+81
View File
@@ -251,6 +251,7 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
if err := json.Unmarshal(want[i].Memberships, &wasM); err != nil {
t.Fatal(err)
}
heldEventsBeyond(gotM, wasM)
if !reflect.DeepEqual(gotM, wasM) {
t.Errorf("the memberships differ from the controller's:\n%s", raw)
}
@@ -825,6 +826,37 @@ func healthChecks(t *testing.T) {
URL: "nats://a:4222"}); w.Valid() != nil || w.OwnSecret() != "broker-nats-tools" {
t.Errorf("the tools account is held in %q (%v)", w.OwnSecret(), w.Valid())
}
// Issue 1952: a module that holds a seat says its own seat's events, also when the tool runner carries it, so the
// self-check's `checked`, the keeper's condition events and the healers' `healer-acted` reach their hearers. Its
// membership lists them, the runtime is granted them; a user of the seat is not, nor a machine's event on a node seat
// (granted per machine, to the holder's own account only).
selfCheck := Declared{Module: "mesh-self-check", Holds: []Seat{{Name: "mesh-self-check", Scope: "mesh", Emits: []string{"checked"}}}}
for _, c := range []struct {
name string
d Declared
subject string
may bool
}{
{"the self-check says checked", selfCheck, "mesh.seat.mesh-self-check.event.checked", true},
{"the keeper says a condition raised", Declared{Module: "mesh-conditions", Holds: []Seat{{Name: "mesh-conditions", Scope: "mesh",
Emits: []string{"condition-raised", "condition-cleared"}}}}, "mesh.seat.mesh-conditions.event.condition-cleared", true},
{"a user does not say the seat's events", Declared{Module: "mesh-delivery-view", Uses: []Seat{{Name: "mesh-self-check",
Scope: "mesh", Emits: []string{"checked"}}}}, "mesh.seat.mesh-self-check.event.checked", false},
{"the holder says no other seat's", selfCheck, "mesh.seat.mesh-conditions.event.condition-raised", false},
{"a machine's event on a node seat is not composed here", Declared{Module: "pause", Holds: []Seat{{Name: "node-power",
Scope: "node", Emits: []string{"paused.*"}}}}, "mesh.seat.node-power.event.paused.b", false},
} {
m := MembershipFor("a", c.d, Placements{})
listed := m.SeatTraffic != nil && slices.ContainsFunc(m.SeatTraffic.Publish, func(p string) bool { return p == c.subject })
perms, err := PermissionsFor(Principal{Kind: KindNodeTools, Node: "a", Carries: []Declared{c.d}})
if err != nil {
t.Fatalf("%s: %v", c.name, err)
}
if listed != c.may || MayPublish(perms, c.subject) != c.may {
t.Errorf("%s: the membership lists %s %v and the runtime may publish it %v, want %v", c.name, c.subject, listed,
MayPublish(perms, c.subject), c.may)
}
}
seen := map[string]bool{}
for _, u := range users {
name := u.Username()
@@ -879,3 +911,52 @@ func healthChecks(t *testing.T) {
}
}
}
// heldEventsBeyond takes out of the copy's memberships the one row they list beyond the controller's on purpose (issue
// 1952): a held seat's own events in its holder's seat traffic, so a module the tool runner carries may say them. The
// controller's composer is frozen; any other difference is still a fault.
func heldEventsBeyond(got, was any) {
gotAll, _ := got.(map[string]any)
wasAll, _ := was.(map[string]any)
for key, m := range gotAll {
gm, _ := m.(map[string]any)
traffic, _ := gm["seat-traffic"].(map[string]any)
if traffic == nil {
continue
}
listedBefore := map[string]bool{}
if wm, _ := wasAll[key].(map[string]any); wm != nil {
if wt, _ := wm["seat-traffic"].(map[string]any); wt != nil {
for _, p := range asStrings(wt["publish"]) {
listedBefore[p] = true
}
}
}
var kept []any
for _, p := range asStrings(traffic["publish"]) {
if parts := strings.Split(p, "."); !listedBefore[p] && len(parts) == 5 && parts[0] == "mesh" && parts[1] == "seat" && parts[3] == "event" {
continue
}
kept = append(kept, p)
}
if kept == nil {
delete(traffic, "publish")
} else {
traffic["publish"] = kept
}
if len(traffic) == 0 {
delete(gm, "seat-traffic")
}
}
}
func asStrings(v any) []string {
list, _ := v.([]any)
out := make([]string, 0, len(list))
for _, x := range list {
if s, ok := x.(string); ok {
out = append(out, s)
}
}
return out
}
+14
View File
@@ -58,6 +58,20 @@ func SeatTrafficOf(module string, holds, uses, watches []Seat) SeatTraffic {
var t SeatTraffic
for _, s := range holds {
if !s.isNewTraffic() {
// **A holder says its own seat's events** (novox/hq ADR 0564 clause 6, issue 1952): its own account is
// granted them by the held seats' rule (PermissionsFor, step 3), and a module the tool runner carries had
// no way to, so the self-check's `checked` was never said. Listed here, the membership names them for the
// runtime and the runtime is granted them. A machine's event on a node seat carries the machine, which
// only the holder's own account is composed with, so it is left to that rule.
if servedOnlyByTheController(s) {
continue
}
for _, e := range s.Emits {
if s.Scope == "node" && perMachineEvents[e] {
continue
}
t.Publish = append(t.Publish, seatSubject(s, "event", e))
}
continue
}
kind := ""