From 75f5c64646449c150f4b27c4481c9f3fb7d4dc8b Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 21:13:24 +0100 Subject: [PATCH] The node-engine says no capabilities-changed: manifest-reads-changed alone says a node's capabilities, as the contracts' K6 has the seat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- internal/link/have.go | 34 ++-------------------------------- internal/link/have_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/internal/link/have.go b/internal/link/have.go index 202cac27..ed4f2f36 100644 --- a/internal/link/have.go +++ b/internal/link/have.go @@ -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..`. 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.`. -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) { diff --git a/internal/link/have_test.go b/internal/link/have_test.go index 041bc536..7a76f716 100644 --- a/internal/link/have_test.go +++ b/internal/link/have_test.go @@ -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.. - 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)