D1 judge-queue-invokes: an asker's queue and a seat's accepted kind are judged against the seat

The third reader release reads G's invokes `seat:<seat>.accept.<queue>` and `seat:<seat>.<stream>.<kind>` with G's
check of them: a queue or kind the seat's definition does not accept is refused, naming it.

Builds: design 47 G §601, A-231, A-102, A-343, A-344

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-13 11:42:08 +01:00
parent 7c0492b06c
commit 4ce2d82645
2 changed files with 47 additions and 4 deletions
+29 -3
View File
@@ -4067,12 +4067,24 @@ func filepathDir(p string) string {
}
// invokeNameValid says a module or process's invoked entry names a tool: <module>.<tool>, a role's
// seat:<seat>.<verb>, or * for every tool on the mesh (novox/hq ADR 0152, ADR 0436 §1).
// seat:<seat>.<verb> or a kind the seat accepts (seat:<seat>.<kind>, seat:<seat>.<stream>.<kind>, design 47 G A-102),
// or * for every tool on the mesh (novox/hq ADR 0152, ADR 0436 §1).
func invokeNameValid(t string) bool {
if t == "*" {
return true
}
t = strings.TrimPrefix(t, "seat:")
if rest, isSeat := strings.CutPrefix(t, "seat:"); isSeat {
// A seat's verb, or a kind the seat accepts: `<kind>`, or `<stream>.<kind>` for one that rides a stream (design 47 G
// A-102), an asker's queue `accept.<queue>` among them (§601, A-231); seatInvokeProblems judges it against the seat.
seat, after, named := strings.Cut(rest, ".")
if !named || !name.MatchString(seat) {
return false
}
if stream, kind, onStream := strings.Cut(after, "."); onStream {
return toolName.MatchString(stream) && toolName.MatchString(kind)
}
return toolName.MatchString(after)
}
first, second, named := strings.Cut(t, ".")
return named && name.MatchString(first) && toolName.MatchString(second)
}
@@ -4586,7 +4598,12 @@ func seatInvokeProblems(shelf catalogue.Shelf, declared map[string]catalogue.Sea
problems = append(problems, fmt.Sprintf("%s invokes %s, a verb of the seat %q, "+catalogue.UndeclaredSeat, module, invoke, seat))
continue
}
if !slices.ContainsFunc(verbs, func(v catalogue.Verb) bool { return v.Name == verb }) {
// An asker's queue of a build seat, `seat:<seat>.accept.<queue>` (design 47 G §544, §601, A-231), read by mesh-sdk's
// one reader: judged against the kinds the seat's definition accepts.
if _, queue, isQueue := catalogue.QueueInvoke(invoke); isQueue {
verb = queue
}
if !slices.ContainsFunc(verbs, func(v catalogue.Verb) bool { return v.Name == verb }) && !acceptsKind(seat, verb, declared) {
problems = append(problems, fmt.Sprintf("%s invokes %s, and the seat %s serves no verb %q", module, invoke, seat, verb))
continue
}
@@ -4598,6 +4615,15 @@ func seatInvokeProblems(shelf catalogue.Shelf, declared map[string]catalogue.Sea
return problems
}
// acceptsKind says the seat accepts the kind an invoke names after its seat (design 47 G A-102): `<kind>`, or
// `<stream>.<kind>` for a kind that rides a stream. A module's own seat names its accepts by kind alone.
func acceptsKind(seat, kind string, declared map[string]catalogue.SeatDeclaration) bool {
if s, isMesh := catalogue.SeatNamed(seat); isMesh {
return slices.Contains(s.Accepts, kind) || slices.Contains(s.AcceptedOn, kind)
}
return slices.Contains(declared[seat].Accepts, kind)
}
// SeatPathProblems is what is wrong with the paths a set of the mesh's own seats names (novox/hq ADR
// 0287): only a node seat names one, each absolute and literal; every path has exactly one seat naming it
// without stepping back; and a seat steps back only for another node seat naming the same path without
@@ -5240,6 +5240,11 @@ func caseTwoModulesCannotDeclareTheSameSeat(t *testing.T) {
func caseUsingASeatNobodyDeclaresIsRefused(t *testing.T) {
unclaimed := telegram()
unclaimed.Claims = nil
// A mesh seat that accepts a kind riding a stream (design 47 G A-102), beside the seat list's own.
was := catalogue.Seats()
t.Cleanup(func() { catalogue.UseSeats(was) })
catalogue.UseSeats(append(catalogue.Seats(), catalogue.Seat{Name: "mesh-steps", Scope: catalogue.ScopeMesh, Decision: "test",
Accepts: []string{"step"}, AcceptedOn: []string{"check.step"}}))
for _, row := range []struct {
name, want string
shop catalogue.Manifest
@@ -5268,8 +5273,20 @@ func caseUsingASeatNobodyDeclaresIsRefused(t *testing.T) {
{"what the engine reads of a manifest, and its event (ADR 0618 §3, §4)", "",
catalogue.Manifest{Module: "shop", Invokes: []string{"seat:node-engine.manifest-reads"},
Consumes: []string{"node-engine.manifest-reads-changed"}}, telegram(), false},
// An asker names the queue it asks as `seat:<seat>.accept.<queue>`, checked against the kinds the seat's definition
// accepts (design 47 G §601, A-231); a kind the seat accepts, alone or on its stream, as `seat:<seat>.<kind>` or
// `seat:<seat>.<stream>.<kind>` (A-102).
{"a queue a build seat accepts", "", catalogue.Manifest{Module: "shop", Invokes: []string{"seat:node-build-go.accept.check"}}, telegram(), false},
{"a queue a build seat does not accept", `the seat node-build-go serves no verb "other"`,
catalogue.Manifest{Module: "shop", Invokes: []string{"seat:node-build-go.accept.other"}}, telegram(), false},
{"a queue a declared seat accepts", "", catalogue.Manifest{Module: "shop", Invokes: []string{"seat:telegram-sender.accept.send"}}, telegram(), false},
{"a kind a seat accepts", "", catalogue.Manifest{Module: "shop", Invokes: []string{"seat:mesh-steps.step"}}, telegram(), false},
{"a kind a seat accepts on its stream", "", catalogue.Manifest{Module: "shop", Invokes: []string{"seat:mesh-steps.check.step"}}, telegram(), false},
{"a kind on a stream the seat does not take it on", `the seat mesh-steps serves no verb "delivery.step"`,
catalogue.Manifest{Module: "shop", Invokes: []string{"seat:mesh-steps.delivery.step"}}, telegram(), false},
} {
got := CatalogueProblems(catalogue.Shelf{"telegram": row.seat, "shop": row.shop})
// The manifest's own invokes rule (at parse) takes the dotted kind as well; the seat's rule then judges it.
got := append(invokeProblems(row.shop), CatalogueProblems(catalogue.Shelf{"telegram": row.seat, "shop": row.shop})...)
joined := strings.Join(got, "; ")
if row.want == "" && joined != "" || row.want != "" && (len(got) != 1 || !strings.Contains(joined, row.want) ||
catalogue.IsUndeclaredSeat(got[0]) != row.note) {