From 9017edaf9ec5d49c3bb57834471759a5fb43de0c Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 14:41:28 +0100 Subject: [PATCH] =?UTF-8?q?seatgen:=20a=20client=20may=20import=20its=20co?= =?UTF-8?q?ntracts=20module's=20domain=20SDK=20(-domain-sdk;=20ADR=200634?= =?UTF-8?q?=20=C2=A72;=20issue=201966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- modules/mesh-build-go/buildstep/cmd/seatgen/main.go | 2 ++ modules/mesh-build-go/buildstep/seatgen/clients.go | 4 +++- .../mesh-build-go/buildstep/seatgen/clients_test.go | 11 +++++++++++ modules/mesh-build-go/buildstep/seatgen/load.go | 3 +++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/mesh-build-go/buildstep/cmd/seatgen/main.go b/modules/mesh-build-go/buildstep/cmd/seatgen/main.go index 1aca2a3d1..f550b911e 100644 --- a/modules/mesh-build-go/buildstep/cmd/seatgen/main.go +++ b/modules/mesh-build-go/buildstep/cmd/seatgen/main.go @@ -15,6 +15,7 @@ func main() { check := flag.Bool("check", false, "compare instead of writing: name every path out of step") goModule := flag.String("go-module", "", "the generated Go tree's module path, such as git.novox.be/novox/mesh-contracts//go (there is no root module)") tsPackage := flag.String("ts-package", "", "the generated TypeScript package's name, such as @novox/mesh-contracts-") + domainSDK := flag.String("domain-sdk", "", "the Go module of the contracts module's domain SDK, which a client may import (ADR 0634 §2); empty when it has none") flag.Parse() dir := flag.Arg(0) m, err := seatgen.Load(dir, *goModule, *tsPackage) @@ -22,6 +23,7 @@ func main() { fmt.Fprintln(os.Stderr, err) os.Exit(1) } + m.DomainSDK = *domainSDK files, err := seatgen.Generate(m) if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/modules/mesh-build-go/buildstep/seatgen/clients.go b/modules/mesh-build-go/buildstep/seatgen/clients.go index 7a83a0106..4a56a0d6b 100644 --- a/modules/mesh-build-go/buildstep/seatgen/clients.go +++ b/modules/mesh-build-go/buildstep/seatgen/clients.go @@ -33,7 +33,7 @@ var toolAddress = re.MustCompile(`^[a-z][a-z0-9-]*\.[a-z0-9]+_[a-z0-9_]+$`) // CheckClients is ADR 0627 §3's catalog check of the clients, and ADR 0625 §5's rule for the files beside them: every // directory under ClientRoot is the client of one seat defined whole, a Go module of its own at ClientGoModule; it -// imports only the standard library, its own seat's contract module and mesh-sdk's runtime (never another seat's +// imports only the standard library, its own seat's contract module, mesh-sdk's runtime and its domain's SDK (never another seat's // contract or client); and no string in it names a module's subject (`mesh.mod.`), a module's tool, or another seat // (its name, its subjects, its `seat:` call). Each problem is named once, sorted. func CheckClients(dir string, m *Module) ([]string, error) { @@ -111,6 +111,8 @@ func checkClient(dir, at, seat string, m *Module) ([]string, error) { switch { case !strings.Contains(strings.SplitN(path, "/", 2)[0], "."): case path == own: + case m.DomainSDK != "" && (path == m.DomainSDK || strings.HasPrefix(path, m.DomainSDK+"/")): + // the domain's SDK (ADR 0634 §2) case path == RuntimeModule || strings.HasPrefix(path, RuntimeModule+"/"): case path == ClientGoModule(m.GoModule, seat) || strings.HasPrefix(path, ClientGoModule(m.GoModule, seat)+"/"): // the client's own packages: a client may be several packages of its module diff --git a/modules/mesh-build-go/buildstep/seatgen/clients_test.go b/modules/mesh-build-go/buildstep/seatgen/clients_test.go index 787636ba3..6f23fa424 100644 --- a/modules/mesh-build-go/buildstep/seatgen/clients_test.go +++ b/modules/mesh-build-go/buildstep/seatgen/clients_test.go @@ -14,6 +14,7 @@ func TestAClientUsesOnlyItsSeatAndTheRuntime(t *testing.T) { if err != nil { t.Fatal(err) } + m.DomainSDK = "example.org/domain/sdk" files, err := Generate(m) if err != nil { t.Fatal(err) @@ -68,6 +69,16 @@ import "example.org/contracts/go/clients/aregistryclient/provider" var _ = provider.X `, }, artifacts(clientArtifact), nil}, + {"a client importing its domain's SDK, which another module's is not", map[string]string{ + "go/clients/aregistryclient/go.mod": "module example.org/contracts/go/clients/aregistryclient\n", + "go/clients/aregistryclient/client.go": `package aregistryclient + +import ( + "example.org/domain/sdk/reference" + "example.org/other/sdk/reference" +) +`, + }, artifacts(clientArtifact), []string{`client.go: imports example.org/other/sdk/reference: a client uses only`}}, {"a client of no seat defined whole, and one not a module of its own", map[string]string{ "go/clients/onestoreclient/x.go": "package onestoreclient\n", "go/clients/aregistryclient/go.mod": "module example.org/contracts/go\n", diff --git a/modules/mesh-build-go/buildstep/seatgen/load.go b/modules/mesh-build-go/buildstep/seatgen/load.go index 572ef8018..697dba1c0 100644 --- a/modules/mesh-build-go/buildstep/seatgen/load.go +++ b/modules/mesh-build-go/buildstep/seatgen/load.go @@ -21,6 +21,9 @@ type Module struct { GoModule string // TSPackage is the npm package's name. TSPackage string + // DomainSDK is the Go module path of the contracts module's domain SDK (ADR 0634 §2), which a seat's client may + // import beside its contract and mesh-sdk's runtime; empty when the domain has none. + DomainSDK string // Order is every seat's name, sorted: a contracts module has no seat list and no reading order (ADR 0635 §1). Order []string // Seats are every definition, resolved, by name.