mesh-build-go 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 7a143d1f22
commit 9b12dfaf21
8 changed files with 33 additions and 40 deletions
@@ -92,7 +92,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)
}
@@ -239,8 +239,8 @@ func TestABuildOutputDirectoryLinkedOutsideTheModuleIsRefused(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; claude-code's real manifest (capturedClaudeCodeManifest) is the fixture.
// 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); claude-code's real manifest (capturedClaudeCodeManifest) is the fixture.
func TestAManifestFallingBackToAPredefinedKeyBuilds(t *testing.T) {
raw := []byte(capturedClaudeCodeManifest)
if !strings.Contains(string(raw), `"predefined": "agent-managed-settings"`) {
@@ -16,15 +16,26 @@ const (
moduleDir = "../.."
)
func readManifest(t *testing.T) catalogue.Manifest {
// 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()
m, err := catalogue.ReadTreeManifest(catalogueRoot, moduleDir)
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
}
func readManifest(t *testing.T) catalogue.Manifest {
t.Helper()
return decodeTree(t, moduleDir)
}
func resource(t *testing.T, m catalogue.Manifest, id string) map[string]any {
t.Helper()
for _, r := range m.Resources {
@@ -36,13 +47,10 @@ func resource(t *testing.T, m catalogue.Manifest, id string) map[string]any {
return nil
}
// 1483/AC1: mesh-sdk's validator accepts the module (alone on a shelf too), and it runs as mesh-build-go with its
// home at mode 0700, requires user-namespaces and declares the four packages.
func TestManifestIsValidAndDeclaresTheAccountTheCapabilityAndThePackages(t *testing.T) {
// 1483/AC1: the module runs as mesh-build-go with its home at mode 0700, requires user-namespaces and declares the four
// packages.
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-go" || m.RunsAs != "mesh-build-go" {
t.Errorf("module %q runs as %q, want mesh-build-go as itself", m.Module, m.RunsAs)
}
@@ -128,22 +136,10 @@ func TestTheToolsServedAreTheToolsTheManifestNames(t *testing.T) {
}
}
// 1482/AC4: gitea's manifest reads the keys state of both build modules, and the catalogue's validator accepts it with the
// build modules beside it: each owner present declares the state read (a read of a state its owner does not keep is
// refused).
func TestGiteaReadsTheBuildModulesKeysAndTheCatalogueAcceptsThemTogether(t *testing.T) {
var all []catalogue.Manifest
var gitea catalogue.Manifest
for _, name := range []string{"gitea", "mesh-build-go", "mesh-build-node"} {
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
}
}
// 1482/AC4: gitea's manifest reads the keys state of both build modules (whether a read names a state its owner keeps is
// the judge's, ADR 0578 §2).
func TestGiteaReadsTheBuildModulesKeys(t *testing.T) {
gitea := decodeTree(t, filepath.Join(catalogueRoot, "modules", "gitea"))
for _, want := range []string{"mesh-build-go.keys", "mesh-build-node.keys", "ssh-client.keys"} {
if !slices.Contains(gitea.Reads, want) {
t.Errorf("gitea reads %v, lacks %s", gitea.Reads, want)
@@ -171,8 +167,8 @@ func TestTheModuleRequiresTheArtifactStoreAndIsToldItsAddress(t *testing.T) {
t.Errorf("requires %v lack artifact-store", m.Requires)
}
r := resource(t, m, "registry")
// The validator reads the template (files/registry) into the resource's content.
if content, _ := r["content"].(string); r["type"] != "file" || r["path"] != "${dir:state}/registry" || !strings.Contains(content, "${seat:mesh-artifact-store:reach}") {
// The template (files/registry) is read below; decoding leaves it unread in the resource.
if r["type"] != "file" || r["path"] != "${dir:state}/registry" || r["template"] != "files/registry" {
t.Errorf("the registry resource is %v", r)
}
raw, err := os.ReadFile(filepath.Join(moduleDir, "files", "registry"))
@@ -9,7 +9,6 @@ import (
"strconv"
"testing"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
@@ -134,9 +133,6 @@ func TestTheUnitLevelIsNotGivenTheNodeToolchains(t *testing.T) {
// 1891 (ADR 0533 §1, §2): the place bound is the Node build module's own: its manifest's cache directory joined with
// toolchain, and that cache directory lets other accounts enter it without listing it (0711).
func TestTheNodeToolchainsAreTheNodeBuildModulesCache(t *testing.T) {
if _, err := catalogue.ReadTreeManifest(catalogueRoot, filepath.Join(catalogueRoot, "modules", "mesh-build-node")); err != nil {
t.Fatalf("mesh-sdk's validator refuses mesh-build-node's manifest: %v", err)
}
raw, err := os.ReadFile(filepath.Join(catalogueRoot, "modules", "mesh-build-node", "module.json"))
if err != nil {
t.Fatal(err)
@@ -1,7 +1,6 @@
# 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), gitea's and mesh-build-node's.
predefined.json
# the manifests gitea's and mesh-build-node's, decoded.
modules/gitea
modules/mesh-build-node
@@ -8,15 +8,15 @@ import (
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// Issue 1657/AC1 unit: the manifest parse build.go uses reads a requires entry that names shares. The fixture is
// Issue 1657/AC1 unit: the manifest decode build.go uses reads a requires entry that names shares. The fixture is
// the shape of mesh-sdk's go/catalogue/bucket_shares_test.go (filesWithShares). The shares are checked through the
// manifest's stored form, so the test compiles against a pin that predates them and fails on the assertion.
func TestBuildReadsManifestWithShares(t *testing.T) {
raw := []byte(`{"module":"files","version":"1","requires":["shell",
{"name":"s3-bucket","shares":[{"to":"seat:node-files","methods":["put"]}]}]}`)
m, err := catalogue.ParseManifest(raw)
m, err := catalogue.Decode(raw)
if err != nil {
t.Fatalf("the manifest is refused: %v", err)
t.Fatalf("the manifest does not decode: %v", err)
}
stored, err := json.Marshal(m)
if err != nil {
+1 -1
View File
@@ -3,7 +3,7 @@ module meshbuildgo
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031084708-acbf80ad910a
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101012031-7cab079e2ec3
github.com/distribution/distribution/v3 v3.1.2
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.20261031084708-acbf80ad910a h1:DcSvfEmdPBKqCg5OmXRNb269EkkPe7BsEp+RykEhkgE=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031084708-acbf80ad910a/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/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=