From 683b1ed693bcf4e28ea6314eb9931b7fac4ea752 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 28 Sep 2026 16:36:45 +0200 Subject: [PATCH] The seat declares the facts its holder states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The grant permitted the control plane to state what it applied and the seat said nothing about it, so the check that every derived subscription has an owner found the catalogue subscribing to a subject nothing publishes — which is exactly the fault that check exists for, pointed at me. A seat carries the protocol of its role (novox/hq ADR 0129), so the facts are the mesh-controller seat's `emits`. That is also what lets another module declare it consumes them. No accepts, so no work queue is raised for the seat — only what its holder may say. The agreement test now holds all three places to one another: the seat, the grant, and the words the mesh states them with. --- internal/broker/states_agreement_test.go | 14 ++++++++++++++ internal/catalogue/seats.go | 6 +++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/broker/states_agreement_test.go b/internal/broker/states_agreement_test.go index 5d64bc3..afd132f 100644 --- a/internal/broker/states_agreement_test.go +++ b/internal/broker/states_agreement_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/novox/mesh-controller/internal/broker" + "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/link" ) @@ -27,4 +28,17 @@ func TestTheFactsTheGrantPermitsAreTheFactsTheMeshStates(t *testing.T) { if len(broker.ControllerStates) != 3 { t.Errorf("the grant permits %v, which is more than the mesh states", broker.ControllerStates) } + // **And the seat says it.** A seat carries the protocol of its role (novox/hq ADR 0129), so the + // facts the control plane states are the seat's `emits` — which is what lets anything else declare + // that it consumes them, and what the subject-agreement check reads to know they have an owner. + var declared []string + for _, seat := range catalogue.SeatsWithAProtocol() { + if seat.Name == broker.ControllerSeat { + declared = seat.Emits + } + } + if !slices.Equal(declared, broker.ControllerStates) { + t.Errorf("the %s seat emits %v and the grant permits %v", broker.ControllerSeat, + declared, broker.ControllerStates) + } } diff --git a/internal/catalogue/seats.go b/internal/catalogue/seats.go index 8d6c873..b41b122 100644 --- a/internal/catalogue/seats.go +++ b/internal/catalogue/seats.go @@ -48,7 +48,11 @@ type Seat struct { // // In the order a person reads it: the mesh's own, then a node's. var defaultSeats = []Seat{ - {Name: "mesh-controller", Scope: ScopeMesh, Decision: "novox/hq ADR 0079"}, + // The control plane states what it did under the seat it holds (novox/hq ADR 0134): a role's + // events belong to the role, so they keep their address while the holder is replaced. No accepts, + // so no work queue is raised for it — only what its holder may say. + {Name: "mesh-controller", Scope: ScopeMesh, Decision: "novox/hq ADR 0079", + Emits: []string{"applied", "refused", "built-before"}}, {Name: "mesh-store", Scope: ScopeMesh, Delivers: "postgres-database", Decision: "novox/hq ADR 0079"}, // **Delivers the mesh's own bus, not `amqp`.** Those were the same word until // ADR 0127 separated them: `amqp` is a backing service a module may require, and this seat is