mesh/delivery-group group feat/r4d rejected: a member's own check failed
mesh/repo-check out of date: judged beside mesh-catalog#434 at c948f3a61c74, it is now at 7451e7edf4e5; judged again on a recheck or when it is merged
mesh/merge-gate group feat/r4d: composed again for the group's new heads
mesh/delivery superseded: a newer head of the same pull request
mesh/check failed: unit: i3/cmd/i3-tools.TestTheMainFileAndEveryModulesContributionLoadTogether: manifest_test.go:247: expected the launcher, clipboar…
A bundle is built only from its module's directory (ADR 0480 clause 4), so the proxy leaves examples/ and the controller's internal/broker; the broker keeps no second copy of what the proxy shares with mesh-sdk. The image and its Dockerfile go, and a test holds that only build-agent declares one (clause 11).
95 lines
3.7 KiB
Go
95 lines
3.7 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
|
|
}
|
|
|
|
// 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
|
|
}
|