A tool reads one module's bindings again from the seat events the shared stream still holds (issue 1966)

A binding the bus module said created that the part did not keep had no
recovery: distribution's holds, windows, mirrors and events are missing on
the bus node's memberships. grants_bindings_again, a hand act, asks the
process to read the seat mesh-broker's created, recreated and dropped of
one module again from EVENTS by sequence and keep each through HearBroker
in order, so a dropped entry stays dropped. grants_bindings shows what it
kept, and says when the stream no longer holds a created: then the seat's
list answering bindings is the path (the record extending ADR 0595 §7).
The part alone is granted the stream's message get, no consumer.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-09 14:11:26 +01:00
parent 44422f21db
commit aa89032f02
12 changed files with 371 additions and 19 deletions
@@ -600,13 +600,18 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
t.Errorf("a one-filter consumer's grants = %v: its update is the no-filter create form", one)
}
for _, u := range users {
if u.Module != GrantsModule || u.Kind != KindModule {
continue
}
perms, err := PermissionsFor(u)
if err != nil {
t.Fatal(err)
}
// Issue 1966: the grants part alone reads the shared events stream by sequence, to read the seat mesh-broker's
// events again (grants_bindings_again).
if may := MayPublish(perms, "$JS.API.STREAM.MSG.GET.EVENTS"); may != (u.Module == GrantsModule && u.Kind == KindModule) {
t.Errorf("%s %s may read EVENTS by sequence: %v", u.Kind, u.Module, may)
}
if u.Module != GrantsModule || u.Kind != KindModule {
continue
}
if !MayPublish(perms, "$JS.API.CONSUMER.CREATE.EVENTS.a_m") || MayPublish(perms, "$JS.API.CONSUMER.CREATE.EVENTS.a_quiet") ||
MayPublish(perms, "$JS.API.CONSUMER.DELETE.EVENTS.a_m") || MayPublish(perms, "$JS.API.CONSUMER.CREATE.EVENTS.a_m.mesh.mod.x.event.happened") {
t.Errorf("the grants part creates consumers as %v", u.CreatesConsumers)
@@ -859,6 +859,10 @@ func PermissionsFor(p Principal) (Permissions, error) {
pub = append(pub, CreateConsumerGrants(p.CreatesConsumers)...)
// And its own object bucket, where it writes the user list sealed to the bus node (ADR 0547 clause 7).
pub = append(pub, GrantsUserListWrites()...)
// And the read of one message of the shared events stream by its sequence and subject, where the part reads the
// seat mesh-broker's created, recreated and dropped again for grants_bindings_again (issue 1966): a get, never a
// consumer, so no other account's consumer is touched.
pub = append(pub, SeatEventsReadAgain)
}
// The view alone reads the build seats' workers for the backlog of asks (issue 1887 A8a).
if p.Module == ViewModule {
@@ -1546,6 +1550,10 @@ func CreateConsumerGrants(consumers []ModuleConsumer) []string {
return out
}
// SeatEventsReadAgain is the grants part's read of the shared events stream by sequence (STREAM.MSG.GET with
// next_by_subj): a binding the bus module said that the part did not keep is read again from it (issue 1966).
const SeatEventsReadAgain = "$JS.API.STREAM.MSG.GET." + EventsStream
// GrantsModule is the one module whose principal publishes the modules' memberships (novox/hq ADR 0547 §5): the grants
// part, which composes them from the manifests and assignments. It defines no stream and is granted no other subject here.
const GrantsModule = "mesh-grants"
File diff suppressed because one or more lines are too long