ADR 0571 §13: the lease write's own delivery drops every entry but the builder's artifacts-gone, the artifact sweep's collect and the engine's following of the controller's user list. Every module of this group stops its read, invoke or consume of the controller (pacman, the keeper, mesh-config, the hand-act log's readers, the watcher, messenger, the assigner, mesh-grants), so the list shrinks to the three and a read put back is refused in either phase. Supersedes the keep of pacman's read at mesh-delivery 4a84370. Tests: rows of TestTheLeaseWritesCheckJudgesEveryDependencyOnTheController changed (the keeper's read put back is refused; the sweep's collect passes); no new test function. (issue 774, 774/S30) Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
239 lines
10 KiB
Go
239 lines
10 KiB
Go
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. It
|
|
// holds the three kept to the write itself and nothing else: the lease write's own delivery dropped every other entry
|
|
// (ADR 0571 §13; issue 774), so its check runs with these alone in either phase. The third, 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{
|
|
{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},
|
|
}
|
|
|
|
// 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
|
|
}
|