The indexer follows its seat list through the wiring every part shares, seatlist.Part, not a copy of its own (milestone 47/G W4.10, W1.9)

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ

Builds: design 47 G §204
This commit is contained in:
2026-11-11 14:36:10 +01:00
parent 86d13a41a3
commit 941af88086
2 changed files with 17 additions and 23 deletions
@@ -9,7 +9,6 @@ import (
"github.com/nats-io/nats.go/jetstream"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/process"
"git.novox.be/novox/mesh-sdk/go/seatlist"
"git.novox.be/novox/mesh-sdk/go/views"
@@ -107,19 +106,15 @@ func (b *seatBus) said(c index.SeatDefinitionChanged) {
}
}
// followSeatList starts the indexer's reader of its own seat list and gives catalogue what it holds at every change, and wraps
// say so that each move the indexer says is also heard by the reader. It returns the reader, whose status the indexer's health
// can show.
func followSeatList(bus *seatBus, say func(context.Context, index.SeatDefinitionChanged) error, logf func(string, ...any)) (*seatlist.Reader, func(context.Context, index.SeatDefinitionChanged) error) {
reader, err := seatlist.Start(seatlist.Config{Bus: bus, Bootstrap: seatlist.Bootstrapped(),
OnChange: func(seats []catalogue.Seat) { catalogue.UseSeats(seats) }})
if err != nil {
logf("the indexer cannot follow its seat list: %v", err)
}
if reader != nil {
catalogue.UseSeats(reader.Seats())
}
return reader, func(ctx context.Context, c index.SeatDefinitionChanged) error {
// followSeatList starts the indexer's reader of its own seat list through the wiring every part shares (mesh-sdk
// seatlist.Part, novox/hq ADR 0636 §7), which gives catalogue what it holds at every change, and wraps say so that each move
// the indexer says is also heard by the reader. Its two tools are not served: the indexer's process serves no request
// subject (ADR 0437 §4), and its list is its own state seat-definitions, which indexer_seat_definition and the seat's verb
// seat-definitions read. It returns the part, whose status the indexer's health can show.
func followSeatList(bus *seatBus, say func(context.Context, index.SeatDefinitionChanged) error, logf func(string, ...any)) (*seatlist.Part, func(context.Context, index.SeatDefinitionChanged) error) {
part := seatlist.NewPart(toolPrefix, nil)
part.Follow(bus, nil, logf)
return part, func(ctx context.Context, c index.SeatDefinitionChanged) error {
if err := say(ctx, c); err != nil {
return err
}
@@ -138,13 +133,11 @@ type bucket interface {
// readWhenBound has the reader read the list again as each of the states it is answered from is bound. The reader starts before
// they are, its first read finds them missing and leaves it unread, and the indexer's own events (said) are the only other thing
// that reads it again: a trunk that moves no definition after a start would leave the list unread for good (ADR 0636 §1).
func readWhenBound(reader *seatlist.Reader, states ...bucket) {
if reader == nil {
return
}
func readWhenBound(part *seatlist.Part, states ...bucket) {
for _, state := range states {
state.Ready(func(jetstream.KeyValue) {
reader.ReloadIfUnread()
})
state.Ready(func(jetstream.KeyValue) { part.EventsLive() })
}
}
// toolPrefix is the prefix of the indexer's tools, its manifest's slug.
const toolPrefix = "indexer"
@@ -44,13 +44,14 @@ func (b *awaitedFake) Ready(f func(jetstream.KeyValue)) { b.ready = append(b.rea
func TestTheSeatListIsReadAgainWhenItsStatesAreBound(t *testing.T) {
src := &unboundSource{}
bus := &seatBus{defs: src, marks: src}
reader, _ := followSeatList(bus, nil, t.Logf)
part, _ := followSeatList(bus, nil, t.Logf)
reader := part.Reader()
<-reader.Ready()
if reader.Status().Heard {
t.Fatal("the list was read with its states missing")
}
b := &awaitedFake{}
readWhenBound(reader, b)
readWhenBound(part, b)
src.mu.Lock()
src.bound = true
src.mu.Unlock()