From d5e4e4e8aba22ea09b78b48c820e985d565ac066 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 14:17:14 +0100 Subject: [PATCH] =?UTF-8?q?Console:=20the=20address=20text=20and=20the=20t?= =?UTF-8?q?ests=20name=20the=20seat=20mesh-module-assigner=20(ADR=200634?= =?UTF-8?q?=20=C2=A79;=20T-2;=20issue=201966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node-tools/internal/bus/caller_bus_test.go | 2 +- node-tools/internal/bus/seat_test.go | 4 ++-- node-tools/internal/bus/state_watch_test.go | 2 +- node-tools/internal/console/address.go | 4 ++-- node-tools/internal/console/address_test.go | 24 +++++++++---------- node-tools/internal/console/mcp.go | 2 +- .../internal/console/node_argument_test.go | 4 ++-- node-tools/internal/console/refusal_test.go | 6 +++++ node-tools/internal/console/runtimes_test.go | 6 ++--- node-tools/internal/console/search_test.go | 10 ++++---- node-tools/internal/provide/provide_test.go | 4 ++-- .../internal/runtime/seat_traffic_test.go | 2 +- 12 files changed, 38 insertions(+), 32 deletions(-) diff --git a/node-tools/internal/bus/caller_bus_test.go b/node-tools/internal/bus/caller_bus_test.go index 502a3dd..c46049d 100644 --- a/node-tools/internal/bus/caller_bus_test.go +++ b/node-tools/internal/bus/caller_bus_test.go @@ -16,7 +16,7 @@ import ( mt "github.com/novox/mesh-tools/node-tools/internal/meshtest" ) -// A bus whose users are granted as the controller grants them (mesh-placement internal/broker): each named +// A bus whose users are granted as the controller grants them (mesh-module-assigner internal/broker): each named // user publishes the subjects listed for it. func grantedBus(t *testing.T, publish map[string][]string) string { t.Helper() diff --git a/node-tools/internal/bus/seat_test.go b/node-tools/internal/bus/seat_test.go index d9de59d..c8dbd0e 100644 --- a/node-tools/internal/bus/seat_test.go +++ b/node-tools/internal/bus/seat_test.go @@ -98,7 +98,7 @@ func TestAModuleSubmitsAndSaysOnlyUnderItsOwnNameAndKind(t *testing.T) { t.Fatalf("an asker could not ask under its own name: %v", err) } for module, subject := range map[string]string{ - "mesh-delivery": "mesh.seat.operator-channel.accept.ask.mesh-placement", + "mesh-delivery": "mesh.seat.operator-channel.accept.ask.mesh-module-assigner", "telegram": "mesh.seat.intake.event.choice.desktop", "messenger": "mesh.seat.intake.event.choice.telegram", } { @@ -206,7 +206,7 @@ func TestAnAskerReadsItsOwnRecordAndNoOther(t *testing.T) { if _, err := kv.Put("mesh-delivery.a1", []byte(`{"state":"open"}`)); err != nil { t.Fatal(err) } - if _, err := kv.Put("mesh-placement.a1", []byte(`{"state":"open"}`)); err != nil { + if _, err := kv.Put("mesh-module-assigner.a1", []byte(`{"state":"open"}`)); err != nil { t.Fatal(err) } got, err := conn.SeatRecord("mesh-delivery", "messenger_asks", "a1") diff --git a/node-tools/internal/bus/state_watch_test.go b/node-tools/internal/bus/state_watch_test.go index b4a40a7..42b16da 100644 --- a/node-tools/internal/bus/state_watch_test.go +++ b/node-tools/internal/bus/state_watch_test.go @@ -33,7 +33,7 @@ func TestAWatchHoldsWhileItsCurrentValuesKeepArriving(t *testing.T) { for i := 0; i < older; i++ { keys = append(keys, fmt.Sprintf("novox/mesh-catalog.%d.%040x.status=mesh/check", 400+i, i)) } - for _, member := range []string{"novox/mesh-sdk.76.34f210e5", "novox/mesh-placement.453.c06f75d0", "novox/mesh-delivery.33.28ee22ef"} { + for _, member := range []string{"novox/mesh-sdk.76.34f210e5", "novox/mesh-module-assigner.453.c06f75d0", "novox/mesh-delivery.33.28ee22ef"} { keys = append(keys, member+strings.Repeat("0", 32)+".status=mesh/check") } for _, k := range keys { diff --git a/node-tools/internal/console/address.go b/node-tools/internal/console/address.go index f4604b5..91d4e5c 100644 --- a/node-tools/internal/console/address.go +++ b/node-tools/internal/console/address.go @@ -52,7 +52,7 @@ const ( // searchCap is how many matches a search answers before it says how many more there were. const searchCap = 25 -const grammar = "Addresses: `.` for a seat held once for the mesh (e.g. `mesh-placement.placement`); " + +const grammar = "Addresses: `.` for a seat held once for the mesh (e.g. `mesh-module-assigner.placement`); " + "`/.` for a seat every machine holds (e.g. `ace/node-packet-filter.rules`); " + "`/.` for a module on one machine (e.g. `novox/postgres.postgres_list_databases`); " + "and `.` also for a module whose instances are interchangeable." @@ -148,7 +148,7 @@ type index struct { // what it serves in time, or it answered before and not now. An address it might answer is never // called missing while it is here (2026-10-05). Unheard []unheard - // Placed is the state seat-holders as the seat mesh-placement's holder wrote it (novox/hq ADR 0571 clause 4), by its + // Placed is the state seat-holders as the seat mesh-module-assigner's holder wrote it (novox/hq ADR 0571 clause 4), by its // keys `` and `.`, read with delivery.HoldersOf; PlacedWhy says why it could not be read. Placed map[string][]byte PlacedWhy string diff --git a/node-tools/internal/console/address_test.go b/node-tools/internal/console/address_test.go index 627de7a..6eb9b0f 100644 --- a/node-tools/internal/console/address_test.go +++ b/node-tools/internal/console/address_test.go @@ -71,9 +71,9 @@ func TestTheMeshsToolsAreFoundByAddress(t *testing.T) { } // The controller: its own seat's verbs announced on the bus like every runtime's; its records are asked by no one. - controller := connect(t, "mesh-placement", "bench") + controller := connect(t, "mesh-module-assigner", "bench") // Its verb nodes, called through the console as any seat's verb is. - stopNodes, err := controller.HandleSubject("mesh.seat.mesh-placement.tool.nodes", func(json.RawMessage) (any, error) { + stopNodes, err := controller.HandleSubject("mesh.seat.mesh-module-assigner.tool.nodes", func(json.RawMessage) (any, error) { return map[string]any{"ok": true, "output": `[{"name":"bench","mode":"converged"},{"name":"desk","mode":"converged"}]`}, nil }) if err != nil { @@ -82,8 +82,8 @@ func TestTheMeshsToolsAreFoundByAddress(t *testing.T) { t.Cleanup(stopNodes) // What the mesh holds, as the indexer and placement write it (ADR 0571 clauses 4, 5): never the controller's records. tools := map[string][]string{"alpha": {"one", "two"}, "beta": {"three"}, "gamma": {"given"}, "delta": nil, - "epsilon": {"seven"}, "mesh-placement": {"nodes"}} - mesh.Placed(t, tools, map[string][]string{"desk": {"alpha", "beta", "delta"}, "bench": {"epsilon", "mesh-placement"}}) + "epsilon": {"seven"}, "mesh-module-assigner": {"nodes"}} + mesh.Placed(t, tools, map[string][]string{"desk": {"alpha", "beta", "delta"}, "bench": {"epsilon", "mesh-module-assigner"}}) console := mt.MembershipOf("node-tools", "desk", false, nil) console.State = mt.ConsoleReads() mesh.Issue(t, console) @@ -93,11 +93,11 @@ func TestTheMeshsToolsAreFoundByAddress(t *testing.T) { } return nil }) - stopAnn, err := announce.Serve(controller, announce.Service{Name: "mesh-placement", ID: "bench"}, func() []announce.Endpoint { - return []announce.Endpoint{{Kind: announce.KindSeat, Module: "mesh-placement", Seat: "mesh-placement", Scope: "mesh", + stopAnn, err := announce.Serve(controller, announce.Service{Name: "mesh-module-assigner", ID: "bench"}, func() []announce.Endpoint { + return []announce.Endpoint{{Kind: announce.KindSeat, Module: "mesh-module-assigner", Seat: "mesh-module-assigner", Scope: "mesh", // As the live controller announces: a mesh seat's holder names no machine. Tool: "nodes", Node: "", Description: "Every machine the mesh knows.", Schema: json.RawMessage(`{}`), - Subject: "mesh.seat.mesh-placement.tool.nodes"}} + Subject: "mesh.seat.mesh-module-assigner.tool.nodes"}} }) if err != nil { t.Fatal(err) @@ -131,13 +131,13 @@ func TestTheMeshsToolsAreFoundByAddress(t *testing.T) { // The overview names the mesh's seats, the machines' seats and the machines. overview, isErr := call(t, endpoint, "mesh_overview", nil) - if isErr || !strings.Contains(overview, "mesh-placement.nodes") || !strings.Contains(overview, "/node-shelf.list") || + if isErr || !strings.Contains(overview, "mesh-module-assigner.nodes") || !strings.Contains(overview, "/node-shelf.list") || !strings.Contains(overview, `"bench"`) || !strings.Contains(overview, `"desk"`) { t.Errorf("overview: %s", overview) } // Silence is named only where tools should have answered: epsilon declares tools on bench and // nothing there announced it; delta declares none and is never a name. - if strings.Contains(overview, "mesh-placement on bench") { + if strings.Contains(overview, "mesh-module-assigner on bench") { t.Errorf("a mesh seat's holder that announced no machine is called silent on its machine:\n%s", overview) } if !strings.Contains(overview, "epsilon on bench") || strings.Contains(overview, "delta") { @@ -150,7 +150,7 @@ func TestTheMeshsToolsAreFoundByAddress(t *testing.T) { } // A mesh seat, a node seat, an assignment and an interchangeable module, each by address. - if got, isErr := call(t, endpoint, "mesh_call", map[string]any{"address": "mesh-placement.nodes"}); isErr || !strings.Contains(got, "converged") { + if got, isErr := call(t, endpoint, "mesh_call", map[string]any{"address": "mesh-module-assigner.nodes"}); isErr || !strings.Contains(got, "converged") { t.Errorf("mesh seat: %s", got) } if got, isErr := call(t, endpoint, "mesh_call", map[string]any{"address": "desk/node-shelf.list"}); isErr || !strings.Contains(got, `"a"`) { @@ -168,7 +168,7 @@ func TestTheMeshsToolsAreFoundByAddress(t *testing.T) { for address, want := range map[string]string{ "beta.three": "keeps state on each machine it runs on, so a call names the machine: write /beta.three — it runs on desk", "node-shelf.list": "held once per machine: write /node-shelf.list — it is held on desk", - "desk/mesh-placement.nodes": "held once for the whole mesh", + "desk/mesh-module-assigner.nodes": "held once for the whole mesh", "bench/beta.three": "beta does not run on bench; it runs on desk", "nonsense": "is not an address", } { @@ -201,7 +201,7 @@ func TestTheMeshsToolsAreFoundByAddress(t *testing.T) { t.Fatalf("gamma was found before it served: %s", got) } mesh.Issue(t, mt.MembershipOf("gamma", "desk", false, nil)) - mesh.Placed(t, tools, map[string][]string{"desk": {"alpha", "beta", "delta", "gamma"}, "bench": {"epsilon", "mesh-placement"}}) + mesh.Placed(t, tools, map[string][]string{"desk": {"alpha", "beta", "delta", "gamma"}, "bench": {"epsilon", "mesh-module-assigner"}}) late := connect(t, "node-tools", "desk") stopLate, err := runtime.Run(late, []runtime.Served{{Module: "gamma", Entrypoints: []string{mt.Fixture("env-gamma.serve.mjs")}}}, nil, (&mt.Logs{}).Logf) diff --git a/node-tools/internal/console/mcp.go b/node-tools/internal/console/mcp.go index 70490d6..489099b 100644 --- a/node-tools/internal/console/mcp.go +++ b/node-tools/internal/console/mcp.go @@ -341,7 +341,7 @@ func (s *Surface) instructions() string { "that serves it; what may be called was fixed when this account was issued, so a " + "refusal means the account, not the tool. The list is what the running modules " + "answered, plus every role's tools from the mesh's records — the mesh's own verbs " + - "(mesh-delivery.deliveries, mesh-placement.propose …) among them; a module that did not answer " + + "(mesh-delivery.deliveries, mesh-module-assigner.propose …) among them; a module that did not answer " + "is named in the list's _meta and can still be called by .." } return "The tools of a Novox mesh, reached as " + s.who + ", found by address rather than listed " + diff --git a/node-tools/internal/console/node_argument_test.go b/node-tools/internal/console/node_argument_test.go index 2f00e51..0ea14b1 100644 --- a/node-tools/internal/console/node_argument_test.go +++ b/node-tools/internal/console/node_argument_test.go @@ -11,9 +11,9 @@ import ( // 2026-10-05 the controller's push was described as taking nothing, and a push naming one machine // ran as a push of every machine behind. func TestAMeshSeatsNodeIsItsOwnArgument(t *testing.T) { - push := Tool{Module: "mesh-placement", Name: "push", Seat: true, Scope: "mesh", + push := Tool{Module: "mesh-module-assigner", Name: "push", Seat: true, Scope: "mesh", Input: json.RawMessage(`{"type":"object","properties":{"node":{"type":"string"},"behind":{"type":"string"}}}`)} - mesh := target{Address: "mesh-placement.push", Tool: push, Seat: true} + mesh := target{Address: "mesh-module-assigner.push", Tool: push, Seat: true} props := schemaAsPassed(mesh)["properties"].(map[string]any) if _, has := props["node"]; !has { diff --git a/node-tools/internal/console/refusal_test.go b/node-tools/internal/console/refusal_test.go index 288cb12..68e350c 100644 --- a/node-tools/internal/console/refusal_test.go +++ b/node-tools/internal/console/refusal_test.go @@ -40,5 +40,11 @@ func TestATimeoutIsNotAFailure(t *testing.T) { if strings.Contains(text, "controller") { t.Errorf("%s names the controller: %s", name, text) } + // The seats were renamed (ADR 0634 §9): no hint names the seat that placed modules or enrolled a machine by its old name. + for _, old := range []string{"mesh-placement", "mesh-enrolment"} { + if strings.Contains(text, old) { + t.Errorf("%s names the seat %s, which is now mesh-module-assigner or mesh-node-enroller: %s", name, old, text) + } + } } } diff --git a/node-tools/internal/console/runtimes_test.go b/node-tools/internal/console/runtimes_test.go index 94af57d..306da31 100644 --- a/node-tools/internal/console/runtimes_test.go +++ b/node-tools/internal/console/runtimes_test.go @@ -60,7 +60,7 @@ func TestARuntimeThatAnswersLateIsFoundAndOneThatNeverDoesIsNamed(t *testing.T) mesh.Placed(t, map[string][]string{"slack": {"slack_check"}, "nap": {"doze"}, "nextcloud-client": {"nextcloud_check"}}, map[string][]string{"laptop": {"slack", "nextcloud-client"}, "sleeper": {"nap", "nextcloud-client"}, "desktop": {"nextcloud-client"}, "bench": nil}) - // Who holds which seat, by placement (ADR 0571 clause 4), as the seat mesh-placement's holder composes it. + // Who holds which seat, by placement (ADR 0571 clause 4), as the seat mesh-module-assigner's holder composes it. placedKV := mesh.Bucket(t, delivery.SeatHoldersBucket) for key, sh := range delivery.SeatHoldersOf(map[string]delivery.Placement{"bench": {Node: "bench", Holds: []delivery.Holding{{Claim: "mesh-wants", Scope: "mesh", Module: "mesh-delivery-assigner"}}}}) { @@ -95,7 +95,7 @@ func TestARuntimeThatAnswersLateIsFoundAndOneThatNeverDoesIsNamed(t *testing.T) Subject: "mesh.mod.nextcloud-client.tool.nextcloud_check.desktop"}, }) - controller := connect(t, "mesh-placement", "bench") + controller := connect(t, "mesh-module-assigner", "bench") var toolsAsked atomic.Int32 for verb, output := range map[string]string{ // The controller's records are served here only to see that none is asked (ADR 0571 clauses 4 and 5). @@ -104,7 +104,7 @@ func TestARuntimeThatAnswersLateIsFoundAndOneThatNeverDoesIsNamed(t *testing.T) "tools": `{"seats":[]}`, } { output, verb := output, verb - stop, err := controller.HandleSubject("mesh.seat.mesh-placement.tool."+verb, func(json.RawMessage) (any, error) { + stop, err := controller.HandleSubject("mesh.seat.mesh-module-assigner.tool."+verb, func(json.RawMessage) (any, error) { toolsAsked.Add(1) return map[string]any{"output": output, "ok": true}, nil }) diff --git a/node-tools/internal/console/search_test.go b/node-tools/internal/console/search_test.go index c624a07..bde1b69 100644 --- a/node-tools/internal/console/search_test.go +++ b/node-tools/internal/console/search_test.go @@ -33,8 +33,8 @@ func meshToSearch(t *testing.T) *index { "node": "What one machine reported it can do, what it is assigned, and why.", "nodes": "Every machine the mesh knows, with whether it is converged or adopted.", } { - endpoints = append(endpoints, announce.Endpoint{Kind: announce.KindSeat, Module: "mesh-placement", Seat: "mesh-placement", - Tool: verb, Scope: "mesh", Description: does, Subject: "mesh.seat.mesh-placement.tool." + verb}) + endpoints = append(endpoints, announce.Endpoint{Kind: announce.KindSeat, Module: "mesh-module-assigner", Seat: "mesh-module-assigner", + Tool: verb, Scope: "mesh", Description: does, Subject: "mesh.seat.mesh-module-assigner.tool." + verb}) } for tool, does := range map[string]string{ "docker_list": "Every container on this machine: name, image, state, ports.", @@ -57,13 +57,13 @@ func TestSearchFindsTheToolThatReplacesACommandFirst(t *testing.T) { x := meshToSearch(t) for query, want := range map[string]string{ "journalctl": "anchor/node-service-manager.journal", - "journalctl -u mesh-placement -n 50": "anchor/node-service-manager.journal", + "journalctl -u mesh-module-assigner -n 50": "anchor/node-service-manager.journal", "sudo journalctl -fu sshd": "anchor/node-service-manager.journal", "/usr/bin/journalctl": "anchor/node-service-manager.journal", "logs": "anchor/node-service-manager.journal", "systemctl status": "anchor/node-service-manager.status", "systemctl status nats.service": "anchor/node-service-manager.status", - "systemctl restart mesh-placement": "anchor/node-service-manager.restart", + "systemctl restart mesh-module-assigner": "anchor/node-service-manager.restart", "systemctl --failed": "anchor/node-service-manager.failed", "docker ps": "anchor/docker.docker_list", "docker ps -a": "anchor/docker.docker_list", @@ -111,7 +111,7 @@ func TestASearchByCommandSaysWhatItReplaces(t *testing.T) { // A synonym is a word, never part of one: `logs` matches through `log`, and finds no `login`. func TestSearchStillMatchesEveryWord(t *testing.T) { x := meshToSearch(t) - if hits := x.search("machine converged"); len(hits) != 1 || hits[0].Address != "mesh-placement.nodes" { + if hits := x.search("machine converged"); len(hits) != 1 || hits[0].Address != "mesh-module-assigner.nodes" { t.Errorf("words in a description: %+v", hits) } if hits := x.search("journalctl kubernetes"); len(hits) != 1 || hits[0].Address != "anchor/node-service-manager.journal" { diff --git a/node-tools/internal/provide/provide_test.go b/node-tools/internal/provide/provide_test.go index 47957c9..496cfa0 100644 --- a/node-tools/internal/provide/provide_test.go +++ b/node-tools/internal/provide/provide_test.go @@ -160,8 +160,8 @@ func TestTheOfferServesThePortTheManifestServes(t *testing.T) { } } for _, r := range m.Reads { - if r == "mesh-placement.assignments" { - t.Error("the manifest still reads mesh-placement.assignments, which the sdk's helper no longer reads (issue 1954)") + if r == "mesh-module-assigner.assignments" { + t.Error("the manifest still reads mesh-module-assigner.assignments, which the sdk's helper no longer reads (issue 1954)") } } for _, a := range m.Build.Artifacts { diff --git a/node-tools/internal/runtime/seat_traffic_test.go b/node-tools/internal/runtime/seat_traffic_test.go index 00a4723..9857a32 100644 --- a/node-tools/internal/runtime/seat_traffic_test.go +++ b/node-tools/internal/runtime/seat_traffic_test.go @@ -70,7 +70,7 @@ func TestABundlesSeatTrafficGoesThroughItsModulesBus(t *testing.T) { if !strings.Contains(string(first), `"duplicate":false`) || !strings.Contains(string(again), `"duplicate":true`) { t.Errorf("the same id twice: %s, then %s", first, again) } - if _, err := asking.Seat("publish", json.RawMessage(`{"subject":"mesh.seat.operator-channel.accept.ask.mesh-placement","body":{}}`), nil); err == nil { + if _, err := asking.Seat("publish", json.RawMessage(`{"subject":"mesh.seat.operator-channel.accept.ask.mesh-module-assigner","body":{}}`), nil); err == nil { t.Error("a module submitted under another's name") } got, err := asking.Seat("record", json.RawMessage(`{"key":"d-1"}`), nil)