From daa7737392476c91dd6d86de60b4ddf6a1ac3273 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 12 Nov 2026 13:08:40 +0100 Subject: [PATCH] Item 5 (join 11): an asker's build-seat queue is served by the seat's holders, so the indexer draws its kind-6 edge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An asker names the queue it asks as seat:.accept. (A-231); no module's serves lists that form, so graph.Servers matched nothing and the indexer drew no served-verb edge from a build seat's holders to the asker. Servers now reads the form through mesh-sdk's catalogue.QueueInvoke and answers every module serving a verb of the seat; the kind-6 edge asks the module graph's Servers, the one rule, instead of a match of its own. Builds: design 47 G §544, §605, A-231 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h --- .../internal/index/catalog_edges.go | 13 ++++++++++--- .../internal/index/catalog_edges_test.go | 5 ++++- modules/mesh-delivery-sdk/graph/graph.go | 10 ++++++++-- modules/mesh-delivery-sdk/graph/graph_test.go | 9 +++++++++ 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/modules/mesh-delivery-indexer/internal/index/catalog_edges.go b/modules/mesh-delivery-indexer/internal/index/catalog_edges.go index 720745b0..ab65952b 100644 --- a/modules/mesh-delivery-indexer/internal/index/catalog_edges.go +++ b/modules/mesh-delivery-indexer/internal/index/catalog_edges.go @@ -9,6 +9,7 @@ import ( "strings" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/graph" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/seatdef" ) @@ -232,6 +233,12 @@ func edgesOf(a meshcatalog.CatalogEntry, all map[string]meshcatalog.CatalogEntry } // Kind 6: a verb or a tool it invokes and another entry serves (§47), unless its edge lies on a cycle and is left out (ADR 0614 §3). // A verb whose answer's fields it relies on (the long form of `invokes`, §558) names them in the edge's `why`. + // Who serves an invoke is the module graph's one rule (graph.Servers): a build seat's queue, `seat:.accept.`, + // is its holders' (A-231). + serving := graph.Graph{} + for _, m := range modules { + serving[m] = graph.Node{Serves: all[m].Serves} + } fields := manifestOf(a).InvokeFields for _, verb := range a.Invokes { if slices.Contains(a.LeftOut, verb) { @@ -241,9 +248,9 @@ func edgesOf(a meshcatalog.CatalogEntry, all map[string]meshcatalog.CatalogEntry if named := fields[verb]; len(named) > 0 { why += " fields " + strings.Join(named, ", ") } - for _, m := range modules { - if b := all[m]; m != a.Module && slices.Contains(b.Serves, verb) { - add(meshcatalog.Edge{Kind: 6, From: primaryOf(b), To: delivers, Word: verb, Why: why}) + for _, m := range serving.Servers(verb) { + if m != a.Module { + add(meshcatalog.Edge{Kind: 6, From: primaryOf(all[m]), To: delivers, Word: verb, Why: why}) } } } diff --git a/modules/mesh-delivery-indexer/internal/index/catalog_edges_test.go b/modules/mesh-delivery-indexer/internal/index/catalog_edges_test.go index 8821d0a8..304c8cfd 100644 --- a/modules/mesh-delivery-indexer/internal/index/catalog_edges_test.go +++ b/modules/mesh-delivery-indexer/internal/index/catalog_edges_test.go @@ -20,9 +20,11 @@ func TestAnEntryCarriesItsEdgesOfKindsOneToFourAndSix(t *testing.T) { Provides: []string{"builder"}, Serves: []string{"seat:mesh-builds.build"}}, "db": {Module: "db", Artifacts: []meshcatalog.Artifact{{Ref: "db:image", Kind: "image"}}, Provides: []string{"sql"}}, "cfg": {Module: "cfg", Artifacts: []meshcatalog.Artifact{{Ref: "cfg:archive", Kind: "archive"}}}, + // A build seat's holder: the queue an asker names in its invokes, `seat:.accept.`, is its (A-231). + "node-go": {Module: "node-go", Artifacts: []meshcatalog.Artifact{{Ref: "node-go:program", Language: "go"}}, Serves: []string{"seat:node-build-go.steps"}}, "app": {Module: "app", Packages: map[string]string{"git.example/lib/go": "1.2.3"}, Bases: []string{"db"}, Toolchains: map[string]meshcatalog.BuildEdge{"go": {Module: "build-go"}}, - Awaits: []string{"sql"}, Invokes: []string{"seat:mesh-builds.build", "seat:mesh-builds.cycle"}, LeftOut: []string{"seat:mesh-builds.cycle"}, + Awaits: []string{"sql"}, Invokes: []string{"seat:mesh-builds.build", "seat:mesh-builds.cycle", "seat:node-build-go.accept.check"}, LeftOut: []string{"seat:mesh-builds.cycle"}, Manifest: map[string]any{"build": map[string]any{"artifacts": []any{map[string]any{"name": "program", "needs": []any{"cfg:archive", "nothing:here"}}}}}, Artifacts: []meshcatalog.Artifact{{Ref: "app:program", Language: "go"}}}, } @@ -35,6 +37,7 @@ func TestAnEntryCarriesItsEdgesOfKindsOneToFourAndSix(t *testing.T) { {Kind: 3, From: "db:image", To: "app:program", Stale: true, Word: "build.on", Why: "db"}, {Kind: 4, From: "db:image", To: "app:program", Word: "requires", Why: "sql"}, {Kind: 6, From: "build-go:program", To: "app:program", Word: "seat:mesh-builds.build", Why: "seat:mesh-builds.build"}, + {Kind: 6, From: "node-go:program", To: "app:program", Word: "seat:node-build-go.accept.check", Why: "seat:node-build-go.accept.check"}, } if !slices.Equal(got, want) { t.Errorf("edges\n got %+v\nwant %+v", got, want) diff --git a/modules/mesh-delivery-sdk/graph/graph.go b/modules/mesh-delivery-sdk/graph/graph.go index 560004c0..0b9e78a4 100644 --- a/modules/mesh-delivery-sdk/graph/graph.go +++ b/modules/mesh-delivery-sdk/graph/graph.go @@ -338,11 +338,17 @@ func (g Graph) Dependents(module string) []string { return out } -// Servers are the modules whose Serves names invoke (a seat verb or a module tool), sorted. +// Servers are the modules whose Serves names invoke (a seat verb or a module tool), sorted. An asker's queue of a build +// seat, `seat:.accept.` (design 47 G §544, A-231, read by catalogue.QueueInvoke), is served by every holder +// of the seat: each module whose Serves names a verb of it. func (g Graph) Servers(invoke string) []string { + seat, _, queue := catalogue.QueueInvoke(invoke) var out []string for name, n := range g { - if slices.Contains(n.Serves, invoke) { + if slices.Contains(n.Serves, invoke) || queue && slices.ContainsFunc(n.Serves, func(v string) bool { + s, _, ok := catalogue.SeatInvoke(v) + return ok && s == seat + }) { out = append(out, name) } } diff --git a/modules/mesh-delivery-sdk/graph/graph_test.go b/modules/mesh-delivery-sdk/graph/graph_test.go index 405b67f7..0d6fc75d 100644 --- a/modules/mesh-delivery-sdk/graph/graph_test.go +++ b/modules/mesh-delivery-sdk/graph/graph_test.go @@ -298,6 +298,15 @@ func readersOfTheSeatList(t *testing.T) { if got := g.Servers(list); !reflect.DeepEqual(got, []string{"indexer"}) { t.Errorf("servers of the seat list = %v", got) } + // A-231: an asker's queue of a build seat, `seat:.accept.`, is served by the seat's holders. + g["build-go"] = node(func(n *Node) { n.Serves = []string{"seat:node-build-go.steps"} }) + if got := g.Servers("seat:node-build-go.accept.check"); !reflect.DeepEqual(got, []string{"build-go"}) { + t.Errorf("servers of a build seat's queue = %v", got) + } + if got := g.Servers("seat:node-build-go.accept.check.extra"); len(got) != 0 { + t.Errorf("servers of what is no queue = %v", got) + } + delete(g, "build-go") if got := g.Before("checker"); !reflect.DeepEqual(got, []string{"indexer"}) { t.Errorf("the checker waits for %v, want the indexer", got) }