From 7a8e9f9706486669e97705f1cdb6b2f6002e24f5 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 27 Oct 2026 21:12:13 +0100 Subject: [PATCH] Wait for a done key whose artifact names no reference, as the controller's take-in does, and hold the fold guard with a row (issue 1126, review round 1) --- go/compose/frombus/frombus_test.go | 22 ++++++++++++++++++++++ go/takein/takein.go | 16 +++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/go/compose/frombus/frombus_test.go b/go/compose/frombus/frombus_test.go index 8f14ebf..20a5ae5 100644 --- a/go/compose/frombus/frombus_test.go +++ b/go/compose/frombus/frombus_test.go @@ -43,9 +43,11 @@ type bus struct { fetched []string served map[string]map[string]any servedRead []string + buildsRead []string } func (b *bus) Build(_ context.Context, module, fp string) (*delivery.BuilderBuild, error) { + b.buildsRead = append(b.buildsRead, module) return b.builds[module+" "+fp], nil } @@ -587,6 +589,26 @@ func TestInput(t *testing.T) { if c := fromBuild[resticKey]; len(c.Filled) != 4 { // its three templated files and its own secret t.Errorf("restic is not handed to the provisioner: its filled files are %v", c.Filled) } + // A done key an artifact of which names no reference is waited for, in the controller's words (ADR 0498 §8). + unreferenced := movedMesh(true) + unreferenced.builds["restic "+movedFps["restic"]].Artifacts = []delivery.BuiltArtifact{{Name: "tools", Kind: "bundle", Digest: digestOf("6")}, resticArtifacts[1]} + if _, _, err := Compose(ctx, unreferenced, Mesh{}, Scope{Node: "n1"}, movedFps); err == nil || !strings.Contains(err.Error(), "the build of restic is done and waits for the reference of tools") { + t.Errorf("a done key with an unreferenced artifact: %v", err) + } + // A module the node runs inside its own composition (the fold) composes from the indexer's manifest though a done + // build of it is at the trunk's FP2: its build is not read. + folding := movedMesh(true) + foldIn, err := Input(ctx, folding, Mesh{}, Scope{Node: "n1", InOwn: []string{"mesh-provisioner-client"}}) + if err != nil { + t.Fatal(err) + } + clientParsed, err := catalogue.ParseManifestForInputs(clientRaw) + if err != nil { + t.Fatal(err) + } + if slices.Contains(folding.buildsRead, "mesh-provisioner-client") || !reflect.DeepEqual(foldIn.Catalogue["mesh-provisioner-client"], clientParsed) { + t.Errorf("a folded module was taken from its build: builds read %v", folding.buildsRead) + } // The controller's: its take-in resolves each manifest with the key's artifacts and the archive's texts, and composes // from those over the same input. storeIn, err := Input(ctx, movedMesh(false), Mesh{}, Scope{Node: "n1"}) diff --git a/go/takein/takein.go b/go/takein/takein.go index 0da03ed..657ab45 100644 --- a/go/takein/takein.go +++ b/go/takein/takein.go @@ -29,13 +29,27 @@ import ( // checked against its digest. type Archive func(ctx context.Context, reference, digest string) ([]byte, error) -// Manifest is m, as its manifest at b's fingerprint reads, resolved with what b made: the built manifest. b must be done. +// Manifest is m, as its manifest at b's fingerprint reads, resolved with what b made: the built manifest. b must be done +// and every artifact must name its reference. // A templated module's templates archive is read through archive; a module without templates reads none, and archive // may then be nil. func Manifest(ctx context.Context, m catalogue.Manifest, b delivery.BuilderBuild, archive Archive) (catalogue.Manifest, error) { if b.State != delivery.BuildDone { return catalogue.Manifest{}, fmt.Errorf("takein: the build of %s is %s, not done: nothing to resolve it with", m.Module, b.State) } + // **A done key whose artifact names no reference is waited for, never composed from** (novox/hq ADR 0498 §8), in the + // controller's words (mesh-controller internal/builderkeys Key.Says): a key done before R4e, or a candidate's image or + // package not yet built again for the trunk. + var missing []string + for _, a := range b.Artifacts { + if strings.TrimSpace(a.Reference) == "" { + missing = append(missing, a.Name) + } + } + if len(missing) > 0 { + return catalogue.Manifest{}, fmt.Errorf("takein: the build of %s is done and waits for the reference of %s", + m.Module, strings.Join(missing, ", ")) + } built, err := Built(ctx, m, b.Artifacts, archive) if err != nil { return catalogue.Manifest{}, err