The controller plans config, templates and moved providers' provisions, hands bus accounts to the vault and judges the new manifest words, on the feature's SDK and node-engine validator (feature 57, config and provisioning).
101 lines
3.9 KiB
Go
101 lines
3.9 KiB
Go
package broker
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// What a session may wait on through the mesh MCP server (novox/hq ADR 0367).
|
|
//
|
|
// **A grant apart from `consumes`, and the tool runner's alone.** The tool runner's module names in its
|
|
// manifest the event families a session may wait on, one by one; the runtime's account is granted a plain
|
|
// subscription on those subjects and nothing wider — no durable consumer, no queue of its own — and its
|
|
// membership names the same list, from which `mesh_wait` refuses any other. Both are read from this one
|
|
// composition, so the grant and what the tool runner offers cannot disagree, and nothing but the manifest
|
|
// adds a family (§6).
|
|
|
|
// Wait is one event family the tool runner's module declares: its name as the manifest gives it, and the
|
|
// emitter — a seat or a module — and the event, as the inventory resolved them.
|
|
type Wait struct {
|
|
// Name is the family as the manifest names it, `<emitter>.<event>`: what a session asks for.
|
|
Name string
|
|
Emitter string
|
|
// Seat says the emitter is a seat, whose events live under the seat's namespace (novox/hq ADR 0121).
|
|
Seat bool
|
|
// Event is the event, with any further token the subject carries: `decided.claude-code`, the asker of a
|
|
// seat that names it last (ADR 0259 §3).
|
|
Event string
|
|
// Last names what the seat's event subject ends in — "kind" for a kinded bench, "asker" for a seat naming
|
|
// its caller (ADR 0259 §3) — and is empty for an event that ends at its name. A family needing it is waited
|
|
// on with it given, and refused without.
|
|
Last string
|
|
}
|
|
|
|
// WaitIssued is one family as a membership issues it: the name a session asks for, and its subject.
|
|
type WaitIssued struct {
|
|
Event string `json:"event"`
|
|
Subject string `json:"subject"`
|
|
}
|
|
|
|
// WaitSubject is where a family's events land. Each token is checked before it becomes a subject: a
|
|
// wildcard, a dot inside a name or an empty token would widen the grant.
|
|
func WaitSubject(w Wait) (string, error) {
|
|
if !safeSubject.MatchString(w.Emitter) {
|
|
return "", fmt.Errorf("%q cannot name an emitter of a waited event: it would widen the subject", w.Emitter)
|
|
}
|
|
for _, t := range strings.Split(w.Event, ".") {
|
|
if !safeSubject.MatchString(t) {
|
|
return "", fmt.Errorf("%q cannot name a waited event: each of its names is plain, never a wildcard "+
|
|
"(novox/hq ADR 0367 §2)", w.Event)
|
|
}
|
|
}
|
|
if w.Last != "" && !strings.Contains(w.Event, ".") {
|
|
return "", fmt.Errorf("%s names no %s: %s's events are said for each %s, so a family is %s.%s.<%s>; "+
|
|
"without it no event would ever be heard (novox/hq ADR 0367 §5)",
|
|
w.Name, w.Last, w.Emitter, w.Last, w.Emitter, w.Event, w.Last)
|
|
}
|
|
if w.Seat {
|
|
return "mesh.seat." + w.Emitter + ".event." + w.Event, nil
|
|
}
|
|
return "mesh.mod." + w.Emitter + ".event." + w.Event, nil
|
|
}
|
|
|
|
// waitGrants is the subscription the tool runner's account is granted for its module's families, and an
|
|
// error for a carried module other than the tool runner's that declares any (ADR 0367 §2).
|
|
func waitGrants(carries []Declared) ([]string, error) {
|
|
var sub []string
|
|
for _, d := range carries {
|
|
if len(d.Waits) == 0 {
|
|
continue
|
|
}
|
|
if d.Module != RuntimeModule {
|
|
return nil, fmt.Errorf("%s declares what a session waits on; only %s, the tool runner's module, "+
|
|
"does (novox/hq ADR 0367 §2)", d.Module, RuntimeModule)
|
|
}
|
|
for _, w := range d.Waits {
|
|
s, err := WaitSubject(w)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sub = append(sub, s)
|
|
}
|
|
}
|
|
return sub, nil
|
|
}
|
|
|
|
// waitsIssuedFor is the families a membership names: the tool runner's module's, and nobody else's.
|
|
func waitsIssuedFor(d Declared) []WaitIssued {
|
|
if d.Module != RuntimeModule {
|
|
return nil
|
|
}
|
|
var out []WaitIssued
|
|
for _, w := range d.Waits {
|
|
s, err := WaitSubject(w)
|
|
if err != nil {
|
|
continue // refused where the account is composed, which names it
|
|
}
|
|
out = append(out, WaitIssued{Event: w.Name, Subject: s})
|
|
}
|
|
return out
|
|
}
|