Files
mesh-catalog/modules/mesh-self-check/cmd/mesh-self-check/tools.go
T
jschoubben 344fc6f6bf End the self-check's bus-consumer probe, whose consumers were the controller's
With the controller ended mesh-sdk defines no mesh consumer, so the probe judged none and always passed (issue 1126).
The account consumers are the grants part's, which now says their differences and delays as conditions of its own;
the probe's three kinds are carried as ended in mesh-conditions' table.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 12:29:56 +01:00

220 lines
7.5 KiB
Go

package main
// The seat's verbs and the module's own tools (novox/hq ADR 0564 §6, §12): `doctor` answers the last verdict at once,
// runs now, or gives one probe's verdict whole; `probes` is the registry; and a read of each state it keeps — the
// findings, the last verdict and the week's verdicts — so a person sees all it holds and a recovery is a call.
import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"
"time"
stdio "git.novox.be/novox/mesh-sdk/go"
)
// Verbs are the seat's verbs, as the manifest claims them.
var Verbs = []string{"doctor", "probes"}
// Tool names: the seat's verbs, and the module's own reads.
const (
DoctorTool = Seat + ".doctor"
ProbesTool = Seat + ".probes"
FindingsTool = "self_check_findings"
VerdictsTool = "self_check_verdicts"
)
func obj(props map[string]any) map[string]any {
return map[string]any{"type": "object", "properties": props}
}
// Tools are what the runtime serves.
func Tools(c *Checker) []stdio.Tool {
return []stdio.Tool{
{Name: DoctorTool,
Description: "The mesh's self-check (novox/hq ADR 0564 §6): the last run's verdict at once, each probe passed, " +
"failed with what it found, could not run (never a pass) or deferred with the facts it lacks; run=true runs " +
"every probe now and answers that run; probe=<id> gives one probe's verdict whole, every finding.",
Input: obj(map[string]any{
"run": map[string]any{"type": "boolean", "description": "run every probe now"},
"probe": map[string]any{"type": "string", "description": "one probe's verdict, whole, e.g. D7"}}),
Run: func(a map[string]any) (any, error) {
return c.Doctor(boolArg(a, "run"), strArg(a, "probe"), "asked by "+stdio.CallerOf(a))
}},
{Name: ProbesTool,
Description: "The self-check's registry: each probe's id, what it asserts, where it came from, the condition kinds " +
"it raises, and for one deferred the facts it lacks on the bus.",
Run: func(map[string]any) (any, error) { return c.Probes(), nil }},
{Name: FindingsTool,
Description: "What the self-check finds wrong now, as the condition keeper reads it (its bucket findings): every " +
"finding's key, kind, severity, the probe that found it and its summary; or one finding whole by key.",
Input: obj(map[string]any{"key": map[string]any{"type": "string", "description": "one finding's key"}}),
Run: func(a map[string]any) (any, error) { return c.Findings(strArg(a, "key")) }},
{Name: VerdictsTool,
Description: "The self-check's runs of the last week (its bucket verdicts, each kept a week) newest first, each " +
"with its counts; or one run whole by its id. The last run is the doctor verb's.",
Input: obj(map[string]any{
"run": map[string]any{"type": "string", "description": "one run's id, e.g. doctor-1793284800-12"},
"limit": map[string]any{"type": "number", "description": "at most this many, newest (default 50)"}}),
Run: func(a map[string]any) (any, error) { return c.Verdicts(strArg(a, "run"), intArg(a, "limit", 50)) }},
}
}
// Doctor is the verb: the last verdict, a run now, or one probe of either.
func (c *Checker) Doctor(run bool, probe, why string) (map[string]any, error) {
var r Run
switch {
case run:
r = c.RunOnce(context.Background(), why)
case c.LastRun() != nil:
r = *c.LastRun()
default:
raw, found, err := c.Store.Get(VerdictState, LastKey)
if err != nil {
return nil, fmt.Errorf("the last verdict cannot be read: %w", err)
}
if !found {
return nil, fmt.Errorf("the self-check has not finished its first run yet: it runs %s after it starts, then "+
"every %s — run=true runs it now", FirstAfter, Every)
}
if err := json.Unmarshal(raw, &r); err != nil {
return nil, fmt.Errorf("the last verdict is not readable: %w", err)
}
}
if probe != "" {
one, ok := oneProbe(r, probe)
if !ok {
return nil, fmt.Errorf("the run %s has no probe %q — %s lists them", r.Run, probe, ProbesTool)
}
r = one
} else {
r = Overview(r)
}
return map[string]any{"run": r, "age": c.Now().Sub(r.At).Round(time.Second).String(),
"note": "a probe that could not run is never a pass; each finding is a condition the keeper raises until a run no " +
"longer sees it, and one a single look can be wrong about is written when two runs in a row see it"}, nil
}
func oneProbe(r Run, id string) (Run, bool) {
for _, p := range r.Probes {
if strings.EqualFold(p.ID, id) {
r.Probes = []ProbeVerdict{p}
return r, true
}
}
return Run{}, false
}
// Probes is the registry as the verb answers it.
func (c *Checker) Probes() map[string]any {
var out []map[string]any
on := c.listed()
for _, p := range c.Registry {
row := map[string]any{"id": p.ID, "asserts": p.Asserts, "from": p.From, "kind": p.Kind}
if len(p.Raises) > 0 {
row["raises"] = p.Raises
}
if !on(p.ID) {
row["off"] = "off by setting"
} else if p.Run == nil {
row["deferred"] = p.Deferred
}
out = append(out, row)
}
return map[string]any{"probes": out, "every": Every.String(), "each within": Within.String()}
}
// Findings are the findings written now, or one whole.
func (c *Checker) Findings(key string) (any, error) {
if key != "" {
raw, found, err := c.Store.Get(FindingsState, key)
if err != nil {
return nil, err
}
if !found {
return nil, fmt.Errorf("no finding is written under %s now", key)
}
return json.RawMessage(raw), nil
}
written, err := c.written()
if err != nil {
return nil, fmt.Errorf("the findings cannot be read: %w", err)
}
rows := []map[string]any{}
for k, f := range written {
rows = append(rows, map[string]any{"key": k, "kind": f.Kind, "severity": f.Severity, "probe": f.Source,
"summary": f.Summary})
}
sort.Slice(rows, func(i, j int) bool { return rows[i]["key"].(string) < rows[j]["key"].(string) })
return map[string]any{"findings": rows, "count": len(rows)}, nil
}
// Verdicts are the week's runs newest first, or one whole.
func (c *Checker) Verdicts(run string, limit int) (any, error) {
if run != "" {
raw, found, err := c.Store.Get(VerdictsState, run)
if err != nil {
return nil, err
}
if !found {
return nil, fmt.Errorf("no run %s is kept: runs are kept a week", run)
}
return json.RawMessage(raw), nil
}
keys, err := c.Store.Keys(VerdictsState)
if err != nil {
return nil, fmt.Errorf("the week's verdicts cannot be read: %w", err)
}
sort.Slice(keys, func(i, j int) bool { return runOrder(keys[i]) > runOrder(keys[j]) })
total := len(keys)
if limit > 0 && len(keys) > limit {
keys = keys[:limit]
}
rows := []map[string]any{}
for _, k := range keys {
raw, found, err := c.Store.Get(VerdictsState, k)
if err != nil || !found {
continue
}
var r Run
if json.Unmarshal(raw, &r) == nil {
rows = append(rows, map[string]any{"run": r.Run, "at": r.At, "why": r.Why, "counts": r.Counts})
}
}
return map[string]any{"verdicts": rows, "kept": total, "kept-for": "a week"}, nil
}
// runOrder orders run ids `doctor-<unix>-<n>` by time, then number.
func runOrder(id string) string {
parts := strings.Split(id, "-")
if len(parts) != 3 {
return id
}
return fmt.Sprintf("%020s-%020s", parts[1], parts[2])
}
func strArg(a map[string]any, k string) string {
s, _ := a[k].(string)
return strings.TrimSpace(s)
}
// boolArg reads a flag as a boolean or as the seat's "true".
func boolArg(a map[string]any, k string) bool {
switch v := a[k].(type) {
case bool:
return v
case string:
return strings.EqualFold(strings.TrimSpace(v), "true")
}
return false
}
func intArg(a map[string]any, k string, otherwise int) int {
if n, ok := a[k].(float64); ok && n > 0 {
return int(n)
}
return otherwise
}