The check's composer composes a module at its build only where that build was made of the change's manifest (issue 1921, milestone 47/G)
mesh/delivery-group group feat/registry-milestone checking: 7 of 7 member(s) ready
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 53addd73c358 on e73aae3686a6 | novox/mesh-host#209 in group feat/registry-milestone

The running indexer could not read 17 of the group's manifests, so the running node-engine composed those modules at
their trunk builds and said nothing; the head composer laid those builds over the change's other manifests and ace
failed on "nothing provides artifact-store, wanted by mesh-build-go" (group check 8a80e8e86858). The input a node
answers a check with now names, per module taken in from its build, the content digest of the manifest that build was
made of (ComposedHow.Manifests); the composer compares it with the case's manifest, and, for an input from a
node-engine before it, compares what the two manifests say beyond what a take-in resolves (the lead's ruling, §17).
Each module lifted either way is composed from the case's manifest, its build awaited, and named in NotJudged with
the comparison that lifted it; one that cannot compose without its build is held as its stub. The digest is taken
over one JSON form because a request through a build module arrives compacted.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-07 07:24:16 +01:00
parent b6c174a324
commit 53addd73c3
5 changed files with 185 additions and 3 deletions
@@ -1,6 +1,7 @@
package frombus
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -218,6 +219,15 @@ func answerNamesEveryRefusalAndLeavesAnOldBuildToTheNode(t *testing.T) {
t.Errorf("spiegel in InOwn wanted at %s: %v, held %v, read as %s; want its build's image", sp.Fp2, err,
read.ComposedHow, raw)
}
// Issue 1921: the input a check is answered with names the manifest spiegel's build was made of (its content
// digest, the same for those bytes compacted inside a request), so the check's composer composes it at that
// build only where the change's manifest is that one.
var compact bytes.Buffer
_ = json.Compact(&compact, bb.manifests[sp.Manifest])
want, _ := compose.ContentDigest(bb.manifests[sp.Manifest])
if got, _ := compose.ContentDigest(compact.Bytes()); err == nil && (want == "" || read.ComposedHow.Manifests["spiegel"] != want || got != want) {
t.Errorf("the manifest spiegel's build was made of: %v, want %s (compacted %s)", read.ComposedHow.Manifests, want, got)
}
// Issue 1921: held by its manifest's verdict, spiegel is still held in what the node composes, and the input a check
// is answered with says what holds it and carries the build the node would compose it at, taken in while done; a
// node's own read takes in no build of it.
+13 -2
View File
@@ -171,6 +171,9 @@ type Scope struct {
// on the node that its manifest's verdict or its decoder holds, into compose.Input.Holds, never into what the node composes: the
// check's composer, judging the change's manifest itself, composes it at that build (issue 1921).
takeInHeld bool
// manifests, set with takeInHeld, is filled with the ContentDigest of the manifest each module placed on the node was
// built from, as it is taken in from that build (compose.ComposedHow.Manifests).
manifests map[string]string
// Holds is told, after Compose read the input, what holds each module it holds by its manifest's verdict or its
// decoder (compose.Input.Holds), with the build it would compose it at, so the node says why it holds it (issue 1921,
// the composition verb). Nil: said nowhere.
@@ -624,12 +627,12 @@ func Compose(ctx context.Context, r Reader, mesh Mesh, s Scope, fps map[string]s
// credential: the sandbox composes no pair value.
func ComposingInput(ctx context.Context, r Reader, mesh Mesh, s Scope, fps map[string]string) (compose.Input, error) {
held := map[string]string{}
s.takeInHeld = true
s.takeInHeld, s.manifests = true, map[string]string{}
in, err := input(ctx, r, mesh, s, func(module string, err error) { held[module] = err.Error() }, fps)
if err != nil {
return compose.Input{}, err
}
c := &compose.ComposedHow{Node: s.Node, Builds: maps.Clone(fps), Held: held}
c := &compose.ComposedHow{Node: s.Node, Builds: maps.Clone(fps), Held: held, Manifests: s.manifests}
for _, run := range s.Running {
if _, isHeld := held[run.Module]; isHeld && run.Module != "" {
c.Running = append(c.Running, run)
@@ -983,6 +986,14 @@ func fromBuilds(ctx context.Context, r Reader, build func(ctx context.Context, m
continue
}
in.Catalogue[pm.Module] = resolved
if s.manifests != nil {
// Unread or not JSON, it is left out: the check's composer then compares what the manifests say (issue 1921).
if raw, err := r.Manifest(ctx, b.Manifest); err == nil {
if digest, err := compose.ContentDigest(raw); err == nil {
s.manifests[pm.Module] = digest
}
}
}
}
for i := range in.Nodes {
if in.Nodes[i].Name != s.Node {