Assigner: hears build.ended and version-released, reads no build log; a want's version and parts come from them

A kept build's or another branch's build names its parts and keeping node from build.ended; the trunk's entry names its version and parts from the version-released whose source is the kept assignment graph revision's. The parts' contract comment says <module>:<artifact>.

Builds: design 47 G §379, §525, §526, A-94, A-96, A-67

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-11 23:32:50 +01:00
parent d1357239e2
commit 6c852a8eea
16 changed files with 258 additions and 159 deletions
@@ -0,0 +1,124 @@
package main
import (
"encoding/json"
"fmt"
"sync"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-sdk/go/process"
"github.com/nats-io/nats.go/jetstream"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"mesh-delivery-assigner/internal/assign"
)
// What a want names of a build is heard, never read from the builder's state (design 47 G §379, §526, A-94, A-96; ADR 0596 §2): the
// builder's event `build.ended` (a done build's parts by digest, its keeping node and its branch) and the catalog's
// `version-released` (a released version and each part's digest, with the source revision it was built from), each kept as heard.
// buildEndedSubjects is the builder's event build.ended on its own stream, as the manifest's consumes names it
// (`mesh-delivery-builder.build.ended`), each module and fingerprint after it.
var buildEndedSubjects = process.ModuleStreamSubject(builds.BuilderModule, builds.BuildEndedEvent+".>")
// builtEnds are the done builds' ends heard, by module and fingerprint (hearBuildEnded).
type builtEnds struct {
mu sync.Mutex
ends map[string]builds.BuildEnded
}
func (b *builtEnds) key(module, fp string) string { return module + "@" + fp }
// put takes one build.ended in.
func (b *builtEnds) put(data []byte) error {
var e builds.BuildEnded
if err := json.Unmarshal(data, &e); err != nil {
return fmt.Errorf("a build.ended that is no end: %w", err)
}
if e.Module == "" || e.Fingerprint == "" {
return fmt.Errorf("a build's end naming no module or fingerprint: %s", data)
}
b.mu.Lock()
defer b.mu.Unlock()
if b.ends == nil {
b.ends = map[string]builds.BuildEnded{}
}
b.ends[b.key(e.Module, e.Fingerprint)] = e
return nil
}
// partsOf is the parts of module's build at fp as its end names them (each artifact's digest by `<module>:<artifact>`), with the
// node that keeps it, for a kept build or a build of another watched branch; false for a build of the trunk not kept, whose
// parts the version's release says (releasedVersions).
func (b *builtEnds) partsOf(module, fp string) (assign.KeptBuild, bool) {
b.mu.Lock()
e, ok := b.ends[b.key(module, fp)]
b.mu.Unlock()
if !ok || (e.KeptOn == "" && e.Branch == "") {
return assign.KeptBuild{}, false
}
parts := map[string]string{}
for name, digest := range e.Made {
parts[module+":"+name] = digest
}
return assign.KeptBuild{Parts: parts, KeptOn: e.KeptOn}, true
}
// releasedVersions are the catalog's version-released heard, by module and source revision.
type releasedVersions struct {
mu sync.Mutex
seen map[string]assign.KeptBuild
}
func (r *releasedVersions) key(module string, source int64) string {
return fmt.Sprintf("%s@%d", module, source)
}
// put takes one version-released in.
func (r *releasedVersions) put(data []byte) error {
var e meshcatalog.VersionReleasedEvent
if err := json.Unmarshal(data, &e); err != nil {
return fmt.Errorf("a version-released that is none: %w", err)
}
if e.Module == "" || e.Version == "" {
return fmt.Errorf("a version-released naming no module or version: %s", data)
}
parts := map[string]string{}
for _, a := range e.Artifacts {
parts[e.Module+":"+a.Artifact] = a.Digest
}
r.mu.Lock()
defer r.mu.Unlock()
if r.seen == nil {
r.seen = map[string]assign.KeptBuild{}
}
r.seen[r.key(e.Module, e.Source)] = assign.KeptBuild{Version: e.Version, Parts: parts}
return nil
}
// of is the version of module released from the source revision, with its parts' digests; false before it is heard.
func (r *releasedVersions) of(module string, source int64) (assign.KeptBuild, bool) {
r.mu.Lock()
defer r.mu.Unlock()
k, ok := r.seen[r.key(module, source)]
return k, ok
}
// hearBuildsAndReleases registers the hub's takers of build.ended and version-released, each waking the pass; before the hub starts.
func hearBuildsAndReleases(hub *process.EventHub, ends *builtEnds, released *releasedVersions, wake func(), logf func(string, ...any)) error {
taker := func(put func([]byte) error) func(jetstream.Msg) {
return func(m jetstream.Msg) {
if err := put(m.Data()); err != nil {
logf("%s is not kept: %v", m.Subject(), err)
}
if err := m.Ack(); err != nil {
logf("acknowledging %s: %v", m.Subject(), err)
}
wake()
}
}
if err := hub.Handle(buildEndedSubjects, taker(ends.put)); err != nil {
return err
}
return hub.Handle(meshcatalog.SubjectVersionReleased, taker(released.put))
}
@@ -0,0 +1,88 @@
package main
import (
"encoding/json"
"reflect"
"testing"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"mesh-delivery-assigner/internal/assign"
)
// Design 47 G §379, §526, A-96: a build.ended heard names a kept build's or another branch's parts by digest (`<module>:<artifact>`) and its
// keeping node; a build of the trunk not kept names none, its parts being the release's.
func TestABuildEndedHeardNamesAKeptBuildsPartsAndItsNode(t *testing.T) {
put := func(b *builtEnds, e builds.BuildEnded) {
raw, _ := json.Marshal(e)
if err := b.put(raw); err != nil {
t.Fatal(err)
}
}
made := map[string]string{"program": "sha256:1"}
var b builtEnds
put(&b, builds.BuildEnded{Module: "node-engine", Fingerprint: "a", Made: made})
if _, ok := b.partsOf("node-engine", "a"); ok {
t.Error("a trunk build not kept names parts")
}
put(&b, builds.BuildEnded{Module: "node-engine", Fingerprint: "b", Made: made, KeptOn: "ace"})
if k, ok := b.partsOf("node-engine", "b"); !ok || k.KeptOn != "ace" || !reflect.DeepEqual(k.Parts, map[string]string{"node-engine:program": "sha256:1"}) {
t.Errorf("kept build = %+v, %v", k, ok)
}
put(&b, builds.BuildEnded{Module: "node-engine", Fingerprint: "c", Made: made, Branch: "cand"})
if k, ok := b.partsOf("node-engine", "c"); !ok || k.KeptOn != "" || len(k.Parts) != 1 {
t.Errorf("branch build = %+v, %v", k, ok)
}
if err := b.put([]byte(`{"module":"x"}`)); err == nil {
t.Error("an end naming no fingerprint is kept")
}
}
// Design 47 G §379, A-96: a version-released heard names the version and the parts' digests of the module's source revision.
func TestAVersionReleasedHeardNamesItsVersionAndPartsBySource(t *testing.T) {
var r releasedVersions
raw, _ := json.Marshal(meshcatalog.VersionReleasedEvent{Module: "mail", Source: 7, Version: "1.2.3", Artifacts: []meshcatalog.VersionArtifact{{Artifact: "image", Digest: "sha256:9"}}})
if err := r.put(raw); err != nil {
t.Fatal(err)
}
want := assign.KeptBuild{Version: "1.2.3", Parts: map[string]string{"mail:image": "sha256:9"}}
if k, ok := r.of("mail", 7); !ok || !reflect.DeepEqual(k, want) {
t.Errorf("released = %+v, %v", k, ok)
}
if _, ok := r.of("mail", 8); ok {
t.Error("another source revision names a version")
}
}
// Design 47 G §379, A-96: the trunk's entry takes its version and parts from the version-released of the kept assignment graph
// revision's source; a kept build from its end; no revision kept, or another fingerprint, names none.
func TestATrunkEntryTakesItsVersionFromTheReleaseOfTheKeptRevisionsSource(t *testing.T) {
w := newWorld()
raw, _ := json.Marshal(meshcatalog.VersionReleasedEvent{Module: "mail", Source: 7, Version: "1.2.3", Artifacts: []meshcatalog.VersionArtifact{{Artifact: "image", Digest: "sha256:9"}}})
if err := w.released.put(raw); err != nil {
t.Fatal(err)
}
endRaw, _ := json.Marshal(builds.BuildEnded{Module: "mail", Fingerprint: "k", Made: map[string]string{"image": "sha256:2"}, KeptOn: "g14"})
if err := w.ends.put(endRaw); err != nil {
t.Fatal(err)
}
p := &passer{w: w, graph: &graphKeeper{ok: true, source: 7}}
named := p.namedBuild("mail", "t")
if k, ok := named("t"); !ok || k.Version != "1.2.3" || k.Parts["mail:image"] != "sha256:9" || k.KeptOn != "" {
t.Errorf("trunk entry = %+v, %v", k, ok)
}
if k, ok := named("k"); !ok || k.KeptOn != "g14" || k.Version != "" {
t.Errorf("kept build = %+v, %v", k, ok)
}
if _, ok := named("other"); ok {
t.Error("another fingerprint names a release")
}
p.graph.source = 8
if _, ok := named("t"); ok {
t.Error("a source with no release names a version")
}
p.graph.ok = false
if _, ok := p.namedBuild("mail", "t")("t"); ok {
t.Error("no revision kept names a release")
}
}
@@ -1,84 +0,0 @@
package main
import (
"fmt"
"sync"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/process"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"mesh-delivery-assigner/internal/assign"
)
// The builder's log, heard (design 47 G §379, §526, §671): each `build.ended` of a done build is kept by module and fingerprint,
// so a want names a kept build's parts by digest and the node that keeps it, taken from the build's end and never from the
// builder's done key (ADR 0596 §2). The log is read on the assigner's own credential as a read of `mesh-delivery-builder.build-log`
// (its manifest's `reads`), every entry from the start of the log and then each new one, and wakes the pass on each.
// builtEnds are the done builds' ends heard, by module and fingerprint (putBuilt).
type builtEnds struct {
mu sync.Mutex
ends map[string]builds.BuildLogEntry
}
func (b *builtEnds) key(module, fp string) string { return module + "@" + fp }
// putBuilt takes one entry of the builder's log: a done build's end is kept, anything else is passed over.
func (b *builtEnds) putBuilt(value []byte) error {
if !builds.IsBuildEntry(value) {
return nil
}
e, err := builds.DecodeBuildLogEntry(value)
if err != nil {
return err
}
if e.Event != builds.BuildEndedEvent || e.Outcome != builds.BuildDone {
return nil
}
if e.Module == "" || e.FP == "" {
return fmt.Errorf("a build's end naming no module or fingerprint: %s", value)
}
b.mu.Lock()
defer b.mu.Unlock()
if b.ends == nil {
b.ends = map[string]builds.BuildLogEntry{}
}
if old, ok := b.ends[b.key(e.Module, e.FP)]; !ok || !e.At.Before(old.At) {
b.ends[b.key(e.Module, e.FP)] = e
}
return nil
}
// keptBuild is the kept build of module at fp, as its end names it: each artifact's digest by `<module>:<artifact>`, and the node
// that keeps it. False for a build not done or not kept (it names no keeping node).
func (b *builtEnds) keptBuild(module, fp string) (assign.KeptBuild, bool) {
b.mu.Lock()
e, ok := b.ends[b.key(module, fp)]
b.mu.Unlock()
if !ok || e.KeptOn == "" {
return assign.KeptBuild{}, false
}
parts := map[string]string{}
for _, a := range e.Made {
parts[module+":"+a.Name] = a.Digest
}
return assign.KeptBuild{Parts: parts, KeptOn: e.KeptOn}, true
}
// hearBuildLog reads the builder's log on conn and wakes the pass on each entry; the watch lasts as long as the connection.
func hearBuildLog(conn *process.Conn, ends *builtEnds, wake func(), logf func(string, ...any)) {
go func() {
err := conn.ReadLog(builds.BuilderModule, builds.BuilderLog).Watch("", stdio.LogFromAll, func(le stdio.LogEntry) error {
if err := ends.putBuilt(le.Value); err != nil {
logf("%s entry %d is not read: %v", builds.BuildLogRead, le.Sequence, err)
return nil
}
wake()
return nil
})
if err != nil {
logf("%s is not read (%v): no want names a kept build's parts or its keeping node until the assigner starts again", builds.BuildLogRead, err)
}
}()
}
@@ -1,46 +0,0 @@
package main
import (
"reflect"
"testing"
"time"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
)
// Design 47 G §526, §379, §671: a done build's end in the builder's log, kept by module and fingerprint, names a kept build's
// parts by digest (`<module>:<artifact>`) and its keeping node; a build not kept, failed or not ended names none.
func TestAKeptBuildsEndNamesItsPartsAndItsNode(t *testing.T) {
at := time.Date(2026, 11, 11, 22, 0, 0, 0, time.UTC)
entry := func(event, outcome, keptOn string, made ...builds.BuiltArtifact) []byte {
raw, err := builds.EncodeBuildLogEntry(builds.BuildLogEntry{Event: event, Module: "node-engine", FP: "src2:a", Source: "s", Repository: "novox/mesh-host",
Directory: ".", Commit: "c1", Node: "ace", At: at, Outcome: outcome, Made: made, KeptOn: keptOn})
if err != nil {
t.Fatal(err)
}
return raw
}
made := builds.BuiltArtifact{Name: "program", Kind: "program", Digest: "sha256:1", Reference: "artifact-store://node-engine/program@sha256:1"}
var b builtEnds
for _, raw := range [][]byte{entry(builds.BuildStartedEvent, "", ""), entry(builds.BuildEndedEvent, builds.BuildFailed, "ace")} {
if err := b.putBuilt(raw); err != nil {
t.Fatal(err)
}
}
if _, ok := b.keptBuild("node-engine", "src2:a"); ok {
t.Error("a start and a failed end name a kept build")
}
if err := b.putBuilt(entry(builds.BuildEndedEvent, builds.BuildDone, "", made)); err != nil {
t.Fatal(err)
}
if _, ok := b.keptBuild("node-engine", "src2:a"); ok {
t.Error("a build not kept names a keeping node")
}
if err := b.putBuilt(entry(builds.BuildEndedEvent, builds.BuildDone, "ace", made)); err != nil {
t.Fatal(err)
}
k, ok := b.keptBuild("node-engine", "src2:a")
if !ok || k.KeptOn != "ace" || !reflect.DeepEqual(k.Parts, map[string]string{"node-engine:program": "sha256:1"}) {
t.Errorf("kept build = %+v, %v", k, ok)
}
}
@@ -14,6 +14,7 @@ import (
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
"git.novox.be/novox/mesh-sdk/go/keys"
"mesh-delivery-assigner/internal/assign"
"mesh-delivery-assigner/internal/assigngraph"
)
@@ -118,3 +119,18 @@ func (p *passer) sayWanted(node, module, fp string, removed bool) {
p.logf("wanted of %s on %s is not said: %v", module, node, err)
}
}
// namedBuild answers what a want of module at fp names of its build (design 47 G §379, §526, A-96): a kept build's or another watched
// branch's parts and keeping node from the builder's `build.ended`; for the trunk's entry (fp is the module's, trunkFP), the released
// version and the parts of the `version-released` whose source is the one the kept assignment graph revision was computed from (A-67).
func (p *passer) namedBuild(module, trunkFP string) func(fp string) (assign.KeptBuild, bool) {
return func(fp string) (assign.KeptBuild, bool) {
if k, ok := p.w.ends.partsOf(module, fp); ok {
return k, true
}
if fp != trunkFP || p.graph == nil || !p.graph.ok {
return assign.KeptBuild{}, false
}
return p.w.released.of(module, p.graph.source)
}
}
@@ -576,11 +576,11 @@ func follow(ctx context.Context, js jetstream.JetStream, nc process.Subscriber,
logger.Printf("a pipeline's end or a registration is not heard, so the wants keep the trunk-only form until the assigner starts again: %v", err)
}
}
if err := hearBuildsAndReleases(hub, &w.ends, &w.released, wake, logger.Printf); err != nil {
logger.Printf("a build's end or a version's release is not heard, so no want names a build's parts, its keeping node or its version until the assigner starts again: %v", err)
}
hearReadsChanged(ctx, js, hub, reads, findings, gate.EventsLive, logger.Printf)
hearRunning(ctx, js, w, wake, findings, logger.Printf)
if e, ok := bus.(emitting); ok && e.Conn != nil {
hearBuildLog(e.Conn, &w.ends, wake, logger.Printf)
}
}
if bus != nil && kvs[buckets.Puts] != nil {
p.acts = newPutActs(bus, wake, logger.Printf)
@@ -3,7 +3,6 @@ package main
import (
"encoding/json"
mcseat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-sdk/go/views"
"mesh-delivery-assigner/internal/assign"
plc "mesh-delivery-assigner/internal/assignment"
@@ -55,9 +54,6 @@ func TestOpensWhatItsManifestNames(t *testing.T) {
if slices.Contains(readFamilies, r) {
continue // a state kept one bucket per node, read by its prefix (ADR 0593 §6), not one bucket
}
if r == builds.BuildLogRead {
continue // the builder's log, read on the connection (buildlog.go), no bucket
}
reads = append(reads, strings.Replace(r, ".", "_", 1))
}
for _, l := range readFamilies {
@@ -325,7 +325,7 @@ func (p *passer) wants(ctx context.Context) {
held, _ := mwc.HeldKey(name, fp)
grant, _ := grantsstate.PartGrantKey(name, fp)
hold, _ := asks.HoldAskKey(name, fp)
m.KeptBuilds = func(fp string) (assign.KeptBuild, bool) { return p.w.ends.keptBuild(name, fp) }
m.KeptBuilds = p.namedBuild(name, m.FP)
m.Released = p.answeredWith(hold, "release")
m.Kept = p.answeredWith(hold, "keep")
m.Built, m.Held, m.Granted = p.w.built(name, fp), p.w.has(buckets.Wants, held) && !m.Released, p.w.has(grants, grant)
@@ -41,8 +41,10 @@ type world struct {
changes map[string]uint64
// meta is, by bucket and key, the revision and the time of the value held now, where the watch said them (putEntry).
meta map[string]entryMeta
// built are the done builds' ends the builder's log says, by module and fingerprint (buildlog.go).
// ends are the done builds' ends the builder says (build.ended), by module and fingerprint (buildlog.go).
ends builtEnds
// released are the catalog's version-released heard, by module and source revision (buildended.go).
released releasedVersions
// assignments is the seat's state assignments assembled node by node from its keys in either form (mesh-sdk
// mmac.Keys; research 072 row 37): the one reader of its keys in the assigner.
assignmentKeys *mmac.Keys
@@ -95,8 +95,8 @@ type Module struct {
// source of a want's Self: every want the rules write or keep is derived from it (selfDerived), so a want written Self while
// the node was placed does not outlive the assignments (issue 1126). Nil: no node does.
Self func(node string) bool
// KeptBuilds answers the kept build of the module at a fingerprint, as the builder's `build.ended` names it (design 47 G §526,
// §671): a want at one names its parts and its keeping node, for a node that states the capability WantParts. Nil: none is read.
// KeptBuilds answers what a want at a fingerprint names of its build: its parts and its keeping node as the builder's `build.ended`
// names them, or the released version and parts of the catalog's `version-released` (design 47 G §379, §526, A-96), for a node that states the capability WantParts. Nil: none is read.
KeptBuilds func(fp string) (KeptBuild, bool)
// MachineHeld says whether the node's own network condition is open, which holds the machine as a whole: no want moves to it
// until that clears (design 47 G §706 *M's own network*). Nil: none is held.
@@ -106,11 +106,13 @@ type Module struct {
// WantParts is the capability a node-engine of G states (ADR 0614 §4, design 47 G §525): it reads a want's `parts` and `kept-on`.
const WantParts = "want-parts"
// KeptBuild is a kept build as the builder's `build.ended` says it: the digest of each of its parts by `<module>:<artifact>`, and
// the build node that keeps it.
// KeptBuild is what a want names of a build: the digest of each of its parts by `<module>:<artifact>`, and, as the builder's
// `build.ended` says it, the build node that keeps a kept build; for a trunk build the released version the catalog's
// `version-released` says (design 47 G §379, §526, A-96).
type KeptBuild struct {
Parts map[string]string
KeptOn string
Parts map[string]string
KeptOn string
Version string
}
// withKept is w naming the kept build at its fingerprint, the parts by digest and the keeping node, only while the node's
@@ -124,7 +126,7 @@ func (n Nodes) withKept(m Module, node string, w mwc.Want) mwc.Want {
if !ok || !slices.Contains(n.Reads[node], WantParts) {
return w
}
w.Parts, w.KeptOn = k.Parts, k.KeptOn
w.Parts, w.KeptOn, w.Version = k.Parts, k.KeptOn, k.Version
return w
}
@@ -1587,7 +1587,7 @@ func TestAProviderRunsHealthyByItsRunning(t *testing.T) {
// Design 47 G §525, §526: a want at a kept build names its parts and the node that keeps it, only for a node whose engine states
// the capability want-parts.
func TestAWantNamesAKeptBuildsPartsAndNodeForANodeThatReadsThem(t *testing.T) {
kept := KeptBuild{Parts: map[string]string{"mail:image": "sha256:1"}, KeptOn: "g14"}
kept := KeptBuild{Parts: map[string]string{"mail:image": "sha256:1"}, KeptOn: "g14", Version: "1.2.3"}
for name, row := range map[string]struct {
reads []string
kept func(string) (KeptBuild, bool)
@@ -1603,7 +1603,7 @@ func TestAWantNamesAKeptBuildsPartsAndNodeForANodeThatReadsThem(t *testing.T) {
n.Reads = map[string][]string{"ace": row.reads, "g14": row.reads}
m.KeptBuilds = row.kept
w := Wants(m, n, wantedOld(), nil).Wants["ace"]
if w.Fp != fNew || (w.KeptOn == "g14") != row.wanted || (len(w.Parts) == 1) != row.wanted {
if w.Fp != fNew || (w.KeptOn == "g14") != row.wanted || (w.Version == "1.2.3") != row.wanted || (len(w.Parts) == 1) != row.wanted {
t.Errorf("%s: ace's want = %+v", name, w)
}
}
@@ -4,6 +4,7 @@ import (
"encoding/json"
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-teller/tellerevents"
"os"
"reflect"
@@ -43,10 +44,9 @@ func TestManifest(t *testing.T) {
slices.Sort(reads)
// ADR 0593 §8: it composes nothing, so it reads no candidate, held or resolved value, compose step or build log; it reads
// every node's published compositions to compare them, and the gate's source and merges, for the release that carries a build (ADR 0601 §1).
// Design 47 G §526, §379: it reads the builder's log for the `build.ended` that names a kept build's parts and keeping node.
// DLV-F2r (ADR 0615 §1, ADR 0631 §1): the indexer's manifests, modules and module graph (whose verb edges a want waits
// on, ADR 0614 §2, §3) are read through its first views and views family, none of the indexer's states by reads.
want := []string{"mesh-delivery-builder.build-log", "mesh-delivery-builder.builds", "mesh-delivery-gate.merges", "mesh-delivery-gate.source",
want := []string{"mesh-delivery-builder.builds", "mesh-delivery-gate.merges", "mesh-delivery-gate.source",
"mesh-delivery-teller.answers", "mesh-delivery-watchman.presence", "mesh-grants.grants", "mesh-host.composed",
"mesh-provisioner.provisions", "node-engine.have"}
if !reflect.DeepEqual(reads, want) {
@@ -56,7 +56,7 @@ func TestManifest(t *testing.T) {
named := []string{buckets.Builds, buckets.Merges, buckets.Source, buckets.Answers, buckets.Presence, buckets.Grants,
buckets.ComposedRead, "mesh-provisioner_provisions", buckets.Have}
slices.Sort(named)
for i, r := range slices.DeleteFunc(slices.Clone(want), func(r string) bool { return r == "mesh-delivery-builder.build-log" }) { // a log, read on the connection
for i, r := range want {
if got := strings.Replace(named[i], "_", ".", 1); got != r {
t.Errorf("the buckets package names %q, the manifest reads %q", got, r)
}
@@ -130,7 +130,7 @@ func TestManifest(t *testing.T) {
}
// ADR 0618 §4: and it hears each node's engine say its reads changed; ADR 0636 §4 (issue 1966): and the seat list's two events;
// issue 1966 note 151: and the teller's ask-ended, its subject the one the assigner hears.
if !slices.Equal(m.Consumes, []string{"mesh-conditions.findings-asked", "mesh-conditions.condition-raised", "mesh-conditions.condition-changed", "mesh-conditions.condition-cleared", "mesh-catalog.catalog-revision", "mesh-catalog.registration-changed", "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", buckets.TellerModule + "." + tellerevents.AskEnded, "mesh-pipeline-runner.pipeline-ended", "mesh-pipeline-runner.group-ended", "node-engine.manifest-reads-changed", "node-engine.running"}) {
if !slices.Equal(m.Consumes, []string{"mesh-conditions.findings-asked", "mesh-conditions.condition-raised", "mesh-conditions.condition-changed", "mesh-conditions.condition-cleared", "mesh-catalog.catalog-revision", "mesh-catalog.registration-changed", "mesh-catalog.version-released", "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", builds.BuilderModule + "." + builds.BuildEndedEvent, buckets.TellerModule + "." + tellerevents.AskEnded, "mesh-pipeline-runner.pipeline-ended", "mesh-pipeline-runner.group-ended", "node-engine.manifest-reads-changed", "node-engine.running"}) {
t.Errorf("consumes = %v, want the catalog half's revisions (design 47 G §195), the teller's ask-ended, the engines' reads changed and what they run", m.Consumes)
}
if want := []string{"mesh-delivery-indexer.manifests", "mesh-delivery-indexer.modules",
+2 -1
View File
@@ -30,7 +30,6 @@
}
],
"reads": [
"mesh-delivery-builder.build-log",
"mesh-delivery-builder.builds",
"mesh-delivery-gate.merges",
"mesh-delivery-gate.source",
@@ -51,8 +50,10 @@
"mesh-conditions.condition-cleared",
"mesh-catalog.catalog-revision",
"mesh-catalog.registration-changed",
"mesh-catalog.version-released",
"mesh-catalog.seat-definition-changed",
"mesh-catalog.seat-definition-removed",
"mesh-delivery-builder.build.ended",
"mesh-delivery-teller.ask-ended",
"mesh-pipeline-runner.pipeline-ended",
"mesh-pipeline-runner.group-ended",