Grants part: the unread seat list never holds the part's own module or the holder of the seat it is read through (ADR 0636 §1; issue 1966)
This commit is contained in:
@@ -2256,6 +2256,33 @@ func TestGrants(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
// What the part needs to hear the seat and read the list is never held by the unread list (ADR 0636 §1; the lead's ruling
|
||||
// of 2026-11-05): its own module (its consumer on EVENTS, its grant of the seat's verb) and the module that holds the seat
|
||||
// the list is read through, whatever other seats their manifests name.
|
||||
t.Run("the seat list unread never holds the part's own module or the holder of the seat it is read through", func(t *testing.T) {
|
||||
b := fixture()
|
||||
own, err := os.ReadFile("module.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b.manifestOf(grants.Module, string(own))
|
||||
b.manifestOf("cat-holder", `{"module":"cat-holder","version":"1","claims":[{"name":"mesh-catalog"}],"invokes":["seat:mesh-vault.account-issue"]}`)
|
||||
b.assign("a", grants.Module)
|
||||
b.assign("a", "cat-holder")
|
||||
p := partOn(b)
|
||||
p.SeatList = &seatGate{} // nothing is bootstrapped: only the exemption lets these two through
|
||||
p.Compose()
|
||||
refused := strings.Join(p.Last().Refused, "\n")
|
||||
for _, module := range []string{grants.Module, "cat-holder"} {
|
||||
if strings.Contains(refused, "manifest of "+module+":") {
|
||||
t.Errorf("%s is held while the seat list is unread: %v", module, p.Last().Refused)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
// DLV-G1, 1966/AC32-AC33 (novox/hq ADR 0595 §1, §7; design 25 § 12.2): the bus module's created writes the binding into
|
||||
// the module's membership on every machine it is assigned, one assigned later included; recreated rewrites it; dropped
|
||||
// removes it; before created, on a refused, or from an emitter holding no mesh-broker, none.
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
@@ -219,6 +220,14 @@ type SeatGate interface {
|
||||
MayAct(seats ...string) bool
|
||||
}
|
||||
|
||||
// readsTheList says whether a module is what the part needs to hear the seat and read the list: the part's own module (its
|
||||
// consumer on EVENTS, its grant of the seat's verb and events) and the holder of the seat the list is read through. The unread
|
||||
// list never holds these, whatever other seats their manifests name: held, the seat's event could not reach the part and
|
||||
// the list would never be read (ADR 0636 §1).
|
||||
func readsTheList(module string, m catalogue.Manifest) bool {
|
||||
return module == Module || slices.ContainsFunc(m.Claims, func(c catalogue.Claim) bool { return c.Name == meshcatalog.Seat })
|
||||
}
|
||||
|
||||
// SeatsOf are the seats a manifest claims or invokes, which the seat list must name before the part composes the module.
|
||||
func SeatsOf(m catalogue.Manifest) []string {
|
||||
var seats []string
|
||||
@@ -422,7 +431,7 @@ func ReadInputs(r Reader, cache *Manifests) Inputs {
|
||||
if err == nil {
|
||||
m, err = cache.get(r, v.Manifest)
|
||||
}
|
||||
if err == nil && gate != nil && !gate.MayAct(SeatsOf(m)...) {
|
||||
if err == nil && gate != nil && !readsTheList(module, m) && !gate.MayAct(SeatsOf(m)...) {
|
||||
err = errors.New("it waits for the seat list: the list has not been read yet and the manifest names seats the bootstrapped list does not (novox/hq ADR 0636 §1)")
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user