diff --git a/go/catalogue/process_bus.go b/go/catalogue/process_bus.go index b83c2e80..6559e59b 100644 --- a/go/catalogue/process_bus.go +++ b/go/catalogue/process_bus.go @@ -13,8 +13,8 @@ const ProcessBusField = "bus" // ProcessBus is what one process of a module does on the bus (novox/hq ADR 0436 §1, §2, §3, §5): its own bus // account, granted exactly this and nothing of the module's other grants or the tool runner's — never a seat -// to hold or serve, never a tool call or seat verb answered, never a restricted stream heard, never a -// warrant published (§3, §5). +// to hold, never a restricted stream heard, never a warrant published (§3, §5). It answers a seat's verb only +// where it declares it in Serves (design 47 G A-107, A-109). type ProcessBus struct { // Emits are the events this process emits, named locally, as the module's own Emits names them // (EmitsAll): a process emits only what its module may emit as a whole. @@ -28,6 +28,10 @@ type ProcessBus struct { // Invokes are the tools and seat verbs this process calls, each . or a role's // seat:. (as Manifest.Invokes), or * for every tool on the mesh. Invokes []string `json:"invokes,omitempty"` + // Serves are the seat verbs this process answers directly over the bus (design 47 G A-107, A-109, A-110), each + // seat:.: the grants part composes the process account's subscribe of the verb's call and tool + // subjects from it, and, for a verb whose seat definition says chunks: true, the chunk and ack subjects. + Serves []string `json:"serves,omitempty"` // Removes are what a tools bundle's account may remove from the bus, from a closed set (BusRemovals; novox/hq ADR // 0564 Limits 4): declared alone, by the module holding mesh-broker, never by a process. Removes []string `json:"removes,omitempty"` @@ -88,8 +92,8 @@ func (m Manifest) BusAccounts() []BusAccount { return out } -// UnmarshalJSON reads a process's bus strictly: an unknown key — serves, a seat to hold or claim, or -// anything else that would answer a tool call or a seat's verb — is refused here, by name (novox/hq ADR +// UnmarshalJSON reads a process's bus strictly: an unknown key — a seat to hold or claim, or +// anything else that would answer a tool call — is refused here, by name (novox/hq ADR // 0436 §3). func (b *ProcessBus) UnmarshalJSON(raw []byte) error { type plain ProcessBus @@ -97,7 +101,7 @@ func (b *ProcessBus) UnmarshalJSON(raw []byte) error { dec := json.NewDecoder(bytes.NewReader(raw)) dec.DisallowUnknownFields() if err := dec.Decode(&p); err != nil { - return fmt.Errorf("a process's bus is {emits, queues, state, asks, invokes, keeps}: %w", typedUnknown(err)) + return fmt.Errorf("a process's bus is {emits, queues, state, asks, invokes, serves, keeps}: %w", typedUnknown(err)) } *b = ProcessBus(p) return nil diff --git a/go/catalogue/process_bus_test.go b/go/catalogue/process_bus_test.go index a7b44ab9..08a7c8a9 100644 --- a/go/catalogue/process_bus_test.go +++ b/go/catalogue/process_bus_test.go @@ -1,5 +1,10 @@ package catalogue +import ( + "encoding/json" + "testing" +) + func strSliceEqual(a, b []string) bool { if len(a) != len(b) { return false @@ -11,3 +16,16 @@ func strSliceEqual(a, b []string) bool { } return true } + +func TestProcessBusReadsServesAndRefusesUnknownKeys(t *testing.T) { + var b ProcessBus + if err := json.Unmarshal([]byte(`{"serves":["seat:mesh-broker.snapshot-get","seat:mesh-broker.list"]}`), &b); err != nil { + t.Fatal(err) + } + if !strSliceEqual(b.Serves, []string{"seat:mesh-broker.snapshot-get", "seat:mesh-broker.list"}) { + t.Errorf("serves = %v", b.Serves) + } + if err := json.Unmarshal([]byte(`{"holds":["x"]}`), &ProcessBus{}); err == nil { + t.Error("an unknown key was accepted") + } +}