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
148 lines
6.0 KiB
Go
148 lines
6.0 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// ProcessBusField is the key a process resource declares its bus on. Controller-side only: never composed
|
|
// onto the node-engine's process resource (novox/hq ADR 0436 §1) — the wire declaration is mesh-host's own,
|
|
// vendored, with a strict decoder that refuses an unknown key.
|
|
const ProcessBusField = "bus"
|
|
|
|
// ProcessBus is what one process of a module does on the bus (novox/hq ADR 0436 §1, §2, §3, §5): its own bus
|
|
// account, granted exactly this and nothing of the module's other grants or the tool runner's — never a seat
|
|
// to hold or serve, never a tool call or seat verb answered, never a restricted stream heard, never a
|
|
// warrant published (§3, §5).
|
|
type ProcessBus struct {
|
|
// Emits are the events this process emits, named locally, as the module's own Emits names them
|
|
// (EmitsAll): a process emits only what its module may emit as a whole.
|
|
Emits []string `json:"emits,omitempty"`
|
|
// Queues are the module's own work-queue streams (ADR 0436 §6) this process consumes.
|
|
Queues []string `json:"queues,omitempty"`
|
|
// State is the module's own state, by local name, this process reads and writes.
|
|
State []string `json:"state,omitempty"`
|
|
// Asks says whether this process reads the router's records of this module's own asks.
|
|
Asks bool `json:"asks,omitempty"`
|
|
// Invokes are the tools and seat verbs this process calls, each <module>.<tool> or a role's
|
|
// seat:<seat>.<verb> (as Manifest.Invokes), or * for every tool on the mesh.
|
|
Invokes []string `json:"invokes,omitempty"`
|
|
// Removes are what a tools bundle's account may remove from the bus, from a closed set (BusRemovals; novox/hq ADR
|
|
// 0564 Limits 4): declared alone, by the module holding mesh-broker, never by a process.
|
|
Removes []string `json:"removes,omitempty"`
|
|
}
|
|
|
|
// What a bus account may remove (novox/hq ADR 0564 Limits 4), each its own grant: the controller's consumer on
|
|
// DEAD_LETTER_NOTICES (the lease write's step, clause 5), and keys of the node-engine's acts bucket. A further consumer
|
|
// or bucket is a change of this set, through a decision.
|
|
const (
|
|
RemovesControllerDeadLetterConsumer = "DEAD_LETTER_NOTICES.controller"
|
|
RemovesEngineActs = "mesh-host_acts"
|
|
// RemovesAssignmentsStream is the stream ASSIGNMENTS, removed whole at the lease write (novox/hq research 072 row 33):
|
|
// its 350 messages have no consumer, and the placement is the provisioner's from then on.
|
|
RemovesAssignmentsStream = "ASSIGNMENTS"
|
|
)
|
|
|
|
// BusAccount is one account of a module's own beside its `broker`: a process's (ADR 0436) or a tools bundle's (ADR 0564
|
|
// Limits 4), by its id (the process resource's, or the bundle's binary), with what its bus says.
|
|
type BusAccount struct {
|
|
ID string
|
|
Bus ProcessBus
|
|
// Tools says it is a tools bundle's, run by the node's tool runtime, not a process the node runs.
|
|
Tools bool
|
|
}
|
|
|
|
// BusAccounts is every own bus account of the module, processes first in resource order, then tools bundles in artifact
|
|
// order: what the grant composition composes a principal for, each delivered in its own secret broker-<id>. A tools
|
|
// bundle's is read from the repository's build, or from the resolved manifest's bundles.
|
|
func (m Manifest) BusAccounts() []BusAccount {
|
|
var out []BusAccount
|
|
buses := m.ProcessBuses()
|
|
for _, r := range m.Resources {
|
|
id := fmt.Sprint(r["id"])
|
|
if b, ok := buses[id]; ok && fmt.Sprint(r["type"]) == "process" {
|
|
out = append(out, BusAccount{ID: id, Bus: b})
|
|
}
|
|
}
|
|
seen := map[string]bool{}
|
|
if m.Build != nil {
|
|
for _, a := range m.Build.Artifacts {
|
|
if a.Bus != nil && a.Kind == ArtifactBundle && BinaryOf(a) != "" && !seen[BinaryOf(a)] {
|
|
seen[BinaryOf(a)] = true
|
|
out = append(out, BusAccount{ID: BinaryOf(a), Bus: *a.Bus, Tools: true})
|
|
}
|
|
}
|
|
}
|
|
for _, b := range m.Bundles {
|
|
if b.Bus != nil && b.Binary != "" && !seen[b.Binary] {
|
|
seen[b.Binary] = true
|
|
out = append(out, BusAccount{ID: b.Binary, Bus: *b.Bus, Tools: true})
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ToolsBusSecret says name is the own secret a tools bundle's bus account is held in: `broker-<binary>` for a bundle
|
|
// whose bus the manifest declares (ADR 0564 Limits 4). Its file belongs to the runtime's account, which runs the bundle.
|
|
func ToolsBusSecret(m Manifest, name string) bool {
|
|
for _, a := range m.BusAccounts() {
|
|
if a.Tools && name == "broker-"+a.ID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// UnmarshalJSON reads a process's bus strictly: an unknown key — serves, a seat to hold or claim, or
|
|
// anything else that would answer a tool call or a seat's verb — is refused here, by name (novox/hq ADR
|
|
// 0436 §3).
|
|
func (b *ProcessBus) UnmarshalJSON(raw []byte) error {
|
|
type plain ProcessBus
|
|
var p plain
|
|
dec := json.NewDecoder(bytes.NewReader(raw))
|
|
dec.DisallowUnknownFields()
|
|
if err := dec.Decode(&p); err != nil {
|
|
return fmt.Errorf("a process's bus is {emits, queues, state, asks, invokes}: %w", typedUnknown(err))
|
|
}
|
|
*b = ProcessBus(p)
|
|
return nil
|
|
}
|
|
|
|
// processBusOf is the bus a process resource declares, and whether it declared one at all.
|
|
func processBusOf(r map[string]any) (bus ProcessBus, present bool, err error) {
|
|
raw, present := r["bus"]
|
|
if !present {
|
|
return ProcessBus{}, false, nil
|
|
}
|
|
body, err := json.Marshal(raw)
|
|
if err != nil {
|
|
return ProcessBus{}, true, err
|
|
}
|
|
if err := json.Unmarshal(body, &bus); err != nil {
|
|
return ProcessBus{}, true, err
|
|
}
|
|
return bus, true, nil
|
|
}
|
|
|
|
// ProcessBuses is every process of this module that declares bus, by its id (the manifest's own resource
|
|
// id, novox/hq ADR 0436, settled by the lead) — what the grant composition reads to grant a process
|
|
// account exactly what it emits, consumes and calls (novox/hq ADR 0436 §2).
|
|
func (m Manifest) ProcessBuses() map[string]ProcessBus {
|
|
var out map[string]ProcessBus
|
|
for _, r := range m.Resources {
|
|
if fmt.Sprint(r["type"]) != "process" {
|
|
continue
|
|
}
|
|
b, present, err := processBusOf(r)
|
|
if !present || err != nil {
|
|
continue
|
|
}
|
|
if out == nil {
|
|
out = map[string]ProcessBus{}
|
|
}
|
|
out[fmt.Sprint(r["id"])] = b
|
|
}
|
|
return out
|
|
}
|