mesh-build-image decodes the manifest it builds from and judges none (ADR 0578 §3)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-11-01 02:29:48 +01:00
parent 9b12dfaf21
commit a924397765
7 changed files with 28 additions and 37 deletions
@@ -126,7 +126,7 @@ func (m *Module) build(ctx context.Context, args map[string]any) (any, error) {
if err != nil {
return nil, fmt.Errorf("no module in %q of workspace %s: %w", module, workspace, err)
}
manifest, err := catalogue.ParseManifestForInputs(raw)
manifest, err := catalogue.Decode(raw)
if err != nil {
return nil, fmt.Errorf("the manifest in %q: %w", module, err)
}
@@ -276,8 +276,8 @@ func TestBuildFailsForWhatIsNotAnImageOrNotThere(t *testing.T) {
}
// Issue 1751 (ADR 0514 §2, the review's K2): a manifest whose config value falls back to a predefined key builds. The
// builder holds no published list of the keys, so it reads the manifest for its inputs (ParseManifestForInputs), not
// as a judge of config values. The manifest is claude-code's real one (capturedClaudeCodeManifest) with the image fixture's
// builder holds no published list of the keys, so it decodes the manifest for its inputs (catalogue.Decode), and judges
// no config value (ADR 0578 §3). The manifest is claude-code's real one (capturedClaudeCodeManifest) with the image fixture's
// base and artifact added to its build, since no image module of the catalogue falls back to a predefined key today.
func TestAManifestFallingBackToAPredefinedKeyBuilds(t *testing.T) {
raw := []byte(capturedClaudeCodeManifest)
@@ -34,11 +34,7 @@ func TestTheModuleComposesWhereItsFilesAreHandedToTheProvisionersClient(t *testi
}
}
read := func(name string) catalogue.Manifest {
m, err := catalogue.ReadTreeManifestWritten(catalogueRoot, filepath.Join(catalogueRoot, "modules", name))
if err != nil {
t.Fatalf("modules/%s: %v", name, err)
}
return m
return decodeTree(t, filepath.Join(catalogueRoot, "modules", name))
}
image := read("mesh-build-image")
resolved, err := image.Resolve(built(image))
@@ -20,9 +20,20 @@ const (
func readManifest(t *testing.T) catalogue.Manifest {
t.Helper()
m, err := catalogue.ReadTreeManifest(catalogueRoot, moduleDir)
return decodeTree(t, moduleDir)
}
// decodeTree is the manifest in a module's directory, decoded and judged by nothing: the checker judges every change's
// manifests (novox/hq ADR 0578 §2), and a module's own tests read what it declares.
func decodeTree(t *testing.T, dir string) catalogue.Manifest {
t.Helper()
raw, err := os.ReadFile(filepath.Join(dir, "module.json"))
if err != nil {
t.Fatalf("mesh-sdk's validator refuses module.json: %v", err)
t.Fatal(err)
}
m, err := catalogue.Decode(raw)
if err != nil {
t.Fatalf("module.json does not decode: %v", err)
}
return m
}
@@ -38,13 +49,10 @@ func resource(t *testing.T, m catalogue.Manifest, id string) map[string]any {
return nil
}
// 1703a: the validator accepts the module (alone on a shelf too), it runs as mesh-build-image with its home at mode
// 0700, lists user-namespaces, and declares git and openssh; buildah is not its package (the buildah module's alone).
func TestManifestIsValidAndDeclaresTheAccountTheCapabilityAndThePackages(t *testing.T) {
// 1703a: the module runs as mesh-build-image with its home at mode 0700, lists user-namespaces, and declares git and
// openssh; buildah is not its package (the buildah module's alone).
func TestManifestDeclaresTheAccountTheCapabilityAndThePackages(t *testing.T) {
m := readManifest(t)
if problems := catalogue.CatalogueProblems(catalogue.Shelf{m.Module: m}); len(problems) > 0 {
t.Errorf("the catalogue's rules refuse it: %v", problems)
}
if m.Module != "mesh-build-image" || m.RunsAs != "mesh-build-image" {
t.Errorf("module %q runs as %q, want mesh-build-image as itself", m.Module, m.RunsAs)
}
@@ -164,21 +172,10 @@ func TestTheToolsServedAreTheToolsTheManifestNames(t *testing.T) {
}
}
// 1703f: gitea's manifest reads the keys state of mesh-build-image beside the other build modules', and the catalogue's
// validator accepts it with the owners present.
// 1703f: gitea's manifest reads the keys state of mesh-build-image beside the other build modules' (whether a read names
// a state its owner keeps is the judge's, ADR 0578 §2).
func TestGiteaReadsTheImageBuildModulesKeys(t *testing.T) {
var all []catalogue.Manifest
var gitea catalogue.Manifest
for _, name := range []string{"gitea", "mesh-build-go", "mesh-build-node", "mesh-build-image"} {
m, err := catalogue.ReadTreeManifest(catalogueRoot, filepath.Join(catalogueRoot, "modules", name))
if err != nil {
t.Fatalf("mesh-sdk's validator refuses modules/%s/module.json: %v", name, err)
}
all = append(all, m)
if name == "gitea" {
gitea = m
}
}
gitea := decodeTree(t, filepath.Join(catalogueRoot, "modules", "gitea"))
for _, want := range []string{"mesh-build-go.keys", "mesh-build-node.keys", "mesh-build-image.keys"} {
if !slices.Contains(gitea.Reads, want) {
t.Errorf("gitea reads %v, lacks %s", gitea.Reads, want)
@@ -197,10 +194,7 @@ func TestACompositionWithoutBuildahIsRefusedNamingItAndWithBuildahComposes(t *te
"runner": {Module: "runner", Version: "1", Provides: []catalogue.Offer{{Name: "node-tools"}}},
}
for _, name := range []string{"mesh-build-image", "buildah", "docker"} {
m, err := catalogue.ReadTreeManifest(catalogueRoot, filepath.Join(catalogueRoot, "modules", name))
if err != nil {
t.Fatalf("modules/%s: %v", name, err)
}
m := decodeTree(t, filepath.Join(catalogueRoot, "modules", name))
shelf[m.Module] = m
}
node := catalogue.Node{Name: "builder", Site: "house",
@@ -1,8 +1,7 @@
# What these tests read outside their package, as paths from the repository's root (a glob, or a directory read whole;
# ? before a path whose absence is read). mesh-build-go hashes them into the package's digest, so its verdict is kept
# until one of them changes (issue 1936). An empty list, or a line matching nothing, keeps nothing.
# the manifests through mesh-sdk's validator (the predefined keys), with the modules they compose with.
predefined.json
# the manifests of the modules they compose with, decoded.
modules/gitea
modules/mesh-build-go
modules/mesh-build-node
+1 -1
View File
@@ -3,7 +3,7 @@ module meshbuildimage
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030174137-a37181261c24
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101012031-7cab079e2ec3
github.com/nats-io/nats-server/v2 v2.11.17
github.com/nats-io/nats.go v1.54.0
)
+2
View File
@@ -1,5 +1,7 @@
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030174137-a37181261c24 h1:s0XXZtR7CFPBbnaDygWnE4/Q827iEFt0YfEzujk4Q9g=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030174137-a37181261c24/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101012031-7cab079e2ec3 h1:an2f0MLx9OhaDLu5i51OMPBbuGwX+aoTzqOkFLMpVfE=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101012031-7cab079e2ec3/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=