State a tool check that waits for the operator as waiting (issue 386, ADR 0283)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed

A module whose only fault was a secret the operator had not given yet was
stated unhealthy, failed its first-node gate and was put back. The engine
now reads the tool's answer through mesh-sdk go/health and states such a
resource waiting, with what it waits for, on one look; a malformed wait
stays unhealthy.
This commit is contained in:
jochen
2026-10-10 21:11:02 +02:00
parent 9d8aa9d6f0
commit 3abb07f1e2
13 changed files with 246 additions and 63 deletions
+20 -3
View File
@@ -39,6 +39,7 @@ import (
"sync"
"time"
"git.novox.be/novox/mesh-sdk/go/health"
"github.com/novox/mesh-host/internal/declaration"
)
@@ -66,6 +67,9 @@ const (
Starting = "starting"
Held = "held"
Unknown = "unknown"
// Waiting is a resource whose tool check says it waits for the operator, and nothing else is wrong
// (novox/hq ADR 0283): a statement, not a fault, said with what it waits for.
Waiting = "waiting"
ReasonDown = "down"
ReasonRestarting = "restarting"
@@ -175,6 +179,8 @@ type State struct {
Since time.Time `json:"since"`
Streak int `json:"streak,omitempty"`
Restarts int `json:"restarts,omitempty"`
// Waits is what a resource stated Waiting waits for (ADR 0283).
Waits []health.Wait `json:"waits,omitempty"`
}
// CheckOf and NeedsOf are a stated resource's declared check, in a word, and the provision it exercises.
@@ -235,6 +241,9 @@ type kept struct {
Passed bool `json:"passed,omitempty"`
Failing int `json:"failing,omitempty"`
Why string `json:"why,omitempty"`
// Waits is what the last look of a tool check said the resource waits for (ADR 0283), nil when it said
// healthy or unhealthy; Why then is the answer's own words.
Waits []health.Wait `json:"waits,omitempty"`
// probing says a look of the engine's own is under way; due when the next is.
probing bool
due time.Time
@@ -319,7 +328,7 @@ func (j *Judge) Set(resources []Resource) {
} else if ok && !sameCheck(k.Check, r.Check) {
// Its check changed — another kind, another port, another timing: what the old one found
// says nothing about the new, which looks again at once.
k.Check, k.Passed, k.Failing, k.Why, k.due = r.Check, false, 0, "", time.Time{}
k.Check, k.Passed, k.Failing, k.Why, k.Waits, k.due = r.Check, false, 0, "", nil, time.Time{}
}
}
for id := range j.f.Kept {
@@ -358,7 +367,7 @@ func (j *Judge) Look(ctx context.Context) (Statement, bool) {
j.judge(k, o, now, r.Kind == KindContainer && held[r.Target], blind, why)
seen[r.ID] = true
st.Resources = append(st.Resources, State{Resource: k.Resource, State: k.State, Reason: k.Reason, Since: k.Since,
Streak: k.Streak, Restarts: k.Counted})
Streak: k.Streak, Restarts: k.Counted, Waits: waitsOf(k)})
word := k.State + "/" + k.Reason
if j.said[r.ID] != word {
changed = true
@@ -392,7 +401,7 @@ func (j *Judge) judge(k *kept, o Observed, now time.Time, held, blind bool, why
k.Seen, k.Identity, k.RuntimeRestarts, k.RuntimeStarted = o.Found, o.Identity, o.Restarts, o.Started
k.Started, k.Recent = started, nil
// Every start is judged ready afresh (to-be 48 §4).
k.Passed, k.Failing, k.Why, k.due = false, 0, "", time.Time{}
k.Passed, k.Failing, k.Why, k.Waits, k.due = false, 0, "", nil, time.Time{}
}
// **Held is neither alive nor dead**, and the window ending is a start: judged from a fresh grace.
@@ -562,3 +571,11 @@ func firstLine(s string) string {
line, _, _ := strings.Cut(strings.TrimSpace(s), "\n")
return line
}
// waitsOf is what a resource stated Waiting waits for, and nothing for any other state.
func waitsOf(k *kept) []health.Wait {
if k.State != Waiting {
return nil
}
return append([]health.Wait(nil), k.Waits...)
}