The node's own key says its agent keeps the bus server, so the assigner writes the bus want only to such a node (ADR 0594 §8)

This commit is contained in:
2026-11-03 09:03:30 +01:00
parent 9fd5429853
commit 052004d1cf
3 changed files with 27 additions and 5 deletions
+3 -1
View File
@@ -633,7 +633,9 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
env.Composed, env.SelfComposed, env.Policy = composer, true, composer.Policy
env.HoldsData = composer.HoldsData // what runs from the kept declaration says Data as its manifest does
env.BusServer = composer.BusServer // the bus server an unwarranted want leaves as it runs (ADR 0594 §2)
session.NoteComposes(linked, delivery.ComposesOwn, "it composes its own modules, and applies no composition made elsewhere")
// And its agent keeps the bus server as it runs unless a want carries the warrant's mark (ADR 0594 §8).
session.NoteComposes(linked, delivery.ComposesOwn, "it composes its own modules, and applies no composition made elsewhere",
delivery.KeepsBusServer)
ag = agent.New(env)
own.set(ag)
current.set(ag)
+12 -4
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"slices"
"sort"
"strings"
"sync"
@@ -55,6 +56,9 @@ type haveState struct {
// nodeComposes and nodeComposesWhy are whose compositions the node applies and why (HaveNode.Composes), written into
// its key.
nodeComposes, nodeComposesWhy string
// nodeKeeps is what the node agent keeps as it runs on a want without the warrant's mark (HaveNode.Keeps, novox/hq
// ADR 0594 §8), written into its key.
nodeKeeps []string
// written is the build of each module this node-engine wrote a have key for and that the declaration it applied
// still holds; removed is each such module the latest declaration no longer holds, with the build its key last
// said, until the key is written once with the phase removed (issue 1369, novox/hq ADR 0408 §4: never deleted).
@@ -428,6 +432,7 @@ func (q *Session) writeHaves(ctx context.Context, bus Bus) {
facts.SealedAt = s.nodeSealed
facts.Candidates = s.nodeCandidates
facts.Composes, facts.ComposesWhy = s.nodeComposes, s.nodeComposesWhy
facts.Keeps = s.nodeKeeps
if raw, err = delivery.EncodeHaveNode(facts); err == nil {
want := facts
if !q.putHave(write, bucket, key, raw, func(current []byte) bool {
@@ -757,14 +762,17 @@ func (q *Session) NoteCandidates(ctx context.Context, answers map[string]deliver
q.writeHaves(ctx, bus)
}
// NoteComposes says, in the node's own key, whose compositions the node applies (delivery.ComposesOwn) and why: written
// at each start of its agent, where every have is read (issue 1126).
func (q *Session) NoteComposes(ctx context.Context, composes, why string) {
// NoteComposes says, in the node's own key, whose compositions the node applies (delivery.ComposesOwn) and why, and what
// its agent keeps as it runs on a want without the warrant's mark (delivery.KeepsBusServer, novox/hq ADR 0594 §8): written
// at each start of its agent, where every have is read (issue 1126). The assigner writes the bus module's want only to
// a node whose key says it keeps the bus server.
func (q *Session) NoteComposes(ctx context.Context, composes, why string, keeps ...string) {
q.init()
s := &q.haves
s.mu.Lock()
changed := s.nodeComposes != composes || s.nodeComposesWhy != why
changed := s.nodeComposes != composes || s.nodeComposesWhy != why || !slices.Equal(s.nodeKeeps, keeps)
s.nodeComposes, s.nodeComposesWhy = composes, why
s.nodeKeeps = slices.Clone(keeps)
known := s.node != nil
s.mu.Unlock()
if !known || !changed {
+12
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"slices"
"strings"
"sync"
"testing"
@@ -579,6 +580,17 @@ func TestAnAppliedModulesHaveNamesTheFingerprintAndTheDigestAndFollowsItsHealth(
if n, _ := delivery.DecodeHaveNode(l.kv["n1.node"].value); n.Candidates != nil {
t.Fatalf("an answer left after its group went: %+v", n.Candidates)
}
// Issue 774, ADR 0594 §4, §8: the bus module's have names what its server runs beside the fingerprint applied, and the
// node's key says its agent keeps the bus server.
q.NoteModule(context.Background(), ModuleFact{Module: "nats", FP: fp, Composed: dig, Server: "build-1792674595610176744"})
if got := l.have(t, "n1.nats"); got.FP != fp || got.Server != "build-1792674595610176744" {
t.Fatalf("the bus's have: %+v", got)
}
q.NoteComposes(context.Background(), delivery.ComposesOwn, "it composes its own", delivery.KeepsBusServer)
if n, err := delivery.DecodeHaveNode(l.kv["n1.node"].value); err != nil || !slices.Equal(n.Keeps, []string{delivery.KeepsBusServer}) ||
n.Composes != delivery.ComposesOwn {
t.Fatalf("the node key does not say it keeps the bus server: %+v %v", n, err)
}
}
// A module applied and running that has nothing long-running to judge is ok once the engine has looked: there is nothing