diff --git a/modules/mesh-delivery-indexer/judge/rules.go b/modules/mesh-delivery-indexer/judge/rules.go index 4ca46f7d..49fa9f40 100644 --- a/modules/mesh-delivery-indexer/judge/rules.go +++ b/modules/mesh-delivery-indexer/judge/rules.go @@ -136,7 +136,7 @@ func spokenSystems() string { // and was simply absent — seven modules' tools went missing that way on 2026-10-03. Refused here, // naming the field that would deliver it. func undeliveredBundles(m catalogue.Manifest) []string { - if m.Build == nil || m.Module == catalogue.RuntimeModule { + if m.Build == nil || catalogue.IsToolRunner(m) { return nil } named := runByAResource(m) @@ -3306,9 +3306,9 @@ func waitProblems(m catalogue.Manifest) []string { if len(m.Waits) == 0 { return nil } - if m.Module != catalogue.RuntimeModule { - return []string{fmt.Sprintf("%s says what a session waits on; only %s, the tool runner's module, "+ - "declares waits (novox/hq ADR 0367 §2)", m.Module, catalogue.RuntimeModule)} + if !catalogue.IsToolRunner(m) { + return []string{fmt.Sprintf("%s says what a session waits on; only the tool runner's module (it says "+ + "\"tool-runner\": true) declares waits (novox/hq ADR 0367 §2)", m.Module)} } var problems []string seen := map[string]bool{} @@ -5420,12 +5420,29 @@ func StateProblems(m catalogue.Manifest) []string { return problems } -// PredefinedFormProblems refuses each stream a module declares that StreamsKeptUntilMoved does not name for it. +// **A module keeps its data in the bus's predefined forms** (the lead and the operator, 2026-10-29; the operator's rule of +// 2026-10-22, "data storage is done using our predefined nats bus options"): a state, a log or an object store, which the +// runtime reads and writes for its tools and other modules read by a declared `reads`. A stream of its own is outside +// them and refused, naming it. streamsKeptUntilMoved are the streams declared before this check, each allowed by name +// until its module moves it; the list only shrinks. The judge's own exception, kept here and no longer in mesh-sdk, +// which names no module (issue 774, the operator 2026-11-03). +var streamsKeptUntilMoved = map[string][]string{ + "brother": {"device-asks"}, + "mesh-build-go": {"toolchains-go"}, + "mesh-build-node": {"toolchains-node"}, + "mesh-mail": {"mail", "mail-sends"}, + "only-office": {"convert-asks"}, + "mesh-delivery-assigner": {"placement"}, + "mesh-delivery-checker": {"checks"}, + "mesh-delivery-forge-bridge": {"forge"}, +} + +// PredefinedFormProblems refuses each stream a module declares that streamsKeptUntilMoved does not name for it. func PredefinedFormProblems(m catalogue.Manifest) []string { var out []string for _, s := range m.Streams { // A stream that keeps its history is one of the bus's predefined forms (novox/hq ADR 0592 §6). - if !s.KeepsHistory() && !slices.Contains(catalogue.StreamsKeptUntilMoved[m.Module], s.Name) { + if !s.KeepsHistory() && !slices.Contains(streamsKeptUntilMoved[m.Module], s.Name) { out = append(out, fmt.Sprintf("%s keeps the stream %q, outside the bus's predefined forms: keep it as a state, "+ "a log or an object store, which the runtime reads for the module's tools and others read by `reads`", m.Module, s.Name)) } diff --git a/modules/mesh-delivery-indexer/judge/rules_test.go b/modules/mesh-delivery-indexer/judge/rules_test.go index d0727909..1c14098a 100644 --- a/modules/mesh-delivery-indexer/judge/rules_test.go +++ b/modules/mesh-delivery-indexer/judge/rules_test.go @@ -157,12 +157,18 @@ func TestABundleNothingDeliversIsRefused(t *testing.T) { run := base() run.Resources = []map[string]any{{"id": "daemon", "type": "process", "artifact": "tools", "run": []any{"node", "tools/index.js"}}} runtime := base() - runtime.Module = catalogue.RuntimeModule + runtime.Module, runtime.ToolRunner = "node-tools", true for name, m := range map[string]catalogue.Manifest{"loads": loads, "tools": tools, "a process": run, "the runtime": runtime} { if p := undeliveredBundles(m); len(p) != 0 { t.Errorf("a bundle delivered by %s was refused: %v", name, p) } } + // The tool runner is what says it is one (issue 774): its name alone exempts nothing. + named := base() + named.Module = "node-tools" + if p := undeliveredBundles(named); len(p) != 1 { + t.Errorf("a bundle of a module named as the tool runner, which does not say it is one: %v", p) + } } func TestABundlesWordsAreRefusedWhenTheyAreNotPathsOrConstants(t *testing.T) { @@ -3108,15 +3114,16 @@ func TestAVerbWithAnUnknownKeyIsRefused(t *testing.T) { // each refusal is said in the manifest's words, at parse, rather than at the composition of the bus's users. func TestAWaitIsRefusedUnlessItNamesOneFamilyOfTheToolRunner(t *testing.T) { for manifest, want := range map[string]string{ - `{"module":"shop","version":"1","waits":["mesh-knowledge.reviewed"]}`: "only node-tools", - `{"module":"node-tools","version":"1","waits":["mesh-knowledge.*"]}`: "never a wildcard", - `{"module":"node-tools","version":"1","waits":["**"]}`: "never a wildcard", - `{"module":"node-tools","version":"1","waits":["mesh-knowledge.>"]}`: "never a wildcard", - `{"module":"node-tools","version":"1","waits":["*.reviewed"]}`: "never a wildcard", - `{"module":"node-tools","version":"1","waits":["reviewed"]}`: "does not name an emitter and an event", - `{"module":"node-tools","version":"1","waits":["mesh-knowledge..reviewed"]}`: "does not name an emitter and an event", - `{"module":"node-tools","version":"1","waits":["Mesh-Knowledge.reviewed x"]}`: "does not name an emitter and an event", - `{"module":"node-tools","version":"1","waits":["a.b","a.b"]}`: "twice", + `{"module":"shop","version":"1","waits":["mesh-knowledge.reviewed"]}`: "only the tool runner's module", + `{"module":"node-tools","version":"1","waits":["mesh-knowledge.reviewed"]}`: "only the tool runner's module", // its name says nothing (issue 774) + `{"module":"node-tools","version":"1","tool-runner":true,"waits":["mesh-knowledge.*"]}`: "never a wildcard", + `{"module":"node-tools","version":"1","tool-runner":true,"waits":["**"]}`: "never a wildcard", + `{"module":"node-tools","version":"1","tool-runner":true,"waits":["mesh-knowledge.>"]}`: "never a wildcard", + `{"module":"node-tools","version":"1","tool-runner":true,"waits":["*.reviewed"]}`: "never a wildcard", + `{"module":"node-tools","version":"1","tool-runner":true,"waits":["reviewed"]}`: "does not name an emitter and an event", + `{"module":"node-tools","version":"1","tool-runner":true,"waits":["mesh-knowledge..reviewed"]}`: "does not name an emitter and an event", + `{"module":"node-tools","version":"1","tool-runner":true,"waits":["Mesh-Knowledge.reviewed x"]}`: "does not name an emitter and an event", + `{"module":"node-tools","version":"1","tool-runner":true,"waits":["a.b","a.b"]}`: "twice", } { _, err := ParseManifest([]byte(manifest)) if err == nil { diff --git a/modules/mesh-grants/grants_test.go b/modules/mesh-grants/grants_test.go index 17e8db89..90ffeaed 100644 --- a/modules/mesh-grants/grants_test.go +++ b/modules/mesh-grants/grants_test.go @@ -11,6 +11,7 @@ import ( "maps" "os" "path/filepath" + "regexp" "slices" "strings" "sync" @@ -2140,26 +2141,27 @@ func TestGrants(t *testing.T) { }) // ADR 0590 clause 5's test for this repository (issue 1126): no module.json here names mesh-controller, the lease - // read and the builder's consume of artifacts-gone included (mesh-sdk catalogue.ManifestRefusals). + // read and the builder's consume of artifacts-gone included. Read as text, as mesh-catalog's controllerdeps check reads + // its own: mesh-sdk names no module, so it holds no such check (issue 774, the operator 2026-11-03). t.Run("no module of this repository names the controller", func(t *testing.T) { paths, _ := filepath.Glob("../*/module.json") - manifests := map[string]catalogue.Manifest{} + if len(paths) < 8 { + t.Fatalf("read %d manifests, want every module of the repository", len(paths)) + } + names := regexp.MustCompile(`(^|[^A-Za-z0-9_-])(seat:)?mesh-controller([._"]|$)`) for _, path := range paths { raw, err := os.ReadFile(path) if err != nil { t.Fatal(err) } - m, err := catalogue.Decode(raw) - if err != nil { + if _, err := catalogue.Decode(raw); err != nil { t.Fatalf("%s: %v", path, err) } - manifests[m.Module] = m - } - if len(manifests) < 8 { - t.Fatalf("read %d manifests, want every module of the repository", len(manifests)) - } - for _, why := range catalogue.ManifestRefusals(manifests) { - t.Error(why) + for i, line := range strings.Split(string(raw), "\n") { + if names.MatchString(line) { + t.Errorf("%s:%d names the ended controller: %s", path, i+1, strings.TrimSpace(line)) + } + } } }) diff --git a/modules/mesh-grants/internal/buscompose/health_grants.go b/modules/mesh-grants/internal/buscompose/health_grants.go index 29036e94..7b06c49a 100644 --- a/modules/mesh-grants/internal/buscompose/health_grants.go +++ b/modules/mesh-grants/internal/buscompose/health_grants.go @@ -45,6 +45,9 @@ func ControllerLeftoverConsumersOn(nodes []string) []string { return out } +// endedController is the name the ended controller had as a module: its buckets are named for it (ADR 0590 §6). +const endedController = "mesh-controller" + // ControllerLeftoverBuckets are the ended controller's buckets, each by its local name (`KV_mesh-controller_`). var ControllerLeftoverBuckets = []string{"asked", "assignments", "builds", "calls", "checks-running", "composed", "condition-history", "conditions", "declared-config", "declared-packages", "delivery-merges", "grants", "hand-acts", @@ -55,7 +58,7 @@ var ControllerLeftoverBuckets = []string{"asked", "assignments", "builds", "call func ControllerLeftoverStreams(nodes []string) []string { out := []string{"CONTROL", AssignmentsStream} for _, b := range ControllerLeftoverBuckets { - out = append(out, "KV_"+BucketName(catalogue.EndedControllerName, b)) + out = append(out, "KV_"+BucketName(endedController, b)) } for _, n := range nodes { if safeSubject.MatchString(n) {