Files
mesh-controller/internal/broker/waits_test.go
T
mesh-toolsandClaude Opus 5.5 755457cc37
mesh/merge-gate head 755457cc379e merged into trunk 4cc6b6c566a5 — pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus…
mesh/repo-check head 755457cc379e merged into trunk 4cc6b6c566a5 — pass: its merge-check.sh passed
mesh/delivery covered after its failure: a later walk that contains it was delivered: plan-1792019685173332639 (novox/mesh-controller at 0b7e740b into ma…
Let the tool runner's account hear the event families its manifest names (issue 745, ADR 0367)
A session on the mesh MCP server polls for every verdict, answer and delivery because the tool
runner's account hears no event. ADR 0367 lets the tool runner's module name, one by one, the
families a session may wait on: the manifest's `waits`, refused for any other module and for a
wildcard. The controller grants the runtime's account a plain subscription on those subjects
alone, makes no durable consumer for them, and issues the same list in the tool runner's
membership, so the grant and what `mesh_wait` offers come from one composition and no call or
setting widens it. A family on a seat whose events end in a kind or an asker must name it, and is
refused at composition without it, so it is never granted a subject nothing publishes on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:05:14 +00:00

136 lines
5.1 KiB
Go

package broker
import (
"reflect"
"sort"
"strings"
"testing"
)
// The families ADR 0367 §5 starts the grant with, resolved as the inventory resolves a manifest: a seat's
// event under the seat's namespace, a module's under the module's.
var firstFamilies = []Wait{
{Name: "mesh-knowledge.reviewed", Emitter: "mesh-knowledge", Event: "reviewed"},
{Name: "mesh-delivery.transition", Emitter: "mesh-delivery", Event: "transition"},
{Name: "gitea.forge.status", Emitter: "gitea", Event: "forge.status"},
{Name: "mesh-controller.checked", Emitter: "mesh-controller", Seat: true, Event: "checked"},
{Name: "operator-channel.decided.claude-code", Emitter: "operator-channel", Seat: true, Event: "decided.claude-code"},
}
var firstSubjects = []string{
"mesh.mod.gitea.event.forge.status",
"mesh.mod.mesh-delivery.event.transition",
"mesh.mod.mesh-knowledge.event.reviewed",
"mesh.seat.mesh-controller.event.checked",
"mesh.seat.operator-channel.event.decided.claude-code",
}
func runtimeCarrying(waits []Wait) Principal {
return Principal{Kind: KindNodeTools, Node: "desk", Module: RuntimeModule, PasswordHash: "x", Carries: []Declared{
{Module: "shop", Emits: []string{"order.placed"}, Consumes: []string{"billing.paid"}, Serves: []string{"price"}},
{Module: RuntimeModule, Invokes: []string{"*"}, Waits: waits},
}}
}
func without(all, some []string) []string {
drop := map[string]bool{}
for _, s := range some {
drop[s] = true
}
var out []string
for _, s := range all {
if !drop[s] {
out = append(out, s)
}
}
return out
}
// novox/hq ADR 0367 "How it is checked": the account's wait grant equals the manifest's families. The tool
// runner's account subscribes exactly the families' subjects beyond what it had without them — nothing
// published, nothing wider — and the membership it is issued names the same subjects.
func TestTheToolRunnersWaitGrantIsTheManifestsFamiliesAlone(t *testing.T) {
before, err := PermissionsFor(runtimeCarrying(nil))
if err != nil {
t.Fatal(err)
}
after, err := PermissionsFor(runtimeCarrying(firstFamilies))
if err != nil {
t.Fatal(err)
}
added := without(after.Subscribe, before.Subscribe)
sort.Strings(added)
if !reflect.DeepEqual(added, firstSubjects) {
t.Errorf("the wait grant is %v, want the families' subjects %v", added, firstSubjects)
}
if extra := without(after.Publish, before.Publish); len(extra) > 0 {
t.Errorf("waiting granted a publish: %v", extra)
}
for _, s := range after.Subscribe {
if strings.HasPrefix(s, "mesh.mod.*.event") || s == "mesh.>" || s == ">" || strings.HasSuffix(s, ".event.>") {
t.Errorf("the runtime may subscribe every event: %s", s)
}
}
for _, other := range []string{"mesh.seat.operator-channel.event.decided.shop",
"mesh.mod.mesh-knowledge.event.proposed", "mesh.mod.gitea.event.forge.push"} {
if MaySubscribe(after, other) {
t.Errorf("the runtime may subscribe %s, which no family names", other)
}
}
issued := MembershipFor("desk", runtimeCarrying(firstFamilies).Carries[1], Placements{})
var subjects []string
for _, w := range issued.Waits {
subjects = append(subjects, w.Subject)
}
sort.Strings(subjects)
if !reflect.DeepEqual(subjects, firstSubjects) {
t.Errorf("the membership names %v, want the grant %v", subjects, firstSubjects)
}
if issued.Waits[0].Event != "mesh-knowledge.reviewed" {
t.Errorf("a family is issued under its manifest name: %+v", issued.Waits[0])
}
if shop := MembershipFor("desk", runtimeCarrying(firstFamilies).Carries[0], Placements{}); len(shop.Waits) > 0 {
t.Errorf("a module beside the runtime is issued waits: %+v", shop.Waits)
}
}
// **A subscription for the call, never a durable consumer** (ADR 0367 §2): composing the users makes no
// consumer that filters a family, and none for the runtime's module at all.
func TestAWaitMakesNoDurableConsumer(t *testing.T) {
for _, c := range ConsumersOf([]Principal{runtimeCarrying(firstFamilies)}) {
if c.Module == RuntimeModule {
t.Errorf("the runtime's module has a durable consumer: %+v", c.Consumer)
}
for _, f := range c.Consumer.Filters {
for _, s := range firstSubjects {
if f == s {
t.Errorf("%s's consumer filters the family %s", c.Module, s)
}
}
}
}
}
// Only the tool runner's module waits (ADR 0367 §2): a carried module declaring waits is refused at
// composition, naming it, and no name that would widen a subject is composed.
func TestAWaitIsComposedOnlyForTheToolRunnerAndOnlyWhole(t *testing.T) {
p := runtimeCarrying(nil)
p.Carries[0].Waits = []Wait{{Name: "mesh-knowledge.reviewed", Emitter: "mesh-knowledge", Event: "reviewed"}}
if _, err := PermissionsFor(p); err == nil || !strings.Contains(err.Error(), "shop") {
t.Errorf("a carried module's waits were composed: %v", err)
}
for _, w := range []Wait{
{Name: "x", Emitter: "mesh-knowledge", Event: "*"},
{Name: "x", Emitter: "mesh-knowledge", Event: "a.>"},
{Name: "x", Emitter: "*", Event: "reviewed"},
{Name: "x", Emitter: "mesh.knowledge", Event: "reviewed"},
{Name: "x", Emitter: "mesh-knowledge", Event: ""},
{Name: "x", Emitter: "mesh-knowledge", Event: "a..b"},
} {
if _, err := PermissionsFor(runtimeCarrying([]Wait{w})); err == nil {
t.Errorf("%+v was composed", w)
}
}
}