Look twice before saying a probe failed, and say conditions in machine names (hq issue 277)
D2 raised a resolver urgent on one query that timed out while its machine was loaded, and its summary carried the resolver's address and socket text, so the operator channel withheld the whole alert. - D2 asks every question up to three times, all at once; a resolver that answers nothing is held for the next run and raised urgent when two runs in a row find it silent. A wrong answer is still raised at once. - Findings a single look can be wrong about carry Confirm: raised on the second look in a row, kept while open, never cleared-and-reraised. Used by D2 silence, D3 (also asks discovery twice), D6 behind, D9, D13 unmeasured, probe-failed of the doctor, and blind watchdog rows. - Probe seat asks (D8, D13) are asked again when the bus brought no answer. - Summaries name machines and say things in words; addresses, paths, domains and raw errors move to the evidence (D2, D5, D8, D9, D13, S12). - internal/outward mirrors the messenger's content rule, allowing the mesh's machine names; the keeper rewords a summary that would be withheld and keeps it whole in the evidence; a TestMain lint fails the suite on any raised or linted finding that would be withheld.
This commit is contained in:
@@ -8,6 +8,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/outward"
|
||||
)
|
||||
|
||||
// Backend is where the open conditions are kept: one value per key, written by compare-and-set.
|
||||
@@ -70,6 +72,8 @@ type Keeper struct {
|
||||
closing sync.Once
|
||||
// Unsaid counts the transitions given up on, for the self-check to say.
|
||||
unsaid int
|
||||
// reworded are the keys whose summary was said in words for the operator's channel, said once each.
|
||||
reworded map[string]bool
|
||||
}
|
||||
|
||||
type clearing struct {
|
||||
@@ -105,7 +109,7 @@ var TellFor = 10 * time.Minute
|
||||
func NewKeeper(ctx context.Context, o Options) *Keeper {
|
||||
k := &Keeper{store: o.Store, history: o.History, teller: o.Teller, now: o.Now, say: o.Say, changed: o.Changed,
|
||||
epoch: o.Epoch,
|
||||
cleared: map[string]clearing{}, out: make(chan Event, 1024), drained: make(chan struct{})}
|
||||
cleared: map[string]clearing{}, reworded: map[string]bool{}, out: make(chan Event, 1024), drained: make(chan struct{})}
|
||||
if k.now == nil {
|
||||
k.now = time.Now
|
||||
}
|
||||
@@ -170,6 +174,7 @@ func (k *Keeper) Observe(ctx context.Context, o Observation) (Condition, error)
|
||||
if err := o.check(); err != nil {
|
||||
return Condition{}, err
|
||||
}
|
||||
o = k.sayable(o)
|
||||
key := o.Key()
|
||||
for i := 0; i < tries; i++ {
|
||||
now := k.now().UTC()
|
||||
@@ -627,3 +632,49 @@ func orSelf(resolver string) string {
|
||||
}
|
||||
return resolver
|
||||
}
|
||||
|
||||
// Unsayable is told of every observation whose words the operator's channel would withhold (novox/hq
|
||||
// ADR 0234 §6): its summary or its key carries an address, a domain, a path or a secret's shape. The
|
||||
// keeper says such a summary in words itself, and keeps what it said whole in the evidence; a test
|
||||
// suite sets this to fail the producer, which is where the summary should have been said rightly.
|
||||
var Unsayable func(o Observation, field string, r outward.Refusal)
|
||||
|
||||
// sayable is an observation whose summary may leave the mesh: **a summary says things in machine names
|
||||
// and the mesh's words** (novox/hq issue 277). One that carries what the channel withholds — a raw error
|
||||
// with an address in it, a path — is said in words here, as the last stand before the operator would
|
||||
// read "its words are withheld" instead of the alert; what it carried goes to the evidence, which
|
||||
// stays inside the mesh.
|
||||
func (k *Keeper) sayable(o Observation) Observation {
|
||||
machines := append([]string{o.Machine}, o.Also...)
|
||||
if r, ok := outward.Check(o.Key(), machines...); !ok && Unsayable != nil {
|
||||
Unsayable(o, "key", r)
|
||||
}
|
||||
r, ok := outward.Check(o.Summary, machines...)
|
||||
if ok {
|
||||
return o
|
||||
}
|
||||
if Unsayable != nil {
|
||||
Unsayable(o, "summary", r)
|
||||
}
|
||||
whole := o.Summary
|
||||
o.Summary = outward.Scrub(whole, fmt.Sprintf("a %s condition about %s %s: what it says is kept in its evidence "+
|
||||
"(`conditions show`)", o.Kind, o.Scope, o.ID), machines...)
|
||||
switch {
|
||||
case o.Said == "":
|
||||
o.Said = whole
|
||||
case !strings.Contains(o.Said, whole):
|
||||
o.Said += " — as raised: " + whole
|
||||
}
|
||||
k.mu.Lock()
|
||||
if k.reworded == nil {
|
||||
k.reworded = map[string]bool{}
|
||||
}
|
||||
first := !k.reworded[o.Key()]
|
||||
k.reworded[o.Key()] = true
|
||||
k.mu.Unlock()
|
||||
if first {
|
||||
k.say("the condition %s's summary carried %s, which the operator's channel withholds: said in words, "+
|
||||
"and kept whole in its evidence — its source should say it so", o.Key(), r.What)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user