mesh-grants: prove the haves fallback lets the indexer serve the seat list's verb

Issue 1966: the seat list is read through mesh-catalog's seat-definitions, which got no
responders only because nothing was placed. A row shows that, the assignments view
unanswered, the indexer placed by its have (its real manifest) is composed to hear
mesh.seat.mesh-catalog.* and its membership serving seat-definitions is published, while
the seat list stays unread and other modules still wait for it.

Claude-Session: https://claude.ai/code/session_01NcbgfFfQ4Z9UE5jK32PmE5
This commit is contained in:
2026-11-07 22:40:31 +01:00
parent 9756dd6f1f
commit ffaff3d3aa
+36
View File
@@ -2542,6 +2542,42 @@ func TestGrants(t *testing.T) {
if !strings.Contains(refused, "manifest of mod-x: it waits for the seat list") {
t.Errorf("another module is not held: %v", p.Last().Refused)
}
// Issue 1966: the assignments view unanswered, the indexer placed by its have alone (its real manifest, claiming
// mesh-catalog and serving seat-definitions) is composed to serve the seat's verbs, in the list and its membership,
// while the seat list stays unread: the bootstrap reaches the verb that reads the list without opening the gate.
indexer, err := os.ReadFile("../mesh-delivery-indexer/module.json")
if err != nil {
t.Fatal(err)
}
fb := fixture()
fb.refused[grants.ReadAssignments] = true
fb.manifestOf("mesh-delivery-indexer", string(indexer))
fb.put(grants.ReadHave, "novox.node", map[string]any{"v": 1})
fb.put(grants.ReadHave, "novox.mesh-delivery-indexer", nodeengine.HaveByNodeModuleValue{V: keys.Version, Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthOK})
gate := &seatGate{}
fp := partOn(fb)
fp.SeatList = gate
res := fp.Compose()
if strings.Contains(strings.Join(res.Refused, "\n"), "manifest of mesh-delivery-indexer:") {
t.Fatalf("the indexer is held in a pass placed by the haves: %v", res.Refused)
}
listRecs, _ := grants.ListRecords(grants.ReadInputs(fb, &grants.Manifests{}))
users, err := grants.ComposeUsers(listRecs)
if err != nil {
t.Fatal(err)
}
at := slices.IndexFunc(users, func(u grants.User) bool { return u.User == "novox.mesh-delivery-indexer" })
if at < 0 || !slices.ContainsFunc(users[at].Subscribe, func(s string) bool { return strings.HasPrefix(s, "mesh.seat.mesh-catalog.") }) {
t.Fatalf("novox.mesh-delivery-indexer is not composed to hear the seat's verbs: %+v", users)
}
raw := fb.own[grants.StateMemberships]["novox.mesh-delivery-indexer"]
if !slices.Contains(res.Published, "novox.mesh-delivery-indexer") || !strings.Contains(string(raw), "seat-definitions") {
t.Errorf("the indexer's membership serving seat-definitions was not published: %v, %s", res.Published, raw)
}
if !strings.Contains(strings.Join(res.Refused, "\n"), "manifest of mod-x: it waits for the seat list") {
t.Errorf("the seat list was opened by a pass placed by the haves: %v", res.Refused)
}
})
// DLV-G1, 1966/AC32-AC33 (novox/hq ADR 0595 §1, §7; design 25 § 12.2): the bus module's created writes the binding into