The node-engine says no capabilities-changed: manifest-reads-changed alone says a node's capabilities, as the contracts' K6 has the seat

The join with mesh-contracts' K6 (7d730a4a) removes the event and its body; H-3's second source of the same fact goes (haveState.capsSaid, capabilitiesToSay, sayCapabilities), the test asserts none is said.

Builds: design 47 G §590

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 21:13:24 +01:00
parent 5d3b446988
commit 75f5c64646
2 changed files with 6 additions and 36 deletions
+2 -32
View File
@@ -9,7 +9,6 @@ import (
nodeengine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
sdkkeys "git.novox.be/novox/mesh-sdk/go/keys"
"github.com/novox/mesh-host/internal/compositions"
"slices"
"sort"
"strings"
"sync"
@@ -68,11 +67,8 @@ type haveState struct {
userList map[string]nodeengine.UserListWritten
// userListNoKey is each module whose user list was said to have no have key to ride on, so it is said once.
userListNoKey map[string]bool
// runningSaid is, by module, the fingerprint and manifest its event running last said, so each is said once; and
// capsSaid the capabilities the event capabilities-changed last said (nil: none said yet this run).
// runningSaid is, by module, the fingerprint and manifest its event running last said, so each is said once.
runningSaid map[string]string
capsSaid []string
capsKnown bool
// unjudged is each module whose composition moved (an apply began, or the fact names another build) and that no look
// has judged since it stopped moving: its health is not yet judged, never the last look's of what ran before.
unjudged map[string]bool
@@ -366,7 +362,7 @@ func (q *Session) writeHaves(ctx context.Context, bus Bus) {
return
}
s := &q.haves
// What the pass says as the seat's events (running, capabilities-changed) is said once the haves' lock is let go: a
// What the pass says as the seat's events (running) is said once the haves' lock is let go: a
// publish never holds it.
var said []func()
defer func() {
@@ -467,9 +463,6 @@ func (q *Session) writeHaves(ctx context.Context, bus Bus) {
}) {
return
}
if ev, ok := s.capabilitiesToSay(node, facts.Record); ok {
said = append(said, func() { q.sayCapabilities(ev) })
}
}
}
if err != nil {
@@ -937,35 +930,12 @@ func (s *haveState) runningToSay(module string, h nodeengine.HaveByNodeModuleVal
return nodeengine.Running{V: sdkkeys.Version, Module: module, Fingerprint: h.Fp, Manifest: digest}, true
}
// capabilitiesToSay is the event capabilities-changed when the node's record says other capabilities than the event last
// said (or none was said this run). Under s.mu.
func (s *haveState) capabilitiesToSay(node string, rec *nodeengine.NodeRecord) (nodeengine.CapabilitiesChanged, bool) {
if rec == nil {
return nodeengine.CapabilitiesChanged{}, false
}
if s.capsKnown && slices.Equal(s.capsSaid, rec.Capabilities) {
return nodeengine.CapabilitiesChanged{}, false
}
s.capsKnown, s.capsSaid = true, slices.Clone(rec.Capabilities)
caps := slices.Clone(rec.Capabilities)
if caps == nil {
caps = []string{}
}
return nodeengine.CapabilitiesChanged{V: sdkkeys.Version, Node: node, Capabilities: caps}, true
}
// sayRunning says the event running, on its subject `running.<node>.<module>`.
func (q *Session) sayRunning(ev nodeengine.Running) {
ev.Node, ev.At = q.Membership.Node, time.Now().UTC()
q.sayEngineOn(nodeengine.SubjectRunning(ev.Node, ev.Module), "running", ev)
}
// sayCapabilities says the event capabilities-changed, on its subject `capabilities-changed.<node>`.
func (q *Session) sayCapabilities(ev nodeengine.CapabilitiesChanged) {
ev.At = time.Now().UTC()
q.sayEngineOn(nodeengine.SubjectCapabilitiesChanged(ev.Node), "capabilities-changed", ev)
}
// SetManifestOf gives the session what names the manifest of the build a module runs (the composer's picture of the
// catalog half): its digest, or "" when the node does not know it.
func (q *Session) SetManifestOf(f func(module, fp string) string) {
+4 -4
View File
@@ -44,7 +44,7 @@ type haveLink struct {
// sayFails fails that many of the node seat's says, as a publish past the bus's retry bound; said is every event said.
sayFails int
said []string
// onSaid are the events said on a subject of their own (running, capabilities-changed), by subject, in order.
// onSaid are the events said on a subject of their own (running), by subject, in order.
onSaid []string
}
@@ -349,9 +349,9 @@ func TestTheNodeKeyCarriesTheNodesFacts(t *testing.T) {
if l.kv["n1.node"].rev != at {
t.Errorf("a report changing nothing wrote the node key again")
}
// Design 47: the node's capabilities, said when they change, on capabilities-changed.<node>.
if got := l.saidOn("mesh.seat.node-engine.event.capabilities-changed.n1 "); len(got) != 1 || !strings.Contains(got[0], `"capabilities":["apt","bus-state-calls","ledger-calls","systemd","want-parts"]`) {
t.Errorf("capabilities-changed said %v, want once for the record's capabilities", got)
// Design 47 G §590: the node's capabilities are said by the event manifest-reads-changed alone; no capabilities-changed.
if got := l.saidOn("mesh.seat.node-engine.event.capabilities-changed."); len(got) != 0 {
t.Errorf("capabilities-changed said %v, want none (G §590)", got)
}
// A runner that does not answer states what it stated before; one answering without any states none (an older runner).
l.statedErr = fmt.Errorf("%w: nothing answered", ErrNoAnswer)