watchman: its findings are written and read in the seat mesh-conditions' layout through meshconditionsclient (ADR 0634 §4; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 16:49:27 +01:00
parent 7c61a7ad37
commit 444fa48fbf
3 changed files with 24 additions and 29 deletions
+1
View File
@@ -39,3 +39,4 @@ replace git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun
require git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
replace git.novox.be/novox/mesh-delivery/sdk => ../../sdk
require git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient v0.0.0-00010101000000-000000000000
@@ -3,40 +3,40 @@ package watch
import (
"time"
"git.novox.be/novox/mesh-sdk/go/health"
mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
"git.novox.be/novox/mesh-delivery/sdk/heard"
)
// The watchman is an observer of the seat mesh-watchdogs (novox/hq ADR 0564 §2, §4): what it observes wrong it writes as a
// finding, one key `<scope>.<id>.<kind>` of its own bucket `findings` in mesh-sdk's layout (health.Finding), only when what
// finding, one key `<scope>.<id>.<kind>` of its own bucket `findings` in the seat mesh-conditions' layout (meshconditionsclient), only when what
// it says changes, and it deletes the key when it no longer observes it. The condition keeper (mesh-conditions) raises and
// clears the conditions from them; the watchman writes no condition and reads none (Limits 11).
// FindingsBucket is the watchman's findings.
var FindingsBucket = health.FindingsBucket(health.WatchmanModule)
var FindingsBucket = mcc.FindingsBucket(Observer)
// Observer is the name a finding of the watchman carries.
const Observer = health.WatchmanModule
const Observer = "mesh-delivery-watchman"
// The scopes and severities a finding of the watchman names, as the controller's conditions name them, so the keeper's
// `compare` meets the same keys.
const (
ScopeMachine = health.ScopeMachine
ScopeDelivery = health.ScopeDelivery
ScopeModule = health.ScopeModule
ScopeProvider = health.ScopeProvider
ScopeBus = health.ScopeBus
ScopeCore = health.ScopeCore
ScopeMachine = mcc.ScopeMachine
ScopeDelivery = mcc.ScopeDelivery
ScopeModule = mcc.ScopeModule
ScopeProvider = mcc.ScopeProvider
ScopeBus = mcc.ScopeBus
ScopeCore = mcc.ScopeCore
Urgent = string(health.Urgent)
Warning = string(health.Warning)
Urgent = mcc.Urgent
Warning = mcc.Warning
)
// Finding is what the watchman builds of one thing it observes wrong: its subject (scope, id, the machine and the others
// it concerns), kind and severity, its evidence (Summary, and Said with Since, its one observation), the words the
// operator reads (ADR 0253; empty leaves the keeper's wording of the kind), and the row or fact it comes from (Source).
// It is written as health.Finding.
// It is written as the seat's finding.
type Finding struct {
Scope, ID, Token, Kind, Machine string
Also []string
@@ -51,8 +51,8 @@ type Finding struct {
Since time.Time
}
// FindingKey composes a finding's key as the controller composes a condition's (health.Key).
func FindingKey(scope, id, token string) string { return health.FindingKey(scope, id, token) }
// FindingKey composes a finding's key as the controller composes a condition's (mcc.Key).
func FindingKey(scope, id, token string) string { return mcc.Key(scope, id, token) }
// Key is the finding's key: its token, or its kind when it has none.
func (f Finding) Key() string {
@@ -63,27 +63,21 @@ func (f Finding) Key() string {
return FindingKey(f.Scope, f.ID, token)
}
// EncodeFinding is f in mesh-sdk's layout, refusing one the keeper could not make a condition of.
// EncodeFinding is f in the seat's layout, refusing one the keeper could not make a condition of.
func EncodeFinding(f Finding) ([]byte, error) {
said := f.Said
if said == "" {
said = f.Summary
}
return health.EncodeFinding(health.Finding{Key: f.Key(), Kind: f.Kind, Ref: f.Token,
Subject: health.Subject{Scope: f.Scope, ID: f.ID, Machine: f.Machine, Also: f.Also},
Severity: health.Severity(f.Severity), Summary: f.Summary, Headline: f.Headline, Explanation: f.Explanation,
Resolved: f.Resolved, Needs: f.Needs, Evidence: []health.Evidence{{At: f.Since.UTC(), Said: said}},
Source: f.Source, Observer: Observer, At: f.Since.UTC()})
return mcc.EncodeFinding(mcc.Observation{Scope: f.Scope, ID: f.ID, Kind: f.Kind, Ref: f.Token, Machine: f.Machine, Also: f.Also,
Severity: f.Severity, Summary: f.Summary, Said: f.Said, Headline: f.Headline, Explanation: f.Explanation,
Resolved: f.Resolved, Needs: f.Needs, Source: f.Source}.Finding(Observer, "", f.Since))
}
// DecodeFinding reads a finding the watchman wrote.
func DecodeFinding(data []byte) (Finding, error) {
h, err := health.DecodeFinding(data)
h, err := mcc.DecodeFinding(data)
if err != nil {
return Finding{}, err
}
f := Finding{Scope: h.Subject.Scope, ID: h.Subject.ID, Token: h.Ref, Kind: h.Kind, Machine: h.Subject.Machine,
Also: h.Subject.Also, Severity: string(h.Severity), Summary: h.Summary, Headline: h.Headline, Explanation: h.Explanation,
Also: h.Subject.Also, Severity: h.Severity, Summary: h.Summary, Headline: h.Headline, Explanation: h.Explanation,
Needs: h.Needs, Resolved: h.Resolved, Source: h.Source, Observer: h.Observer, Since: h.At}
if len(h.Evidence) > 0 {
f.Said = h.Evidence[0].Said
@@ -9,13 +9,13 @@ import (
"git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient"
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
"git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein"
"git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/health"
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
"git.novox.be/novox/mesh-delivery/sdk/heard"
@@ -277,7 +277,7 @@ func TestTheWatchmansFindings(t *testing.T) {
case !row.noState && row.want == "" && stands:
t.Errorf("%s: the finding stands once bound", row.name)
case row.want != "":
f, err := health.DecodeFinding(raw)
f, err := mcc.DecodeFinding(raw)
// Review round 2: it says it clears at the part's next start, since Bind runs at the start.
if err != nil || f.Kind != heard.Kind || f.Summary != row.want || f.Observer != "mesh-delivery-indexer" || !strings.Contains(f.Explanation, "next start") {
t.Errorf("%s: the finding %+v (%v), want summary %q", row.name, f, err, row.want)