From c3754e48c8c3fbef8c0474e8c5824d1ef515264e Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 18:37:28 +0100 Subject: [PATCH] No client names a holder: each reader keeps its own constant, held to its manifest (ADR 0625 section 4, issue 1966) Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- node-tools/internal/console/address.go | 14 ++++----- node-tools/internal/console/runtimes_test.go | 3 +- node-tools/internal/firstview/firstview.go | 20 +++++++------ .../internal/firstview/firstview_test.go | 25 ++++++++++++---- node-tools/internal/meshtest/meshtest.go | 29 +++++++++++-------- 5 files changed, 56 insertions(+), 35 deletions(-) diff --git a/node-tools/internal/console/address.go b/node-tools/internal/console/address.go index 87b509f..2a2de9f 100644 --- a/node-tools/internal/console/address.go +++ b/node-tools/internal/console/address.go @@ -340,7 +340,7 @@ func indexOn(conn *bus.Conn, src Source, held announce.Known, before func(announ } l.NotAnswering = append(l.NotAnswering, recordsWhy...) if x.PlacedWhy != "" { - l.NotAnswering = append(l.NotAnswering, assigner.SeatHoldersRead+" (who holds which seat by placement could not be read: "+ + l.NotAnswering = append(l.NotAnswering, firstview.SeatHoldersRead+" (who holds which seat by placement could not be read: "+ x.PlacedWhy+")") } for _, u := range x.Unheard { @@ -360,7 +360,7 @@ func indexOn(conn *bus.Conn, src Source, held announce.Known, before func(announ } // Source is where the console reads the four states of the indexer and the assigner it indexes the mesh by: the assigner's -// placement and seat-holders, the indexer's modules and manifests, by `.` (assigner.PlacementRead and the +// placement and seat-holders, the indexer's modules and manifests, by `.` (firstview.PlacementRead and the // rest). The tool runner's is its first views (firstview.Views), never the owners' state (novox/hq ADR 0615 §1). type Source interface { // State is every current value of a state by key; an error says why it cannot be told yet. @@ -369,9 +369,9 @@ type Source interface { Entry(name, key string) ([]byte, error) } -// seatHoldersOn reads the state seat-holders whole (novox/hq ADR 0571 clause 4), as assigner.SeatHoldersRead. +// seatHoldersOn reads the state seat-holders whole (novox/hq ADR 0571 clause 4), as firstview.SeatHoldersRead. func seatHoldersOn(src Source) (map[string][]byte, error) { - return src.State(assigner.SeatHoldersRead) + return src.State(firstview.SeatHoldersRead) } // The indexer's states the console reads (novox/hq ADR 0571 clauses 4 and 5): every module, and each manifest by its @@ -394,9 +394,9 @@ var manifestsKept sync.Map // digest → manifestFacts // and every machine placement names (moduleNames, assigner.MachineNames). What cannot be read is said, one line // per state, for the listing's not answering. func recordsOn(src Source) (recorded []recordedModule, machines []string, why []string) { - placements, err := src.State(assigner.PlacementRead) + placements, err := src.State(firstview.PlacementRead) if err != nil { - why = append(why, assigner.PlacementRead+" (where modules are placed could not be read, so what is missing cannot "+ + why = append(why, firstview.PlacementRead+" (where modules are placed could not be read, so what is missing cannot "+ "be said: "+err.Error()+")") } machines = assigner.MachineNames(placements) @@ -404,7 +404,7 @@ func recordsOn(src Source) (recorded []recordedModule, machines []string, why [] // The placement's keys in either form, a node key and its module keys or the form before (research 072 row 37). assembled, err := assigner.PlacementsOf(placements) if err != nil { - why = append(why, assigner.PlacementRead+" (not every key is a placement: "+err.Error()+")") + why = append(why, firstview.PlacementRead+" (not every key is a placement: "+err.Error()+")") } for _, node := range machines { for _, m := range assembled[node].Modules { diff --git a/node-tools/internal/console/runtimes_test.go b/node-tools/internal/console/runtimes_test.go index 3a0bcac..f871069 100644 --- a/node-tools/internal/console/runtimes_test.go +++ b/node-tools/internal/console/runtimes_test.go @@ -14,6 +14,7 @@ import ( "github.com/novox/mesh-tools/node-tools/internal/announce" "github.com/novox/mesh-tools/node-tools/internal/bus" + "github.com/novox/mesh-tools/node-tools/internal/meshtest" mt "github.com/novox/mesh-tools/node-tools/internal/meshtest" ) @@ -62,7 +63,7 @@ func TestARuntimeThatAnswersLateIsFoundAndOneThatNeverDoesIsNamed(t *testing.T) 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-module-assigner's holder composes it. - placedKV := mesh.Bucket(t, assigner.SeatHoldersBucket) + placedKV := mesh.Bucket(t, meshtest.SeatHoldersBucket) holdersKey, err := assigner.SeatHoldersKey("mesh-wants", "") if err != nil { t.Fatal(err) diff --git a/node-tools/internal/firstview/firstview.go b/node-tools/internal/firstview/firstview.go index efc776f..6ae8b05 100644 --- a/node-tools/internal/firstview/firstview.go +++ b/node-tools/internal/firstview/firstview.go @@ -11,13 +11,13 @@ import ( "context" "encoding/json" "fmt" - conditions "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" - condseat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" - assigner "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" "sort" "sync" "time" + conditions "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" + condseat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" + "git.novox.be/novox/mesh-sdk/go/views" "github.com/novox/mesh-tools/node-tools/internal/bus" @@ -26,14 +26,16 @@ import ( // The owners and their first-view tools (the tools' addresses are `.`). const ( Indexer = "mesh-delivery-indexer" - Assigner = assigner.Module + Assigner = "mesh-delivery-assigner" ) -// The names the console reads the indexer's two states by (`.`), as a manifest's `reads` names them; the -// assigner's two are its client's (assigner.PlacementRead, assigner.SeatHoldersRead). +// The names the console reads the four states by (`.`), as the manifest's `consumes` names their views +// families (no seat's client names a holder, novox/hq ADR 0625 §4; TestTheFirstViewsAreTheManifestsViews). const ( - ModulesRead = Indexer + ".modules" - ManifestsRead = Indexer + ".manifests" + PlacementRead = Assigner + ".placement" + SeatHoldersRead = Assigner + ".seat-holders" + ModulesRead = Indexer + ".modules" + ManifestsRead = Indexer + ".manifests" ) // Spec is one first view the runner holds. @@ -47,7 +49,7 @@ var Specs = []Spec{ {Indexer, "modules", "modules"}, } -// Name is the name a state is read by, `.`: assigner.PlacementRead, assigner.SeatHoldersRead and the indexer's. +// Name is the name a state is read by, `.`: PlacementRead, SeatHoldersRead and the indexer's. func (s Spec) Name() string { return s.Owner + "." + s.State } // Views is the four followers. diff --git a/node-tools/internal/firstview/firstview_test.go b/node-tools/internal/firstview/firstview_test.go index ed25d20..9911e09 100644 --- a/node-tools/internal/firstview/firstview_test.go +++ b/node-tools/internal/firstview/firstview_test.go @@ -4,12 +4,13 @@ import ( "context" "encoding/json" "errors" + "os" + "slices" "strings" "sync" "testing" "time" - assigner "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-sdk/go/views" "github.com/novox/mesh-tools/node-tools/internal/bus" @@ -168,15 +169,27 @@ func TestTheRunnerHoldsTheFourStatesFromTheOwnersFirstViewsAndTheirEvents(t *tes } } -// The four first views are read by the names the seats' clients give the states, and the console reads the indexer's -// two by the names here. -func TestTheFirstViewsAreNamedAsTheClientsNameTheirStates(t *testing.T) { - want := map[string]bool{assigner.PlacementRead: true, assigner.SeatHoldersRead: true, ModulesRead: true, ManifestsRead: true} +// The four first views are the manifest's `consumes` families, and the console reads each by the name here. +func TestTheFirstViewsAreTheManifestsViews(t *testing.T) { + raw, err := os.ReadFile("../../module.json") + if err != nil { + t.Fatal(err) + } + var m struct { + Consumes []string `json:"consumes"` + } + if err := json.Unmarshal(raw, &m); err != nil { + t.Fatal(err) + } + want := map[string]bool{PlacementRead: true, SeatHoldersRead: true, ModulesRead: true, ManifestsRead: true} for _, s := range Specs { if !want[s.Name()] { - t.Errorf("the first view %s is read by no name a client gives", s.Name()) + t.Errorf("the first view %s is read by no name here", s.Name()) } delete(want, s.Name()) + if view := s.Owner + ".views." + s.Tool; !slices.Contains(m.Consumes, view) { + t.Errorf("the first view %s has no family in the manifest's consumes %v", view, m.Consumes) + } } if len(want) > 0 { t.Errorf("no first view reads %v", want) diff --git a/node-tools/internal/meshtest/meshtest.go b/node-tools/internal/meshtest/meshtest.go index 5966866..b106673 100644 --- a/node-tools/internal/meshtest/meshtest.go +++ b/node-tools/internal/meshtest/meshtest.go @@ -13,11 +13,6 @@ package meshtest import ( "context" "encoding/json" - assigner "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - seatplacement "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" - meshcatalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" - meshcatalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" - "git.novox.be/novox/mesh-sdk/go/membership" "os" "path/filepath" "runtime" @@ -26,6 +21,12 @@ import ( "testing" "time" + assigner "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + seatplacement "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + meshcatalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + meshcatalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-sdk/go/membership" + "github.com/nats-io/nats-server/v2/server" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" @@ -155,19 +156,23 @@ func sdkMembership(t *testing.T, mem bus.Membership) membership.Membership { // The indexer's two states the console reads, and their buckets (the test bed's own names: the indexer's client names // no holder). const ( - indexer = "mesh-delivery-indexer" - ModulesBucket = indexer + "_modules" - ManifestsBucket = indexer + "_manifests" + indexer = "mesh-delivery-indexer" + assignerHolder = "mesh-delivery-assigner" + // PlacementBucket and SeatHoldersBucket are the assigner's states placement and seat-holders. + PlacementBucket = assignerHolder + "_placement" + SeatHoldersBucket = assignerHolder + "_seat-holders" + ModulesBucket = indexer + "_modules" + ManifestsBucket = indexer + "_manifests" ) // ConsoleReads are the states a console's runtime reads to say what the mesh holds (novox/hq ADR 0571 clauses 4, 5): // placement, the indexer's modules and manifests, and the seat holders. func ConsoleReads() []bus.StateIssued { return []bus.StateIssued{ - {Name: assigner.PlacementRead, Bucket: assigner.PlacementBucket}, + {Name: assignerHolder + ".placement", Bucket: PlacementBucket}, {Name: indexer + ".modules", Bucket: ModulesBucket}, {Name: indexer + ".manifests", Bucket: ManifestsBucket}, - {Name: assigner.SeatHoldersRead, Bucket: assigner.SeatHoldersBucket}, + {Name: assignerHolder + ".seat-holders", Bucket: SeatHoldersBucket}, } } @@ -183,8 +188,8 @@ func (m *Mesh) Placed(t *testing.T, tools map[string][]string, on map[string][]s } return b } - modules, manifests, placement := kv(ModulesBucket), kv(ManifestsBucket), kv(assigner.PlacementBucket) - kv(assigner.SeatHoldersBucket) + modules, manifests, placement := kv(ModulesBucket), kv(ManifestsBucket), kv(PlacementBucket) + kv(SeatHoldersBucket) for module, declared := range tools { manifest, _ := json.Marshal(map[string]any{"module": module, "tools": declared}) digest := meshcatalogclient.ManifestDigest(manifest)