From 2877b87f296c4ff62526c4af6d3c65d4eb3ff99b Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 2 Nov 2026 00:21:13 +0100 Subject: [PATCH] =?UTF-8?q?A=20catalogue=20test=20refuses=20a=20manifest?= =?UTF-8?q?=20that=20still=20depends=20on=20the=20controller=20at=20the=20?= =?UTF-8?q?lease=20write=20(ADR=200571=20=C2=A713)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit checks/controllerdeps decodes every module.json and judges the set with mesh-sdk's ManifestRefusals at the phase of the lease write's own delivery, so no module reads, invokes or consumes the controller beyond the lease and the entries kept to the write. At origin/main it refuses 11 declarations (the ones S7-S12 drop); at this head it passes. Two tests (the real tree, and four rows on a tree of its own), in a new check of its own; go.mod pins the previous mesh-sdk until the group's head is pinned, and go.sum needs the group's entries then. (issue 774, 774/S13) Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL --- checks/controllerdeps/controllerdeps.go | 39 ++++++++++++++ checks/controllerdeps/controllerdeps_test.go | 56 ++++++++++++++++++++ checks/controllerdeps/go.mod | 5 ++ checks/controllerdeps/go.sum | 2 + checks/controllerdeps/reads-outside | 3 ++ 5 files changed, 105 insertions(+) create mode 100644 checks/controllerdeps/controllerdeps.go create mode 100644 checks/controllerdeps/controllerdeps_test.go create mode 100644 checks/controllerdeps/go.mod create mode 100644 checks/controllerdeps/go.sum create mode 100644 checks/controllerdeps/reads-outside diff --git a/checks/controllerdeps/controllerdeps.go b/checks/controllerdeps/controllerdeps.go new file mode 100644 index 00000000..9c9177f3 --- /dev/null +++ b/checks/controllerdeps/controllerdeps.go @@ -0,0 +1,39 @@ +// Package controllerdeps is the catalogue's side of the lease write's manifest test (novox/hq ADR 0571 §13, ADR 0582 +// clause 2, issue 774): no module of this tree reads, invokes, consumes, waits on or uses the controller beyond what the +// named list of mesh-sdk's catalogue.KeepList keeps to the write itself, and the lease key, which names the lease's holder +// whoever that is. It decodes every manifest under root/modules and judges them with mesh-sdk's ManifestRefusals, the +// judge the grants part's grants_lease_check and the other two repositories' tests run, so the rule is said once. +package controllerdeps + +import ( + "fmt" + "os" + "path/filepath" + "sort" + + "git.novox.be/novox/mesh-sdk/go/catalogue" +) + +// Refusals is each reason the lease write's check gives over the manifests under root/modules in one phase +// (catalogue.PhaseBefore or catalogue.PhaseWrite), none when it passes. A module.json that does not decode is an error: +// the manifests check says which, and nothing here is judged on a tree it cannot read. +func Refusals(root, phase string) ([]string, error) { + files, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json")) + if err != nil { + return nil, err + } + sort.Strings(files) + manifests := map[string]catalogue.Manifest{} + for _, f := range files { + raw, err := os.ReadFile(f) + if err != nil { + return nil, err + } + m, err := catalogue.Decode(raw) + if err != nil { + return nil, fmt.Errorf("%s: %w", f, err) + } + manifests[m.Module] = m + } + return catalogue.ManifestRefusals(manifests, phase), nil +} diff --git a/checks/controllerdeps/controllerdeps_test.go b/checks/controllerdeps/controllerdeps_test.go new file mode 100644 index 00000000..5f5cd28e --- /dev/null +++ b/checks/controllerdeps/controllerdeps_test.go @@ -0,0 +1,56 @@ +package controllerdeps + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "git.novox.be/novox/mesh-sdk/go/catalogue" +) + +// 774/S13/AC1: every manifest of the catalogue at this head, at the phase of the lease write's own delivery, reads, +// invokes and consumes nothing of the controller beyond the lease and the entries kept to the write. +func TestNoManifestDependsOnTheControllerBeyondTheLeaseAndTheEntriesKeptToTheWrite(t *testing.T) { + got, err := Refusals("../..", catalogue.PhaseWrite) + if err != nil { + t.Fatal(err) + } + if len(got) > 0 { + t.Errorf("the catalogue still declares on the controller (novox/hq ADR 0571 §13):\n%s", strings.Join(got, "\n")) + } +} + +// The check refuses what it must and lets the lease and the kept entries through, on a tree of its own. +func TestRefusalsJudgeATreeByPhase(t *testing.T) { + for _, row := range []struct { + name, phase string + manifests map[string]string + want string // empty: passes + }{ + {"the lease is no dependency", catalogue.PhaseWrite, map[string]string{"a": `{"module":"a","reads":["mesh-controller.lease"]}`}, ""}, + {"a read of the controller's states", catalogue.PhaseWrite, map[string]string{"a": `{"module":"a","reads":["mesh-controller.conditions"]}`}, "a declares read mesh-controller.conditions"}, + {"a kept entry passes before the delivery", catalogue.PhaseBefore, map[string]string{"artifact-store-tools": `{"module":"artifact-store-tools","invokes":["seat:mesh-controller.collect"]}`}, ""}, + {"a kept-to-the-write entry passes at the write", catalogue.PhaseWrite, map[string]string{"artifact-store-tools": `{"module":"artifact-store-tools","invokes":["seat:mesh-controller.collect"]}`}, ""}, + } { + t.Run(row.name, func(t *testing.T) { + root := t.TempDir() + for module, text := range row.manifests { + dir := filepath.Join(root, "modules", module) + if err := os.MkdirAll(dir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "module.json"), []byte(text), 0o644); err != nil { + t.Fatal(err) + } + } + got, err := Refusals(root, row.phase) + if err != nil { + t.Fatal(err) + } + if joined := strings.Join(got, "\n"); (row.want == "") != (joined == "") || !strings.Contains(joined, row.want) { + t.Errorf("refusals %q, want %q", joined, row.want) + } + }) + } +} diff --git a/checks/controllerdeps/go.mod b/checks/controllerdeps/go.mod new file mode 100644 index 00000000..5974999f --- /dev/null +++ b/checks/controllerdeps/go.mod @@ -0,0 +1,5 @@ +module checks/controllerdeps + +go 1.26.0 + +require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101022206-8af6e7266915 diff --git a/checks/controllerdeps/go.sum b/checks/controllerdeps/go.sum new file mode 100644 index 00000000..2e60a9c1 --- /dev/null +++ b/checks/controllerdeps/go.sum @@ -0,0 +1,2 @@ +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101022206-8af6e7266915 h1:x8k7IeMirMTxUo8BH+xUO+MDnHYgU+636jl1NIw1wvs= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101022206-8af6e7266915/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= diff --git a/checks/controllerdeps/reads-outside b/checks/controllerdeps/reads-outside new file mode 100644 index 00000000..efc1a11c --- /dev/null +++ b/checks/controllerdeps/reads-outside @@ -0,0 +1,3 @@ +# What these tests read outside their package, as paths from the repository's root (issue 1936). +# every module's directory (its manifest), decoded and judged for what it still declares on the controller. +modules