Issue 1585, S19: the strict parser, every manifest problem function and the judge's tables (192 declarations, about 5,200 lines) and their 244 tests leave; composition keeps its own refusals and the helpers it uses. The catalogue's tests decode the manifests they build. ParseManifest, ParseManifestWith, ParseManifestForInputs, CatalogueProblems and the rest are gone; mesh-delivery-indexer/judge holds them. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
150 lines
7.5 KiB
Go
150 lines
7.5 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Seats a module declares of its own (novox/hq ADR 0118).
|
|
//
|
|
// The set of seats a mesh has is **derived**: the mesh's own, in seats.go, plus those declared by
|
|
// every module it has registered. Still closed — a seat named nowhere is refused — but computed
|
|
// from the catalogue rather than written in the controller, which is what ADR 0110 actually
|
|
// needed and a hand-maintained table could not keep. Its own evidence: the enumeration done by
|
|
// hand while that record was written reported eleven claims where there were thirteen.
|
|
//
|
|
// **What can be checked from one manifest and what cannot.** A declaration's shape, its scope,
|
|
// and the reserved prefix are facts about the manifest in front of you. Whether a seat anybody
|
|
// names actually exists, whether two modules declared the same one, and whether a holder
|
|
// satisfies the protocol are facts about the *catalogue* — so they are checked at registration,
|
|
// by CatalogueProblems, which is the last moment the mesh can still say no.
|
|
|
|
// A SeatDeclaration is a role a module offers on the bus: what may be sent to it, what it says,
|
|
// and what it answers. A caller declares that it uses the *seat*, never the module, so the
|
|
// implementation can be replaced under it.
|
|
type SeatDeclaration struct {
|
|
Name string `json:"name"`
|
|
Scope string `json:"scope,omitempty"`
|
|
|
|
// Accepts are the verbs others may submit work on. Each becomes a work-queue subject, and
|
|
// the holder is the only consumer — so exactly one worker does the job, by construction
|
|
// rather than by how carefully somebody wrote a subscribe call.
|
|
Accepts []string `json:"accepts,omitempty"`
|
|
// Emits are the verbs the holder publishes: 1:many, nobody obliged to act.
|
|
Emits []string `json:"emits,omitempty"`
|
|
// Serves are the verbs the holder answers: request and reply, awaited. A bare name, or the
|
|
// verb in full with its schema (novox/hq ADR 0132).
|
|
Serves []Verb `json:"serves,omitempty"`
|
|
|
|
// RetainSeconds is how long the inbound backlog survives with no holder, zero for the
|
|
// mesh's default. Retention belongs to whoever owns the namespace (design 29 §3) — a seat
|
|
// owns its own, which is why a seat is also the answer for a module that needs retention
|
|
// its events cannot have.
|
|
RetainSeconds int `json:"retain-seconds,omitempty"`
|
|
|
|
// Kinded makes the seat a kinded bench (novox/hq ADR 0234 §2, ADR 0259 §3): its holders are different
|
|
// modules, each claiming one kind, and each verb's subject carries the kind. Only the benches in
|
|
// KindedBenches may be kinded; making another is a decision, recorded.
|
|
Kinded bool `json:"kinded,omitempty"`
|
|
// ByCaller are accepts and emits whose subject's last token names the calling module (ADR 0259 §3): a
|
|
// user submits such an accept, and hears such an event, under its own name and no other.
|
|
ByCaller []string `json:"by-caller,omitempty"`
|
|
// Proofs are verbs carried as core request and reply, never on a stream: what travels on them (a code
|
|
// the operator typed) is never kept (ADR 0259 §3). On a kinded bench a holder asks with its own kind and
|
|
// the modules watching the seat answer.
|
|
Proofs []string `json:"proofs,omitempty"`
|
|
// Records are state buckets of the declaring module that each user reads under its own name — the
|
|
// keys `<user>.…` and no other (ADR 0259 §3).
|
|
Records []string `json:"records,omitempty"`
|
|
// Paths are the paths on its machine the seat's holder owns (novox/hq ADR 0287), under the rules the
|
|
// mesh's own seats name theirs by: a node seat only, each absolute and literal, and none another seat
|
|
// names. A module's seat never steps back.
|
|
Paths []string `json:"paths,omitempty"`
|
|
// Needs is every node seat this seat's holder needs held on its own node (novox/hq ADR 0220, ADR
|
|
// 0378 §8): a role whose holder is only right while another role is filled beside it. A module
|
|
// claiming this seat depends on each, exactly as a module declaring a service depends on the
|
|
// service manager (ADR 0207) — derived from the claim, never written again in the claimant's own
|
|
// manifest. Only a node seat may carry needs, and a compiled seat carries none.
|
|
Needs []string `json:"needs,omitempty"`
|
|
}
|
|
|
|
// KindedBenches are the seats that may be kinded (novox/hq ADR 0234 §2): `channel` sends to the operator,
|
|
// `intake` takes what the operator answers. Another is a decision, recorded, as ADR 0223 asks of a bench.
|
|
var KindedBenches = map[string]bool{"channel": true, "intake": true}
|
|
|
|
// NamedByCaller says whether one of the seat's verbs is named by its caller.
|
|
func (s SeatDeclaration) NamedByCaller(verb string) bool {
|
|
for _, v := range s.ByCaller {
|
|
if v == verb {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// At is this declaration's scope, with the default applied. Mesh by default, because a seat
|
|
// declared by a module is nearly always "there is one of these in the mesh" — a per-node worker
|
|
// is the deliberate case, and says so.
|
|
func (s SeatDeclaration) At() string {
|
|
if s.Scope == "" {
|
|
return ScopeMesh
|
|
}
|
|
return s.Scope
|
|
}
|
|
|
|
// A Shelf is every manifest the mesh has registered, by module name.
|
|
type Shelf map[string]Manifest
|
|
|
|
// CatalogueProblems are the rules no single manifest can be judged against.
|
|
//
|
|
// Run at registration, which is the last moment the mesh can still refuse: after it, a caller is
|
|
// bound to a seat and a refusal is an outage rather than a conversation.
|
|
// UndeclaredSeat ends the problem of a `uses` or a claim naming a seat no manifest given declares: over the whole
|
|
// catalogue (registration, the catalogue's own check) a refusal, and over some manifests alone a seat whose
|
|
// declaring module was not given (novox/hq issue 364).
|
|
const UndeclaredSeat = "which no module declares and the mesh does not define"
|
|
|
|
// IsUndeclaredSeat says whether a problem CatalogueProblems gave is a seat no manifest given declares.
|
|
func IsUndeclaredSeat(problem string) bool {
|
|
return strings.HasSuffix(problem, UndeclaredSeat) || strings.HasSuffix(problem, UnheldSeat)
|
|
}
|
|
|
|
// ChannelCapabilities is the fixed vocabulary `channel-capabilities/1` (novox/hq ADR 0234 §2): a word
|
|
// outside it is refused. `max-length:<N>` takes a number.
|
|
var ChannelCapabilities = map[string]bool{
|
|
"deliver": true, "reaches-away": true, "loud": true, "silent": true, "edit": true,
|
|
"reaches-when-mesh-down": true, "private": true,
|
|
"choice": true, "reply": true, "threads": true, "operator-first": true,
|
|
"verified-sender": true, "exact-render": true, "code-factor": true, "key-factor": true,
|
|
}
|
|
|
|
// TrustedHolding is why a module must run as its own account (novox/hq ADR 0259 §8), or "": it holds a seat
|
|
// whose events it says to one caller each (a warrant), or speaks for a kind of a kinded bench that proves
|
|
// its sender, or is private (a link's code is shown there). None may be carried by the machine's runtime, which
|
|
// runs as the operator's account.
|
|
func TrustedHolding(m Manifest, declared map[string]SeatDeclaration) string {
|
|
for _, c := range m.Claims {
|
|
s, ok := declared[c.Name]
|
|
if !ok {
|
|
continue
|
|
}
|
|
for _, e := range s.Emits {
|
|
if s.NamedByCaller(e) {
|
|
return fmt.Sprintf("it holds %s, whose %s it says to one caller each", c.Name, e)
|
|
}
|
|
}
|
|
if s.Kinded {
|
|
for _, capability := range c.Capabilities {
|
|
switch capability {
|
|
case "verified-sender":
|
|
return fmt.Sprintf("it holds %s of kind %s, which proves its sender", c.Name, c.Kind)
|
|
case "private":
|
|
// A private kind is shown a link's code, which makes an account the operator's.
|
|
return fmt.Sprintf("it holds %s of kind %s, which is private: a link's code is shown there", c.Name, c.Kind)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|