package catalogue import ( "fmt" "slices" "strings" ) // A seat verb a module invokes is one some holder serves (issue 774, sweep item 1). messenger invoked // `seat:node-engine.root-free`, which nothing in the mesh answered, so every ask that offers an approval was held on // Telegram, silently. A manifest names what it calls (novox/hq ADR 0152); that a seat by that name exists, promises // that verb, and has a holder is only known over the whole catalogue, so it is judged here, beside the seats' other // rules (CatalogueProblems). // UnheldSeat ends the problem of a `seat:.` whose seat no module given claims: over the whole catalogue a // refusal, over some manifests alone a seat whose holder was not given (IsUndeclaredSeat says both). const UnheldSeat = "which no module claims, so nothing answers it" // SeatInvoke splits an invoke `seat:.` into its seat and verb; ok is false for anything else (a module's // tool, `*`). func SeatInvoke(invoke string) (seat, verb string, ok bool) { rest, isSeat := strings.CutPrefix(invoke, "seat:") if !isSeat { return "", "", false } seat, verb, ok = strings.Cut(rest, ".") return seat, verb, ok && seat != "" && verb != "" } // seatInvokeProblems are the `seat:.` a module or one of its processes invokes that nothing answers: a seat // no module declares and the mesh does not define, a verb the seat does not promise, or a seat with no holder (a // seat a module declares and no module claims; a mesh seat's holder, the mesh itself or a module of any repository, // is not judged). func seatInvokeProblems(shelf Shelf, declared map[string]SeatDeclaration) []string { claimed := map[string]bool{} for _, m := range shelf { for _, c := range m.Claims { claimed[c.Name] = true if s, ok := SeatNamed(c.Name); ok { claimed[s.Name] = true } } } var problems []string for _, module := range shelfOrder(shelf) { m := shelf[module] for _, invoke := range invokesOf(m) { seat, verb, ok := SeatInvoke(invoke) if !ok { continue } var verbs []Verb held := claimed[seat] if s, isMesh := SeatNamed(seat); isMesh { // A mesh seat is held by the mesh itself or by a module of any repository: one repository's catalogue // cannot say it has no holder, so only its verbs are judged (the lead, 2026-10-30: mesh-control's check // runs over the captured mesh-catalog alone, whose mesh-self-check calls the watchman's seat). verbs, held = s.Serves, true } else if d, isModule := declared[seat]; isModule { verbs = d.Serves } else { problems = append(problems, fmt.Sprintf("%s invokes %s, a verb of the seat %q, "+UndeclaredSeat, module, invoke, seat)) continue } if !slices.ContainsFunc(verbs, func(v Verb) bool { return v.Name == verb }) { problems = append(problems, fmt.Sprintf("%s invokes %s, and the seat %s serves no verb %q", module, invoke, seat, verb)) continue } if !held { problems = append(problems, fmt.Sprintf("%s invokes %s, a verb of the seat %q, "+UnheldSeat, module, invoke, seat)) } } } return problems } // invokesOf is everything a module says it invokes: its own and each process's bus, once each. func invokesOf(m Manifest) []string { out := append([]string(nil), m.Invokes...) for _, r := range m.Resources { if fmt.Sprint(r["type"]) != "process" { continue } if _, present := r["bus"]; !present { continue } if b, _, err := processBusOf(r); err == nil { out = append(out, b.Invokes...) } } slices.Sort(out) return slices.Compact(out) }