D2 (view): build_usage reads the build seats' steps and pauses as seat reads in the holder's state

The manifest reads seat:<seat>.steps and .pauses, which the grants part resolves, and the tools read <holder>.steps and <holder>.pauses.

Builds: design 47 G A-82, A-128

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 22:17:20 +01:00
parent 406e17446f
commit b056f32c13
4 changed files with 44 additions and 9 deletions
@@ -422,6 +422,21 @@ func holderOf(ask func(key string, body any) (json.RawMessage, error)) func(seat
}
}
// ofHolder reads a build seat's state (`steps`, `pauses`) in the state of the seat's holder, `<holder>.<state>`, the holder found
// as seatlist.HolderOf finds it (design 47 G A-128, A-126), as the manifest's `seat:<seat>.<state>` reads name it: the grants
// part resolves a read only in that form. A seat whose holder cannot be found reads as an error naming it.
func ofHolder(holder func(seat string) (string, bool), read func(name string) view.StateRead, state string) func(seat string) view.StateRead {
return func(seat string) view.StateRead {
module, ok := holder(seat)
if !ok {
fail := fmt.Errorf("the holder of %s is not found", seat)
return view.StateRead{Keys: func() ([]string, error) { return nil, fail },
Get: func(string) (json.RawMessage, error) { return nil, fail }}
}
return read(module + "." + state)
}
}
func main() {
logf := func(f string, a ...any) { fmt.Fprintf(os.Stderr, "[mesh-delivery-view-tools] "+f+"\n", a...) }
// The catalog half per watched branch, followed by the bundle itself through meshcatalogclient's branch follower on
@@ -463,8 +478,8 @@ func main() {
}, holderOf(stdio.Ask))
u := view.UsageReader{Haves: r.HavesState, Resolved: read(stdio.State("mesh-config.resolved")), Tries: read(stdio.State("mesh-delivery-checker.tries")),
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)) },
StepsOf: ofHolder(holderOf(stdio.Ask), func(name string) view.StateRead { return read(stdio.State(name)) }, view.StepsState),
PausesOf: ofHolder(holderOf(stdio.Ask), func(name string) view.StateRead { return read(stdio.State(name)) }, 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.
@@ -7,6 +7,7 @@ import (
"fmt"
"mesh-delivery-view/internal/view"
"reflect"
"slices"
"strings"
"sync/atomic"
"testing"
@@ -650,3 +651,22 @@ func TestABuildSeatsHolderIsTheOneTheSeatListNames(t *testing.T) {
t.Error("a seat list that cannot be read names a holder")
}
}
// A build seat's steps and pauses are read in the state of the seat's holder, `<holder>.steps` and `<holder>.pauses`, the form
// the manifest's `seat:<seat>.<state>` reads name and the grants part resolves (design 47 G A-82, A-128), never the seat's own name.
func TestBuildSeatStatesAreReadInTheHoldersState(t *testing.T) {
var named []string
read := func(name string) view.StateRead {
named = append(named, name)
return view.StateRead{Keys: func() ([]string, error) { return nil, nil }}
}
holder := func(seat string) (string, bool) { return "acme-" + seat, seat != "node-build-node" }
ofHolder(holder, read, view.StepsState)("node-build-go")
ofHolder(holder, read, "pauses")("node-build-go")
if want := []string{"acme-node-build-go.steps", "acme-node-build-go.pauses"}; !slices.Equal(named, want) {
t.Errorf("read %v, want %v", named, want)
}
if _, err := ofHolder(holder, read, view.StepsState)("node-build-node").Keys(); err == nil {
t.Error("a seat with no holder reads without an error")
}
}
+1 -1
View File
@@ -153,7 +153,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
if m.Module != "mesh-delivery-view" {
t.Errorf("module = %q", m.Module)
}
want := []string{"mesh-conditions.conditions", "mesh-config.resolved", "mesh-delivery-builder.build-log", "mesh-delivery-builder.builds", "mesh-delivery-checker.tries", "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 read of the gate's source: each watched branch's is its verb source's (§512, A-173); // 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-watchman.presence", "mesh-delivery-watchman.stuck", "mesh-healers.attempts", "mesh-provisioner.provisions", "mesh-vault.signers", "node-engine.have", "seat:node-build-go.pauses", "seat:node-build-go.steps", "seat:node-build-image.pauses", "seat:node-build-image.steps", "seat:node-build-node.pauses", "seat:node-build-node.steps"} // the build seats' states by seat, never a build module (design 47 G A-82); no read of the gate's source: each watched branch's is its verb source's (§512, A-173); // 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) {
+6 -6
View File
@@ -121,12 +121,12 @@
"seat:node-engine.manifest-reads"
],
"reads": [
"node-build-go.pauses",
"node-build-go.steps",
"node-build-image.pauses",
"node-build-image.steps",
"node-build-node.pauses",
"node-build-node.steps",
"seat:node-build-go.pauses",
"seat:node-build-go.steps",
"seat:node-build-image.pauses",
"seat:node-build-image.steps",
"seat:node-build-node.pauses",
"seat:node-build-node.steps",
"mesh-config.resolved",
"mesh-delivery-builder.build-log",
"mesh-delivery-builder.builds",