NATS3 (part, branch-independent): which module sends, and a catalog entry read for the bus module

Sends reads a claim of a seat whose definition has a verb or an event, or an event of its own; AsksOf reads
an entry's bus-state asks and claims. The follower's branch and the wiring wait on the design's answer.

Builds: design 47 G §344, §433

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 12:23:40 +01:00
parent fc90e6c67b
commit 6062feaa22
4 changed files with 131 additions and 0 deletions
@@ -0,0 +1,57 @@
package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"nats-tools/internal/keeper"
)
// The keeper reads a module's asks from the module's entry in the catalog half of its revision view, not from the
// indexer's event (design 47 G §344, §433).
// Asks is what one catalog graph entry says of a module for the keeper: what its manifest asks of the bus as an accepted
// verdict, the seats it claims, and whether it declares an event of its own (the facts keeper.Sends reads).
type Asks struct {
Accepted keeper.Accepted
Claims []string
EmitsOwn bool
}
// AsksOf reads one entry. An entry whose manifest is not accepted asks nothing of the bus now, as an accepted verdict of
// no entries does; the digest it carries is of the entries it asks, so an entry changed in nothing the bus module reads
// is not applied again.
func AsksOf(e catalog.CatalogEntry) (Asks, error) {
if e.Module == "" {
return Asks{}, fmt.Errorf("a catalog entry names no module")
}
raw, err := json.Marshal(e.Manifest)
if err != nil {
return Asks{}, fmt.Errorf("the manifest of %s is not readable: %w", e.Module, err)
}
var m catalogue.Manifest
if err := json.Unmarshal(raw, &m); err != nil {
return Asks{}, fmt.Errorf("the manifest of %s is not readable: %w", e.Module, err)
}
state := m.BusState
if e.Verdict != "" && e.Verdict != "accepted" {
state = nil
}
sum := sha256.Sum256(mustJSON(state))
out := Asks{Accepted: keeper.Accepted{Module: e.Module, Digest: hex.EncodeToString(sum[:]), BusState: state},
EmitsOwn: len(m.Emits) > 0 || len(e.Streams) > 0}
for _, c := range m.Claims {
out.Claims = append(out.Claims, c.Name)
}
return out, nil
}
func mustJSON(v any) []byte {
b, _ := json.Marshal(v)
return b
}
@@ -0,0 +1,28 @@
package main
import (
"testing"
catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
)
func TestAsksOf(t *testing.T) {
m := map[string]any{"module": "m", "claims": []any{map[string]any{"name": "seat-a"}}, "emits": []any{"done"}}
a, err := AsksOf(catalog.CatalogEntry{Module: "m", Verdict: "accepted", Manifest: m})
if err != nil {
t.Fatal(err)
}
if len(a.Claims) != 1 || a.Claims[0] != "seat-a" || !a.EmitsOwn || a.Accepted.Module != "m" || a.Accepted.Digest == "" {
t.Errorf("read %+v", a)
}
b, _ := AsksOf(catalog.CatalogEntry{Module: "m", Manifest: map[string]any{"module": "m"}})
if b.EmitsOwn || len(b.Claims) != 0 {
t.Errorf("an entry with neither: %+v", b)
}
if b.Accepted.Digest != a.Accepted.Digest {
t.Errorf("the digest is of the bus-state asked alone: %s vs %s", b.Accepted.Digest, a.Accepted.Digest)
}
if _, err := AsksOf(catalog.CatalogEntry{}); err == nil {
t.Error("an entry with no module was read")
}
}