From 28c7f05b3989dad95e7484f5f31c1d212926d9f6 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 2 Oct 2026 23:13:22 +0200 Subject: [PATCH] A resolved manifest keeps a built reference in the store's own form, never the address it was reached by (hq ADR 0155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first bundle resolved on the mesh carried the store's host in bundles[0].source, and registration refused node-tools as naming an installation — rightly. The build record already keeps the store-relative form; the resolved manifest now keeps the same for bundles and archive resources, and composition routes it through the store a machine reaches, as it already did for a kept one. --- internal/catalogue/build.go | 10 ++++- .../resolve_keeps_references_test.go | 42 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 internal/catalogue/resolve_keeps_references_test.go diff --git a/internal/catalogue/build.go b/internal/catalogue/build.go index 7caa350..db38f6a 100644 --- a/internal/catalogue/build.go +++ b/internal/catalogue/build.go @@ -84,8 +84,13 @@ func (m Manifest) Resolve(built []Built) (Manifest, error) { if a.Loads == nil && len(m.Tools) > 0 { loads = append([]string(nil), a.Entrypoints...) } + // **Kept, never routed** (ADR 0155): the builder publishes to the store at the address + // it reached it by, and a manifest carrying that address names an installation — + // registration refused node-tools for exactly this on 2026-10-02. The build record + // already keeps the store-relative form; the resolved manifest keeps the same, and + // composition routes it through the store a machine reaches (Routed). out.Bundles = append(out.Bundles, Bundle{ - Name: a.Name, Source: made.Reference, Digest: made.Digest, + Name: a.Name, Source: Recorded(made.Reference), Digest: made.Digest, Language: a.Language, Entrypoints: append([]string(nil), a.Entrypoints...), Loads: loads, }) @@ -135,7 +140,8 @@ func (m Manifest) Resolve(built []Built) (Manifest, error) { // The same on the wire: both are bytes fetched by digest and unpacked. They differ in // how they were made — one packed as it stood, the other compiled first — and a // machine has no reason to care which. - filled["source"] = artifact.Reference + // Kept, not routed, for the reason the bundles above are (ADR 0155). + filled["source"] = Recorded(artifact.Reference) filled["digest"] = artifact.Digest // **And `${version}`, so a resource can name a place that is this build's alone** // (novox/hq ADR 0141, 04-ISSUES/142). A component is unpacked into a directory named diff --git a/internal/catalogue/resolve_keeps_references_test.go b/internal/catalogue/resolve_keeps_references_test.go new file mode 100644 index 0000000..24eb07c --- /dev/null +++ b/internal/catalogue/resolve_keeps_references_test.go @@ -0,0 +1,42 @@ +package catalogue + +import ( + "strings" + "testing" +) + +// A resolved manifest keeps a built artifact's reference in the store-relative form, never the +// address the builder reached the store by (novox/hq ADR 0155): on 2026-10-02 the first bundle +// resolved on the mesh carried the store's host in bundles[0].source and registration refused it +// as naming an installation. Archive resources are the same kind of reference and get the same. +func TestAResolvedReferenceIsKeptNotRouted(t *testing.T) { + m, err := ParseManifest([]byte(`{ + "module": "sample", "version": "1", + "tools": ["one"], + "build": {"artifacts": [ + {"name": "code", "kind": "bundle", "language": "typescript", "entrypoints": ["tools/index.js"]}, + {"name": "files", "kind": "archive", "from": "files"} + ]}, + "resources": [{"id": "packed", "type": "archive", "path": "/opt/sample", "artifact": "files"}] + }`)) + if err != nil { + t.Fatal(err) + } + digest := "sha256:" + strings.Repeat("ab", 32) + resolved, err := m.Resolve([]Built{ + {Name: "code", Kind: ArtifactBundle, Reference: "http://store.example:5100/v2/sample/code/blobs/" + digest, Digest: digest}, + {Name: "files", Kind: ArtifactArchive, Reference: "http://store.example:5100/v2/sample/files/blobs/" + digest, Digest: digest}, + }) + if err != nil { + t.Fatal(err) + } + if got, want := resolved.Bundles[0].Source, ArtifactStoreScheme+"sample/code/blobs/"+digest; got != want { + t.Errorf("bundle source %q, want the kept form %q", got, want) + } + if got, want := resolved.Resources[0]["source"], ArtifactStoreScheme+"sample/files/blobs/"+digest; got != want { + t.Errorf("archive source %q, want the kept form %q", got, want) + } + if problems := InstallationProblems(resolved); len(problems) != 0 { + t.Errorf("a resolved manifest names an installation: %v", problems) + } +}