Read how a module says each resource is ready, and send it to engines that read it (hq ADR 0240, to-be 48 Phase B)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery covered: a later merge that contains it was delivered: novox/mesh-controller@4291fee68e95 (merged as a5a132ac into main, walk plan-17913907…
mesh/delivery-group group feat/health-the-field delivered: every member is delivered
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery covered: a later merge that contains it was delivered: novox/mesh-controller@4291fee68e95 (merged as a5a132ac into main, walk plan-17913907…
mesh/delivery-group group feat/health-the-field delivered: every member is delivered
A module could say nothing about what ready means for what it runs, so a web application with its port open and its requests hanging passed everything for eleven hours (issue 145). A long-running resource now carries `health` — the image's own check adopted by name, http, tcp, exec, unit or a module's own tool, with its timing — refused near its author when it names a port or an address, an endpoint the module does not declare, a tool it does not serve, a tool check alone, or a timing outside the record's bounds. It is composed with the endpoint as the port this machine published it on, and sent only to a node-engine whose statement says it reads it: an older one would refuse the whole declaration. The engine is granted its own machine's instance of each health tool. `module check` warns of every long-running resource without `health`, counts them for the catalogue, and refuses them from 2026-11-18. A check's findings stay out of a condition's summary. The node-engine's validator is vendored at its Phase B commit, so what is composed is judged by the words the engine takes.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/catalogue"
|
||||
)
|
||||
@@ -107,6 +108,28 @@ func moduleCheckFor(paths []string, longestMachine int, out io.Writer) error {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
// **Every long-running resource says how it is ready** (novox/hq ADR 0240 rule 8): warned until the
|
||||
// date, refused from it. The count is the catalogue's: its merge check keeps the number and lets a
|
||||
// change lower it, never raise it.
|
||||
undeclared := 0
|
||||
required := !checkNow().Before(catalogue.HealthRequiredFrom)
|
||||
for _, name := range names {
|
||||
missing := catalogue.Undeclared(shelf[name])
|
||||
undeclared += len(missing)
|
||||
if len(missing) == 0 {
|
||||
continue
|
||||
}
|
||||
if required {
|
||||
for _, id := range missing {
|
||||
fmt.Fprintf(out, "%s: %s stays up and does not say how it is ready: a long-running resource declares "+
|
||||
"health since %s (novox/hq ADR 0240 rule 8)\n", name, id, catalogue.HealthRequiredFrom.Format("2006-01-02"))
|
||||
}
|
||||
failed += len(missing)
|
||||
faulted[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
m := shelf[name]
|
||||
if faulted[name] {
|
||||
@@ -138,8 +161,24 @@ func moduleCheckFor(paths []string, longestMachine int, out io.Writer) error {
|
||||
}
|
||||
fmt.Fprintf(out, ", keeps %s", strings.Join(kept, ", "))
|
||||
}
|
||||
// How what it runs is ready (ADR 0240): each declared check, and what is judged by liveness alone.
|
||||
var checks []string
|
||||
for _, r := range m.Resources {
|
||||
if h, has, _ := catalogue.ReadHealth(r); has {
|
||||
checks = append(checks, fmt.Sprintf("%v by %s", r["id"], catalogue.HealthWords(h)))
|
||||
}
|
||||
}
|
||||
if len(checks) > 0 {
|
||||
fmt.Fprintf(out, ", ready: %s", strings.Join(checks, "; "))
|
||||
}
|
||||
if missing := catalogue.Undeclared(m); len(missing) > 0 {
|
||||
fmt.Fprintf(out, "; WARNING: %s stay(s) up and say(s) not how it is ready — judged by liveness alone, "+
|
||||
"refused from %s (ADR 0240 rule 8)", strings.Join(missing, ", "), catalogue.HealthRequiredFrom.Format("2006-01-02"))
|
||||
}
|
||||
fmt.Fprintln(out)
|
||||
}
|
||||
// The count the catalogue keeps (ADR 0240 rule 8), in a line its merge check reads.
|
||||
fmt.Fprintf(out, "%s %d\n", UndeclaredHealthLine, undeclared)
|
||||
if failed > 0 {
|
||||
return fmt.Errorf("%d problem(s) in %d manifest(s)", failed, len(paths))
|
||||
}
|
||||
@@ -150,6 +189,13 @@ func moduleCheckFor(paths []string, longestMachine int, out io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UndeclaredHealthLine starts the line `module check` says the count of long-running resources without
|
||||
// `health` in, over the manifests given: the catalogue's merge check compares it with the number it keeps.
|
||||
const UndeclaredHealthLine = "long-running resources without health:"
|
||||
|
||||
// checkNow is the clock `module check` judges the date by; a test sets it.
|
||||
var checkNow = time.Now
|
||||
|
||||
func joinInvokes(invokes []string) string {
|
||||
if len(invokes) == 1 && invokes[0] == "*" {
|
||||
return "every tool"
|
||||
|
||||
Reference in New Issue
Block a user