From b1343147e80f846b889467143f4ae145e4b682f7 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 00:59:07 +0100 Subject: [PATCH] S7: the assigner's and the grants part's seat-list again answers a proved operator's session Their seat_list_again answered person. and the healers, but every call through the mesh MCP server is proved as its machine's tool runner (issue 627), so no operator's session could ask it; node-tools now stands for that session, the kind met a second time in the node-engine (issue 1966). The assigner's silent stand-in keeps the bus's retry bound short, since the seat list's reader now asks again while its ask reached no one. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../cmd/mesh-delivery-assigner/seat_tools_test.go | 10 ++++++++++ .../cmd/mesh-delivery-assigner/seatlist.go | 4 +++- .../cmd/mesh-delivery-assigner/seatlist_test.go | 7 +++++++ .../mesh-grants/cmd/mesh-grants/integration_test.go | 11 +++++++++++ modules/mesh-grants/cmd/mesh-grants/seatlist.go | 6 ++++-- 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools_test.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools_test.go index cb3d0468..704f820c 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools_test.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools_test.go @@ -19,6 +19,7 @@ import ( "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-sdk/go/keys" + "git.novox.be/novox/mesh-sdk/go/seatlist" mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" @@ -252,6 +253,15 @@ func TestTheSeatsVerbs(t *testing.T) { }) } s.seatList = nil + t.Run("1966: the seat list's again tool answers an operator's session through the mesh MCP server", func(t *testing.T) { + // Such a session is proved as its machine's tool runner (issue 627); the healers too, no other module. + for caller, refused := range map[string]bool{"person.jochen": false, "shanks.node-tools": false, "ace.mesh-healers": false, + "ace.messenger": true} { + if err := seatlist.Refusal(seatListAgainTool, seatListAgainCallers, caller); (err != nil) != refused { + t.Errorf("%s: refused=%v, err %v", caller, refused, err) + } + } + }) t.Run("1953/D4-D6: the seat mesh-wants' upgrade and bus", wantsVerbs) t.Run("1955/L7 (ADR 0571 §6): rule 7 reads the node's own composition; an unplaced node is said", func(t *testing.T) { w := newWorld() diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seatlist.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seatlist.go index e39c554a..ec80d6e0 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seatlist.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seatlist.go @@ -25,7 +25,9 @@ const ( seatListAgainTool = "assigner_seat_list_again" ) -var seatListAgainCallers = []string{stdio.AnyPerson, "mesh-healers"} +// A call through the mesh MCP server is proved only as its machine's tool runner (.node-tools, issue 627), so +// node-tools stands for an operator's session until a session is proved (issue 1966). +var seatListAgainCallers = []string{stdio.AnyPerson, "node-tools", "mesh-healers"} // seatGate is the reader a runtime of the assigner judges by, set once it is started; before, the list counts as unread. type seatGate struct { diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seatlist_test.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seatlist_test.go index bd7e485d..67f44e2f 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seatlist_test.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seatlist_test.go @@ -5,7 +5,9 @@ import ( "errors" "sync" "testing" + "time" + "git.novox.be/novox/mesh-sdk/go/busretry" "git.novox.be/novox/mesh-sdk/go/seatdef" "git.novox.be/novox/mesh-sdk/go/seatlist" "git.novox.be/novox/mesh-sdk/go/seatlist/seatlisttest" @@ -50,6 +52,11 @@ func (standInCatalog) Subscribe(string, func(string, json.RawMessage) error) err // (catalogue.UseSeats), as a host's does, for the tests after it too: the list is the same everywhere. func gateOver(t *testing.T, silent bool) *seatGate { t.Helper() + if silent { + // A silent seat reached no one: its reader asks again within the bus's retry bound (issue 1966), which a test + // that waits for it to stay unread keeps short. + t.Cleanup(busretry.Waits(20*time.Millisecond, time.Millisecond, 5*time.Millisecond)) + } g := &seatGate{} r := g.follow(standInCatalog{silent: silent}, nil, t.Logf) if r == nil { diff --git a/modules/mesh-grants/cmd/mesh-grants/integration_test.go b/modules/mesh-grants/cmd/mesh-grants/integration_test.go index efbf6f98..1931e920 100644 --- a/modules/mesh-grants/cmd/mesh-grants/integration_test.go +++ b/modules/mesh-grants/cmd/mesh-grants/integration_test.go @@ -31,6 +31,7 @@ import ( "git.novox.be/novox/mesh-sdk/go/membership" "git.novox.be/novox/mesh-sdk/go/process" "git.novox.be/novox/mesh-sdk/go/seal" + "git.novox.be/novox/mesh-sdk/go/seatlist" "git.novox.be/novox/mesh-sdk/go/views" "mesh-grants/internal/grants" @@ -321,6 +322,16 @@ func TestTheGrantsPartWhole(t *testing.T) { t.Errorf("the bucket %s: %v, want none", bucket, err) } } + // Issue 1966: the seat list's again tool answers an operator's session, which the mesh MCP server proves as its + // machine's tool runner (issue 627), and the healers; no other module. + t.Run("the seat list's again tool answers an operator's session", func(t *testing.T) { + for caller, refused := range map[string]bool{"person.jochen": false, "shanks.node-tools": false, "ace.mesh-healers": false, + "ace.messenger": true} { + if err := seatlist.Refusal(grants.ToolSeatListAgain, seatListAgainCallers, caller); (err != nil) != refused { + t.Errorf("%s: refused=%v, err %v", caller, refused, err) + } + } + }) // ADR 0640 §5: the real additions change the subjects and the filters and nothing else, and recreate nothing. t.Run("adding subjects and filters changes only those", func(t *testing.T) { stream, err := js.CreateStream(ctx, jetstream.StreamConfig{Name: "ADDS", Subjects: []string{"a.>"}, MaxAge: time.Hour, MaxMsgs: 100, diff --git a/modules/mesh-grants/cmd/mesh-grants/seatlist.go b/modules/mesh-grants/cmd/mesh-grants/seatlist.go index da8d824b..8ad180bc 100644 --- a/modules/mesh-grants/cmd/mesh-grants/seatlist.go +++ b/modules/mesh-grants/cmd/mesh-grants/seatlist.go @@ -16,8 +16,10 @@ import ( // seat the list does not yet is held back, as a manifest waiting for a judge is, and what runs is composed from its running // build's manifest. The process reads over its connection and the event hub; the tools' bundle over the runtime. -// seatListTools are the reader's two tools (ADR 0636 §7): every operator's session and the healers may read again. -var seatListAgainCallers = []string{stdio.AnyPerson, "mesh-healers"} +// seatListTools are the reader's two tools (ADR 0636 §7): every operator's session and the healers may read again. A call +// through the mesh MCP server is proved only as its machine's tool runner (.node-tools, issue 627), so node-tools +// stands for an operator's session until a session is proved (issue 1966). +var seatListAgainCallers = []string{stdio.AnyPerson, "node-tools", "mesh-healers"} // seatGate is the reader the part gates by, set once it is started; before, it acts on nothing new. type seatGate struct {