DLV-B9: a bundle answered in the registry seat's form keeps the artifact-store form it derives for each counted engine that does not read it

The builder's forms gain seat-reference for bundles and archives (oci-artifact-registry/<module>/<artifact>@<digest>);
the old-form window writes the older artifact-store reference beside it (catalogue.InArtifactStore), the oldest kept
in the single reference (ADR 0624 §1, §3), so mesh-build-go's seat-form answer no longer strands an engine stating nothing.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 14:45:00 +01:00
parent 3a64fde7d9
commit 3caa8291b0
3 changed files with 87 additions and 7 deletions
@@ -1342,6 +1342,44 @@ func aDoneKeyKeepsEachFormACountedNodeReads(t *testing.T) {
if got := fs.Needs(withBoth); !slices.Equal(got, []string{"form:new", "form:old"}) {
t.Errorf("needs %v", got)
}
// The builder's own forms (forms.Current): a bundle a build step answers in the registry seat's form (mesh-build-go's
// oci-artifact-registry/<module>/<artifact>@<digest>) keeps the artifact-store form it derives for each counted node
// whose engine does not state form:seat-reference, that one in the single reference (ADR 0624 §1, §3).
seatRef, storeRef := "oci-artifact-registry/m/code@"+digest, "artifact-store://m/code/blobs/"+digest
seat := []string{forms.FormNeed(forms.FormSeatReference), "word:module"}
for _, row := range []struct {
name string
present, counted map[string][]string
reference string
references []string
needs []string
}{
{"seat form answered, B away stating nothing: the derived store form stays, in the single reference",
map[string][]string{"a": seat}, map[string][]string{"a": seat, "b": nil}, storeRef, []string{storeRef, seatRef},
[]string{"form:artifact-store", "form:seat-reference"}},
{"seat form answered, every counted engine reads it: the seat form only",
map[string][]string{"a": seat}, map[string][]string{"a": seat, "b": seat}, seatRef, nil, []string{"form:seat-reference"}},
{"seat form answered, a present engine stating nothing: the store form only",
map[string][]string{"a": seat, "b": nil}, map[string][]string{"a": seat, "b": nil}, storeRef, nil, []string{"form:artifact-store"}},
{"seat form answered, no node present: the store form", nil, map[string][]string{"a": seat}, storeRef, nil, []string{"form:artifact-store"}},
} {
a := forms.Current.Choose(delivery.BuiltArtifact{Name: "code", Kind: "bundle", Digest: digest}, []string{seatRef}, row.present, row.counted)
if a.Reference != row.reference || !slices.Equal(a.References, row.references) {
t.Errorf("%s: %q %v, want %q %v", row.name, a.Reference, a.References, row.reference, row.references)
}
needs := forms.Current.Needs([]delivery.BuiltArtifact{a})
if !slices.Equal(needs, row.needs) {
t.Errorf("%s: needs %v, want %v", row.name, needs, row.needs)
}
for node, stated := range row.counted {
if forms.Current.Held(needs, stated) {
t.Errorf("%s: the want of %s is held", row.name, node)
}
}
if _, err := delivery.EncodeBuilderBuild(delivery.BuilderBuild{State: delivery.BuildDone, Manifest: strings.TrimPrefix(digest, "sha256:"), Artifacts: []delivery.BuiltArtifact{a}, Needs: needs}); err != nil {
t.Errorf("%s: %v", row.name, err)
}
}
// What the assigner holds a want by (ADR 0624 §2), from the same forms.
for _, row := range []struct {
name string
@@ -308,7 +308,7 @@ func TestTheBuildersVersions(t *testing.T) {
}
})
t.Run("old form", func(t *testing.T) {
// One kind, a first form and a newer one (the builder's own forms are all first today, so no window opens).
// One kind, a first form and a newer one.
fs := forms.ArtifactForms{{Name: "registry", First: true, Kinds: []string{"image"}}, {Name: "oci", Kinds: []string{"image"}}}
both := []string{forms.FormNeed("oci")}
for _, c := range []struct {
@@ -327,8 +327,10 @@ func TestTheBuildersVersions(t *testing.T) {
t.Errorf("%s: Holding = %v, want %v", c.name, got, c.want)
}
}
if got := Holding(forms.Current, map[string][]string{"a": nil}, nil); got != nil {
t.Errorf("forms.Current, a node stating nothing: Holding = %v, want none", got)
// The builder's own forms: the registry seat's form of a bundle is not a first form, so a node stating nothing
// holds the window and one stating form:seat-reference does not.
if got := Holding(forms.Current, map[string][]string{"a": nil, "b": {forms.FormNeed(forms.FormSeatReference)}}, nil); !reflect.DeepEqual(got, []string{"a"}) {
t.Errorf("forms.Current, a node stating nothing and one stating form:seat-reference: Holding = %v, want a", got)
}
t0 := time.Unix(0, 0)
w, closed, overdue := OldFormWindow{}.Step([]string{"c"}, t0)
+44 -4
View File
@@ -32,20 +32,28 @@ const (
FormPackageVersion = "package-version"
)
// ArtifactForm is one reference form: its name, whether it is a first form, the artifact kinds it names, and how a
// reference is told to be in it.
// FormSeatReference is the form a registry seat answers for a bundle or an archive it published,
// oci-artifact-registry/<module>/<artifact>@sha256:<digest> (novox/hq ADR 0605 §9; catalogue.SeatReference, read back by
// catalogue.InRegistrySeat), read by a node-engine that states form:seat-reference (catalogue.ReferenceFormSeat). Not a
// first form: an engine stating nothing reads its blob only in the older artifact-store form, which it derives (Older).
const FormSeatReference = catalogue.ReferenceFormSeat
// ArtifactForm is one reference form: its name, whether it is a first form, the artifact kinds it names, how a
// reference is told to be in it, and, for a form that names the same artifact an older form does, that older reference
// (Older, nil when it derives none): the old-form window writes it beside the answered one (ADR 0624 §1, §3).
type ArtifactForm struct {
Name string
First bool
Kinds []string
Is func(reference string) bool
Older func(reference string) string
}
// ArtifactForms are the forms a builder knows, oldest first.
type ArtifactForms []ArtifactForm
// Current are the forms the builder writes and the assigner knows: the three first forms (delivery.BuiltArtifact's
// reference by kind).
// reference by kind), then the registry seat's form of a bundle or an archive.
var Current = ArtifactForms{
{Name: FormArtifactStore, First: true, Is: func(r string) bool { return strings.HasPrefix(r, artifactStore) },
Kinds: []string{catalogue.ArtifactBundle, catalogue.ArtifactArchive, catalogue.ArtifactImage, catalogue.ArtifactUpstream}},
@@ -53,6 +61,16 @@ var Current = ArtifactForms{
Kinds: []string{catalogue.ArtifactImage, catalogue.ArtifactUpstream}},
{Name: FormPackageVersion, First: true, Is: func(r string) bool { return strings.LastIndex(r, "@") > 0 && !strings.Contains(r, "@sha256:") },
Kinds: []string{catalogue.ArtifactPackage}},
{Name: FormSeatReference, Kinds: []string{catalogue.ArtifactBundle, catalogue.ArtifactArchive},
Is: func(r string) bool {
seat, _, ok := catalogue.InRegistrySeat(r)
return ok && seat == catalogue.OciArtifactRegistrySeat
},
// The same blob in the artifact store's form: oci-artifact-registry/<m>/<a>@<d> is artifact-store://<m>/<a>/blobs/<d>.
Older: func(r string) string {
path, _ := catalogue.InArtifactStore(r)
return catalogue.ArtifactStoreScheme + path
}},
}
// FormNeed is the reader capability a form needs of a node-engine: `form:<name>`.
@@ -180,9 +198,12 @@ func (fs ArtifactForms) Kept(kind string, present, counted map[string][]string)
// Choose names a built artifact by the references the build answered in the forms Kept keeps (ADR 0624 §1, §3): the
// oldest kept form in the single Reference, every kept one in References when there are more than one. A reference in a
// form Kept does not keep is left out, unless none of the answered is kept: then the artifact keeps what was answered,
// its first answered in Reference, since a key names where its artifact is (ADR 0498 clause 1).
// its first answered in Reference, since a key names where its artifact is (ADR 0498 clause 1). An answered reference
// in a form that derives an older one (ArtifactForm.Older) answers that older one too, so the build answers one form and
// the key keeps each a counted node reads.
func (fs ArtifactForms) Choose(a delivery.BuiltArtifact, answered []string, present, counted map[string][]string) delivery.BuiltArtifact {
kept := fs.Kept(a.Kind, present, counted)
answered = fs.withOlder(a.Kind, answered)
type ref struct {
at int
reference string
@@ -224,3 +245,22 @@ func (fs ArtifactForms) Choose(a delivery.BuiltArtifact, answered []string, pres
}
return a
}
// withOlder is answered with, after it, each older reference an answered one's form derives that was not answered itself.
func (fs ArtifactForms) withOlder(kind string, answered []string) []string {
out := slices.Clone(answered)
for _, r := range answered {
name, ok := fs.FormOf(kind, r)
if !ok {
continue
}
for _, f := range fs {
if f.Name == name && f.Older != nil {
if older := f.Older(r); !slices.Contains(out, older) {
out = append(out, older)
}
}
}
}
return out
}