Say checked, healer-acted and act-recorded on their seats' subjects, where their consumers hear them, and pin mesh-sdk fbd00c8 (issues 1952, 1953)

stdio.Emit put them on the module's subject, which no consumer of a seat's event hears. The manifests no longer list a seat's events as the module's own, which the SDK now refuses. mesh-hand-acts takes its act layout and names from mesh-sdk's handacts, and counts a repeated cause by its Repair rule.
This commit is contained in:
2026-10-29 22:12:08 +01:00
parent d632461eb9
commit b5fdcb88c8
26 changed files with 102 additions and 130 deletions
@@ -45,6 +45,8 @@ const (
ConditionsRead = "mesh-conditions.conditions"
HandActsRead = "mesh-controller.hand-acts"
ActedEvent = health.EventHealerActed
// ActedSubject is where the seat says it, which its consumers hear (catalogue.SeatSays).
ActedSubject = "mesh.seat." + Seat + ".event." + ActedEvent
// The events it hears: the keeper's, on the seat mesh-conditions.
RaisedEvent = "mesh-conditions.condition-raised"
ChangedEvent = "mesh-conditions.condition-changed"
@@ -152,9 +154,10 @@ func Registry() []Healer {
// Healers is mesh-healers: the registry, what its acts reach, and what it holds.
type Healers struct {
Store Store
Ask func(key string, body any) (json.RawMessage, error)
Emit func(event string, body any) error
Store Store
Ask func(key string, body any) (json.RawMessage, error)
// Publish says a seat event on its subject, de-duplicated by id (stdio.SeatPublish).
Publish func(subject string, body any, id string) error
Now func() time.Time
Logf func(format string, args ...any)
Registry []Healer
@@ -354,10 +357,10 @@ func (h *Healers) escalate(row Healer, c Condition, budget string, spent []Attem
func (h *Healers) tell(e Acted) {
e.Event, e.At, e.By = ActedEvent, h.Now().UTC(), "healer "+e.Healer
e.Show = "mesh-conditions.conditions key=" + e.Condition
if h.Emit == nil {
if h.Publish == nil {
return
}
if err := h.Emit(ActedEvent, e); err != nil {
if err := h.Publish(ActedSubject, e, e.Healer+"."+e.Condition+"."+e.Outcome+"."+strconv.FormatInt(e.At.UnixNano(), 10)); err != nil {
h.Logf("[%s] healer %s %s %s, and that could NOT be said on the bus: %v", Module, e.Healer, e.Outcome, e.Condition, err)
}
}
@@ -112,8 +112,8 @@ func (w *world) fresh() {
w.asked = append(w.asked, key+" "+string(raw))
return json.RawMessage(`{"says":"2 the bus lacks: created at this ask"}`), nil
},
Emit: func(event string, body any) error {
if a, ok := body.(Acted); ok && event == ActedEvent {
Publish: func(subject string, body any, _ string) error {
if a, ok := body.(Acted); ok && subject == "mesh.seat.mesh-healers.event.healer-acted" {
w.said = append(w.said, a)
}
return nil
@@ -61,9 +61,9 @@ func watched(state, key string, handle func(stdio.StateChange) error) {
func main() {
h := &Healers{Store: runtimeStore{}, Registry: Registry(), Now: time.Now, Logf: logf,
Ask: func(key string, body any) (json.RawMessage, error) { return stdio.Ask(key, body) },
Emit: func(event string, body any) error { return stdio.Emit(event, body) },
After: func(d time.Duration, f func()) { time.AfterFunc(d, f) }}
Ask: func(key string, body any) (json.RawMessage, error) { return stdio.Ask(key, body) },
Publish: stdio.SeatPublish,
After: func(d time.Duration, f func()) { time.AfterFunc(d, f) }}
// The lease first: until it is read the healers write would-acts only. Once it is first read, every settle is
// re-armed from the attempts kept, so a restart loses no wake.
var started sync.Once
@@ -47,7 +47,7 @@ func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) {
name string
got, want []string
}{
{"emits", m.Emits, []string{ActedEvent}},
{"emits (healer-acted is the seat's, said on its subject)", m.Emits, nil},
{"consumes", m.Consumes, []string{RaisedEvent, ChangedEvent}},
{"invokes", m.Invokes, []string{AssertTool}},
{"reads", m.Reads, []string{LeaseRead, ConditionsRead, HandActsRead}},
@@ -216,6 +216,13 @@ func (p *player) handle(m wire) {
err = kv.Delete(p.ctx, a.Key)
}
p.answer(m.ID, map[string]any{}, err)
case "mesh/seat.publish":
var e struct {
Subject string `json:"subject"`
Body json.RawMessage `json:"body"`
}
_ = json.Unmarshal(m.Params, &e)
p.answer(m.ID, map[string]any{}, p.nc.Publish(e.Subject, e.Body))
case "mesh/publish":
var e struct {
Key string `json:"key"`
@@ -332,7 +339,7 @@ func TestTheHealersWholeActOnTheBusAndBrake(t *testing.T) {
if _, err := lease.Put(ctx, LeaseKey, []byte(`{"instance":"novox/1/2026-10-27","epoch":57}`)); err != nil {
t.Fatal(err)
}
acted, err := nc.SubscribeSync("mesh.mod." + Module + ".event." + ActedEvent)
acted, err := nc.SubscribeSync("mesh.seat." + Seat + ".event." + ActedEvent)
if err != nil {
t.Fatal(err)
}
+1 -1
View File
@@ -3,7 +3,7 @@ module mesh-healers
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261029204453-69fcb6c1c1ba
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261029211044-fbd00c89436c
github.com/nats-io/nats.go v1.54.0
)
+2 -2
View File
@@ -1,5 +1,5 @@
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261029204453-69fcb6c1c1ba h1:uXwkcQWgbu/UthizGHFaKPiA4NBVJgqL9q2jJnnGR1o=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261029204453-69fcb6c1c1ba/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261029211044-fbd00c89436c h1:Yd6AslGN7pCniQ9Q7ch05xAWHP57grXRoH3AsDQrSeY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261029211044-fbd00c89436c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
-3
View File
@@ -13,9 +13,6 @@
]
}
],
"emits": [
"healer-acted"
],
"consumes": [
"mesh-conditions.condition-raised",
"mesh-conditions.condition-changed"