From cde22ff6272445980d95794166646a365bec6376 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 02:50:02 +0200 Subject: [PATCH] module check names a read of state its owner does not keep, and says what each module keeps and reads (novox/hq ADR 0201) --- cmd/mesh-controller/check.go | 12 ++++++++++++ internal/catalogue/seats_declared.go | 7 +++++++ internal/catalogue/state_test.go | 12 ++++++++++++ 3 files changed, 31 insertions(+) diff --git a/cmd/mesh-controller/check.go b/cmd/mesh-controller/check.go index 1b01554..dbd9225 100644 --- a/cmd/mesh-controller/check.go +++ b/cmd/mesh-controller/check.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "sort" + "strings" "github.com/novox/mesh-controller/internal/catalogue" ) @@ -90,6 +91,17 @@ func moduleCheck(paths []string, out io.Writer) error { if len(m.Invokes) > 0 { fmt.Fprintf(out, ", invokes %s", joinInvokes(m.Invokes)) } + // The state it keeps and reads (novox/hq ADR 0201), so a reviewer sees what lands on the bus. + if len(m.State) > 0 { + kept := make([]string, 0, len(m.State)) + for _, s := range m.State { + kept = append(kept, s.Name) + } + fmt.Fprintf(out, ", keeps state %s", strings.Join(kept, ", ")) + } + if len(m.Reads) > 0 { + fmt.Fprintf(out, ", reads %s", strings.Join(m.Reads, ", ")) + } fmt.Fprintln(out) } if failed > 0 { diff --git a/internal/catalogue/seats_declared.go b/internal/catalogue/seats_declared.go index f125cac..4afb221 100644 --- a/internal/catalogue/seats_declared.go +++ b/internal/catalogue/seats_declared.go @@ -215,6 +215,13 @@ func CatalogueProblems(shelf Shelf) []string { } } } + // A read of a module's state that module does not keep (novox/hq ADR 0201) — said only where the + // owner is on the shelf, as a consumer may be installed before its emitter. + var manifests []Manifest + for _, module := range shelfOrder(shelf) { + manifests = append(manifests, shelf[module]) + } + problems = append(problems, StateReadsNothingDeclares(manifests)...) sort.Strings(problems) return problems } diff --git a/internal/catalogue/state_test.go b/internal/catalogue/state_test.go index 4cf6206..6229787 100644 --- a/internal/catalogue/state_test.go +++ b/internal/catalogue/state_test.go @@ -80,3 +80,15 @@ func TestEveryManifestsStateIsLocalAndEveryReadIsKept(t *testing.T) { t.Fatalf("the catalogue's state is not what ADR 0201 says:\n %s", strings.Join(problems, "\n ")) } } + +// `module check` says it too: the cross-catalogue pass names a read nothing on the shelf keeps. +func TestTheCataloguePassNamesAReadItsOwnerDoesNotKeep(t *testing.T) { + shelf := Shelf{ + "licence-manager": {Module: "licence-manager", State: []StateDeclaration{{Name: "bindings"}}}, + "claude-code": {Module: "claude-code", Reads: []string{"licence-manager.tokens"}}, + } + problems := CatalogueProblems(shelf) + if len(problems) != 1 || !strings.Contains(problems[0], `keeps no state called "tokens"`) { + t.Fatalf("the catalogue pass said %v", problems) + } +}