Answer the controller's lease, what still depends on it and the lease write's check in one call (issue 774) #120

Merged
mesh-tools merged 2 commits from feat/lease-tool into main 2026-10-30 22:49:20 +00:00
5 changed files with 650 additions and 2 deletions
@@ -159,6 +159,18 @@ func toolsOf(p *grants.Part) []stdio.Tool {
"every act with what became of it (asked, switched, declined, expired, refused and why).",
Input: schema(nil),
Run: func(map[string]any) (any, error) { return p.TakeOverState() }},
{Name: grants.ToolLeaseCheck, Description: "The controller's lease and the controller's hand-over, in one call from bus state (issue 774, " +
"design 47 L3; novox/hq ADR 0547 §11, ADR 0561 §3, ADR 0571 §13): who holds the controller lease now, since when, and what " +
"the holder and the parts watching the lease do because of it; what still depends on the controller: every module whose " +
"manifest at the indexer's trunk reads a mesh-controller state, invokes a mesh-controller verb or tool, consumes or waits on " +
"its events, with which one and the record that keeps it until the lease write (the named keep list), the unkept ones by module; " +
"the controller's remaining live duties as the bus shows them: each of its streams and buckets someone consumes (with the " +
"durable consumers by name), and its standing assignments; and a dry run of the lease write's check (the take-over check that " +
"hands the lease to a holder that is no controller): whether it would pass now, at the write with only the three kept to the " +
"write and before the write's delivery with the whole list, each with its exact reasons, and the write's other conditions " +
"with the tool that answers each. Asks the controller nothing; writes nothing. Read-only.",
Input: schema(nil),
Run: func(map[string]any) (any, error) { return p.LeaseCheck() }},
}
}
@@ -0,0 +1,479 @@
package grants
import (
"encoding/json"
"fmt"
"slices"
"sort"
"strings"
"time"
"git.novox.be/novox/mesh-sdk/go/buscompose"
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// **The controller's lease, what still depends on the controller, and a dry run of the lease write's check** (issue 774,
// design 47 L3; novox/hq ADR 0547 §11, ADR 0561 §3, ADR 0571 §13). One read-only answer from bus state: the lease key,
// every manifest the indexer holds, and the bus module's streams. It writes nothing, and asks the controller nothing.
//
// It lives on the grants part because the part is where the lease write takes most of its effect and already reads all
// it needs: the lease (buscompose.NamedReaders names it), every manifest at the indexer's trunk, placement, and the bus's
// streams through the bus module's read tools. No record names the home of the lease write's check; ADR 0571 §13 names
// its test in three repositories, and this is the same judgement over the manifests the mesh holds now.
// ToolLeaseCheck is the tool's name.
const ToolLeaseCheck = "grants_lease_check"
// Controller is the module and the seat the check is about.
const Controller = "mesh-controller"
// The kinds of a dependency on the controller, as a manifest declares them.
const (
DependsRead = "read"
DependsInvoke = "invoke"
DependsConsume = "consume"
DependsUses = "uses"
DependsWaits = "waits"
)
// The phases of the lease write's check (ADR 0571 §13): before the lease write's own delivery the whole named list is
// allowed; that delivery drops every entry but the three kept to the write itself, and its check runs with those alone.
const (
PhaseBefore = "before-delivery"
PhaseWrite = "write"
)
// Dependency is one declaration of a manifest other than the controller's on the controller: a read of one of its
// states, an invoke of one of its verbs or tools, a consume or a wait on one of its events, or a send to its seat.
type Dependency struct {
Module string `json:"module"`
// By is the account declaring it when it is not the module's own: `process <id>` or `tools <binary>`.
By string `json:"by,omitempty"`
Kind string `json:"kind"`
What string `json:"what"`
// Kept is the record that keeps it until the lease write, from the named list; empty when none does.
Kept string `json:"kept,omitempty"`
// ToTheWrite says it is one of the three kept to the write itself, which the lease write's own delivery keeps.
ToTheWrite bool `json:"to_the_write,omitempty"`
}
// Kept is one entry of the named, temporary list ADR 0571 §13 allows: a read, invoke or consume another accepted record
// keeps until the lease write, with that record. ToTheWrite marks the three kept to the write itself.
type Kept struct {
Module, Kind, What, Record string
ToTheWrite bool
}
// KeepList is the named list. Each entry names the clause that keeps it; a dependency not on it fails the check. The
// third entry kept to the write, the bus node agent's following of the controller's user list (ADR 0571 §8, ADR 0547
// §8), is no manifest's declaration but the node-engine's own, so no manifest matches it; it stays named here so the
// list reads as the record has it.
var KeepList = []Kept{
// Kept to the write itself (ADR 0571 §4, §8, §13).
{Module: "mesh-delivery-builder", Kind: DependsConsume, What: "mesh-controller.artifacts-gone", Record: "ADR 0571 §4, §13", ToTheWrite: true},
{Module: "artifact-store-tools", Kind: DependsInvoke, What: "seat:mesh-controller.collect", Record: "ADR 0571 §4, §13", ToTheWrite: true},
{Module: "node-engine", Kind: DependsRead, What: "the controller's user list of the bus module's composition", Record: "ADR 0571 §8, §13; ADR 0547 §8", ToTheWrite: true},
// Kept until the lease write's own delivery.
{Module: "mesh-delivery-assigner", Kind: DependsInvoke, What: "seat:mesh-controller.placement-export", Record: "ADR 0567 §7"},
{Module: "mesh-delivery-assigner", Kind: DependsInvoke, What: "seat:mesh-controller.assign", Record: "ADR 0567 §8"},
{Module: "mesh-delivery-assigner", Kind: DependsInvoke, What: "seat:mesh-controller.unassign", Record: "ADR 0567 §8"},
{Module: "mesh-delivery-assigner", Kind: DependsInvoke, What: "seat:mesh-controller.pin", Record: "ADR 0567 §8"},
{Module: "mesh-delivery-assigner", Kind: DependsInvoke, What: "seat:mesh-controller.unpin", Record: "ADR 0567 §8"},
{Module: "mesh-delivery-assigner", Kind: DependsRead, What: "mesh-controller.assignments", Record: "ADR 0567 §8"},
{Module: "mesh-delivery-assigner", Kind: DependsRead, What: "mesh-controller.composed", Record: "ADR 0571 §8"},
{Module: "mesh-grants", Kind: DependsRead, What: "mesh-controller.assignments", Record: "ADR 0549 §5"},
{Module: "mesh-grants", Kind: DependsConsume, What: "mesh-controller.condition-raised", Record: "ADR 0564 §9"},
{Module: "mesh-grants", Kind: DependsConsume, What: "mesh-controller.condition-cleared", Record: "ADR 0564 §9"},
{Module: "messenger", Kind: DependsConsume, What: "mesh-controller.condition-raised", Record: "ADR 0564 §9"},
{Module: "messenger", Kind: DependsConsume, What: "mesh-controller.condition-cleared", Record: "ADR 0564 §9"},
{Module: "messenger", Kind: DependsConsume, What: "mesh-controller.condition-changed", Record: "ADR 0564 §9"},
{Module: "messenger", Kind: DependsInvoke, What: "seat:mesh-controller.conditions", Record: "ADR 0564 §9"},
{Module: "mesh-watcher", Kind: DependsConsume, What: "mesh-controller.doctor-heartbeat", Record: "ADR 0564 §9"},
{Module: "mesh-conditions", Kind: DependsRead, What: "mesh-controller.conditions", Record: "ADR 0564 §9"},
{Module: "mesh-config", Kind: DependsRead, What: "mesh-controller.declared-config", Record: "ADR 0565 §4"},
{Module: "mesh-config", Kind: DependsRead, What: "mesh-controller.predefined", Record: "ADR 0565 §4"},
{Module: "mesh-healers", Kind: DependsRead, What: "mesh-controller.hand-acts", Record: "ADR 0565 §11"},
{Module: "mesh-hand-acts", Kind: DependsRead, What: "mesh-controller.hand-acts", Record: "ADR 0565 §11"},
}
// EndedBy names, for a declaration no entry keeps, the record that already ended it, so a refusal says where it moved.
// The controller's `grants` is one: ADR 0571 §9 has no manifest read it from L7's delivery, which changes ADR 0567 §10
// and ADR 0547 §5's shadow compare, so neither the assigner's nor the grants part's read of it is kept.
var EndedBy = map[string]string{
DependsRead + " mesh-controller.grants": "ADR 0571 §9: no manifest reads the controller's grants; the grants part's key is the one read",
DependsInvoke + " seat:mesh-controller.hand-act": "ADR 0565 §11: every recorder calls mesh-hand-acts",
DependsInvoke + " seat:mesh-controller.warranted": "ADR 0565 §11: every recorder calls mesh-hand-acts",
DependsInvoke + " seat:mesh-controller.root-free": "ADR 0571 §11: root-free is the self-check's probe",
DependsInvoke + " seat:mesh-controller.held": "ADR 0571 §2: held is the seat mesh-artifact-sweep's",
DependsRead + " mesh-controller.declared-packages": "ADR 0565 §6: each node agent writes its own node's packages",
}
// keptFor is the list's entry for one dependency, or nil.
func keptFor(d Dependency) *Kept {
for i, k := range KeepList {
if k.Module == d.Module && k.Kind == d.Kind && k.What == d.What {
return &KeepList[i]
}
}
return nil
}
// onController says whether a name `<owner>.<rest>` is the controller's.
func onController(name string) bool { return strings.HasPrefix(name, Controller+".") }
// invokesController says whether an invoke names a verb of the controller's seat or a tool of its module. `*` names none:
// it is the console's grant to every tool, judged by no record as a dependency.
func invokesController(invoke string) bool {
if seat, _, ok := catalogue.SeatInvoke(invoke); ok {
return seat == Controller
}
return onController(invoke)
}
// Dependencies are every declaration on the controller in the manifests given, by module, each with the list's entry
// that keeps it. The controller's own manifest is not judged, nor a read of its lease key: it names the lease's holder,
// whoever that is, and parts read it to learn that the write happened (ADR 0571 §13).
func Dependencies(manifests map[string]catalogue.Manifest) []Dependency {
var out []Dependency
add := func(module, by, kind, what string) {
d := Dependency{Module: module, By: by, Kind: kind, What: what}
if k := keptFor(d); k != nil {
d.Kept, d.ToTheWrite = k.Record, k.ToTheWrite
}
if !slices.Contains(out, d) {
out = append(out, d)
}
}
for name, m := range manifests {
if name == Controller || m.Module == Controller {
continue
}
for _, r := range m.Reads {
if onController(r) && r != buscompose.ControllerLease {
add(name, "", DependsRead, r)
}
}
for _, i := range m.Invokes {
if invokesController(i) {
add(name, "", DependsInvoke, i)
}
}
for _, c := range m.Consumes {
if onController(c) {
add(name, "", DependsConsume, c)
}
}
for _, w := range m.Waits {
if onController(w) {
add(name, "", DependsWaits, w)
}
}
for _, u := range m.Uses {
if u == Controller {
add(name, "", DependsUses, u)
}
}
for _, a := range m.BusAccounts() {
by := "process " + a.ID
if a.Tools {
by = "tools " + a.ID
}
for _, i := range a.Bus.Invokes {
if invokesController(i) {
add(name, by, DependsInvoke, i)
}
}
}
}
sort.Slice(out, func(i, j int) bool {
a, b := out[i], out[j]
if a.Module != b.Module {
return a.Module < b.Module
}
if a.Kind != b.Kind {
return a.Kind < b.Kind
}
if a.What != b.What {
return a.What < b.What
}
return a.By < b.By
})
return out
}
// Verdict is the lease write's check in one phase.
type Verdict struct {
Phase string `json:"phase"`
Passes bool `json:"passes"`
Reasons []string `json:"reasons"`
}
// Coverage is how much of the indexer the dependencies were derived from: the manifests read, the modules the indexer
// holds (-1 when its keys could not be listed, which Unread then names), and each manifest that could not be read.
type Coverage struct {
Read, Indexed int
Unread []Gap
}
// Judge runs the lease write's check over the dependencies (ADR 0571 §13) in one phase: before the lease write's own
// delivery, every dependency must be on the named list; at the write, only the three kept to the write itself may stay.
// It passes only over every module the indexer holds: no manifest read, fewer read than the indexer holds, or one that
// could not be read fails it, since what was not read could hide a dependency. A lease already handed away leaves
// nothing to write.
func Judge(deps []Dependency, phase string, cov Coverage, handedAway bool, leaseSays string) Verdict {
v := Verdict{Phase: phase, Reasons: []string{}}
if handedAway {
v.Reasons = append(v.Reasons, "there is nothing to write: "+leaseSays)
}
switch {
case cov.Read == 0:
v.Reasons = append(v.Reasons, fmt.Sprintf("no manifest was read from the indexer (it holds %d modules): with nothing read, nothing on the controller can be judged absent", max(cov.Indexed, 0)))
case cov.Indexed >= 0 && cov.Read < cov.Indexed:
v.Reasons = append(v.Reasons, fmt.Sprintf("only %d of the %d modules the indexer holds had their manifest read, so what the others declare on the controller is not known", cov.Read, cov.Indexed))
}
for _, d := range deps {
who := d.Module
if d.By != "" {
who += " (" + d.By + ")"
}
switch {
case d.Kept == "":
why := fmt.Sprintf("%s declares %s %s, which no record keeps until the lease write (ADR 0571 §13)", who, d.Kind, d.What)
if ended := EndedBy[d.Kind+" "+d.What]; ended != "" {
why += "; ended by " + ended
}
v.Reasons = append(v.Reasons, why)
case phase == PhaseWrite && !d.ToTheWrite:
v.Reasons = append(v.Reasons, fmt.Sprintf("%s declares %s %s, kept by %s only until the lease write's own delivery, which drops it", who, d.Kind, d.What, d.Kept))
}
}
for _, g := range cov.Unread {
v.Reasons = append(v.Reasons, fmt.Sprintf("%s could not be read (%s), so what it declares on the controller is not known", g.Input, g.Why))
}
v.Passes = len(v.Reasons) == 0
return v
}
// LeaseNow is who holds the lease and what follows from it.
type LeaseNow struct {
HandedAway bool `json:"handed_away"`
Says string `json:"says"`
Holder *buscompose.LeaseHolder `json:"holder,omitempty"`
Since *time.Time `json:"since,omitempty"`
Does []string `json:"does"`
}
// While the controller holds the lease, and once it is handed away: what the holder and the parts watching the key do
// because of it, by the records that say so.
var (
DoesWhileController = []string{
"the controller mints every new bus account; the vault mints none and says what it holds back (ADR 0561 §3)",
"the grants part creates no durable consumer an account needs, and only compares them (ADR 0561 §6)",
"the grants part publishes memberships on the controller's assignments stream beside its own state (ADR 0571 §10)",
"mesh-healers writes each act it would take in `would`, and acts on none (ADR 0564 §8)",
"the artifact sweep lets go of nothing; the controller's take-in sweeps and says `artifacts-gone` (ADR 0571 §4)",
"the watchman only lists dead letters; the controller keeps DEAD_LETTERS through its consumer of DEAD_LETTER_NOTICES (ADR 0564 §5)",
"placement proposals take only assignments, pins and config values, and the assigner keeps the controller's assignments in step (ADR 0567 §8, §9)",
"the controller keeps composing for the bus node, whose agent follows its user list, so the hand-backs of config, conditions, placement and the user list still answer (ADR 0547 §8, ADR 0564 §9, ADR 0565 §4, ADR 0567 §11)",
}
DoesHandedAway = []string{
"the vault mints every new bus account (ADR 0561 §1)",
"the grants part creates the durable consumers an account needs, and stops publishing on the assignments stream (ADR 0561 §6, ADR 0571 §10)",
"mesh-healers acts (ADR 0564 §8)",
"the artifact sweep sweeps and records every deletion in the hand-act log (ADR 0571 §4)",
"the watchman keeps the dead letters (ADR 0564 §5)",
"every hand-back is answered as gone (ADR 0547 §11, ADR 0564 §9, ADR 0565 §4, ADR 0567 §11)",
}
)
// Lease reads the lease key and says who holds it, since when, and what follows.
func Lease(r Reader) LeaseNow {
raw, found, err := r.Get(ReadLease, LeaseKey)
away, says := buscompose.LeaseHandedAway(raw, found, err)
out := LeaseNow{HandedAway: away, Says: says, Does: DoesWhileController}
if away {
out.Does = DoesHandedAway
}
if err == nil && found && len(raw) > 0 {
var h buscompose.LeaseHolder
if json.Unmarshal(raw, &h) == nil {
out.Holder = &h
if !h.Taken.IsZero() {
t := h.Taken
out.Since = &t
}
}
}
return out
}
// StreamUse is one of the controller's streams (its buckets, the assignments stream, the dead-letter notices) as the bus
// module answers it: who consumes it now, and what it last carried.
type StreamUse struct {
Stream string `json:"stream"`
Consumers int `json:"consumers"`
Durable []string `json:"durable,omitempty"`
Messages int64 `json:"messages"`
Subjects int64 `json:"subjects_held,omitempty"`
Last string `json:"last,omitempty"`
}
// The streams of the controller's that are not its buckets: the assignments stream it publishes memberships on, and the
// notices its consumer `controller` turns into dead letters until the lease write (ADR 0564 §5).
const (
StreamAssignments = "ASSIGNMENTS"
StreamDeadLetterNotices = "DEAD_LETTER_NOTICES"
)
// controllerStream says whether a stream is the controller's.
func controllerStream(name string) bool {
return strings.HasPrefix(name, "KV_"+Controller+"_") || name == StreamAssignments || name == StreamDeadLetterNotices
}
// Duties reads, from the bus module's streams, every stream of the controller's someone consumes, with its durable
// consumers by name (a watcher of a bucket is ephemeral and names nobody). Streams with no consumer are counted apart.
func Duties(c Caller) (used []StreamUse, idle []string, err error) {
raw, err := c.Call(CallStreams, map[string]any{})
if err != nil {
return nil, nil, fmt.Errorf("%s: %w", CallStreams, err)
}
type consumer struct {
Name string `json:"name"`
Kind string `json:"kind"`
Max int `json:"max_deliver"`
Ack string `json:"ack_policy"`
}
type stream struct {
Name string `json:"name"`
Consumers int `json:"consumers"`
Messages int64 `json:"messages"`
Held int64 `json:"subjects_held"`
Last string `json:"last"`
Detail []consumer `json:"consumer_detail"`
}
var got struct {
Detail []stream `json:"detail"`
}
if err := json.Unmarshal(unwrap(raw), &got); err != nil {
return nil, nil, fmt.Errorf("%s answered no streams: %w", CallStreams, err)
}
for _, s := range got.Detail {
if !controllerStream(s.Name) {
continue
}
if s.Consumers == 0 {
idle = append(idle, s.Name)
continue
}
u := StreamUse{Stream: s.Name, Consumers: s.Consumers, Messages: s.Messages, Subjects: s.Held, Last: s.Last}
if one, err := c.Call(CallStreams, map[string]any{"stream": s.Name}); err == nil {
var d struct {
Detail []stream `json:"detail"`
}
if json.Unmarshal(unwrap(one), &d) == nil && len(d.Detail) > 0 {
for _, cs := range d.Detail[0].Detail {
if durable(cs.Name) {
u.Durable = append(u.Durable, cs.Name)
}
}
}
}
used = append(used, u)
}
sort.Slice(used, func(i, j int) bool { return used[i].Stream < used[j].Stream })
sort.Strings(idle)
return used, idle, nil
}
// durable says whether a consumer's name is one a part gave it: the bus names an ephemeral watcher with eight random
// letters and digits, which no part of the mesh uses.
func durable(name string) bool {
if len(name) != 8 {
return true
}
for _, r := range name {
if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9') {
return true
}
}
return false
}
// LeaseAnswer is the tool's answer.
type LeaseAnswer struct {
Lease LeaseNow `json:"lease"`
Dependencies []Dependency `json:"dependencies"`
// Unkept are the dependencies no record keeps, by module: what the check refuses in every phase.
Unkept map[string][]string `json:"unkept"`
Manifests int `json:"manifests_read"`
// Indexed is how many modules the indexer holds; -1 when its keys could not be listed.
Indexed int `json:"modules_indexed"`
Unread []Gap `json:"manifests_unread,omitempty"`
// Streams are the controller's streams someone consumes now; Idle those no one does.
Streams []StreamUse `json:"streams,omitempty"`
Idle []string `json:"streams_idle,omitempty"`
StreamsError string `json:"streams_unread,omitempty"`
// Assignments is how many of the controller's assignments stand, which it publishes memberships and grants for.
Assignments int `json:"assignments_standing"`
// Check is the dry run at the write (the three alone), Before the same over the whole list before the write's
// delivery.
Check Verdict `json:"check"`
Before Verdict `json:"check_before_delivery"`
Beside []string `json:"not_judged_here"`
}
// NotJudgedHere are the lease write's other conditions, each with the tool that answers it (design 47 L3, ADR 0547 §11).
var NotJudgedHere = []string{
"L4 to L7 delivered: the issue tracker's milestones 1952, 1953, 1954 and 1955",
"every node's record trusts the vault's key: mesh-delivery-view.node_trusts (ADR 0550 §2)",
"mesh-config's compare empty and its take-over done, and mesh-wants.upgrade compare empty: mesh-config.compare, mesh-wants.upgrade (ADR 0565 §14)",
"the condition keeper's take-over done: mesh-conditions.compare (ADR 0564 §9)",
}
// LeaseCheck answers the tool: who holds the lease and what follows, every dependency on the controller with what keeps
// it, the controller's streams in use, and the lease write's check run dry. Read-only.
func (p *Part) LeaseCheck() (any, error) {
in := ReadInputs(p.Read, &p.cache)
out := LeaseAnswer{Lease: Lease(p.Read), Unkept: map[string][]string{}, Manifests: len(in.Manifests), Unread: in.Refused,
Beside: NotJudgedHere}
for _, g := range in.Gaps {
if g.Failed && strings.HasPrefix(g.Input, ReadModules) {
out.Unread = append(out.Unread, g)
}
}
out.Dependencies = Dependencies(in.Manifests)
if out.Dependencies == nil {
out.Dependencies = []Dependency{}
}
for _, d := range out.Dependencies {
if d.Kept == "" {
out.Unkept[d.Module] = append(out.Unkept[d.Module], d.Kind+" "+d.What)
}
}
var gaps []Gap
for _, raw := range each(p.Read, ReadAssignments, &gaps) {
var a assignment
if json.Unmarshal(raw, &a) == nil && a.stands() {
out.Assignments++
}
}
if used, idle, err := Duties(p.Call); err != nil {
out.StreamsError = err.Error()
} else {
out.Streams, out.Idle = used, idle
}
// Only the indexer's own modules count as read: a manifest the mesh provides itself must not stand in for one missing.
cov := Coverage{Indexed: -1, Unread: out.Unread}
if keys, err := p.Read.Keys(ReadModules); err == nil {
cov.Indexed = len(keys)
for _, k := range keys {
if _, ok := in.Manifests[k]; ok {
cov.Read++
}
}
}
out.Indexed = cov.Indexed
out.Check = Judge(out.Dependencies, PhaseWrite, cov, out.Lease.HandedAway, out.Lease.Says)
out.Before = Judge(out.Dependencies, PhaseBefore, cov, out.Lease.HandedAway, out.Lease.Says)
return out, nil
}
+1 -1
View File
@@ -29,7 +29,7 @@ const (
// Tools is every tool, in the manifest's order.
var Tools = []string{ToolGrants, ToolMembership, ToolUserList, ToolComposeNow, ToolAssert, ToolTakeOver, ToolHandBack, ToolTakeOverState,
ToolAccounts, ToolConsumerMatch, ToolAcceptHashes}
ToolAccounts, ToolConsumerMatch, ToolAcceptHashes, ToolLeaseCheck}
// WritesGrants is what grants says of the part's own grants in this milestone: written, and read by nobody until the
// controller's lease is handed away (delivery.GrantSource).
+156
View File
@@ -0,0 +1,156 @@
package manifest
import (
"encoding/json"
"slices"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
"mesh-grants/internal/grants"
)
// Issue 774, design 47 L3 (ADR 0547 §11, ADR 0561 §3, ADR 0571 §13): grants_lease_check answers from bus state who holds
// the controller's lease, every manifest's dependency on the controller with what keeps it, the controller's streams in
// use, and the lease write's check run dry, at the write (the three kept to it alone) and before its delivery.
func TestTheLeaseCheckJudgesEveryDependencyOnTheController(t *testing.T) {
const mManifest = `{"module":"mod-m","version":"1","reads":["mesh-controller.declared-packages"]}`
controllerHolds := map[string]any{"instance": "controller@novox-be pid 1 since 2026-10-30T14:52:10Z", "epoch": 60,
"taken": "2026-10-30T14:52:10Z", "renewed": "2026-10-30T22:00:00Z"}
handedAway := map[string]any{"handed-to": "the operator's hand-over", "handed-by": "operator"}
type want struct {
deps []string // "<module> <kind> <what> <kept?>" with kept as "kept" or "unkept"
writePasses bool
beforePasses bool
reason string // a reason the check at the write must name, or ""
bothPhases bool // the check before the delivery must name the reason too
handedAway bool
durable []string // durable consumers of KV_mesh-controller_assignments
}
for _, row := range []struct {
name string
manifests map[string]string
lease map[string]any
want want
setup func(b *fakeBus)
}{
{"a read of the lease alone is no dependency, and the controller's own manifest is not judged",
map[string]string{"mod-l": `{"module":"mod-l","version":"1","reads":["mesh-controller.lease"]}`,
"mesh-controller": `{"module":"mesh-controller","version":"1","invokes":["seat:mesh-controller.status"]}`},
controllerHolds, want{writePasses: true, beforePasses: true}, nil},
{"the builder's consume of artifacts-gone is kept to the write itself",
map[string]string{"mesh-delivery-builder": `{"module":"mesh-delivery-builder","version":"1","consumes":["mesh-controller.artifacts-gone"]}`},
controllerHolds, want{deps: []string{"mesh-delivery-builder consume mesh-controller.artifacts-gone kept"}, writePasses: true, beforePasses: true}, nil},
{"the assigner's read of composed is kept until the write's delivery, which drops it",
map[string]string{"mesh-delivery-assigner": `{"module":"mesh-delivery-assigner","version":"1","reads":["mesh-controller.composed","mesh-controller.lease"]}`},
controllerHolds, want{deps: []string{"mesh-delivery-assigner read mesh-controller.composed kept"}, beforePasses: true,
reason: "mesh-delivery-assigner declares read mesh-controller.composed, kept by ADR 0571 §8 only until the lease write's own delivery"}, nil},
{"a read no record keeps fails both phases, named",
map[string]string{"pacman": `{"module":"pacman","version":"1","reads":["mesh-controller.declared-packages"]}`},
controllerHolds, want{deps: []string{"pacman read mesh-controller.declared-packages unkept"},
reason: "pacman declares read mesh-controller.declared-packages, which no record keeps until the lease write"}, nil},
{"a seat verb and a module tool of the controller's, a consume and a wait are each one; every tool (*) is none",
map[string]string{"mod-i": `{"module":"mod-i","version":"1","invokes":["seat:mesh-controller.root-free","mesh-controller.status","*","nats.nats_users"],` +
`"consumes":["mesh-controller.checked","mod-x.thing"],"uses":["mesh-controller"]}`,
"node-tools": `{"module":"node-tools","version":"1","waits":["mesh-controller.checked"]}`},
controllerHolds, want{deps: []string{"mod-i consume mesh-controller.checked unkept", "mod-i invoke mesh-controller.status unkept",
"mod-i invoke seat:mesh-controller.root-free unkept", "mod-i uses mesh-controller unkept", "node-tools waits mesh-controller.checked unkept"},
reason: "mod-i declares invoke seat:mesh-controller.root-free, which no record keeps"}, nil},
{"a process's own invoke of the controller is its module's dependency, named by the process",
map[string]string{"mod-p": `{"module":"mod-p","version":"1","resources":[{"id":"account","type":"user","name":"mod-p"},{"id":"worker","type":"process","run":["./w"],"user":"mod-p",` +
`"bus":{"invokes":["seat:mesh-controller.held"]}}]}`},
controllerHolds, want{deps: []string{"mod-p invoke seat:mesh-controller.held unkept"},
reason: "mod-p (process worker) declares invoke seat:mesh-controller.held"}, nil},
{"no manifest read from the indexer fails both phases, though nothing read depends on the controller",
map[string]string{},
controllerHolds, want{reason: "no manifest was read from the indexer (it holds 0 modules)", bothPhases: true}, nil},
{"a module whose manifest digest is missing fails both phases, named",
map[string]string{"mod-l": `{"module":"mod-l","version":"1"}`, "mod-m": mManifest},
controllerHolds, want{reason: "manifest of mod-m could not be read", bothPhases: true}, func(b *fakeBus) {
delete(b.states[grants.ReadManifests], delivery.ManifestDigest([]byte(mManifest)))
}},
{"a manifest mesh-sdk refuses fails both phases, named",
map[string]string{"mod-l": `{"module":"mod-l","version":"1"}`, "mod-r": `{"module":"mod-r","version":"1","waits":["mesh-controller.checked"]}`},
controllerHolds, want{reason: "manifest of mod-r could not be read", bothPhases: true}, nil},
{"a lease already handed away leaves nothing to write",
map[string]string{},
handedAway, want{handedAway: true, reason: "there is nothing to write: the lease is handed to the operator's hand-over"}, nil},
} {
t.Run(row.name, func(t *testing.T) {
b := newFakeBus()
for m, raw := range row.manifests {
b.manifestOf(m, raw)
}
b.put(grants.ReadLease, grants.LeaseKey, row.lease)
if row.setup != nil {
row.setup(b)
}
b.streams = []map[string]any{
{"name": "KV_mesh-controller_assignments", "consumers": 3, "messages": 2991, "subjects_held": 40, "last": "2026-10-30T12:55:13Z"},
{"name": "KV_mesh-controller_rollouts", "consumers": 0, "messages": 6652},
{"name": "KV_mesh-grants_memberships", "consumers": 243, "messages": 1573},
}
b.consumers = map[string][]map[string]any{"KV_mesh-controller_assignments": {{"name": "0AYfjxta"}, {"name": "1g9XJFUX"}, {"name": "grants-assignments"}}}
raw, err := partOn(b).LeaseCheck()
if err != nil {
t.Fatal(err)
}
var got grants.LeaseAnswer
data, _ := json.Marshal(raw)
if err := json.Unmarshal(data, &got); err != nil {
t.Fatal(err)
}
var deps []string
for _, d := range got.Dependencies {
kept := "unkept"
if d.Kept != "" {
kept = "kept"
}
deps = append(deps, d.Module+" "+d.Kind+" "+d.What+" "+kept)
}
if !slices.Equal(deps, row.want.deps) {
t.Errorf("dependencies %q, want %q", deps, row.want.deps)
}
if got.Check.Phase != grants.PhaseWrite || got.Check.Passes != row.want.writePasses {
t.Errorf("check at the write passes=%v (%v), want %v", got.Check.Passes, got.Check.Reasons, row.want.writePasses)
}
if got.Before.Phase != grants.PhaseBefore || got.Before.Passes != row.want.beforePasses {
t.Errorf("check before the delivery passes=%v (%v), want %v", got.Before.Passes, got.Before.Reasons, row.want.beforePasses)
}
if row.want.reason != "" && !slices.ContainsFunc(got.Check.Reasons, func(r string) bool { return strings.Contains(r, row.want.reason) }) {
t.Errorf("reasons %q name nothing like %q", got.Check.Reasons, row.want.reason)
}
if row.want.bothPhases && !slices.ContainsFunc(got.Before.Reasons, func(r string) bool { return strings.Contains(r, row.want.reason) }) {
t.Errorf("reasons before the delivery %q name nothing like %q", got.Before.Reasons, row.want.reason)
}
if got.Lease.HandedAway != row.want.handedAway {
t.Errorf("lease handed away %v (%s), want %v", got.Lease.HandedAway, got.Lease.Says, row.want.handedAway)
}
if !row.want.handedAway && (got.Lease.Since == nil || got.Lease.Since.Format("15:04") != "14:52" ||
!strings.Contains(got.Lease.Says, "the controller holds the lease") || !slices.Equal(got.Lease.Does, grants.DoesWhileController)) {
t.Errorf("lease %+v: the controller's instance since 14:52, and what it does while it holds it", got.Lease)
}
if len(got.Streams) != 1 || got.Streams[0].Stream != "KV_mesh-controller_assignments" || got.Streams[0].Consumers != 3 ||
!slices.Equal(got.Streams[0].Durable, []string{"grants-assignments"}) || !slices.Equal(got.Idle, []string{"KV_mesh-controller_rollouts"}) {
t.Errorf("streams %+v idle %v: the controller's assignments bucket with its one durable consumer, the rollouts idle, no other module's", got.Streams, got.Idle)
}
})
}
}
// Every entry of the keep list names the record that keeps it, and exactly three are kept to the write itself (ADR 0571 §13).
func TestTheKeepListNamesItsRecordsAndThreeToTheWrite(t *testing.T) {
toTheWrite := 0
for _, k := range grants.KeepList {
if !strings.HasPrefix(k.Record, "ADR ") || k.Module == "" || k.What == "" {
t.Errorf("entry %+v names no record, module or declaration", k)
}
if k.ToTheWrite {
toTheWrite++
}
}
if toTheWrite != 3 {
t.Errorf("%d entries kept to the write itself, ADR 0571 §13 names three", toTheWrite)
}
}
+2 -1
View File
@@ -52,7 +52,8 @@
"grants_hand_back",
"grants_accounts",
"grants_consumer_match",
"grants_accept_hashes"
"grants_accept_hashes",
"grants_lease_check"
],
"state": [
{