H-find: the node-engine raises what only it knows of itself as its own findings

The have's failed phase (apply-failing), unhealthy health (module-unhealthy) and kept words (kept-as-it-runs), and each part of
its health report (machine-network, network-rewritten, network-unreachable, machine-units, relogin-needed,
directory-used-as-found, node-account-fallback), raised and cleared by the held writer where the engine knows the fact.

Builds: design 47 G A-197 (ADR 0596, A-192, A-149)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 06:00:42 +01:00
parent 4021de1d42
commit 4b215ba6ae
8 changed files with 734 additions and 2 deletions
+9 -1
View File
@@ -1402,7 +1402,15 @@ func writeHealthReport(ctx context.Context, queue *link.Session, h link.Health,
if c := machinesNow.Load(); c != nil {
machines.ByAddress, machines.Hub = c.Machines()
}
_, err := queue.WriteNodeHealth(ctx, link.NodeHealthOf(queue.Membership.Node, h, peers, machines))
report := link.NodeHealthOf(queue.Membership.Node, h, peers, machines)
if queue.Fallback != nil && report.Fallback == nil {
report.Fallback = queue.Fallback() // node-account-fallback (ADR 0583 §6), as the written report carries it
}
_, err := queue.WriteNodeHealth(ctx, report)
// The findings the report says are raised by the engine here, at every look, written to the bus or not (design 47 G A-197).
if keeper := ownFindingsNow.Load(); keeper != nil {
keeper.Report(report, machines.ByAddress)
}
switch {
case err != nil && err.Error() != *unwritten:
*unwritten = err.Error()
+11
View File
@@ -616,6 +616,11 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
// The marks of the haves run off the composition pass, each bounded (see marker).
marks := newMarker(run)
heldFindings := &seatwait.HeldKeeper{Node: node, ObserverOf: findings.observer, Writer: findings, Ctx: run, Say: say}
// And what only this engine knows of itself, raised where it knows it (design 47 G A-197): its haves' failed phase, health
// and kept words as they are written, and each part of its health report at every look.
ownFindings := &seatwait.NodeKeeper{Node: node, ObserverOf: findings.observer, Writer: findings, Ctx: run, Say: say}
session.SetHaveSaid(ownFindings.Have, ownFindings.HaveGone)
ownFindingsNow.Store(ownFindings)
hooks := composerHooks{
entryNotFound: func(fp string, notFound bool) {
if err := seatwait.SayEntryNotFound(run, findings, node, findings.observer(), fp, notFound, time.Now); err != nil {
@@ -707,6 +712,8 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
cancel()
current.set(nil)
composerNow.Store(nil)
ownFindingsNow.Store(nil)
session.SetHaveSaid(nil, nil)
session.SetManifestOf(nil)
apply.UseEngineModule(nil)
shadowNow.Store(nil)
@@ -909,6 +916,10 @@ func busyModules() []string {
// (writeHealthReport); nil on a node that does not compose its own, whose parts then name no target.
var machinesNow atomic.Pointer[selfcompose.Composer]
// ownFindingsNow is the keeper of the findings this engine raises of itself in the run that composes the node's own now, for the
// health loop (writeHealthReport); nil while no run holds one.
var ownFindingsNow atomic.Pointer[seatwait.NodeKeeper]
// composerNow is the composer of the run that composes the node's own now, nil while the node applies the controller's
// compositions or no run is going: what the verb compose asks a pass of (issue 774).
var composerNow atomic.Pointer[selfcompose.Composer]
+1 -1
View File
@@ -124,7 +124,7 @@ func newFindingsOfTheNode(session *link.Session, node, module string, say func(s
Module: module,
Node: node,
State: link.FindingsState{Session: session},
Kinds: []string{seatwait.Kind, seatwait.HeldKind, seatwait.EntryNotFoundKind},
Kinds: append([]string{seatwait.Kind, seatwait.HeldKind, seatwait.EntryNotFoundKind}, seatwait.NodeKinds...),
Asked: condseat.SubjectFindingsAsked,
Logf: func(format string, args ...any) { say(fmt.Sprintf(format, args...)) },
Publish: session.PublishEvent,