Drop the keeper's and config's take-over verbs with the controller's sources
mesh-sdk drops compare, take-over and hand-back from the keeper's and config's seats (ADR 0590): there is no controller to compare with or hand back to. mesh-conditions loses those verbs, its take-over key and asks; mesh-config reads its own sources always and pull-declares is gone; and mesh-hand-acts no longer names the controller's log (issue 1126). Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -1,185 +0,0 @@
|
||||
package main
|
||||
|
||||
// **Built dark, compared, then switched by the operator** (novox/hq ADR 0564 clause 9; issue 1952 C5, C8): `compare`
|
||||
// lists the open keys of the keeper and of the controller, each difference with its reason; the kinds that end with the
|
||||
// controller, the kinds only the keeper raises and the keys held while their module is delivered are listed apart and
|
||||
// not counted. `take-over` is refused (issue 1126): the keeper is the one source since the controller ended by its
|
||||
// unassignment; a yes to an ask raised before still writes the take-over key naming the keeper. `hand-back` is gone (issue 774): nothing
|
||||
// reads the take-over key any more, so it is answered as gone and changes nothing.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/asks"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
// Difference is one key the two sides do not agree on, or one listed apart.
|
||||
type Difference struct {
|
||||
Key string `json:"key"`
|
||||
Kind string `json:"kind"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// Comparison is compare's answer.
|
||||
type Comparison struct {
|
||||
Differences []Difference `json:"differences"`
|
||||
// Apart are the keys not counted: of kinds that end with the controller, raised only by the keeper, or held.
|
||||
Apart []Difference `json:"apart"`
|
||||
Same int `json:"same"`
|
||||
At time.Time `json:"at"`
|
||||
}
|
||||
|
||||
// Empty says nothing differs.
|
||||
func (c Comparison) Empty() bool { return len(c.Differences) == 0 }
|
||||
|
||||
// Compare is the keeper's open conditions against the controller's.
|
||||
func (k *Keeper) Compare() (Comparison, error) {
|
||||
mine, err := k.Open()
|
||||
if err != nil {
|
||||
return Comparison{}, err
|
||||
}
|
||||
k.mu.Lock()
|
||||
k.init()
|
||||
theirs := map[string]health.Condition{}
|
||||
for key, c := range k.ctrl {
|
||||
theirs[key] = c
|
||||
}
|
||||
heldNow := map[string]held{}
|
||||
for key, h := range k.held {
|
||||
heldNow[key] = h
|
||||
}
|
||||
k.mu.Unlock()
|
||||
out := Comparison{Differences: []Difference{}, Apart: []Difference{}, At: k.Now().UTC()}
|
||||
open := map[string]health.Condition{}
|
||||
for _, c := range mine {
|
||||
open[c.Key] = c
|
||||
}
|
||||
keys := map[string]bool{}
|
||||
for key := range open {
|
||||
keys[key] = true
|
||||
}
|
||||
for key := range theirs {
|
||||
keys[key] = true
|
||||
}
|
||||
for key := range heldNow {
|
||||
keys[key] = true
|
||||
}
|
||||
sorted := make([]string, 0, len(keys))
|
||||
for key := range keys {
|
||||
sorted = append(sorted, key)
|
||||
}
|
||||
sort.Strings(sorted)
|
||||
for _, key := range sorted {
|
||||
c, inMine := open[key]
|
||||
t, inTheirs := theirs[key]
|
||||
kind := c.Kind
|
||||
if !inMine {
|
||||
kind = t.Kind
|
||||
}
|
||||
home, homed := k.homeOf(kind)
|
||||
if h, ok := heldNow[key]; ok {
|
||||
out.Apart = append(out.Apart, Difference{Key: key, Kind: kind,
|
||||
Reason: fmt.Sprintf("held while %s is delivered on %s (version %s)", h.Module, h.Node, short(h.Version))})
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case homed && (home.Carried || home.Replaced):
|
||||
why := "ends with the controller: no longer carried (issue 1126)"
|
||||
if home.Replaced {
|
||||
why = "ends with the controller: its fault is observed otherwise"
|
||||
}
|
||||
if home.Why != "" {
|
||||
why += " (" + home.Why + ")"
|
||||
}
|
||||
out.Apart = append(out.Apart, Difference{Key: key, Kind: kind, Reason: why})
|
||||
case inMine && !inTheirs && (!homed || !home.Controller):
|
||||
out.Apart = append(out.Apart, Difference{Key: key, Kind: kind, Reason: "keeper only: the controller never raises this kind"})
|
||||
case inMine && !inTheirs:
|
||||
out.Differences = append(out.Differences, Difference{Key: key, Kind: kind,
|
||||
Reason: "open at the keeper, not at the controller"})
|
||||
case !inMine && !homed:
|
||||
out.Differences = append(out.Differences, Difference{Key: key, Kind: kind,
|
||||
Reason: fmt.Sprintf("open at the controller; no home is decided for the kind %s (ADR 0564's gap)", kind)})
|
||||
case !inMine:
|
||||
out.Differences = append(out.Differences, Difference{Key: key, Kind: kind,
|
||||
Reason: fmt.Sprintf("open at the controller; %s has not written its finding", home.Observer)})
|
||||
case c.Severity != t.Severity:
|
||||
out.Differences = append(out.Differences, Difference{Key: key, Kind: kind,
|
||||
Reason: fmt.Sprintf("%s at the keeper, %s at the controller", c.Severity, t.Severity)})
|
||||
default:
|
||||
out.Same++
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Asks is the operator channel as the keeper asks it: an approve ask under its own name, and where one stands.
|
||||
type Asks interface {
|
||||
Ask(a asks.Ask) error
|
||||
}
|
||||
|
||||
// TakeOver is refused: the keeper is the one source of conditions since the controller ended (issue 1126).
|
||||
func (k *Keeper) TakeOver(a Asks, by, why string) (map[string]any, error) {
|
||||
if strings.TrimSpace(why) == "" {
|
||||
return nil, errors.New("a take-over says why")
|
||||
}
|
||||
return nil, errors.New("the keeper is the one source already: the controller ended by its unassignment (issue 1126)")
|
||||
}
|
||||
|
||||
// Warranted is the act on the operator's answer: a yes, compare still empty, writes the take-over key naming the keeper.
|
||||
func (k *Keeper) Warranted(o asks.Option, w asks.Warrant) error {
|
||||
if o.ID != "yes" {
|
||||
k.Logf("the take-over %s was answered %s: alarms stay with the controller", w.Ask, o.ID)
|
||||
return nil
|
||||
}
|
||||
cmp, err := k.Compare()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cmp.Empty() {
|
||||
return fmt.Errorf("the take-over %s was approved, and compare now shows %d difference(s): not switched", w.Ask,
|
||||
len(cmp.Differences))
|
||||
}
|
||||
return k.writeTakeover(health.Takeover{Follow: health.FollowKeeper, At: k.Now().UTC(), By: w.Says(), Ask: w.Ask})
|
||||
}
|
||||
|
||||
// HandBack is gone (issue 774): the readers of conditions hear the keeper alone and nothing reads the take-over key, so
|
||||
// a hand-back written there would answer success and do nothing (ADR 0564 clause 9). It is answered as gone, naming the
|
||||
// record, and changes nothing.
|
||||
func (k *Keeper) HandBack(by, why string) (map[string]any, error) {
|
||||
return nil, errors.New("hand-back is gone: the readers of conditions hear the condition keeper alone, and nothing " +
|
||||
"reads the take-over key, so there is no controller to hand them back to (ADR 0564 clause 9)")
|
||||
}
|
||||
|
||||
// writeTakeover writes the take-over key, and keeps it in history as the record of the switch.
|
||||
func (k *Keeper) writeTakeover(t health.Takeover) error {
|
||||
raw, err := health.EncodeTakeover(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, rev, found, err := k.Store.Get(TakeoverState, health.TakeoverKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
_, err = k.Store.Update(TakeoverState, health.TakeoverKey, json.RawMessage(raw), rev)
|
||||
} else {
|
||||
_, err = k.Store.Create(TakeoverState, health.TakeoverKey, json.RawMessage(raw))
|
||||
}
|
||||
if errors.Is(err, stdio.ErrChanged) {
|
||||
return fmt.Errorf("the take-over key moved under this write: read it again and ask again")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.Takeover(t)
|
||||
k.Logf("the readers of conditions follow the %s from now (%s)", t.Follow, t.By)
|
||||
return nil
|
||||
}
|
||||
@@ -309,7 +309,7 @@ func short(fp string) string {
|
||||
return fp
|
||||
}
|
||||
|
||||
// Held is every key held now, for the read tool and compare.
|
||||
// Held is every key held now, for the read tool.
|
||||
func (k *Keeper) Held() []held {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
|
||||
@@ -73,17 +73,6 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) {
|
||||
if got := r.changes(); got != "raised "+other+", raised "+machine {
|
||||
t.Errorf("%s: during the delivery said %q: another module's and the machine's at once, nothing of its own", c.name, got)
|
||||
}
|
||||
// C8: compare lists the held keys as held, never as a difference.
|
||||
cmp, _ := r.k.Compare()
|
||||
held := 0
|
||||
for _, d := range cmp.Apart {
|
||||
if strings.HasPrefix(d.Reason, "held while mesh-controller is delivered on novox") {
|
||||
held++
|
||||
}
|
||||
}
|
||||
if held != 1 {
|
||||
t.Errorf("%s: compare lists %d held, %+v", c.name, held, cmp)
|
||||
}
|
||||
// Its own finding goes unless it stands.
|
||||
if !c.standing {
|
||||
_ = r.k.Finding(watchman, own, nil, true)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package main
|
||||
|
||||
// **Where each condition kind lives after the take-over** (novox/hq ADR 0564 clauses 4, 6, 7 and 9, the Context's probe
|
||||
// table; issue 1952 C3, C5): data, so a kind's home is a row and never code. `compare` reads it to tell a difference from
|
||||
// a kind that ends with the controller or that the controller never raises, and the keeper reads it to clear, once, what
|
||||
// it carried of the kinds that ended with the controller (EndCarried, issue 1126).
|
||||
// **Where each condition kind the controller raised lives now** (novox/hq ADR 0564 clauses 4, 6, 7 and 9, the Context's
|
||||
// probe table; issue 1952 C3, C5): data, so a kind's home is a row and never code. The keeper reads it to clear, once,
|
||||
// what it carried of the kinds that ended with the controller (EndCarried, issue 1126); `compare`, which read it too,
|
||||
// went with the controller (ADR 0590).
|
||||
//
|
||||
// The rows are ADR 0564's and the lead's table of 2026-10-29, by the controller's source of each kind. A kind with no
|
||||
// row is named by `compare` as "no home decided".
|
||||
// The rows are ADR 0564's and the lead's table of 2026-10-29, by the controller's source of each kind.
|
||||
|
||||
// Home is where one condition kind lives.
|
||||
type Home struct {
|
||||
|
||||
@@ -28,12 +28,9 @@ const (
|
||||
Seat = "mesh-conditions"
|
||||
|
||||
ConditionsState = health.ConditionsState
|
||||
TakeoverState = health.TakeoverState
|
||||
HistoryState = "history"
|
||||
HeldState = "held"
|
||||
BoundsState = "bounds"
|
||||
// AskedState keeps the keeper's asks of the operator (go/asker).
|
||||
AskedState = "asked"
|
||||
)
|
||||
|
||||
// HistoryKeptFor is how long a transition is kept (clause 3).
|
||||
@@ -94,7 +91,7 @@ type Keeper struct {
|
||||
Say func(e health.Event) error
|
||||
Now func() time.Time
|
||||
Logf func(format string, a ...any)
|
||||
// Homes says, by kind, where each controller kind lives after the take-over (homes.go); Homes when nil.
|
||||
// Homes says, by kind, where each kind the controller raised lives now (homes.go); Homes when nil.
|
||||
Homes map[string]Home
|
||||
// Bound is a delivery's hold bound; delivery.DefaultDeliveryBound when zero (hold.go).
|
||||
Bound time.Duration
|
||||
@@ -103,10 +100,6 @@ type Keeper struct {
|
||||
findings map[string]map[string]health.Finding
|
||||
blind map[string]string
|
||||
attempts map[string]health.Attempt
|
||||
// ctrl is the controller's conditions as Controller hands them: compare's other side alone, never a condition's
|
||||
// input (issue 1126). Nothing feeds it since issue 774; it goes with compare.
|
||||
ctrl map[string]health.Condition
|
||||
takeover health.Takeover
|
||||
wants map[string]delivery.Want
|
||||
haves map[string]delivery.Have
|
||||
// holders are, by `<node>.<seat>`, the module the placement says holds the seat there.
|
||||
@@ -125,11 +118,10 @@ type Keeper struct {
|
||||
|
||||
func (k *Keeper) init() {
|
||||
if k.findings == nil {
|
||||
k.findings, k.blind, k.attempts, k.ctrl = map[string]map[string]health.Finding{}, map[string]string{},
|
||||
map[string]health.Attempt{}, map[string]health.Condition{}
|
||||
k.findings, k.blind, k.attempts = map[string]map[string]health.Finding{}, map[string]string{},
|
||||
map[string]health.Attempt{}
|
||||
k.wants, k.haves, k.stuck, k.held, k.cleared = map[string]delivery.Want{}, map[string]delivery.Have{},
|
||||
map[string]string{}, map[string]held{}, map[string]clearedMemo{}
|
||||
k.takeover = health.Takeover{V: health.TakeoverVersion, Follow: health.FollowController}
|
||||
k.passed, k.outOfHold, k.holders = map[string]string{}, map[string]string{}, map[string]string{}
|
||||
k.placement = delivery.NewPlacementKeys()
|
||||
}
|
||||
@@ -209,26 +201,6 @@ func (k *Keeper) Attempt(key string, value []byte, deleted bool) error {
|
||||
return k.Settle(condition)
|
||||
}
|
||||
|
||||
// Controller takes one change of the controller's `conditions` bucket, for compare alone: no condition is carried from
|
||||
// it (issue 1126).
|
||||
func (k *Keeper) Controller(key string, value []byte, deleted bool) error {
|
||||
k.mu.Lock()
|
||||
k.init()
|
||||
if deleted {
|
||||
delete(k.ctrl, key)
|
||||
} else {
|
||||
var c health.Condition
|
||||
if err := json.Unmarshal(value, &c); err != nil {
|
||||
k.mu.Unlock()
|
||||
k.Logf("the controller's condition %s is not readable, so compare does not list it: %v", key, err)
|
||||
return nil
|
||||
}
|
||||
k.ctrl[key] = c
|
||||
}
|
||||
k.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// endedWhy is why a condition of a kind that ended with the controller is cleared.
|
||||
const endedWhy = "its kind ended with the controller, which was unassigned (issue 1126): no finding raises it again"
|
||||
|
||||
@@ -273,14 +245,6 @@ func (k *Keeper) EndCarried() (int, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Takeover takes the keeper's own take-over key as read.
|
||||
func (k *Keeper) Takeover(t health.Takeover) {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
k.init()
|
||||
k.takeover = t
|
||||
}
|
||||
|
||||
// Settle derives the condition key should be and writes the difference (clause 2), retried while another write moves
|
||||
// it under this one.
|
||||
func (k *Keeper) Settle(key string) error {
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
@@ -242,28 +241,6 @@ func TestTheKeeperCarriesNoControllerConditionAndClearsWhatItCarriedOnce(t *test
|
||||
t.Fatal(err)
|
||||
}
|
||||
r.said = nil
|
||||
until := r.now.Add(time.Hour)
|
||||
for _, c := range []health.Condition{
|
||||
{Key: "core.controller.deaf", Kind: "controller-deaf", Source: "S4", Subject: health.Subject{Scope: health.ScopeCore, ID: "controller"},
|
||||
Severity: health.Urgent, Summary: "deaf", Resolver: "self"},
|
||||
{Key: key, Kind: "silent", Severity: health.Urgent, Resolver: health.ResolverOperator,
|
||||
Tried: []health.Tried{{At: r.now, What: "x", Outcome: "escalated", By: "healer H1"}},
|
||||
Silenced: &health.Silence{Until: until}},
|
||||
} {
|
||||
raw, _ := json.Marshal(c)
|
||||
if err := r.k.Controller(c.Key, raw, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := r.k.Resync(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if _, open := r.open("core.controller.deaf"); open || r.changes() != "" {
|
||||
t.Error("the controller's condition was carried")
|
||||
}
|
||||
if c, _ := r.open(key); c.Severity != health.Warning || c.Resolver != health.ResolverSelf || len(c.Tried) != 0 || c.Silenced != nil {
|
||||
t.Errorf("the controller's escalation reached the keeper's condition: %+v", c)
|
||||
}
|
||||
// Carried while the controller ran: two of kinds that ended with it, still open in conditions.
|
||||
for _, c := range []health.Condition{
|
||||
{Key: "core.controller.deaf", Kind: "controller-deaf", Severity: health.Urgent, Resolver: "self", Count: 1},
|
||||
@@ -285,51 +262,10 @@ func TestTheKeeperCarriesNoControllerConditionAndClearsWhatItCarriedOnce(t *test
|
||||
}
|
||||
}
|
||||
|
||||
// ADR 0564 clause 9 (issue 1952 C5, C8; the lead 2026-10-29): compare counts only what both sides can raise. A key held
|
||||
// while its module is delivered, a kind that ends with the controller and a kind the controller never raises are listed
|
||||
// apart; a kind with no home decided is a difference naming the gap; take-over is refused (issue 1126), and
|
||||
// hand-back is gone (issue 774, review g1): answered as gone, naming its record, and writing nothing.
|
||||
func TestCompareCountsOnlyWhatBothSidesRaise(t *testing.T) {
|
||||
r := newRig()
|
||||
put := func(key, kind string) []byte {
|
||||
raw, _ := json.Marshal(health.Condition{Key: key, Kind: kind, Severity: health.Warning, Source: "x"})
|
||||
return raw
|
||||
}
|
||||
key, raw := finding(t, watchman, health.ScopeMachine, "g14", "silent", "S1", health.Warning, r.now)
|
||||
_ = r.k.Finding(watchman, key, raw, false)
|
||||
_ = r.k.Controller(key, put(key, "silent"), false)
|
||||
only, onlyRaw := finding(t, delivery.AssignerModule, health.ScopeSeat, "mesh-placement.ace", "states-unbound", "say", health.Warning, r.now)
|
||||
_ = r.k.Finding(delivery.AssignerModule, only, onlyRaw, false)
|
||||
_ = r.k.Controller("core.controller.deaf", put("core.controller.deaf", "controller-deaf"), false)
|
||||
_ = r.k.Controller("delivery.x.mystery", put("delivery.x.mystery", "mystery"), false)
|
||||
_ = r.k.Controller("bus.x.consumer-wrong", put("bus.x.consumer-wrong", "consumer-wrong"), false)
|
||||
_ = r.k.Controller("seat.vault.ace.holder-silent", put("seat.vault.ace.holder-silent", "holder-silent"), false)
|
||||
_ = r.k.Controller("bus.nats.upgrade-failed", put("bus.nats.upgrade-failed", "bus-upgrade-failed"), false)
|
||||
cmp, err := r.k.Compare()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reasons := map[string]string{}
|
||||
for _, d := range append(append([]Difference(nil), cmp.Differences...), cmp.Apart...) {
|
||||
reasons[d.Key] = d.Reason
|
||||
}
|
||||
for key, says := range map[string]string{
|
||||
only: "keeper only",
|
||||
"core.controller.deaf": "carried",
|
||||
"delivery.x.mystery": "no home is decided for the kind mystery",
|
||||
"bus.x.consumer-wrong": "mesh-self-check has not written its finding",
|
||||
// A kind of a probe the operator dropped (2026-10-30) is carried, never missing.
|
||||
"seat.vault.ace.holder-silent": "no longer carried (issue 1126) (dropped 2026-10-30 by the operator",
|
||||
// The controller's own bus upgrade ends with its lease (the operator, 2026-10-31; issue 1952).
|
||||
"bus.nats.upgrade-failed": "ends with the controller",
|
||||
} {
|
||||
if !strings.Contains(reasons[key], says) {
|
||||
t.Errorf("%s: %q, want it to say %q", key, reasons[key], says)
|
||||
}
|
||||
}
|
||||
if cmp.Same != 1 || len(cmp.Differences) != 2 {
|
||||
t.Errorf("compare: %+v", cmp)
|
||||
}
|
||||
// ADR 0564 (issue 1952 C5; the lead 2026-10-29): every kind the controller raised has its home in the table, and the
|
||||
// kinds that ended with it are marked carried, which EndCarried clears once (issue 1126). compare, take-over and
|
||||
// hand-back went with the controller (ADR 0590): there is no other side to compare and nothing to hand back to.
|
||||
func TestEveryControllerKindHasItsHome(t *testing.T) {
|
||||
// The lead's table of 2026-10-29: each kind has its home, lease-split and image-missing do not end, and the 22
|
||||
// that end are carried; with the 11 kinds of the probes the operator dropped on 2026-10-30, 33.
|
||||
carried := 0
|
||||
@@ -355,14 +291,4 @@ func TestCompareCountsOnlyWhatBothSidesRaise(t *testing.T) {
|
||||
if len(Homes) != 84 || carried != 34 { // 19 of ADR 0564, the 63 of the table, plan-failed and stalled
|
||||
t.Errorf("the table holds %d kinds, %d carried", len(Homes), carried)
|
||||
}
|
||||
if _, err := r.k.TakeOver(nil, "jochen", "now"); err == nil || !strings.Contains(err.Error(), "one source already") {
|
||||
t.Errorf("take-over: %v", err)
|
||||
}
|
||||
if _, err := r.k.HandBack("jochen", "back"); err == nil || !strings.Contains(err.Error(), "hand-back is gone") ||
|
||||
!strings.Contains(err.Error(), "ADR 0564 clause 9") {
|
||||
t.Errorf("hand-back: %v, want it answered as gone naming its record", err)
|
||||
}
|
||||
if _, _, found, _ := r.k.Store.Get(TakeoverState, health.TakeoverKey); found {
|
||||
t.Error("hand-back or take-over wrote the take-over key")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ import (
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/asker"
|
||||
"git.novox.be/novox/mesh-sdk/go/asks"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
@@ -90,15 +88,6 @@ func Keep(k *Keeper) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if e, err := stdio.State(TakeoverState).Get(health.TakeoverKey); err == nil {
|
||||
var raw []byte
|
||||
if e != nil {
|
||||
raw = e.Value
|
||||
}
|
||||
if t, err := health.DecodeTakeover(raw, e != nil); err == nil {
|
||||
k.Takeover(t)
|
||||
}
|
||||
}
|
||||
said := func(err error) error {
|
||||
if err != nil {
|
||||
logf("%v", err)
|
||||
@@ -134,20 +123,11 @@ func Keep(k *Keeper) {
|
||||
|
||||
func main() {
|
||||
k := NewKeeper()
|
||||
client := asker.New(AskedState)
|
||||
client.Module = Module
|
||||
go func() {
|
||||
<-stdio.Served()
|
||||
Keep(k)
|
||||
if err := client.Listen(k.Warranted, func(h asker.Handled, w asks.Warrant, err error) {
|
||||
if err != nil {
|
||||
logf("the answer to %s (%s): %v", w.Ask, h, err)
|
||||
}
|
||||
}); err != nil {
|
||||
logf("the operator's answers cannot be heard: %v", err)
|
||||
}
|
||||
}()
|
||||
if err := stdio.Serve("", Tools(k, client)); err != nil {
|
||||
if err := stdio.Serve("", Tools(k)); err != nil {
|
||||
logf("%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -49,11 +49,10 @@ func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) {
|
||||
ttl[HistoryState], ttl[BoundsState], keyTTL[BoundsState])
|
||||
}
|
||||
// Clause 12: each state with the tool that reads it, each act with the tool that starts it.
|
||||
readBy := map[string]string{ConditionsState: Seat + ".conditions", HistoryState: HistoryTool, TakeoverState: TakeoverTool,
|
||||
HeldState: HeldTool, BoundsState: HeldTool, AskedState: Seat + ".take-over", "a resync": Seat + ".resync",
|
||||
"what it reads": InputsTool}
|
||||
readBy := map[string]string{ConditionsState: Seat + ".conditions", HistoryState: HistoryTool,
|
||||
HeldState: HeldTool, BoundsState: HeldTool, "a resync": Seat + ".resync", "what it reads": InputsTool}
|
||||
tools := map[string]bool{}
|
||||
for _, tool := range Tools(&Keeper{}, nil) {
|
||||
for _, tool := range Tools(&Keeper{}) {
|
||||
tools[tool.Name] = true
|
||||
}
|
||||
for name := range ttl {
|
||||
|
||||
@@ -17,14 +17,13 @@ import (
|
||||
)
|
||||
|
||||
// Verbs are the seat's verbs, as the manifest claims them.
|
||||
var Verbs = []string{"conditions", "silence", "compare", "take-over", "hand-back", "resync"}
|
||||
var Verbs = []string{"conditions", "silence", "resync"}
|
||||
|
||||
// The keeper's own read tools.
|
||||
const (
|
||||
HistoryTool = "conditions_history"
|
||||
HeldTool = "conditions_held"
|
||||
TakeoverTool = "conditions_takeover"
|
||||
InputsTool = "conditions_inputs"
|
||||
HistoryTool = "conditions_history"
|
||||
HeldTool = "conditions_held"
|
||||
InputsTool = "conditions_inputs"
|
||||
)
|
||||
|
||||
// briefEvidence is how many observations a condition carries in a list: its newest (the controller's, issue 314).
|
||||
@@ -50,7 +49,7 @@ func str(description string) map[string]any {
|
||||
}
|
||||
|
||||
// Tools are what the runtime serves.
|
||||
func Tools(k *Keeper, a Asks) []stdio.Tool {
|
||||
func Tools(k *Keeper) []stdio.Tool {
|
||||
return []stdio.Tool{
|
||||
{Name: Seat + ".conditions", Description: "What is wrong with the mesh now: every open condition, urgent first, then " +
|
||||
"oldest, raised from the observers' findings and cleared when the finding goes, never by hand. Given a key, that " +
|
||||
@@ -70,19 +69,6 @@ func Tools(k *Keeper, a Asks) []stdio.Tool {
|
||||
}
|
||||
return k.Silence(strArg(args, "key"), d, stdio.CallerOf(args), strArg(args, "why"))
|
||||
}},
|
||||
{Name: Seat + ".compare", Description: "The keeper's open conditions against the controller's, each difference with " +
|
||||
"its reason; what ends with the controller, what only the keeper raises and what is held listed apart.",
|
||||
Run: func(map[string]any) (any, error) { return k.Compare() }},
|
||||
{Name: Seat + ".take-over", Description: "Refused: the keeper is the one source of conditions since the " +
|
||||
"controller ended by its unassignment (issue 1126); kept as the seat's verb until the seat drops it.",
|
||||
Input: obj(map[string]any{"why": str("why now")}, "why"),
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
return k.TakeOver(a, stdio.CallerOf(args), strArg(args, "why"))
|
||||
}},
|
||||
{Name: Seat + ".hand-back", Description: "Gone: the readers of conditions hear the keeper alone, so there is nothing " +
|
||||
"to hand back; answered as gone and changes nothing (ADR 0564 clause 9).",
|
||||
Input: obj(map[string]any{"why": str("why")}, "why"),
|
||||
Run: func(args map[string]any) (any, error) { return k.HandBack(stdio.CallerOf(args), strArg(args, "why")) }},
|
||||
{Name: Seat + ".resync", Description: "Read every finding again and raise and clear what differs.",
|
||||
Run: func(map[string]any) (any, error) {
|
||||
n, err := k.Resync()
|
||||
@@ -99,16 +85,8 @@ func Tools(k *Keeper, a Asks) []stdio.Tool {
|
||||
bounds, err := k.Store.Keys(BoundsState)
|
||||
return map[string]any{"held": k.Held(), "bounds": bounds}, err
|
||||
}},
|
||||
{Name: TakeoverTool, Description: "The take-over key: which source the router, the grants part and mesh-watcher hear.",
|
||||
Run: func(map[string]any) (any, error) {
|
||||
raw, _, found, err := k.Store.Get(TakeoverState, health.TakeoverKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return health.DecodeTakeover(raw, found)
|
||||
}},
|
||||
{Name: InputsTool, Description: "What the keeper reads, as it stands: each observer's findings counted, an observer it " +
|
||||
"cannot read and why, the healers' attempts, the controller's conditions and whether its lease is handed away.",
|
||||
"cannot read and why, the healers' attempts, the wants and the haves.",
|
||||
Run: func(map[string]any) (any, error) { return k.Inputs(), nil }},
|
||||
}
|
||||
}
|
||||
@@ -212,7 +190,6 @@ func (k *Keeper) Inputs() map[string]any {
|
||||
findings[o] = len(fs)
|
||||
}
|
||||
return map[string]any{"findings": findings, "blind": k.blind, "attempts": len(k.attempts),
|
||||
"controller-conditions": len(k.ctrl), "follow": k.takeover.Follow,
|
||||
"wants": len(k.wants), "haves": len(k.haves)}
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ func TestTheKeeperWholeRaisesAndClearsOnTheBus(t *testing.T) {
|
||||
for _, o := range Observers {
|
||||
buckets = append(buckets, o+"_findings")
|
||||
}
|
||||
for _, s := range []string{ConditionsState, HistoryState, TakeoverState, HeldState, BoundsState, AskedState} {
|
||||
for _, s := range []string{ConditionsState, HistoryState, HeldState, BoundsState} {
|
||||
buckets = append(buckets, Module+"_"+s)
|
||||
}
|
||||
for _, b := range buckets {
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
"serves": [
|
||||
"conditions",
|
||||
"silence",
|
||||
"compare",
|
||||
"take-over",
|
||||
"hand-back",
|
||||
"resync"
|
||||
]
|
||||
}
|
||||
@@ -38,13 +35,11 @@
|
||||
"name": "history",
|
||||
"ttl-seconds": 7776000
|
||||
},
|
||||
"takeover",
|
||||
"held",
|
||||
{
|
||||
"name": "bounds",
|
||||
"key-ttl": true
|
||||
},
|
||||
"asked"
|
||||
}
|
||||
],
|
||||
"own-secrets": {
|
||||
"broker": "${dir:state}/broker"
|
||||
|
||||
Reference in New Issue
Block a user