Files
mesh-delivery/sdk/forms/forms.go
T
jschoubben e68a47886f sdk: the reference forms a build keeps and holds by, and the keeper of what each node's engine reads, shared by the assigner, the builder and the view (ADR 0618, ADR 0624, issue 774)
Logic only mesh-delivery's parts share, so it is mesh-delivery's and not mesh-sdk's (ADR 0593 §1, ADR 0608 §5). Tested through the assigner's and the builder's rows (the test budget). mesh-sdk is pinned by a local replace to the group's head while developing.

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 12:19:34 +01:00

227 lines
8.1 KiB
Go

// Package forms holds the artifact reference forms the builder writes a done build key in and the assigner holds a want
// by (novox/hq ADR 0618 §1-§2, ADR 0624): logic only those two modules of mesh-delivery share, so it is mesh-delivery's
// and not mesh-sdk's (ADR 0593 §1, ADR 0608 §5). The build key's fields (delivery.BuilderBuild.Needs,
// delivery.BuiltArtifact.References) are the contract, in mesh-sdk.
package forms
import (
"slices"
"sort"
"strings"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// artifactStore opens the artifact store's references (delivery.BuiltArtifact).
const artifactStore = "artifact-store://"
// The artifact reference forms (novox/hq ADR 0605 §9, ADR 0618 §1-§2, ADR 0624): each form a done key's reference may be
// written in, named as the reader capability `form:<name>` a node-engine states when it reads it. The forms written
// before any engine stated what it reads are first forms: every engine reads them, so none needs a statement, and a
// node whose engine states nothing (the baseline, ADR 0614 §4) reads exactly those. A new form is added to
// Current, newest last, in the change that teaches the node-engine to read it.
// The first forms, by name.
const (
// FormArtifactStore: artifact-store://<module>/<artifact>/blobs/<digest>, or an image's kept artifact-store://…@<digest>.
FormArtifactStore = "artifact-store"
// FormRegistry: an image's or an upstream copy's <registry>/<module>/<artifact>@<digest>.
FormRegistry = "registry"
// FormPackageVersion: a package's <name>@<version>.
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.
type ArtifactForm struct {
Name string
First bool
Kinds []string
Is func(reference string) bool
}
// 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).
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}},
{Name: FormRegistry, First: true, Is: func(r string) bool { return !strings.HasPrefix(r, artifactStore) && strings.Contains(r, "@sha256:") },
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}},
}
// FormNeed is the reader capability a form needs of a node-engine: `form:<name>`.
func FormNeed(form string) string { return catalogue.ReadForm + form }
// FormOf is the name of the form reference is written in for an artifact of kind, and whether one of fs names it.
func (fs ArtifactForms) FormOf(kind, reference string) (string, bool) {
for i := len(fs) - 1; i >= 0; i-- { // the newest first: a newer form may be a narrower shape of an older one
f := fs[i]
if (kind == "" || slices.Contains(f.Kinds, kind)) && f.Is(reference) {
return f.Name, true
}
}
return "", false
}
// Reads says whether an engine whose statement is stated reads form: a first form always (the baseline included), any
// other only when stated names `form:<name>`. A form fs does not know is read only when stated.
func (fs ArtifactForms) Reads(stated []string, form string) bool {
for _, f := range fs {
if f.Name == form && f.First {
return true
}
}
return slices.Contains(stated, FormNeed(form))
}
// Needs are the reader capabilities a done build's artifacts carry (ADR 0618 §1): `form:<name>` of every form any of
// its references, single or listed, is written in, sorted. Nil when it names none (no artifact, or no reference).
func (fs ArtifactForms) Needs(artifacts []delivery.BuiltArtifact) []string {
set := map[string]bool{}
for _, a := range artifacts {
for _, r := range append([]string{a.Reference}, a.References...) {
if r == "" {
continue
}
if form, ok := fs.FormOf(a.Kind, r); ok {
set[FormNeed(form)] = true
}
}
}
if len(set) == 0 {
return nil
}
out := make([]string, 0, len(set))
for n := range set {
out = append(out, n)
}
sort.Strings(out)
return out
}
// Held says whether a build's reference forms hold a node's want (ADR 0624 §2): only when the build names forms and the
// node's engine, its statement stated, reads none of them. A build that names none holds no want.
func (fs ArtifactForms) Held(needs, stated []string) bool {
if len(needs) == 0 {
return false
}
for _, n := range needs {
if form, ok := strings.CutPrefix(n, catalogue.ReadForm); ok && fs.Reads(stated, form) {
return false
}
}
return true
}
// Writable are the forms of kind every present node's engine reads (ADR 0618 §2: a new form is written only once every
// present engine states it), oldest first, by node its statement. With no node present, the first forms only.
func (fs ArtifactForms) Writable(kind string, present map[string][]string) []string {
var out []string
for _, f := range fs {
if !slices.Contains(f.Kinds, kind) {
continue
}
every := f.First || len(present) > 0
for _, stated := range present {
every = every && fs.Reads(stated, f.Name)
}
if every {
out = append(out, f.Name)
}
}
return out
}
// Kept are the forms a build key names an artifact of kind in (ADR 0624 §1, §3), oldest first, so the first is the one
// the single reference holds: the newest form every present engine reads (Writable), then, for each counted node, present
// or away, whose engine reads none of those kept so far, the newest form of kind it reads. A node stating nothing reads
// the first forms. A form leaves only once no counted node reads it alone; a node leaves the count when it is retired.
func (fs ArtifactForms) Kept(kind string, present, counted map[string][]string) []string {
writable := fs.Writable(kind, present)
kept := map[string]bool{}
if len(writable) > 0 {
kept[writable[len(writable)-1]] = true
}
nodes := make([]string, 0, len(counted))
for node := range counted {
nodes = append(nodes, node)
}
sort.Strings(nodes)
for _, node := range nodes {
stated := counted[node]
var newest string
readsKept := false
for _, f := range fs {
if !slices.Contains(f.Kinds, kind) || !fs.Reads(stated, f.Name) {
continue
}
newest = f.Name
readsKept = readsKept || kept[f.Name]
}
if !readsKept && newest != "" {
kept[newest] = true
}
}
var out []string
for _, f := range fs {
if kept[f.Name] {
out = append(out, f.Name)
}
}
return out
}
// 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).
func (fs ArtifactForms) Choose(a delivery.BuiltArtifact, answered []string, present, counted map[string][]string) delivery.BuiltArtifact {
kept := fs.Kept(a.Kind, present, counted)
type ref struct {
at int
reference string
}
var in []ref
for _, r := range answered {
if r == "" {
continue
}
form, ok := fs.FormOf(a.Kind, r)
if at := slices.Index(kept, form); ok && at >= 0 {
in = append(in, ref{at, r})
}
}
sort.SliceStable(in, func(i, j int) bool { return in[i].at < in[j].at })
a.References = nil
switch {
case len(in) > 0:
a.Reference = in[0].reference
if len(in) > 1 {
for _, r := range in {
a.References = append(a.References, r.reference)
}
}
default:
a.Reference = ""
for _, r := range answered {
if r == "" {
continue
}
if a.Reference == "" {
a.Reference = r
}
a.References = append(a.References, r)
}
if len(a.References) < 2 {
a.References = nil
}
}
return a
}