From 7a86c624928a99f972ba3e16e9c1c20e28848ac2 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 27 Oct 2026 22:50:32 +0100 Subject: [PATCH] =?UTF-8?q?mesh-delivery-checker=20claims=20its=20seat=20a?= =?UTF-8?q?nd=20serves=20try,=20try-read=20and=20try-end=20by=20the=20tool?= =?UTF-8?q?s'=20own=20code,=20so=20the=20try=20outlives=20the=20controller?= =?UTF-8?q?=20(ADR=200547=20=C2=A710,=20issue=20774)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cmd/mesh-delivery-checker-tools/main.go | 17 ++++++++- .../mesh-delivery-checker-tools/main_test.go | 38 +++++++++++++++++++ modules/mesh-delivery-checker/go.mod | 2 +- modules/mesh-delivery-checker/go.sum | 4 +- .../mesh-delivery-checker/manifest_test.go | 12 +++++- modules/mesh-delivery-checker/module.json | 11 ++++++ 6 files changed, 79 insertions(+), 5 deletions(-) diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker-tools/main.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker-tools/main.go index a6a6936..b5bf8a1 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker-tools/main.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker-tools/main.go @@ -170,6 +170,21 @@ func TryTools(tr Trying) []stdio.Tool { } } +// Seat is the seat the checker holds (novox/hq ADR 0547 §10, issue 774); its verbs are served as tools named .. +const Seat = "mesh-delivery-checker" + +// SeatTryTools are the try's tools as the seat's verbs: try, try-read and try-end, run by the same code as checker_try, +// checker_try_read and checker_try_end, so the answer is the same. The module tools stay beside them. +func SeatTryTools(tr Trying) []stdio.Tool { + verbs := map[string]string{check.TryTool: "try", check.TryReadTool: "try-read", check.TryEndTool: "try-end"} + var out []stdio.Tool + for _, t := range TryTools(tr) { + t.Name = Seat + "." + verbs[t.Name] + out = append(out, t) + } + return out +} + // Tools are the read tools over the checker's states. func Tools(r check.Reader) []stdio.Tool { return []stdio.Tool{ @@ -277,7 +292,7 @@ func main() { return err }} trying := Trying{Tries: store, Ender: store, Trunks: trunks} - if err := stdio.Serve("", logged(append(Tools(r), TryTools(trying)...))); err != nil { + if err := stdio.Serve("", logged(append(append(Tools(r), TryTools(trying)...), SeatTryTools(trying)...))); err != nil { fmt.Fprintf(os.Stderr, "[mesh-delivery-checker-tools] %v\n", err) os.Exit(1) } diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker-tools/main_test.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker-tools/main_test.go index 9f250df..34315b6 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker-tools/main_test.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker-tools/main_test.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "errors" + "reflect" "slices" "strings" "testing" @@ -93,3 +94,40 @@ func TestTheTryToolsAskAndReadATry(t *testing.T) { // ADR 0501 §5, the agreed i t.Errorf("hq: %v", err) } } + +// The seat's verbs answer as the tools do (ADR 0547 §10, issue 774): the same ask gives the same try, read and ended alike. +func TestTheSeatVerbsAnswerLikeTheTools(t *testing.T) { + store := &memStore{vals: map[string]json.RawMessage{}} + tr := Trying{Tries: store, Ender: store, Now: func() time.Time { return time.Unix(1, 5) }, + Trunks: func() (map[string]string, error) { + return map[string]string{"novox/mesh-delivery": strings.Repeat("a", 40)}, nil + }} + tools, verbs := TryTools(tr), SeatTryTools(tr) + if len(verbs) != len(tools) { + t.Fatalf("%d verbs for %d tools", len(verbs), len(tools)) + } + for i, want := range []string{"try", "try-read", "try-end"} { + if verbs[i].Name != Seat+"."+want { + t.Errorf("verb %d = %s", i, verbs[i].Name) + } + } + ask := map[string]any{"repository": "novox/mesh-delivery", "head": strings.Repeat("1", 40), "trunk": "main"} + a, err := tools[0].Run(ask) + if err != nil { + t.Fatal(err) + } + b, err := verbs[0].Run(ask) // a second ask of a head that waits is the first's answer + if err != nil || !reflect.DeepEqual(a, b) { + t.Errorf("verb try = %v, %v; tool = %v", b, err, a) + } + id := a.(map[string]any)["id"].(string) + ra, _ := tools[1].Run(map[string]any{"id": id}) + rb, err := verbs[1].Run(map[string]any{"id": id}) + if err != nil || !reflect.DeepEqual(ra, rb) { + t.Errorf("verb try-read = %v, %v; tool = %v", rb, err, ra) + } + if _, err := verbs[0].Run(map[string]any{"repository": "novox/hq", "head": strings.Repeat("1", 40)}); err == nil || + err.Error() != "no level of novox/hq is checked by the checker" { + t.Errorf("hq: %v", err) + } +} diff --git a/modules/mesh-delivery-checker/go.mod b/modules/mesh-delivery-checker/go.mod index c34164e..91ad8cd 100644 --- a/modules/mesh-delivery-checker/go.mod +++ b/modules/mesh-delivery-checker/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-checker go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027193820-8ad9372b1952 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027214944-9fdd33b03edc github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-checker/go.sum b/modules/mesh-delivery-checker/go.sum index fa4f833..03a9f24 100644 --- a/modules/mesh-delivery-checker/go.sum +++ b/modules/mesh-delivery-checker/go.sum @@ -1,5 +1,5 @@ -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027193820-8ad9372b1952 h1:TmveT3Bija643RGWkilvkzP/h8UWlVvHFZZtQfJK5oU= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027193820-8ad9372b1952/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027214944-9fdd33b03edc h1:3/wLzFyz4vJvt2TqDNGvLt7X9gF6JVBBPSS8l6+z6Zs= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027214944-9fdd33b03edc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-delivery-checker/manifest_test.go b/modules/mesh-delivery-checker/manifest_test.go index 815ce91..55585a0 100644 --- a/modules/mesh-delivery-checker/manifest_test.go +++ b/modules/mesh-delivery-checker/manifest_test.go @@ -38,6 +38,11 @@ type stream struct { } type manifest struct { + Claims []struct { + Name string `json:"name"` + Scope string `json:"scope"` + Serves []string `json:"serves"` + } `json:"claims"` Streams []stream `json:"streams"` Tools []string `json:"tools"` Module string `json:"module"` @@ -128,7 +133,12 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1 if want := []string{"checker_kept", "checker_previews", "checker_pull", "checker_status", "checker_try", "checker_try_end", "checker_try_read", "checker_verdicts"}; !reflect.DeepEqual(sorted(m.Tools), want) { t.Errorf("tools = %v, want %v", m.Tools, want) } - for _, k := range []string{"logs", "emits", "claims", "provides", "requires", "defines-seats", "seat", "seats", "bus", "config"} { + // The try is the checker's own seat's verbs (ADR 0547 §10, issue 774), served beside the tools by the same code. + if len(m.Claims) != 1 || m.Claims[0].Name != "mesh-delivery-checker" || m.Claims[0].Scope != "mesh" || + !reflect.DeepEqual(m.Claims[0].Serves, []string{"try", "try-read", "try-end"}) { + t.Errorf("claims = %+v, want the mesh seat mesh-delivery-checker serving try, try-read and try-end", m.Claims) + } + for _, k := range []string{"logs", "emits", "provides", "requires", "defines-seats", "seat", "seats", "bus", "config"} { if _, ok := top[k]; ok { t.Errorf("the manifest declares %q: the checker has none", k) } diff --git a/modules/mesh-delivery-checker/module.json b/modules/mesh-delivery-checker/module.json index a968770..8f455ef 100644 --- a/modules/mesh-delivery-checker/module.json +++ b/modules/mesh-delivery-checker/module.json @@ -2,6 +2,17 @@ "module": "mesh-delivery-checker", "version": "1", "slug": "checker", + "claims": [ + { + "name": "mesh-delivery-checker", + "scope": "mesh", + "serves": [ + "try", + "try-read", + "try-end" + ] + } + ], "consumes": [ "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported"