claude-code: the seat list is read from the seat mesh-catalog and followed on its events, the hand acts go through the hand-acts client, the seat is mesh-module-assigner (K-23, K-21 part, K-4..K-20 part; ADR 0635 §1, ADR 0634 §9; issue 1966)

This commit is contained in:
2026-11-05 14:14:32 +01:00
parent 7ae2ed88c0
commit 2f7ff1c911
10 changed files with 175 additions and 43 deletions
+55 -8
View File
@@ -7,7 +7,7 @@ package main
// Read from the bus at start and every few minutes (issue 1126 B13; the controller that answered this before is
// not placed, novox/hq ADR 0590): the seats' verbs from the seats' contract (mesh-contracts), the modules'
// own tools from the manifests the indexer keeps (`mesh-delivery-indexer.modules` and `.manifests`), and the
// machines and where each module runs from the seat mesh-placement's `placement`. Kept in the module's state so
// machines and where each module runs from the seat mesh-module-assigner's `placement`. Kept in the module's state so
// a render never waits for the bus, and rendered whenever it changes. Nothing here is written by hand: a verb
// that gains a `replaces` is in the next render's table and the guard's next refusal.
@@ -21,10 +21,13 @@ import (
"path/filepath"
"sort"
"strings"
"sync"
"time"
"git.novox.be/novox/mesh-contracts/go/seats"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/seatdef"
"git.novox.be/novox/mesh-sdk/go/seatlist"
)
// Replacement is one verb or tool and the shell commands it is the mesh's way to do.
@@ -104,7 +107,7 @@ type ModuleTools struct {
Replaces map[string][]string
}
// placements is every node's placement, from the seat mesh-placement's state.
// placements is every node's placement, from the seat mesh-module-assigner's state.
func placements(st States) (map[string]delivery.Placement, error) {
keys, err := st.Keys(PlacementRead)
if err != nil {
@@ -217,14 +220,14 @@ func firstSentence(s string) string {
// ReplacementsOf is every seat verb and module tool that says it replaces a command: the seats' verbs first, in the
// seats' order, then the modules' own tools by module and tool.
func ReplacementsOf(defined []seats.Seat, modules []ModuleTools) []Replacement {
func ReplacementsOf(defined []seatdef.Definition, modules []ModuleTools) []Replacement {
var out []Replacement
for _, s := range defined {
for _, v := range s.Serves {
for _, v := range s.Verbs {
if len(v.Replaces) == 0 {
continue
}
out = append(out, Replacement{Address: s.Name + "." + v.Name, NodeScoped: s.Scope == "node", Seat: true,
out = append(out, Replacement{Address: s.Seat + "." + v.Name, NodeScoped: s.Scope == "node", Seat: true,
Does: firstSentence(v.Description), Replaces: v.Replaces})
}
}
@@ -257,7 +260,7 @@ func lookup(name string) []string {
// ReadMeshToolsFrom reads the bus now: the seats' verbs, the placed modules' own tools, and every node placement names
// with its domains and addresses. A part that cannot be read is left out and said in the error; what was read is kept.
func ReadMeshToolsFrom(st States, defined []seats.Seat, resolve func(string) []string) (MeshTools, error) {
func ReadMeshToolsFrom(st States, defined []seatdef.Definition, resolve func(string) []string) (MeshTools, error) {
var m MeshTools
placed, err := placements(st)
if err != nil {
@@ -349,8 +352,9 @@ func refreshMeshTools(p Paths) {
case <-time.After(MeshToolsFirst):
say("the first views of the tools and the placement were not answered in %s; reading what is held", MeshToolsFirst)
}
startSeatList(seatlist.StdioBus())
for {
m, err := ReadMeshToolsFrom(viewStates{meshViews}, seats.List(), lookup)
m, err := ReadMeshToolsFrom(viewStates{meshViews}, definedSeats(), lookup)
if err != nil {
say("reading what each tool replaces and which machines the mesh has: %v", err)
}
@@ -403,3 +407,46 @@ func GuardStatus(p Paths, last int) map[string]any {
"record": ReadGuardLog(d.Log, last),
}
}
// seatList is the seat list this module holds: the definitions of the seats, read from the seat mesh-catalog and
// followed on its events (novox/hq ADR 0635 §1, ADR 0636), the foundation's bootstrapped ones until it answers.
var seatList *seatlist.Reader
// startSeatList starts the reader once the runtime serves; a change of the list is told like any followed state, so the
// instructions' table is read again.
func startSeatList(bus seatlist.Bus) {
r, err := seatlist.Start(seatlist.Config{Bus: bus, Bootstrap: seatlist.Bootstrapped(),
OnChange: func([]catalogue.Seat) {
select {
case meshChanged <- struct{}{}:
default:
}
}})
if err != nil {
say("following the seat list: %v", err)
}
seatListMu.Lock()
seatList = r
seatListMu.Unlock()
}
var seatListMu sync.Mutex
// definedSeats are the held definitions in reading order, mesh seats first, then node seats, each by name; empty until
// the reader has started.
func definedSeats() []seatdef.Definition {
seatListMu.Lock()
r := seatList
seatListMu.Unlock()
if r == nil {
return nil
}
held := r.Definitions()
var out []seatdef.Definition
for _, s := range r.Seats() {
if d, ok := held[s.Name]; ok {
out = append(out, d)
}
}
return out
}