Fix 10: the catalogue reads a seat's views and stream-ridden accepts, and a consumed view of a seat

Builds: design 47 G §616, §699, A-102, A-131

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 02:17:44 +01:00
parent cd4baef750
commit 1bae840487
4 changed files with 49 additions and 3 deletions
+8
View File
@@ -27,6 +27,14 @@ func SeatFromDefinition(d seatdef.Definition) Seat {
}
for _, st := range d.States {
s.States = append(s.States, st.Name)
if st.View {
s.Views = append(s.Views, st.Name)
}
}
for _, a := range d.Accepts {
if a.Stream != "" {
s.AcceptedOn = append(s.AcceptedOn, a.Stream+"."+a.Name)
}
}
for _, r := range d.Receives {
rv := Receivable{Kind: r.Kind, Comment: r.Comment, Dirs: r.Dirs, Offered: r.Offered}
+6
View File
@@ -49,6 +49,12 @@ type Seat struct {
// States are the names of the state the seat's holder keeps, which a module reads by `seat:<seat>.<state>` (design 47 G
// A-116, A-128). Compiled from the definition's states.
States []string
// Views are the names of those states the seat offers as views, which a module follows by `consumes`
// `seat:<seat>.views.<state>` (design 47 G §616, §699, A-131). Compiled from the definition's states marked `view`.
Views []string
// AcceptedOn are the accepted kinds that ride a stream, each as `<stream>.<kind>`, which a part names in `invokes` as
// `seat:<seat>.<stream>.<kind>` (design 47 G A-102, §415). Compiled from the definition's accepts.
AcceptedOn []string
// Receives is what other modules may contribute to the seat's holder, by kind (novox/hq ADR
// 0212): each kind is text in the tool's own grammar, placed by the holder with
// ${contribution:<seat>:<kind>}. Compiled, never stored: like the protocol, it is the mesh's
+15
View File
@@ -97,6 +97,21 @@ func SeatRead(read string) (seat, local string, ok bool, err error) {
return rest[:at], rest[at+1:], true, nil
}
// SeatViewConsume splits a consumed view of a seat, `seat:<seat>.views.<state>`, into the seat and the state; ok is false for a
// consumed entry that is not of this kind (an event, `<emitter>.<event>`), and err says why a `seat:` entry is malformed. The
// holder is not in it: the grants part resolves it from the catalog half (design 47 G §616, A-131).
func SeatViewConsume(consume string) (seat, state string, ok bool, err error) {
rest, isSeat := strings.CutPrefix(consume, SeatReadPrefix)
if !isSeat {
return "", "", false, nil
}
seat, view, named := strings.Cut(rest, ".views.")
if !named || !stateName.MatchString(seat) || !stateName.MatchString(view) {
return "", "", true, fmt.Errorf("%q does not name a seat and its view: a consumed view of a seat is seat:<seat>.views.<state>", consume)
}
return seat, view, true, nil
}
// ReadState splits a read into the owning module and the local name, or says why it is not one.
func ReadState(read string) (module, local string, err error) {
at := strings.LastIndex(read, ".")
+20 -3
View File
@@ -88,8 +88,25 @@ func TestSeatRead(t *testing.T) {
t.Errorf("SeatRead(%q) = %q, %q, %v, %v", row.read, seat, local, ok, err)
}
}
s := SeatFromDefinition(seatdef.Definition{Seat: "mesh-provisioner", Scope: "mesh", States: []seatdef.State{{Name: "plans"}, {Name: "provisions"}}})
if len(s.States) != 2 || s.States[0] != "plans" || s.States[1] != "provisions" {
t.Errorf("the seat's states = %v", s.States)
for _, row := range []struct {
consume, seat, state string
ok, bad bool
}{
{"seat:mesh-provisioner.views.plans", "mesh-provisioner", "plans", true, false},
{"mesh-provisioner.views.plans", "", "", false, false},
{"seat:mesh-provisioner.plans", "", "", true, true},
{"seat:.views.plans", "", "", true, true},
} {
seat, state, ok, err := SeatViewConsume(row.consume)
if seat != row.seat || state != row.state || ok != row.ok || (err != nil) != row.bad {
t.Errorf("SeatViewConsume(%q) = %q, %q, %v, %v", row.consume, seat, state, ok, err)
}
}
s := SeatFromDefinition(seatdef.Definition{Seat: "mesh-provisioner", Scope: "mesh",
States: []seatdef.State{{Name: "plans", View: true}, {Name: "provisions"}},
Accepts: []seatdef.Message{{Name: "step", Stream: "check"}, {Name: "step"}}})
if len(s.States) != 2 || s.States[0] != "plans" || s.States[1] != "provisions" || len(s.Views) != 1 || s.Views[0] != "plans" ||
len(s.AcceptedOn) != 1 || s.AcceptedOn[0] != "check.step" {
t.Errorf("the seat's states = %v, views = %v, accepted on = %v", s.States, s.Views, s.AcceptedOn)
}
}