mesh-healers: attempts, would, the healer-acted event and its findings are the seats' contract types (B5d; ADR 0634 §1, §4; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 15:09:43 +01:00
parent 97cc9b1ccf
commit 916bdde6a4
5 changed files with 44 additions and 36 deletions
@@ -24,9 +24,11 @@ import (
"sync"
"time"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
meshconditions "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
meshhealers "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/handacts"
"git.novox.be/novox/mesh-sdk/go/health"
)
// Module and Seat are this module and the seat it holds once for the mesh.
@@ -37,11 +39,11 @@ const (
// The states it keeps and reads, and the event it says.
const (
AttemptsState = health.AttemptsState
WouldState = health.WouldState
FindingsState = health.FindingsState
AttemptsState = meshhealers.StateAttempts
WouldState = meshhealers.StateWould
FindingsState = meshconditions.StateFindings
ConditionsRead = "mesh-conditions.conditions"
ActedEvent = health.EventHealerActed
ActedEvent = meshhealers.EventHealerActed
// ActedSubject is where the seat says it, which its consumers hear (catalogue.SeatSays).
ActedSubject = "mesh.seat." + Seat + ".event." + ActedEvent
// RecordedEvent is the hand-act log's seat event, each act recorded there (ADR 0565 §11).
@@ -59,10 +61,10 @@ const (
// The outcomes an attempt has.
const (
OutcomeActing = health.OutcomeActing
OutcomeActed = health.OutcomeActed
OutcomeFailed = health.OutcomeFailed
OutcomeEscalated = health.OutcomeEscalated
OutcomeActing = meshhealers.HealerActedEventOutcomeActing
OutcomeActed = meshhealers.HealerActedEventOutcomeActed
OutcomeFailed = meshhealers.HealerActedEventOutcomeFailed
OutcomeEscalated = meshhealers.HealerActedEventOutcomeEscalated
)
// Store is the bus's buckets as this module reaches them, through its runtime. CreateTTL writes a key only when it has
@@ -113,9 +115,9 @@ func (c Condition) Escalated() bool { return c.Resolver == "operator" && len(c.T
// (one key per healer and condition, `<healer>.<condition key>`, its newest), and the body of healer-acted,
// epoch left out (ADR 0564 Limits 10).
type (
Attempt = health.Attempt
Would = health.Would
Acted = health.HealerActed
Attempt = meshhealers.AttemptsByAttemptValue
Would = meshhealers.WouldByWouldValue
Acted = meshhealers.HealerActedEvent
)
// Healer is one row of the registry.
@@ -481,7 +483,7 @@ func (h *Healers) brakeHolds(acts []Attempt, now time.Time) (bool, string) {
return false, said
}
var brakeKey = health.Key(ScopeMesh, "healers", "braked")
var brakeKey = meshconditionsclient.Key(ScopeMesh, "healers", "braked")
// brakeFinding is the brake, as the urgent finding that says it.
func brakeFinding(acts []Attempt, said string) Observation {
@@ -519,7 +521,7 @@ func (h *Healers) blind(err error) {
// no longer sees (ADR 0564 §2).
func (h *Healers) see(source string, found []Observation) {
now := h.Now()
want := map[string]health.Finding{}
want := map[string]meshconditions.FindingsByKeyValue{}
for _, o := range found {
o.Source = source
want[o.Key()] = o.Finding("", now)
@@ -534,7 +536,8 @@ func (h *Healers) see(source string, found []Observation) {
if err != nil || !found {
continue
}
was, err := health.DecodeFinding(raw)
var was meshconditions.FindingsByKeyValue
err = json.Unmarshal(raw, &was)
if err != nil || was.Source != source {
continue
}
@@ -549,7 +552,7 @@ func (h *Healers) see(source string, found []Observation) {
}
}
for k, f := range want {
raw, err := health.EncodeFinding(f)
raw, err := json.Marshal(f)
if err == nil {
err = h.Store.Put(FindingsState, k, json.RawMessage(raw))
}
@@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
meshconditions "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
"slices"
"sort"
"strings"
@@ -12,7 +13,6 @@ import (
"time"
"git.novox.be/novox/mesh-sdk/go/handacts"
"git.novox.be/novox/mesh-sdk/go/health"
)
type memStore struct {
@@ -286,7 +286,8 @@ func TestBudgetSettleEscalationAndBrake(t *testing.T) {
t.Errorf("past the brake: %q, asked %v", said, b.asked)
}
raw, braked, _ := b.store.Get(FindingsState, brakeKey)
f, _ := health.DecodeFinding(raw)
var f meshconditions.FindingsByKeyValue
_ = json.Unmarshal(raw, &f)
if !braked || f.Severity != Urgent || f.Kind != "healers-braked" || f.Observer != Module {
t.Errorf("the brake is written as %s", raw)
}
@@ -1,33 +1,34 @@
package main
// What the healers observe of themselves, and the finding it is written as (novox/hq ADR 0564 §2): the brake, their
// blindness and S15, each written into the bucket `findings` as mesh-sdk's health.Finding, whose key is the
// blindness and S15, each written into the bucket `findings` as the seat mesh-conditions' finding, whose key is the
// controller's key for the same condition.
import (
"encoding/json"
"time"
"git.novox.be/novox/mesh-sdk/go/health"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
meshconditions "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
)
// The severities and scopes, as mesh-sdk's health names them.
const (
Urgent = health.Urgent
Warning = health.Warning
ScopeProbe = health.ScopeProbe
ScopeMesh = health.ScopeMesh
Urgent = meshconditionsclient.Urgent
Warning = meshconditionsclient.Warning
ScopeProbe = meshconditionsclient.ScopeProbe
ScopeMesh = meshconditionsclient.ScopeMesh
)
// Subject is what a finding is about.
type Subject = health.Subject
type Subject = meshconditions.Subject
// Observation is one thing a probe sees wrong now.
type Observation struct {
Kind string
Ref string
Subject Subject
Severity health.Severity
Severity string
Summary string
Said string
Headline string
@@ -44,14 +45,14 @@ func (o Observation) Key() string {
if token == "" {
token = o.Kind
}
return health.Key(o.Subject.Scope, o.Subject.ID, token)
return meshconditionsclient.Key(o.Subject.Scope, o.Subject.ID, token)
}
// Finding is the observation as written by run at, its evidence the one line it said.
func (o Observation) Finding(run string, at time.Time) health.Finding {
f := health.Finding{Key: o.Key(), Kind: o.Kind, Subject: o.Subject, Severity: o.Severity, Summary: o.Summary,
func (o Observation) Finding(run string, at time.Time) meshconditions.FindingsByKeyValue {
f := meshconditions.FindingsByKeyValue{V: meshconditionsclient.FindingVersion, Key: o.Key(), Kind: o.Kind, Subject: o.Subject, Severity: o.Severity, Summary: o.Summary,
Headline: o.Headline, Explanation: o.Explanation, Resolved: o.Resolved, Needs: o.Needs, Source: o.Source,
Observer: Module, Run: run, At: at.UTC(), Evidence: []health.Evidence{}}
Observer: Module, Run: run, At: at.UTC(), Evidence: []meshconditions.Evidence{}}
if o.Ref != "" && o.Ref != o.Kind {
f.Ref = o.Ref
}
@@ -59,17 +60,17 @@ func (o Observation) Finding(run string, at time.Time) health.Finding {
if said == "" {
said = o.Summary
}
f.Evidence = append(f.Evidence, health.Evidence{At: at.UTC(), Said: said})
f.Evidence = append(f.Evidence, meshconditions.Evidence{At: at.UTC(), Said: said})
return f
}
// says is what a finding says, its times and the run that wrote it aside: two findings that say the same are one write
// (ADR 0289 §1).
func says(f health.Finding) string {
func says(f meshconditions.FindingsByKeyValue) string {
f.At, f.Run, f.V = time.Time{}, "", 0
ev := make([]health.Evidence, len(f.Evidence))
ev := make([]meshconditions.Evidence, len(f.Evidence))
for i, e := range f.Evidence {
ev[i] = health.Evidence{Said: e.Said}
ev[i] = meshconditions.Evidence{Said: e.Said}
}
f.Evidence = ev
raw, _ := json.Marshal(f)
@@ -15,6 +15,7 @@ import (
"encoding/json"
"errors"
"fmt"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
"io"
"os"
"os/exec"
@@ -27,7 +28,6 @@ import (
"github.com/nats-io/nats.go"
"github.com/nats-io/nats.go/jetstream"
"git.novox.be/novox/mesh-sdk/go/health"
"git.novox.be/novox/mesh-sdk/go/testbus"
)
@@ -312,7 +312,7 @@ func eventually(t *testing.T, what string, ok func() bool) {
func raised(key, module string) []byte {
raw, _ := json.Marshal(map[string]any{"key": key, "kind": "consumer-lost", "severity": "warning", "resolver": "self",
"subject": map[string]any{"scope": "bus", "id": "EVENTS." + module}, "event": "condition-raised",
"show": health.ShowKeeper(key)})
"show": meshconditionsclient.ShowKeeper(key)})
return raw
}
+3
View File
@@ -21,3 +21,6 @@ require (
golang.org/x/sys v0.48.0 // indirect
golang.org/x/time v0.16.0 // indirect
)
require git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient v0.0.0-00010101000000-000000000000
require git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000
require git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers v0.0.0-00010101000000-000000000000