IX8: a kind-6 edge names the fields a long-form invoke relies on

The entry's manifest is read by the second reader release (RR2-4), and an invoked verb given as {verb, fields} puts the
fields in its edge's `why`.

Builds: design 47 G §558 (A-208)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 06:03:20 +01:00
parent ca4aa88583
commit 0892f112bc
2 changed files with 36 additions and 1 deletions
@@ -2,11 +2,13 @@ package index
import (
"cmp"
"encoding/json"
"maps"
"slices"
"strings"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// The edges of a catalog graph entry (design 47 G §47 to §50, §121; A-208): the indexer writes, in each entry of a revision,
@@ -116,13 +118,19 @@ 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`.
fields := manifestOf(a).InvokeFields
for _, verb := range a.Invokes {
if slices.Contains(a.LeftOut, verb) {
continue
}
why := verb
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: verb})
add(meshcatalog.Edge{Kind: 6, From: primaryOf(b), To: delivers, Word: verb, Why: why})
}
}
}
@@ -132,6 +140,19 @@ func edgesOf(a meshcatalog.CatalogEntry, all map[string]meshcatalog.CatalogEntry
return out
}
// manifestOf is the manifest the entry carries whole, read by the manifest reader; none where it cannot be read.
func manifestOf(e meshcatalog.CatalogEntry) catalogue.Manifest {
raw, err := json.Marshal(e.Manifest)
if err != nil {
return catalogue.Manifest{}
}
m, err := catalogue.Decode(raw)
if err != nil {
return catalogue.Manifest{}
}
return m
}
type need struct{ by, ref string }
// needsOf are the artifact references each artifact of the entry's manifest names in its `needs`, with the artifact that names them.
@@ -64,3 +64,17 @@ func TestAKeptRevisionHoldsTheEdgesOfItsEntries(t *testing.T) {
}
}
}
// Design 47 G §558, A-208: a verb invoked in the long form {verb, fields} gives its kind-6 edge the fields it relies on,
// read by the second reader release; the short form names none.
func TestAKindSixEdgeNamesTheFieldsALongFormInvokeRelies(t *testing.T) {
entries := map[string]meshcatalog.CatalogEntry{
"holder": {Module: "holder", Artifacts: []meshcatalog.Artifact{{Ref: "holder:p"}}, Serves: []string{"seat:s.list", "seat:s.get"}},
"user": {Module: "user", Artifacts: []meshcatalog.Artifact{{Ref: "user:p"}}, Invokes: []string{"seat:s.get", "seat:s.list"},
Manifest: map[string]any{"module": "user", "version": "1.0", "invokes": []any{"seat:s.get", map[string]any{"verb": "seat:s.list", "fields": []any{"binding", "node"}}}}},
}
got := WithEdges(entries)["user"].Edges
if len(got) != 2 || got[0].Why != "seat:s.get" || got[1].Why != "seat:s.list fields binding, node" || got[1].Word != "seat:s.list" {
t.Errorf("edges %+v, want the short verb as it is and the long form with its fields", got)
}
}