Move the controller-dependence judge into mesh-sdk, so three repositories run the same lease-write check

ADR 0571 §13 names a test in mesh-catalog, mesh-delivery and mesh-tools that refuses a manifest depending on
the controller beyond the lease and the entries kept. The judge lived in mesh-grants (leasecheck.go); it moves
here whole (the kinds, the phases, KeepList, EndedBy, Dependencies, Judge) with ManifestRefusals(manifests,
phase) for those tests. Kept is renamed KeptDependency (catalogue already has a Kept), and a gap is Unread.
mesh-grants re-exports it in its own commit; the keep list is the one at mesh-delivery 4a84370.

Test: TestTheLeaseWritesCheckJudgesEveryDependencyOnTheController, moved from mesh-grants'
leasecheck_test.go (its manifest rows and the keep-list test, which mesh-grants deletes), with the
conditions row of 774/S4/AC1 added; not a new test.

(issue 774, 774/S4)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-11-02 00:17:13 +01:00
parent feadebd331
commit d7a008bee5
2 changed files with 358 additions and 0 deletions
+261
View File
@@ -0,0 +1,261 @@
package catalogue
import (
"fmt"
"slices"
"sort"
"strings"
)
// **What still depends on the controller, and the lease write's check** (novox/hq ADR 0571 §13, ADR 0582 clause 2,
// issue 774): every 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, a use of its seat), judged
// against the named, temporary list of what another accepted record keeps until the lease write. The grants part's
// grants_lease_check runs it over every manifest the indexer holds; ManifestRefusals runs it over a repository's
// manifests, in the test ADR 0571 §13 names in mesh-catalog, mesh-delivery and mesh-tools. A read of the lease key is no
// dependency: it names the lease's holder, whoever that is, and parts read it to learn that the write happened.
// ControllerLease is the controller's lease as a reader names it (`<module>.<state>`): no dependency.
const ControllerLease = ControllerSeatName + ".lease"
// 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"`
}
// KeptDependency 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 KeptDependency 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 = []KeptDependency{
// 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"},
// pacman's fallback for a node whose engine has not written its own packages yet.
{Module: "pacman", Kind: DependsRead, What: "mesh-controller.declared-packages", Record: "ADR 0565 §6"},
}
// 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: each machine's engine answers root-free (seat:node-engine.root-free@machine)",
DependsInvoke + " seat:mesh-controller.held": "ADR 0571 §2: held is the seat mesh-artifact-sweep's",
}
// keptFor is the list's entry for one dependency, or nil.
func keptFor(d Dependency) *KeptDependency {
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, ControllerSeatName+".") }
// 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 := SeatInvoke(invoke); ok {
return seat == ControllerSeatName
}
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]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 == ControllerSeatName || m.Module == ControllerSeatName {
continue
}
for _, r := range m.Reads {
if onController(r) && r != 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 == ControllerSeatName {
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 mesh's manifests 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 []Unread
}
// Unread is one manifest, or one listing of them, that could not be read, and why.
type Unread struct {
Input string `json:"input"`
Why string `json:"why"`
}
// 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
}
// ManifestRefusals is the lease write's check over a repository's manifests, by module name, in one phase (PhaseBefore
// or PhaseWrite): each reason the check gives, none when it passes. Every manifest given counts as read: the caller reads
// them from its own tree, and none given fails it.
func ManifestRefusals(manifests map[string]Manifest, phase string) []string {
return Judge(Dependencies(manifests), phase, Coverage{Read: len(manifests), Indexed: -1}, false, "").Reasons
}
@@ -0,0 +1,97 @@
package catalogue
import (
"slices"
"strings"
"testing"
)
// Issue 774 (ADR 0571 §13, ADR 0582 clause 2): the lease write's check judges every manifest's dependency on the
// controller against the named list, at the write (the three kept to it alone) and before its delivery. Moved with the
// judge from mesh-grants' leasecheck_test.go (TestTheLeaseCheckJudgesEveryDependencyOnTheController's manifest rows and
// TestTheKeepListNamesItsRecordsAndThreeToTheWrite), whose manifests were mesh-grants' fixtures; the conditions row is
// mesh-conditions' manifest at mesh-catalog ce3bedd5 (module.json:31-32).
func TestTheLeaseWritesCheckJudgesEveryDependencyOnTheController(t *testing.T) {
toTheWrite := 0
for _, k := range 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)
}
type want struct {
deps []string // "<module> <kind> <what> <kept by>", kept by the record or "unkept"
write string // a reason the check at the write names, or "" when it passes
before string // a reason the check before the delivery names, or "" when it passes
}
for _, row := range []struct {
name string
manifests map[string]string
want want
}{
// 774/S4/AC2.
{"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"]}`},
want{}},
// 774/S4/AC1.
{"the keeper's read of the controller's conditions is kept before the write's delivery and refused at the write",
map[string]string{"mesh-conditions": `{"module":"mesh-conditions","version":"1","reads":["mesh-controller.conditions","mesh-controller.lease"]}`},
want{deps: []string{"mesh-conditions read mesh-controller.conditions kept by ADR 0564 §9"},
write: "mesh-conditions declares read mesh-controller.conditions, kept by ADR 0564 §9 only until the lease write's own delivery"}},
{"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"]}`},
want{deps: []string{"mesh-delivery-builder consume mesh-controller.artifacts-gone kept by ADR 0571 §4, §13"}}},
{"a read no record keeps fails both phases, named",
map[string]string{"mod-u": `{"module":"mod-u","version":"1","reads":["mesh-controller.declared-packages"]}`},
want{deps: []string{"mod-u read mesh-controller.declared-packages unkept"},
write: "mod-u declares read mesh-controller.declared-packages, which no record keeps until the lease write",
before: "mod-u declares read mesh-controller.declared-packages, which no record keeps until the lease write"}},
{"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"]}`},
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"},
write: "mod-i declares invoke seat:mesh-controller.root-free, which no record keeps until the lease write (ADR 0571 §13); ended by ADR 0571 §11",
before: "node-tools declares waits mesh-controller.checked"}},
{"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"]}}]}`},
want{deps: []string{"mod-p invoke seat:mesh-controller.held unkept"},
write: "mod-p (process worker) declares invoke seat:mesh-controller.held", before: "mod-p (process worker)"}},
{"no manifest fails both phases", map[string]string{}, want{write: "no manifest was read", before: "no manifest was read"}},
} {
manifests := map[string]Manifest{}
for name, raw := range row.manifests {
m, err := Decode([]byte(raw))
if err != nil {
t.Fatalf("%s: %s: %v", row.name, name, err)
}
manifests[name] = m
}
var deps []string
for _, d := range Dependencies(manifests) {
kept := "unkept"
if d.Kept != "" {
kept = "kept by " + d.Kept
}
deps = append(deps, d.Module+" "+d.Kind+" "+d.What+" "+kept)
}
if !slices.Equal(deps, row.want.deps) {
t.Errorf("%s: dependencies %q, want %q", row.name, deps, row.want.deps)
}
for phase, reason := range map[string]string{PhaseWrite: row.want.write, PhaseBefore: row.want.before} {
got := ManifestRefusals(manifests, phase)
if (reason == "") != (len(got) == 0) || !slices.ContainsFunc(got, func(r string) bool { return strings.Contains(r, reason) }) && reason != "" {
t.Errorf("%s: at %s refused %q, want %q", row.name, phase, got, reason)
}
}
}
}