From 9ab75bab490c1f9a2f57b7f5cf888e320c93e3fe Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 3 Nov 2026 16:23:34 +0100 Subject: [PATCH] =?UTF-8?q?The=20seat=20mesh-catalog's=20contract:=20four?= =?UTF-8?q?=20verbs=20read=20from=20its=20holder's=20states,=20modules,=20?= =?UTF-8?q?module,=20provides=20and=20dependents,=20each=20optional=20whil?= =?UTF-8?q?e=20the=20module=20that=20claimed=20it=20before=20still=20stand?= =?UTF-8?q?s=20(ADR=200604=20=C2=A71,=20=C2=A76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go/catalogue/catalog_seat.go | 30 ++++++++++++++++++++++++++++++ go/catalogue/seats.go | 3 ++- go/catalogue/verbs_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 go/catalogue/catalog_seat.go diff --git a/go/catalogue/catalog_seat.go b/go/catalogue/catalog_seat.go new file mode 100644 index 0000000..63b380b --- /dev/null +++ b/go/catalogue/catalog_seat.go @@ -0,0 +1,30 @@ +package catalogue + +// CatalogSeat is the seat of the catalog: what the mesh holds of every module (novox/hq ADR 0604 §1). Its states are +// its holder's: the modules the trunk holds, their manifests by digest and the module graph (the indexer's states +// `modules`, `manifests` and `graph`, delivery.ModulesBucket, ManifestsBucket and GraphBucket), announced with who may +// read them; a reader that only watches reads them and calls no verb. It names no event. What must be built again is +// no verb: a module whose fingerprint differs from its newest build, which the delivery view derives. +const CatalogSeat = "mesh-catalog" + +// catalogVerbs are the seat's four verbs, each read from its holder's states. Each is optional until the holder that +// runs serves it (design 33 §7): the module that claimed the seat before serves none of them, and a judge holding them +// required would read it as outgrown for as long as both claims stand (novox/hq ADR 0604 §6). +func catalogVerbs() []Verb { + return []Verb{ + {Name: "modules", Optional: true, Description: "Every module the trunk holds, each with its repository, its " + + "directory there, the merge commit it was last changed at and its fingerprint (or why it has none): the " + + "catalog of the mesh, read from its holder's state. Reads only.", + Input: schema(map[string]string{}, nil)}, + {Name: "module", Optional: true, Description: "One module whole: its repository, directory, commit and " + + "fingerprint, and its manifest as the trunk holds it. Older versions are in its repository's history. Reads only.", + Input: schema(map[string]string{"module": "the module's name"}, []string{"module"})}, + {Name: "provides", Optional: true, Description: "Which modules provide a provision (a module provides its own " + + "name too) and which require it, from the module graph's provision edges. Reads only.", + Input: schema(map[string]string{"provision": "the provision, such as postgres-database"}, []string{"provision"})}, + {Name: "dependents", Optional: true, Description: "The modules whose build edges reach a module, directly or " + + "through others (a base, a mesh package it publishes, the build module that builds them): what a change of it " + + "builds again, from the module graph. Reads only.", + Input: schema(map[string]string{"module": "the module's name"}, []string{"module"})}, + } +} diff --git a/go/catalogue/seats.go b/go/catalogue/seats.go index b382b3e..15154bb 100644 --- a/go/catalogue/seats.go +++ b/go/catalogue/seats.go @@ -272,7 +272,8 @@ var defaultSeats = append([]Seat{ // images and archives, by digest — which is why the provision is the artifact store and not an // image registry. {Name: "mesh-artifact-store", Scope: ScopeMesh, Delivers: "artifact-store", Decision: "novox/hq ADR 0075"}, - {Name: "mesh-catalog", Scope: ScopeMesh, Decision: "novox/hq ADR 0121"}, + // The catalog (novox/hq ADR 0604): what the mesh holds of every module, its verbs read from its holder's states (CatalogSeat). + {Name: CatalogSeat, Scope: ScopeMesh, Serves: catalogVerbs(), Decision: "novox/hq ADR 0604"}, // The delivery's owner (novox/hq ADR 0239): one commit's journey from its pull request's head to every // machine, and a group of them, recorded and ordered by its holder, which asks the controller for each act. // What it says it says as its module (`mesh-delivery.transition`), through the runtime that launches it. diff --git a/go/catalogue/verbs_test.go b/go/catalogue/verbs_test.go index da85cec..af45955 100644 --- a/go/catalogue/verbs_test.go +++ b/go/catalogue/verbs_test.go @@ -50,6 +50,35 @@ func TestHoldingAMeshSeatRequiresServingItsVerbs(t *testing.T) { t.Errorf("a holder saying it serves mint: %v", err) } + // L3: the seat mesh-catalog promises four verbs, each optional while two claimants stand (ADR 0604 §1, §6). + catalog, ok := SeatNamed(CatalogSeat) + if !ok || catalog.Scope != ScopeMesh || catalog.Decision != "novox/hq ADR 0604" || len(catalog.Serves) != 4 { + t.Fatalf("mesh-catalog: in the set %v, %+v", ok, catalog) + } + four := []string{"modules", "module", "provides", "dependents"} + for i, v := range catalog.Serves { + if v.Name != four[i] || !v.Optional || v.Input == nil { + t.Errorf("mesh-catalog's verb %d is %+v, want %s, optional, with its input", i, v, four[i]) + } + } + cataloguer := func(serves ...string) Manifest { + return Manifest{Module: "a-catalog", Tools: serves, Claims: []Claim{{Name: CatalogSeat, Scope: ScopeMesh, Serves: serves}}} + } + for name, c := range map[string]struct { + serves []string + word string + }{ + "serving the four": {four, ""}, + "serving three, optional": {four[:3], ""}, + "serving none, the old one": {nil, ""}, + "serving a fifth": {append(slices.Clone(four), "rebuild"), "rebuild"}, + } { + err := CanHold(cataloguer(c.serves...), catalog) + if (c.word == "") != (err == nil) || (err != nil && !strings.Contains(err.Error(), c.word)) { + t.Errorf("a mesh-catalog holder %s: %v", name, err) + } + } + was := Seats() t.Cleanup(func() { UseSeats(was) }) UseSeats([]Seat{{Name: "mesh-controller", Scope: ScopeMesh, Decision: "test",