Node-engine: seat-list and seat-list-again answer every operator's session and the healers, no other caller (ADR 0636 §7; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 14:45:43 +01:00
parent c642471c16
commit 308dbb94b8
5 changed files with 28 additions and 7 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"testing"
"time"
occ "git.novox.be/novox/mesh-contracts/go/clients/operatorchannelclient"
oc "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel"
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
stdio "git.novox.be/novox/mesh-sdk/go"
)
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"time"
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
oc "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel"
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"github.com/nats-io/nats.go"
+15 -2
View File
@@ -13,9 +13,9 @@ import (
"sync"
"time"
occ "git.novox.be/novox/mesh-contracts/go/clients/operatorchannelclient"
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
oc "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel"
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
@@ -58,6 +58,13 @@ const (
ActSeatListAgain = "seat-list-again"
)
// SeatListCallers are who may call the engine's two seat-list verbs: every operator's session and the healers (novox/hq
// ADR 0636 §7, the node-engine seat's definition: the operator and the healers).
var SeatListCallers = stdio.VerbCallers{
ActSeatList: {stdio.AnyPerson, "mesh-healers"},
ActSeatListAgain: {stdio.AnyPerson, "mesh-healers"},
}
// The states of an act: asked, done and refused are also the seat's events.
const (
HandAsked = nodeengine.EventAsked
@@ -228,11 +235,17 @@ func (h *HandActs) Call(c EngineCall) (any, error) {
}
return h.Compose(arg("module"))
case ActSeatList:
if err := SeatListCallers.Allowed(c.Verb, c.Caller); err != nil {
return nil, err
}
if h.SeatList == nil {
return nil, errors.New("this engine reads no seat list yet: its node agent has not started")
}
return h.SeatList()
case ActSeatListAgain:
if err := SeatListCallers.Allowed(c.Verb, c.Caller); err != nil {
return nil, err
}
if h.SeatListAgain == nil {
return nil, errors.New("this engine reads no seat list yet: its node agent has not started")
}
+9 -1
View File
@@ -264,7 +264,9 @@ func TestALinkThatHearsHandOversIsGivenTheQueues(t *testing.T) {
Composition: func(module, digest string, diff bool) (any, error) {
return map[string]any{"module": module, "digest": digest, "diff": diff}, nil
},
Compose: func(module string) (any, error) { return map[string]any{"composed": true, "retried": module}, nil },
Compose: func(module string) (any, error) { return map[string]any{"composed": true, "retried": module}, nil },
SeatList: func() (any, error) { return map[string]any{"held": true}, nil },
SeatListAgain: func() (any, error) { return map[string]any{"read": true}, nil },
// ADR 0618 §3: what this engine states it reads, and when it last stated it.
ManifestReads: func() ([]string, time.Time) {
return []string{"form:a", "seat:b"}, time.Date(2026, 11, 4, 10, 0, 0, 0, time.UTC)
@@ -299,6 +301,12 @@ func TestALinkThatHearsHandOversIsGivenTheQueues(t *testing.T) {
{"packages", EngineToolSubject("packages", "n1") + " " + `{"write":true}`, `"key":null,"wrote":true`},
{"packages", EngineToolSubject("packages", "n1") + " " + `{"write":"true"}`, `"note":"the engine has written no key for this node yet`},
{"manifest-reads", EngineToolSubject("manifest-reads", "n1"), `"manifest-reads":["form:a","seat:b"],"node":"n1","stated-at":"2026-11-04T10:00:00Z","v":1`},
// ADR 0636 §7: the seat-list verbs answer every operator's session and the healers, no other module.
{"seat-list", "mesh.seat.node-engine.call.seat-list.n1.person~jochen", `"held":true`},
{"seat-list-again", "mesh.seat.node-engine.call.seat-list-again.n1.person~jochen", `"read":true`},
{"seat-list-again", "mesh.seat.node-engine.call.seat-list-again.n1.ace~mesh-healers", `"read":true`},
{"seat-list-again", "mesh.seat.node-engine.call.seat-list-again.n1.ace~messenger", `not on the holder's caller list for seat-list-again`},
{"seat-list-again", EngineToolSubject("seat-list-again", "n1"), `no caller the tool runner proved`},
{"unserved", "mesh.seat.node-engine.call.unserved.n1.x", `"error":"the node seat node-engine serves no verb \"unserved\""`},
} {
subject, body, ok := strings.Cut(row.subject, " ")
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"fmt"
"time"
occ "git.novox.be/novox/mesh-contracts/go/clients/operatorchannelclient"
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
)
// Membership is what a node needs to reach its mesh again, held by the caller.