fix9-6: the view's build usage finds the build seats and their holders, never a build module's name

build_usage reads each build seat's steps and pauses through the seat; the view's tools follow the seat list (view_seat_list, view_seat_list_again) and ask the holder of each seat of the seat mesh-module-assigner.

Builds: design 47 G A-82, A-74, §204
This commit is contained in:
2026-11-12 01:07:00 +01:00
parent a8601f5a5c
commit 13c5433c0d
7 changed files with 189 additions and 47 deletions
@@ -13,14 +13,18 @@ import (
"git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient"
"git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient/enginereads"
mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate"
"git.novox.be/novox/mesh-delivery/sdk/buildstep"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/busretry"
"git.novox.be/novox/mesh-sdk/go/seatdef"
"git.novox.be/novox/mesh-sdk/go/seatlist"
"git.novox.be/novox/mesh-sdk/go/views"
)
@@ -405,6 +409,29 @@ func ownersRead(ask func(key string, body any) (json.RawMessage, error)) view.Re
GraphState: indexer("indexer_read_graph")}
}
// holderOf is a seat's holder as the seat mesh-module-assigner's verb assignments answers it with the seats (design 47 G A-82,
// A-74): the module the seat list names for the seat, and false while the seat has none or the list cannot be read.
func holderOf(ask func(key string, body any) (json.RawMessage, error)) func(seat string) (string, bool) {
return func(seat string) (string, bool) {
raw, err := ask("seat:"+mma.Seat+"."+mma.VerbAssignments, mma.AssignmentsInput{Seats: mma.AssignmentsInputSeatsTrue})
if err != nil {
return "", false
}
var held struct {
Seats []mma.SeatHolders `json:"seats"`
}
if json.Unmarshal(raw, &held) != nil {
return "", false
}
for _, s := range held.Seats {
if s.Seat == seat && len(s.Holders) > 0 {
return s.Holders[0].Module, true
}
}
return "", false
}
}
func main() {
r := ownersRead(stdio.Ask)
r.Builds, r.HavesState = read(stdio.State("mesh-delivery-builder.builds")), read(stdio.State("node-engine.have"))
@@ -416,16 +443,28 @@ func main() {
r.ProvisionsState = read(stdio.State(view.ProvisionsRead))
r.BuildLog = stdio.Log(builds.BuildLogRead).Since
r.Pending = pendingOf
// The build modules are no names (design 47 G A-82): the build seats on the seat list, each held by the module the seat
// list names for it; the seat list and its two tools are wired once by mesh-sdk (§204).
gate := seatlist.NewPart("view", []string{stdio.AnyPerson, "node-tools", "mesh-healers"})
go func() {
<-stdio.Served()
gate.Follow(seatlist.StdioBus(), nil, func(f string, a ...any) { fmt.Fprintf(os.Stderr, "[mesh-delivery-view-tools] "+f+"\n", a...) })
}()
seats := buildrun.NewBuildSeats(func() map[string]seatdef.Definition {
if rd := gate.Reader(); rd != nil {
return rd.Definitions()
}
return nil
}, holderOf(stdio.Ask))
u := view.UsageReader{Haves: r.HavesState, Resolved: read(stdio.State("mesh-config.resolved")), Tries: read(stdio.State("mesh-delivery-checker.tries")),
Steps: map[string]view.StateRead{}, Pauses: map[string]view.StateRead{}, Backlog: backlogOf(socket())}
for _, lang := range buildstep.AskLanguages {
module, _ := buildstep.BuildModuleOf(lang)
u.Steps[lang] = read(stdio.State(module + "." + view.StepsState))
u.Pauses[lang] = read(stdio.State(module + "." + buildstep.PausesState))
}
Seats: seats,
StepsOf: func(seat string) view.StateRead { return read(stdio.State(seat + "." + view.StepsState)) },
PausesOf: func(seat string) view.StateRead { return read(stdio.State(seat + "." + buildstep.PausesState)) },
Backlog: backlogOf(socket())}
// view_first_views and view_first_view_again: where the view's process stands with its first views of the indexer's and
// the assigner's states (ADR 0615 §6), and a re-read by hand.
tools := append(Tools(r, u), views.StatusTools(viewstate.ViewModule, "view_first_views", "view_first_view_again", stdio.State(views.StatusState), meshhandactsclient.Recorder{Ask: stdio.Ask}, time.Now)...)
tools := append(Tools(r, u), gate.Tools()...)
tools = append(tools, views.StatusTools(viewstate.ViewModule, "view_first_views", "view_first_view_again", stdio.State(views.StatusState), meshhandactsclient.Recorder{Ask: stdio.Ask}, time.Now)...)
if err := stdio.Serve("", tools); err != nil {
fmt.Fprintf(os.Stderr, "[mesh-delivery-view-tools] %v\n", err)
os.Exit(1)
@@ -3,6 +3,7 @@ package main
import (
"crypto/ed25519"
"encoding/json"
"errors"
"fmt"
"mesh-delivery-view/internal/view"
"reflect"
@@ -625,3 +626,26 @@ func manifestReadsAsksEveryNode(t *testing.T) {
t.Errorf("node_manifest_reads = %+v", rows)
}
}
// Design 47 G A-82, A-74: a build seat's holder is the module the seat mesh-module-assigner's verb assignments names for it with
// the seats, and none while the seat has no holder or the list cannot be read.
func TestABuildSeatsHolderIsTheOneTheSeatListNames(t *testing.T) {
var asked []string
ask := func(key string, _ any) (json.RawMessage, error) {
asked = append(asked, key)
return json.RawMessage(`{"seats":[{"seat":"node-build-go","scope":"node","holders":[{"module":"acme-go","node":"n1"}]},{"seat":"node-build-node","scope":"node","holders":[]}]}`), nil
}
holder := holderOf(ask)
if m, ok := holder("node-build-go"); !ok || m != "acme-go" {
t.Errorf("the holder of node-build-go = %q, %v; want acme-go", m, ok)
}
if m, ok := holder("node-build-node"); ok {
t.Errorf("a seat with no holder has %q", m)
}
if len(asked) != 2 || asked[0] != "seat:mesh-module-assigner.assignments" {
t.Errorf("asked %v; want the seat mesh-module-assigner's assignments", asked)
}
if _, ok := holderOf(func(string, any) (json.RawMessage, error) { return nil, errors.New("down") })("node-build-go"); ok {
t.Error("a seat list that cannot be read names a holder")
}
}
+1
View File
@@ -16,6 +16,7 @@ require (
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000
@@ -21,6 +21,7 @@ import (
"git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient"
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
buildseats "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun"
"git.novox.be/novox/mesh-delivery/sdk/buildrun"
"git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
@@ -50,9 +51,11 @@ const (
// UsageReader reads what build_usage answers from.
type UsageReader struct {
// Steps and Pauses are each build module's states by language (go, node, image).
Steps map[string]StateRead
Pauses map[string]StateRead
// Seats are the build seats, each by the acting rule that builds, with the module holding it on the seat list (design 47 G
// A-82): no build module is named here. StepsOf and PausesOf read a build seat's steps and pauses through the seat.
Seats *buildseats.BuildSeats
StepsOf func(seat string) StateRead
PausesOf func(seat string) StateRead
// Resolved is mesh-config's resolved values (<node>.<module>.<key>); Haves the node-engine's have.
Resolved StateRead
Haves StateRead
@@ -209,6 +212,16 @@ func (u UsageReader) Usage() UsageAnswer {
note := func(f string, a ...any) { ans.Notes = append(ans.Notes, fmt.Sprintf(f, a...)) }
tries := u.tries(now, &ans, note)
// The build seats once for this answer: each asks the seat list for its holder (design 47 G A-82).
buildSeats := u.Seats.All()
buildSeatOfModule := func(module string) buildseats.BuildModule {
for _, m := range buildSeats {
if m.Module == module && module != "" {
return m
}
}
return buildseats.BuildModule{}
}
seats := map[string]map[string]*SeatUsage{} // node -> language -> seat
seat := func(node, lang string) *SeatUsage {
if seats[node] == nil {
@@ -217,9 +230,13 @@ func (u UsageReader) Usage() UsageAnswer {
if s := seats[node][lang]; s != nil {
return s
}
name, _ := buildstep.BuildSeat(lang)
module, _ := buildstep.BuildModuleOf(lang)
s := &SeatUsage{Seat: name, Module: module, Running: RunningUsage{ByKind: map[string]int{}}, LeftRunning: []StepUsage{}, EndedStale: []StepUsage{}, Steps: []StepUsage{}}
var bm buildseats.BuildModule
for _, m := range buildSeats {
if m.AskLanguage() == lang {
bm = m
}
}
s := &SeatUsage{Seat: bm.Seat, Module: bm.Module, Running: RunningUsage{ByKind: map[string]int{}}, LeftRunning: []StepUsage{}, EndedStale: []StepUsage{}, Steps: []StepUsage{}}
seats[node][lang] = s
return s
}
@@ -233,10 +250,8 @@ func (u UsageReader) Usage() UsageAnswer {
} else {
var want []string
for _, k := range keys {
for _, lang := range buildstep.AskLanguages {
if m, _ := buildstep.BuildModuleOf(lang); strings.HasSuffix(k, "."+m) {
want = append(want, k)
}
if _, module, _ := strings.Cut(k, "."); buildSeatOfModule(module).Seat != "" {
want = append(want, k)
}
}
raws, err := u.Haves.values(want)
@@ -256,8 +271,8 @@ func (u UsageReader) Usage() UsageAnswer {
}
for k, h := range haves {
node, module, _ := strings.Cut(k, ".")
lang := strings.TrimPrefix(module, "mesh-build-")
s := seat(node, lang)
bm := buildSeatOfModule(module)
s := seat(node, bm.AskLanguage())
s.Phase, s.Holds = string(h.Phase), h.Phase == nodeengineclient.PhaseRunning
}
@@ -310,19 +325,27 @@ func (u UsageReader) Usage() UsageAnswer {
}
for key := range unreadable {
node, module, _ := strings.Cut(key, ".")
seat(node, strings.TrimPrefix(module, "mesh-build-"))
bm := buildSeatOfModule(module)
seat(node, bm.AskLanguage())
}
// Every node with a resolved capacity is listed, so "is this node eligible" is answered for it.
for key := range resolved {
node, module, _ := strings.Cut(key, ".")
seat(node, strings.TrimPrefix(module, "mesh-build-"))
bm := buildSeatOfModule(module)
seat(node, bm.AskLanguage())
}
if len(buildSeats) == 0 {
note("no build seat is known from the seat definitions and the seat list, so no steps or pauses are read")
}
// The steps of each build module.
steps := map[string]map[string][]usageStep{} // node -> language -> steps
for _, lang := range buildstep.AskLanguages {
module, _ := buildstep.BuildModuleOf(lang)
sr, ok := u.Steps[lang]
if !ok || sr.Keys == nil || sr.Get == nil {
for _, bm := range buildSeats {
lang, module := bm.AskLanguage(), cmpOr(bm.Module, bm.Seat)
var sr StateRead
if u.StepsOf != nil {
sr = u.StepsOf(bm.Seat)
}
if sr.Keys == nil || sr.Get == nil {
note("the steps of %s are not readable here", module)
continue
}
@@ -379,10 +402,13 @@ func (u UsageReader) Usage() UsageAnswer {
// Pauses: paused.<node> of each module; and capacity.<node>, the capacity the module says it judges by.
paused := map[string]bool{} // <node>.<language>
judged := map[string]buildstep.Capacity{}
for _, lang := range buildstep.AskLanguages {
module, _ := buildstep.BuildModuleOf(lang)
pr, ok := u.Pauses[lang]
if !ok || pr.Get == nil {
for _, bm := range buildSeats {
lang, module := bm.AskLanguage(), cmpOr(bm.Module, bm.Seat)
var pr StateRead
if u.PausesOf != nil {
pr = u.PausesOf(bm.Seat)
}
if pr.Get == nil {
note("the pauses of %s are not readable here, so none is said paused", module)
continue
}
@@ -422,7 +448,8 @@ func (u UsageReader) Usage() UsageAnswer {
for _, node := range nodes {
nu := NodeUsage{Node: node, Seats: []SeatUsage{}, Said: []string{},
CPU: "not on the bus: the disk-load module keeps no state; " + node + "/disk-load.cpu_top reads it (the build modules' share among its units)"}
for _, lang := range buildstep.AskLanguages {
for _, bm := range buildSeats {
lang := bm.AskLanguage()
s := seats[node][lang]
if s == nil {
continue
@@ -696,3 +723,11 @@ func sumMedian(d []int64) (sum, median int64) {
}
return sum, s[(len(s)-1)/2]
}
// cmpOr is the first of two names that is not empty.
func cmpOr(a, b string) string {
if a != "" {
return a
}
return b
}
@@ -4,6 +4,8 @@ import (
"encoding/json"
"errors"
"fmt"
buildseats "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun"
"git.novox.be/novox/mesh-sdk/go/seatdef"
"strconv"
"strings"
"testing"
@@ -82,8 +84,19 @@ func usageFixture() UsageReader {
"shanks.mesh-config.other": `{"value":"x"}`,
"g14.mesh-build-node.capacity": `{"node":"g14","module":"mesh-build-node","key":"capacity","source":"unreadable"}`,
}),
Steps: map[string]StateRead{"go": states(goSteps), "node": states(map[string]string{}), "image": states(map[string]string{})},
Pauses: map[string]StateRead{"go": states(map[string]string{"paused.ace": `{"v":1,"paused":true,"by":"lead@shanks","at":"2026-10-26T09:00:00Z"}`}), "node": states(nil), "image": states(nil)},
Seats: buildSeatsOf(),
StepsOf: func(seat string) StateRead {
if seat == "node-build-go" {
return states(goSteps)
}
return states(map[string]string{})
},
PausesOf: func(seat string) StateRead {
if seat == "node-build-go" {
return states(map[string]string{"paused.ace": `{"v":1,"paused":true,"by":"lead@shanks","at":"2026-10-26T09:00:00Z"}`})
}
return states(nil)
},
Tries: states(map[string]string{
"try-" + strconv.FormatInt(at("11:29").UnixNano(), 10): `{"id":"try-1","repository":"novox/mesh-sdk","head":"h","trunk":"t","state":"pass","asks":["` + tryAsk + `"]}`,
"try-" + strconv.FormatInt(at("11:58").UnixNano(), 10): `{"id":"try-2","repository":"novox/mesh-delivery","head":"h","trunk":"t","state":"running","started":"2026-10-26T11:58:00Z"}`,
@@ -167,7 +180,7 @@ func TestBuildUsageAnswersEveryNodesUseFromTheStates(t *testing.T) {
// Nothing readable: each state is named in a note, the backlog's refusal too, and nothing is said held.
none := UsageReader{Now: func() time.Time { return usageNow }, Backlog: func() ([]BacklogRow, error) { return nil, errors.New("no socket") }}.Usage()
notes := strings.Join(none.Notes, " | ")
for _, want := range []string{"have is not readable", "capacity unknown to this tool: mesh-config's resolved values are not readable", "steps of mesh-build-go are not readable", "tries are not readable", "did not answer: no socket"} {
for _, want := range []string{"have is not readable", "capacity unknown to this tool: mesh-config's resolved values are not readable", "no build seat is known", "tries are not readable", "did not answer: no socket"} {
if !strings.Contains(notes, want) {
t.Errorf("the notes %q do not say %q", notes, want)
}
@@ -196,8 +209,8 @@ func TestBuildUsageAnswersEveryNodesUseFromTheStates(t *testing.T) {
// Issue 1806's follow-up: the module on shanks says it judges by 1 of mesh-config's 2 (its build CPU bounds it); the
// seat says the module's capacity and why. And issue 1890: a running try that names its asks is said with them.
u = usageFixture()
u.Pauses["go"] = states(map[string]string{"capacity.shanks": `{"v":1,"effective":1,"configured":2,"quota-from":"the cpu.max of /meshyield.slice","quota-cores":6,"cores-per-step":6,"by-cpu":1,"why":"1, bounded by the CPU","at":"2026-10-26T11:00:00Z"}`,
"capacity.ace": `{"v":1,"effective":2,"configured":3,"why":"stale: mesh-config says 2 now"}`})
setPauses(&u, "node-build-go", states(map[string]string{"capacity.shanks": `{"v":1,"effective":1,"configured":2,"quota-from":"the cpu.max of /meshyield.slice","quota-cores":6,"cores-per-step":6,"by-cpu":1,"why":"1, bounded by the CPU","at":"2026-10-26T11:00:00Z"}`,
"capacity.ace": `{"v":1,"effective":2,"configured":3,"why":"stale: mesh-config says 2 now"}`}))
u.Tries = states(map[string]string{"try-" + strconv.FormatInt(at("11:58").UnixNano(), 10): `{"id":"try-2","repository":"novox/mesh-delivery","head":"h","trunk":"t","state":"running","asks":["` + tryAsk0 + `"],"steps":[{"ask":"` + tryAsk0 + `","node":"shanks","state":"running","phase":"unit: tests of 3 packages"}]}`})
bound := u.Usage()
sc := seatOf(t, bound, "shanks", "node-build-go")
@@ -208,13 +221,13 @@ func TestBuildUsageAnswersEveryNodesUseFromTheStates(t *testing.T) {
t.Errorf("a module's word older than mesh-config's value is not taken: %+v", sa)
}
// The operator, 2026-10-26: outside its build hours the seat takes nothing new, and says so.
u.Pauses["go"] = states(map[string]string{"capacity.shanks": `{"v":1,"effective":0,"configured":2,"build-hours":"18:00-06:00 CET","outside-hours":true,"why":"0, outside its build hours 18:00-06:00 CET until 18:00 (otherwise 2)","at":"2026-10-26T11:00:00Z"}`})
setPauses(&u, "node-build-go", states(map[string]string{"capacity.shanks": `{"v":1,"effective":0,"configured":2,"build-hours":"18:00-06:00 CET","outside-hours":true,"why":"0, outside its build hours 18:00-06:00 CET until 18:00 (otherwise 2)","at":"2026-10-26T11:00:00Z"}`}))
if so := seatOf(t, u.Usage(), "shanks", "node-build-go"); so.Capacity != 0 || !so.OutsideHours || so.Eligible == nil || *so.Eligible ||
so.WhyNot != "outside its build hours 18:00-06:00 CET on shanks: it takes nothing new until they open" {
t.Errorf("a seat outside its build hours: %+v", so)
}
// A build-hours value the module cannot read: it builds at any hour, and the seat says the value and why.
u.Pauses["go"] = states(map[string]string{"capacity.shanks": `{"v":1,"effective":2,"configured":2,"build-hours-fault":"the build hours \"shanks=18:00-6\": \"6\" is not a time HH:MM","why":"2; at any hour, because its build hours cannot be read","at":"2026-10-26T11:00:00Z"}`})
setPauses(&u, "node-build-go", states(map[string]string{"capacity.shanks": `{"v":1,"effective":2,"configured":2,"build-hours-fault":"the build hours \"shanks=18:00-6\": \"6\" is not a time HH:MM","why":"2; at any hour, because its build hours cannot be read","at":"2026-10-26T11:00:00Z"}`}))
if sf := seatOf(t, u.Usage(), "shanks", "node-build-go"); sf.BuildHours != "any hour" || !strings.Contains(sf.BuildHoursFault, "shanks=18:00-6") || sf.Eligible == nil || !*sf.Eligible {
t.Errorf("a seat whose build hours cannot be read: %+v", sf)
}
@@ -225,3 +238,25 @@ func TestBuildUsageAnswersEveryNodesUseFromTheStates(t *testing.T) {
}
var tryAsk0 = stepID(at("11:58"), "a00010")
// buildSeatsOf is the build seats as the seat definitions and the seat list give them (design 47 G A-82): the seats whose
// acting rule builds, each held by the module the seat list names.
func buildSeatsOf() *buildseats.BuildSeats {
rule := func(on string) seatdef.Definition {
return seatdef.Definition{Acts: []seatdef.ActingRule{{Step: seatdef.StepBuild, On: on}}}
}
defs := map[string]seatdef.Definition{"node-build-go": rule("go"), "node-build-node": rule("typescript"), "node-build-image": rule(seatdef.OnKindImage)}
holders := map[string]string{"node-build-go": "mesh-build-go", "node-build-node": "mesh-build-node", "node-build-image": "mesh-build-image"}
return buildseats.NewBuildSeats(func() map[string]seatdef.Definition { return defs }, func(seat string) (string, bool) { h, ok := holders[seat]; return h, ok })
}
// setPauses replaces what one build seat's pauses read.
func setPauses(u *UsageReader, seat string, st StateRead) {
prev := u.PausesOf
u.PausesOf = func(s string) StateRead {
if s == seat {
return st
}
return prev(s)
}
}
+5 -3
View File
@@ -106,7 +106,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
if m.Module != "mesh-delivery-view" {
t.Errorf("module = %q", m.Module)
}
want := []string{"mesh-build-go.pauses", "mesh-build-go.steps", "mesh-build-image.pauses", "mesh-build-image.steps", "mesh-build-node.pauses", "mesh-build-node.steps", "mesh-conditions.conditions", "mesh-config.resolved", "mesh-delivery-builder.build-log", "mesh-delivery-builder.builds", "mesh-delivery-checker.tries", "mesh-delivery-gate.asks", "mesh-delivery-gate.source", "mesh-delivery-teller.answers", "mesh-delivery-watchman.presence", "mesh-delivery-watchman.stuck", "mesh-healers.attempts", "mesh-provisioner.provisions", "mesh-vault.signers", "node-engine.have"} // no reads of the indexer's or the assigner's states (ADR 0615 §1, DLV-M4); view_tiers reads the module graph and the provisioner's keys (ADR 0603 §6); view_status reads the keeper's conditions and the healers' attempts (1952/V1); build_usage reads the build modules' steps and pauses, mesh-config's capacities and the checker's tries; 1168/AC1, 1241/AC4, 1345/AC3, 1297/B8, B9
want := []string{"mesh-conditions.conditions", "mesh-config.resolved", "mesh-delivery-builder.build-log", "mesh-delivery-builder.builds", "mesh-delivery-checker.tries", "mesh-delivery-gate.asks", "mesh-delivery-gate.source", "mesh-delivery-teller.answers", "mesh-delivery-watchman.presence", "mesh-delivery-watchman.stuck", "mesh-healers.attempts", "mesh-provisioner.provisions", "mesh-vault.signers", "node-build-go.pauses", "node-build-go.steps", "node-build-image.pauses", "node-build-image.steps", "node-build-node.pauses", "node-build-node.steps", "node-engine.have"} // the build seats' states by seat, never a build module (design 47 G A-82); // no reads of the indexer's or the assigner's states (ADR 0615 §1, DLV-M4); view_tiers reads the module graph and the provisioner's keys (ADR 0603 §6); view_status reads the keeper's conditions and the healers' attempts (1952/V1); build_usage reads the build modules' steps and pauses, mesh-config's capacities and the checker's tries; 1168/AC1, 1241/AC4, 1345/AC3, 1297/B8, B9
got := slices.Clone(m.Reads)
slices.Sort(got)
if !reflect.DeepEqual(got, want) {
@@ -124,7 +124,9 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
"mesh-delivery-indexer.indexer_read_graph", "mesh-delivery-indexer.indexer_read_heard", "mesh-delivery-indexer.indexer_pending",
"mesh-delivery-indexer.indexer_read_packages", "mesh-delivery-indexer.indexer_read_predefined", "mesh-delivery-indexer.indexer_read_taken",
"seat:mesh-catalog.revision", // ADR 0663: the modules are the catalog half's, read through the seat's verb revision, never the indexer's
"seat:mesh-catalog.seat-definitions", // design 47 G A-82, §204: the build seats are read from the seat list
"seat:mesh-hand-acts.hand-act", // the join: its first view asked again by hand is a hand act (views.StatusTools)
"seat:mesh-module-assigner.assignments", // A-82: the holder of each build seat
"seat:node-engine.manifest-reads"}; !reflect.DeepEqual(m.Invokes, want) { // ADR 0618 §3: node_manifest_reads asks the node-engine seat's verb of every node; 1585/T24: view_tiers asks indexer_pending, a verb edge (ADR 0614 §2)
t.Errorf("invokes = %v, want %v", m.Invokes, want)
}
@@ -139,7 +141,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
t.Errorf("version = %q, want 0.1", m.Version)
}
if want := []string{"mesh-conditions.findings-asked", "mesh-delivery-assigner.views.asks", "mesh-delivery-assigner.views.assignments", "mesh-delivery-assigner.views.wants",
"mesh-catalog.catalog-revision", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.taken"}; !reflect.DeepEqual(m.Consumes, want) {
"mesh-catalog.catalog-revision", "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.taken"}; !reflect.DeepEqual(m.Consumes, want) {
t.Errorf("consumes = %v, want %v", m.Consumes, want)
}
// The one event the view says: a source revision is delivered (1906/D9, ADR 0539 §17), derived by it from the wants and the haves.
@@ -164,7 +166,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
}
// The two read tools of the indexer's modules (issue 1562), carried by a bundle the runtime launches; the indexer
// itself serves no tool (ADR 0437 §4). Carrying them gives the tool runner read access to every bucket the view reads.
if want := []string{"view_status", "indexer_module", "indexer_modules", "indexer_candidate", "indexer_candidates", "indexer_predefined", "indexer_config_candidates", "indexer_packages", "builder_build", "builder_builds", "builder_history", "assigner_wants", "assigner_want", "assigner_differences", "node_haves", "node_trusts", "build_usage", "view_reach", "indexer_module_history", "indexer_heard", "view_tiers", "view_first_views", "view_first_view_again", "node_manifest_reads"}; !reflect.DeepEqual(m.Tools, want) {
if want := []string{"view_status", "indexer_module", "indexer_modules", "indexer_candidate", "indexer_candidates", "indexer_predefined", "indexer_config_candidates", "indexer_packages", "builder_build", "builder_builds", "builder_history", "assigner_wants", "assigner_want", "assigner_differences", "node_haves", "node_trusts", "build_usage", "view_reach", "indexer_module_history", "indexer_heard", "view_tiers", "view_first_views", "view_first_view_again", "node_manifest_reads", "view_seat_list", "view_seat_list_again"}; !reflect.DeepEqual(m.Tools, want) {
t.Errorf("tools = %v, want %v", m.Tools, want)
}
// D-11: the holders and buckets the view names are its manifest's (no contract names a holder, novox/hq ADR 0634 §1): a
+13 -7
View File
@@ -27,7 +27,9 @@
"view_tiers",
"view_first_views",
"view_first_view_again",
"node_manifest_reads"
"node_manifest_reads",
"view_seat_list",
"view_seat_list_again"
],
"emits": [
"delivered",
@@ -56,6 +58,8 @@
"mesh-delivery-assigner.views.assignments",
"mesh-delivery-assigner.views.wants",
"mesh-catalog.catalog-revision",
"mesh-catalog.seat-definition-changed",
"mesh-catalog.seat-definition-removed",
"mesh-delivery-indexer.views.candidates",
"mesh-delivery-indexer.views.taken"
],
@@ -73,16 +77,18 @@
"mesh-delivery-indexer.indexer_read_predefined",
"mesh-delivery-indexer.indexer_read_taken",
"seat:mesh-catalog.revision",
"seat:mesh-catalog.seat-definitions",
"seat:mesh-hand-acts.hand-act",
"seat:mesh-module-assigner.assignments",
"seat:node-engine.manifest-reads"
],
"reads": [
"mesh-build-go.pauses",
"mesh-build-go.steps",
"mesh-build-image.pauses",
"mesh-build-image.steps",
"mesh-build-node.pauses",
"mesh-build-node.steps",
"node-build-go.pauses",
"node-build-go.steps",
"node-build-image.pauses",
"node-build-image.steps",
"node-build-node.pauses",
"node-build-node.steps",
"mesh-config.resolved",
"mesh-delivery-builder.build-log",
"mesh-delivery-builder.builds",