fix9-7: a module that hears a seat's kept event is granted that stream's info (process.HearSeatEvents reads it); the forge bridge's bed holds the grant

Cause of the five forge bridge integration failures: HearSeatEvents (G §641) asks STREAM.INFO of the kept stream, which the grants part never composed.

Builds: design 47 G §641, A-19, §556
This commit is contained in:
2026-11-12 01:10:17 +01:00
parent 13c5433c0d
commit 90718ba7a3
3 changed files with 23 additions and 0 deletions
@@ -86,9 +86,11 @@ accounts {
"$JS.ACK.EVENTS.n1_mesh-delivery-forge-bridge.>"
"$JS.API.CONSUMER.INFO.EVENTS.n1_mesh-delivery-forge-bridge"
"$JS.API.CONSUMER.MSG.NEXT.EVENTS.n1_mesh-delivery-forge-bridge"
"$JS.API.STREAM.INFO.SEAT_mesh-delivery_owed"
"$JS.ACK.SEAT_mesh-delivery_owed.n1_mesh-delivery-forge-bridge.>"
"$JS.API.CONSUMER.INFO.SEAT_mesh-delivery_owed.n1_mesh-delivery-forge-bridge"
"$JS.API.CONSUMER.MSG.NEXT.SEAT_mesh-delivery_owed.n1_mesh-delivery-forge-bridge"
"$JS.API.STREAM.INFO.SEAT_mesh-delivery-checker_owed"
"$JS.ACK.SEAT_mesh-delivery-checker_owed.n1_mesh-delivery-forge-bridge.>"
"$JS.API.CONSUMER.INFO.SEAT_mesh-delivery-checker_owed.n1_mesh-delivery-forge-bridge"
"$JS.API.CONSUMER.MSG.NEXT.SEAT_mesh-delivery-checker_owed.n1_mesh-delivery-forge-bridge"
@@ -4,6 +4,8 @@ import (
"fmt"
"sort"
"strings"
"git.novox.be/novox/mesh-sdk/go/seatdef"
)
// A module's own stream on the bus (novox/hq ADR 0363).
@@ -260,6 +262,11 @@ func heardGrants(p Principal, hears []Heard) []string {
"$JS.API.CONSUMER.INFO."+h.Stream+"."+durable,
"$JS.API.CONSUMER.MSG.NEXT."+h.Stream+"."+durable,
"$JS.ACK."+h.Stream+"."+durable+".>")
if strings.HasPrefix(h.Stream, seatdef.KeptStreamPrefix) {
// A seat's kept event is heard through process.HearSeatEvents, which reads the stream's own info to see whether it is
// kept last per subject (design 47 G §641, A-19): the info of this stream alone.
out = append(out, "$JS.API.STREAM.INFO."+h.Stream)
}
if h.LastPerSubject {
// A stream kept last per subject is read whole at each start through an ordered consumer, made for that one read
// and with no durable name: its creation, its next message, its info and the deletion of its own (design 47 G §556, A-4).
@@ -21,3 +21,17 @@ func TestAReaderOfAStreamKeptLastPerSubjectIsGrantedItsOrderedConsumer(t *testin
t.Errorf("a reader of another stream is granted an ordered consumer: %v", plain)
}
}
// A reader of a seat's kept event is granted the info of that stream, which process.HearSeatEvents reads to see whether the
// stream is kept last per subject, and no stream's info besides (design 47 G §641, A-19).
func TestAReaderOfASeatsKeptEventIsGrantedItsStreamInfo(t *testing.T) {
p := Principal{Module: "m", Node: "a"}
kept := heardGrants(p, []Heard{{Stream: "SEAT_mesh-delivery_owed", Filters: []string{"f"}}})
if !slices.Contains(kept, "$JS.API.STREAM.INFO.SEAT_mesh-delivery_owed") {
t.Errorf("a reader of a seat's kept event is not granted its stream's info: %v", kept)
}
other := heardGrants(p, []Heard{{Stream: "mesh-x-events", Filters: []string{"f"}}})
if slices.Contains(other, "$JS.API.STREAM.INFO.mesh-x-events") {
t.Errorf("a reader of a module's stream is granted its info: %v", other)
}
}