The assigner's state and verb placement are assignments, with its events, tools, views and every caller; the assigner copies the retired state at its start (issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 22:13:20 +01:00
parent 369ebd092d
commit 0eafb2fe95
164 changed files with 1699 additions and 1498 deletions
+90 -92
View File
@@ -22,17 +22,17 @@ import (
"github.com/nats-io/nats.go/jetstream"
"git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient"
"git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient"
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
"git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient"
"git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshnodeenroller"
"git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault"
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
"git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
"git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient"
"git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-delivery/modules/mesh-grants/grantsstate"
@@ -339,12 +339,9 @@ func (b *fakeBus) refuse(module, raw, problem string) {
Problems: []string{problem}})
}
// assign places module on node in the seat's placement, the one place the part reads it (ADR 0549 §5; issue 774).
// assign places module on node in the seat's assignments, the one place the part reads it (ADR 0549 §5; issue 774).
func (b *fakeBus) assign(node, module string) {
var pl meshmoduleassigner.Placement
if raw, ok := b.states[grants.ReadPlacement]["placement."+node]; ok {
pl, _ = meshmoduleassignerclient.DecodePlacement(raw)
}
pl := b.assignmentsOf(node)
pl.Node = node
for _, m := range pl.Modules {
if m.Module == module {
@@ -352,25 +349,37 @@ func (b *fakeBus) assign(node, module string) {
}
}
pl.Modules = append(pl.Modules, meshmoduleassigner.PlacedModule{Module: module})
raw, err := meshmoduleassignerclient.EncodePlacement(pl)
b.setAssignments(pl)
}
// unassign takes module off node in the seat's assignments.
func (b *fakeBus) unassign(node, module string) {
pl := b.assignmentsOf(node)
pl.Modules = slices.DeleteFunc(pl.Modules, func(m meshmoduleassigner.PlacedModule) bool { return m.Module == module })
b.setAssignments(pl)
}
// assignmentsOf is what the seat's state holds of node, assembled from its keys.
func (b *fakeBus) assignmentsOf(node string) meshmoduleassigner.Assignments {
got, _ := meshmoduleassignerclient.AssignmentsOf(b.states[grants.ReadAssignments])
return got[node]
}
// setAssignments holds a node's assignments in the seat's state as its keys: a key per module, and the node key.
func (b *fakeBus) setAssignments(pl meshmoduleassigner.Assignments) {
if b.states[grants.ReadAssignments] == nil {
b.states[grants.ReadAssignments] = map[string][]byte{}
}
for key := range b.states[grants.ReadAssignments] {
if strings.HasPrefix(key, pl.Node+".") {
delete(b.states[grants.ReadAssignments], key)
}
}
values, err := meshmoduleassignerclient.AssignmentsValues(pl)
if err != nil {
panic(err)
}
if b.states[grants.ReadPlacement] == nil {
b.states[grants.ReadPlacement] = map[string][]byte{}
}
b.states[grants.ReadPlacement]["placement."+node] = raw
}
// unassign takes module off node in the seat's placement.
func (b *fakeBus) unassign(node, module string) {
pl, err := meshmoduleassignerclient.DecodePlacement(b.states[grants.ReadPlacement]["placement."+node])
if err != nil {
return
}
pl.Modules = slices.DeleteFunc(pl.Modules, func(m meshmoduleassigner.PlacedModule) bool { return m.Module == module })
raw, _ := meshmoduleassignerclient.EncodePlacement(pl)
b.states[grants.ReadPlacement]["placement."+node] = raw
maps.Copy(b.states[grants.ReadAssignments], values)
}
const (
@@ -576,8 +585,8 @@ func TestGrants(t *testing.T) {
key, _ := builds.BuilderBuildKey("mod-x", fp)
b.put(grants.ReadBuilds, key, map[string]any{"v": keys.Version, "state": "done", "manifest": digest})
}, []string{"a.mod-x", "a.nats", "b.mod-y", "node.a", "node.b"}, "people"},
{"placement refused is a gap; the machines come from the haves", func(b *fakeBus) { b.refused[grants.ReadPlacement] = true },
[]string{"node.a", "node.b"}, grants.ReadPlacement},
{"assignments refused is a gap; the machines come from the haves", func(b *fakeBus) { b.refused[grants.ReadAssignments] = true },
[]string{"node.a", "node.b"}, grants.ReadAssignments},
} {
t.Run(row.name, func(t *testing.T) {
b := fixture()
@@ -644,7 +653,7 @@ func TestGrants(t *testing.T) {
}
})
}
// ADR 0567 §3, §9: the seat mesh-placement's candidates are read by every node agent (mesh-sdk NodeComposeReads)
// ADR 0567 §3, §9: the seat mesh-module-assigner's candidates are read by every node agent (mesh-sdk NodeComposeReads)
// and by a module whose manifest reads them (the view), written by their holder, the assigner, and read by no other.
cands := fixture()
cands.manifestOf("mesh-delivery-assigner", `{"module":"mesh-delivery-assigner","version":"1","own-secrets":{"broker":"/var/lib/as/broker"},"state":[{"name":"candidates","history":5}]}`)
@@ -655,12 +664,12 @@ func TestGrants(t *testing.T) {
}
var readers []string
for _, u := range shadowOf(t, cands) {
if slices.Contains(u.Publish, "$JS.API.DIRECT.GET.KV_"+buscompose.PlacementCandidatesBucket+".>") {
if slices.Contains(u.Publish, "$JS.API.DIRECT.GET.KV_"+buscompose.AssignmentsCandidatesBucket+".>") {
readers = append(readers, u.User)
}
}
if want := []string{"a.mesh-delivery-assigner", "a.mesh-delivery-view", "node.a", "node.b"}; !slices.Equal(readers, want) {
t.Errorf("the placement candidates are read by %v, want the node agents, the view and their holder %v", readers, want)
t.Errorf("the assignments candidates are read by %v, want the node agents, the view and their holder %v", readers, want)
}
b := fixture()
users := shadowOf(t, b)
@@ -880,7 +889,7 @@ func TestGrants(t *testing.T) {
recs, _ := grants.Records(grants.ReadInputs(b, &grants.Manifests{}))
grants.WithRootFree(&recs, b)
kept := false
for _, k := range buscompose.PlacementsOf(recs, recs.Interchangeable).Kinds {
for _, k := range buscompose.AssignmentsOf(recs, recs.Interchangeable).Kinds {
kept = kept || slices.Contains(k.Capabilities, "verified-sender")
}
if kept != row.keeps {
@@ -1134,7 +1143,7 @@ func TestGrants(t *testing.T) {
{"equal", shadow, nil, nil, nil, nil, true, nil, nil, 0},
{"one user the bus lacks", shadow[1:], []string{shadow[0].User}, nil, nil, nil, false, nil, []string{shadow[0].User}, 0},
{"one loaded user still placed", append(slices.Clone(shadow), grants.User{User: "a.mod-x.worker"}), nil, []string{"a.mod-x.worker"}, nil, nil, false, nil, []string{"a.mod-x.worker"}, 0},
{"one loaded user placement no longer names", append(slices.Clone(shadow), grants.User{User: "b.gone"}), nil, []string{"b.gone"}, []string{"b.gone"}, nil, true, nil, nil, 0},
{"one loaded user assignments no longer names", append(slices.Clone(shadow), grants.User{User: "b.gone"}), nil, []string{"b.gone"}, []string{"b.gone"}, nil, true, nil, nil, 0},
{"grants differ", changed, nil, nil, nil, []string{"a.mod-x"}, false, nil, []string{"a.mod-x"}, 1},
{"only a right added", fewer, nil, nil, nil, []string{"a.mod-x"}, false, nil, nil, 1},
{"a deny added refuses", readable, nil, nil, nil, []string{"a.mod-x"}, false, nil, []string{"a.mod-x"}, 1},
@@ -1196,9 +1205,9 @@ func TestGrants(t *testing.T) {
}
})
// 1126 (ADR 0549 §1, §3): once the catalogue places the nodes, a seat held once for the mesh is held where placement
// says, not on every machine running a module that claims it; what placement adds is composed beside the assignments.
t.Run("a mesh seat is held where placement says", func(t *testing.T) {
// 1126 (ADR 0549 §1, §3): once the catalogue places the nodes, a seat held once for the mesh is held where assignments
// says, not on every machine running a module that claims it; what assignments adds is composed beside the assignments.
t.Run("a mesh seat is held where assignments says", func(t *testing.T) {
store := catalogue.Manifest{Module: "store", Version: "1",
DefinesSeats: []catalogue.SeatDeclaration{{Name: "store-seat", Scope: catalogue.ScopeMesh, Serves: []catalogue.Verb{{Name: "databases"}}}},
Claims: []catalogue.Claim{{Name: "store-seat", Scope: catalogue.ScopeMesh}}}
@@ -1212,51 +1221,41 @@ func TestGrants(t *testing.T) {
return n
}
if recs, _ := grants.Records(in); holds(recs, "a") != 1 || holds(recs, "b") != 1 {
t.Fatalf("before placement the seat is held on a %d and b %d times, want both", holds(recs, "a"), holds(recs, "b"))
t.Fatalf("before assignments the seat is held on a %d and b %d times, want both", holds(recs, "a"), holds(recs, "b"))
}
in.Placed = map[string]meshmoduleassigner.Placement{
in.Placed = map[string]meshmoduleassigner.Assignments{
"a": {Node: "a", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}}, Holds: []meshmoduleassigner.Holding{{Claim: "store-seat", Scope: "mesh", Module: "store"}}},
"b": {Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}}}}
if recs, _ := grants.Records(in); holds(recs, "a") != 1 || holds(recs, "b") != 0 {
t.Errorf("placed, the seat is held on a %d and b %d times, want a alone", holds(recs, "a"), holds(recs, "b"))
}
b := fixture()
pl, _ := meshmoduleassignerclient.EncodePlacement(meshmoduleassigner.Placement{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "mod-y"}, {Module: "mod-x"}}})
b.states[grants.ReadPlacement] = map[string][]byte{"placement.b": pl}
b.setAssignments(meshmoduleassigner.Assignments{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "mod-y"}, {Module: "mod-x"}}})
read := grants.ReadInputs(b, &grants.Manifests{})
if !slices.Contains(read.Assigned["b"], "mod-x") || read.Placed["b"].Node != "b" || slices.ContainsFunc(read.Gaps, func(g grants.Gap) bool { return g.Input == "seat holdings" }) {
t.Errorf("read: assigned %v, placed %v, gaps %v", read.Assigned, read.Placed, read.Gaps)
}
// Research 072 row 37: a node key and a key per module, the legacy mirror left out, read the same.
perModule, _ := meshmoduleassignerclient.PlacementValues(meshmoduleassigner.Placement{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "mod-y"}, {Module: "mod-x"}}})
delete(perModule, "placement.b")
nb := fixture()
nb.states[grants.ReadPlacement] = perModule
if got := grants.ReadInputs(nb, &grants.Manifests{}); !slices.Equal(meshmoduleassignerclient.Assigned(got.Placed["b"]), []string{"mod-y", "mod-x"}) ||
slices.ContainsFunc(got.Gaps, func(g grants.Gap) bool { return strings.HasPrefix(g.Input, grants.ReadPlacement) }) {
t.Errorf("read of the keys per module: placed %v, gaps %v", got.Placed, got.Gaps)
}
b.refused[grants.ReadPlacement] = true
// The placement alone places (issue 774, 774/S29): not read, nothing is placed, and it is a failed read.
if read := grants.ReadInputs(b, &grants.Manifests{}); !slices.ContainsFunc(read.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadPlacement && g.Failed }) ||
b.refused[grants.ReadAssignments] = true
// The assignments alone places (issue 774, 774/S29): not read, nothing is placed, and it is a failed read.
if read := grants.ReadInputs(b, &grants.Manifests{}); !slices.ContainsFunc(read.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadAssignments && g.Failed }) ||
len(read.Assigned) != 0 {
t.Errorf("placement not on the bus is no failed read, or something is placed: %v, %v", read.Gaps, read.Assigned)
t.Errorf("assignments not on the bus is no failed read, or something is placed: %v, %v", read.Gaps, read.Assigned)
}
// Issue 1945, the user list of 2026-10-27 09:21: a placement's holds are the holders on record, not every holder.
// A mesh seat no placement holds keeps the module claiming it as its holder, wherever placement puts it.
// Issue 1945, the user list of 2026-10-27 09:21: the assignments' holds are the holders on record, not every holder.
// A mesh seat no assignments holds keeps the module claiming it as its holder, wherever assignments puts it.
in.Manifests["other"] = catalogue.Manifest{Module: "other", Version: "1", Claims: []catalogue.Claim{{Name: "other-seat", Scope: catalogue.ScopeMesh}},
DefinesSeats: []catalogue.SeatDeclaration{{Name: "other-seat", Scope: catalogue.ScopeMesh, Serves: []catalogue.Verb{{Name: "v"}}}}}
in.Assigned["b"] = append(in.Assigned["b"], "other")
in.Placed["b"] = meshmoduleassigner.Placement{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}, {Module: "other"}}}
in.Placed["b"] = meshmoduleassigner.Assignments{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}, {Module: "other"}}}
if recs, _ := grants.Records(in); holds(recs, "a") != 1 || holds(recs, "b") != 1 {
t.Errorf("a seat no placement holds is held on b %d times, want once (and store-seat on a alone: a %d)", holds(recs, "b"), holds(recs, "a"))
t.Errorf("a seat no assignments holds is held on b %d times, want once (and store-seat on a alone: a %d)", holds(recs, "b"), holds(recs, "a"))
}
in.Placed["b"] = meshmoduleassigner.Placement{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}, {Module: "other"}},
in.Placed["b"] = meshmoduleassigner.Assignments{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}, {Module: "other"}},
Holds: []meshmoduleassigner.Holding{{Claim: "other-seat", Scope: "mesh", Module: "someone-else"}}}
if recs, _ := grants.Records(in); holds(recs, "b") != 0 {
t.Errorf("a seat placement holds by another module is still held by other on b")
t.Errorf("a seat assignments holds by another module is still held by other on b")
}
// The live shapes: the seed's novox placement (mesh-catalog 0c61bad, its twelve holdings, without
// The live shapes: the seed's novox assignments (mesh-catalog 0c61bad, its twelve holdings, without
// anthropic-licence-manager), the licence manager's manifest (mesh-catalog c05aa5e6), and the subjects the bus
// grants novox.node-tools for its seat (nats_users, 2026-10-27 19:40Z).
file := func(name string, v any) {
@@ -1268,9 +1267,9 @@ func TestGrants(t *testing.T) {
t.Fatal(err)
}
}
var seed meshmoduleassigner.Placement
var seed meshmoduleassigner.Assignments
var live struct{ Users []grants.User }
file("placement-novox-0c61bad.json", &seed)
file("assignments-novox-0c61bad.json", &seed)
file("node-tools-novox-licence-subjects.json", &live)
licence, _ := os.ReadFile(filepath.Join("testdata", "claude-licence-manager.json"))
n := newFakeBus()
@@ -1285,14 +1284,13 @@ func TestGrants(t *testing.T) {
n.assign("novox", "claude-licence-manager")
n.manifestOf("node-tools", `{"module":"node-tools","version":"1","tool-runner":true}`) // the machine's tool runner, which carries the seat
n.assign("novox", "node-tools")
// The live placement names mesh-host; with its manifest's state the runner once carried it and the whole list was
// The live assignments names mesh-host; with its manifest's state the runner once carried it and the whole list was
// refused as a second writer of the engine's health (issue 774, 2026-10-30): the shadow below now composes, with the states the
// engine's manifest declares today (health, findings, data, packages): the findings state, written by each node's engine
// alone (ADR 0636 §1), does not bring the refusal back, and the runner is granted none of them.
n.manifestOf(buscompose.EngineModule, `{"module":"mesh-host","version":"1","consumes":["restic.backed-up"],"state":[{"name":"health"},{"name":"findings"},{"name":"data"},{"name":"packages"}]}`)
n.put(grants.ReadHave, "novox.node", map[string]any{"v": 1})
pl, _ = meshmoduleassignerclient.EncodePlacement(seed)
n.states[grants.ReadPlacement] = map[string][]byte{"placement.novox": pl}
n.setAssignments(seed)
recs, _ := grants.Records(grants.ReadInputs(n, &grants.Manifests{}))
var tools []buscompose.ProcessPrincipal
for _, d := range recs.Assigned["novox"] {
@@ -1305,7 +1303,7 @@ func TestGrants(t *testing.T) {
t.Errorf("the bus module's principals = %+v, want the keeper keeping and nats-tools carrying both removals", tools)
}
if m := grants.Memberships(recs)["novox.claude-licence-manager"]; len(m.Membership.Seats) == 0 {
t.Errorf("the licence manager's membership under the seeded placement carries no seats: %+v", m.Membership)
t.Errorf("the licence manager's membership under the seeded assignments carries no seats: %+v", m.Membership)
}
var runtime grants.User
for _, u := range shadowOf(t, n) {
@@ -1318,7 +1316,7 @@ func TestGrants(t *testing.T) {
}
for _, s := range live.Users[0].Subscribe {
if strings.Contains(s, ".call.") && !slices.Contains(runtime.Subscribe, s) {
t.Errorf("under the seeded placement novox.node-tools is not granted %s, which the bus grants it today", s)
t.Errorf("under the seeded assignments novox.node-tools is not granted %s, which the bus grants it today", s)
}
}
})
@@ -1448,7 +1446,7 @@ func TestGrants(t *testing.T) {
moved := p.Compose()
if !moved.Written || moved.Revision != 2 || !slices.Equal(moved.Published, []string{"a.mod-x", "b.mod-x"}) || len(moved.NotPublished) != 0 || moved.UserList.Compare.Equal() ||
!slices.Equal(moved.UserList.Compare.Added, []string{"b.mod-x"}) {
t.Errorf("a composition after placement moved = %+v", moved)
t.Errorf("a composition after assignments moved = %+v", moved)
}
got, _ = p.UserList()
ul, _ := got.(map[string]any)
@@ -1483,10 +1481,10 @@ func TestGrants(t *testing.T) {
var said []oc.Condition
p.Say = func(c oc.Condition) error { said = append(said, c); return nil }
// settled is the placement of the lists agreeing, every hash held, and the loaded list as edit leaves it.
// settled is the assignments of the lists agreeing, every hash held, and the loaded list as edit leaves it.
settled := func(edit func(modX *grants.User)) func() {
return func() {
b.states[grants.ReadPlacement] = nil
b.states[grants.ReadAssignments] = nil
b.assign("a", "nats")
b.assign("a", "mod-x")
b.assign("b", "mod-y")
@@ -1981,11 +1979,11 @@ func TestGrants(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
done := make(chan struct{})
go func() { f.Run(ctx); close(done) }()
waitOpened(t, w, grants.ReadPlacement)
waitOpened(t, w, grants.ReadAssignments)
if r := f.Refused(); r[grants.ReadModules] == "" {
waitFor(t, func() bool { return f.Refused()[grants.ReadModules] != "" })
}
w.change(grants.ReadPlacement, "placement.a")
w.change(grants.ReadAssignments, "a.node")
waitOpened(t, w, grants.ReadModules)
waitFor(t, func() bool { return len(f.Refused()) == 0 })
select {
@@ -2199,11 +2197,11 @@ func TestGrants(t *testing.T) {
}
})
// 774/S29/AC1 (ADR 0549 §5, ADR 0571 §10): the memberships composed from the placement alone, with no read of the
// controller, are the ones the union of placement and assignments gave on the same input: the fixture's (two nodes,
// 774/S29/AC1 (ADR 0549 §5, ADR 0571 §10): the memberships composed from the assignments alone, with no read of the
// controller, are the ones the union of assignments and assignments gave on the same input: the fixture's (two nodes,
// the bus and mod-x on a, mod-y on b, as placed on the live mesh's own shape of nats beside its modules), whose
// assignments named the same modules. Neither the controller's assignments nor its condition events are read.
t.Run("placement alone places, with the memberships the union gave", func(t *testing.T) {
t.Run("assignments alone places, with the memberships the union gave", func(t *testing.T) {
b := fixture()
p := partOn(b)
p.Compose()
@@ -2211,7 +2209,7 @@ func TestGrants(t *testing.T) {
for key := range want {
raw, _, found, _ := b.ownGet(grants.StateMemberships, key)
if !found {
t.Fatalf("no membership of %s from the placement", key)
t.Fatalf("no membership of %s from the assignments", key)
}
want[key] = string(raw)
}
@@ -2379,9 +2377,9 @@ func TestGrants(t *testing.T) {
b.manifestOf("mesh-delivery-indexer", `{"module":"mesh-delivery-indexer","version":"1","own-secrets":{"broker":"/var/lib/i/broker"},`+
`"emits":["views.manifests","views.modules"],"streams":[{"name":"indexer-views","events":["views.*"],"max-age-days":7,"max-mib":256}]}`)
b.manifestOf("mesh-delivery-assigner", `{"module":"mesh-delivery-assigner","version":"1","own-secrets":{"broker":"/var/lib/as/broker"},`+
`"emits":["views.placement","views.seat-holders"],"streams":[{"name":"assigner-views","events":["views.*"],"max-age-days":7,"max-mib":256}]}`)
`"emits":["views.assignments","views.seat-holders"],"streams":[{"name":"assigner-views","events":["views.*"],"max-age-days":7,"max-mib":256}]}`)
b.manifestOf("node-tools", `{"module":"node-tools","version":"1","tool-runner":true,"own-secrets":{"broker":"/var/lib/nt/broker"},`+
`"consumes":["mesh-delivery-assigner.views.placement","mesh-delivery-assigner.views.seat-holders","mesh-delivery-indexer.views.manifests",`+
`"consumes":["mesh-delivery-assigner.views.assignments","mesh-delivery-assigner.views.seat-holders","mesh-delivery-indexer.views.manifests",`+
`"mesh-delivery-indexer.views.modules"],"state":[{"name":"findings","history":5}]}`)
for _, m := range []string{"mesh-delivery-indexer", "mesh-delivery-assigner", "node-tools"} {
b.assign("a", m)
@@ -2422,7 +2420,7 @@ func TestGrants(t *testing.T) {
if fv.Tool != tool {
continue
}
if empty.Load() && fv.State == "placement" {
if empty.Load() && fv.State == "assignments" {
return views.Answer{Revision: 0, Created: created, Values: map[string]json.RawMessage{}}, nil
}
values := map[string]json.RawMessage{}
@@ -2484,37 +2482,37 @@ func TestGrants(t *testing.T) {
if !raised(grants.FirstViewUnanswered+".mesh-delivery-indexer.verdicts", occ.StateRaised) {
t.Fatalf("the refused raise was not said again at the pass: %+v", said)
}
if !slices.ContainsFunc(before.UserList.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadPlacement && g.Failed }) {
t.Fatalf("placement answered empty at revision 0 was not a failed read: %+v", before.UserList.Gaps)
if !slices.ContainsFunc(before.UserList.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadAssignments && g.Failed }) {
t.Fatalf("assignments answered empty at revision 0 was not a failed read: %+v", before.UserList.Gaps)
}
if !raised(grants.FirstViewEmpty+".mesh-delivery-assigner.placement", occ.StateRaised) {
if !raised(grants.FirstViewEmpty+".mesh-delivery-assigner.assignments", occ.StateRaised) {
t.Fatalf("no first-view-empty: %+v", said)
}
empty.Store(false)
down.Store(false)
if _, err := fv.Again(ctx, "placement", "the assigner composed its wants again"); err != nil {
if _, err := fv.Again(ctx, "assignments", "the assigner composed its wants again"); err != nil {
t.Fatal(err)
}
waitFor(t, func() bool { return fv.Followers[grants.ReadPlacement].Status().Revision == 7 })
waitFor(t, func() bool { return fv.Followers[grants.ReadAssignments].Status().Revision == 7 })
waitFor(t, func() bool {
return raised(grants.FirstViewUnanswered+".mesh-delivery-indexer.verdicts", occ.StateCleared)
})
after := p.Compose()
if slices.ContainsFunc(after.UserList.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadPlacement }) ||
!raised(grants.FirstViewEmpty+".mesh-delivery-assigner.placement", occ.StateCleared) {
if slices.ContainsFunc(after.UserList.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadAssignments }) ||
!raised(grants.FirstViewEmpty+".mesh-delivery-assigner.assignments", occ.StateCleared) {
t.Fatalf("after an answer above 0: gaps %+v, said %+v", after.UserList.Gaps, said)
}
if after.UserList.Users != before.UserList.Users && before.UserList.Users > after.UserList.Users {
t.Errorf("the empty first view dropped users: %d then %d", before.UserList.Users, after.UserList.Users)
}
// An event of the owner's views family changes the picture with no ask.
asksBefore := fv.Followers[grants.ReadPlacement].Status().Asks
asksBefore := fv.Followers[grants.ReadAssignments].Status().Asks
raw, _ := json.Marshal(map[string]any{"v": 1, "node": "c"})
if gap, why := fv.Followers[grants.ReadPlacement].Apply(views.Event{V: 1, Owner: grants.AssignerModule, State: "placement",
Key: "placement.c", Revision: 8, Created: created, Value: raw}); gap {
if gap, why := fv.Followers[grants.ReadAssignments].Apply(views.Event{V: 1, Owner: grants.AssignerModule, State: "assignments",
Key: "c.node", Revision: 8, Created: created, Value: raw}); gap {
t.Fatalf("a next revision was a gap: %s", why)
}
if _, found, _ := fv.Get(grants.ReadPlacement, "placement.c"); !found || fv.Followers[grants.ReadPlacement].Status().Asks != asksBefore {
if _, found, _ := fv.Get(grants.ReadAssignments, "c.node"); !found || fv.Followers[grants.ReadAssignments].Status().Asks != asksBefore {
t.Error("the event did not reach the picture, or asked")
}
// The verdicts follow the indexer's manifest-accepted and manifest-refused at the revision each carries (ADR 0622 §2):
@@ -2577,9 +2575,9 @@ func TestGrants(t *testing.T) {
})
// 774/AC4 (novox/hq ADR 0561 §2): every account the vault mints that the records compose is written to the state
// accounts once, with the bus's address and a module's claims, read by the vault alone; a key is deleted when placement
// accounts once, with the bus's address and a module's claims, read by the vault alone; a key is deleted when assignments
// no longer names its user, and never in a pass whose reads failed.
t.Run("wanted accounts: written once each, read by the vault alone, retired only when placement drops them", func(t *testing.T) {
t.Run("wanted accounts: written once each, read by the vault alone, retired only when assignments drops them", func(t *testing.T) {
b := fixture()
b.loaded = shadowOf(t, b)
p := partOn(b)
@@ -2613,7 +2611,7 @@ func TestGrants(t *testing.T) {
if !slices.Equal(readers, []string{"mesh-vault"}) || buscompose.ReaderMay("mesh-delivery-view", buscompose.GrantsAccounts) {
t.Errorf("the wanted accounts are read by %v", readers)
}
// A read that fails retires nothing; once read, the user placement dropped is retired.
// A read that fails retires nothing; once read, the user assignments dropped is retired.
b.unassign("b", "mod-y")
b.refused[grants.ReadResolved] = true
if res := p.Compose(); res.Accounts == nil || len(res.Accounts.Deleted) != 0 || b.own[grants.StateAccounts]["b.mod-y"] == nil {
@@ -2621,7 +2619,7 @@ func TestGrants(t *testing.T) {
}
delete(b.refused, grants.ReadResolved)
if res := p.Compose(); res.Accounts == nil || !slices.Equal(res.Accounts.Deleted, []string{"b.mod-y"}) {
t.Fatalf("the user placement dropped is not retired: %+v", res.Accounts)
t.Fatalf("the user assignments dropped is not retired: %+v", res.Accounts)
}
// The tool reads them, one and all, each with when it was written.
one, err := p.Accounts("a.nats")