From e01d265decb36d6bbf878e901847028d5008934e Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 7 Nov 2026 20:00:26 +0100 Subject: [PATCH] Put the indexer's old seatfiles module back for now, so the running indexer can take in mesh-delivery's merges (issue 1966) The running indexer (built at the registry merge) publishes every Go module of a repository it published before in one forge listing; seatfiles, published at 6bc232eb and removed since, is not in the tree, so the listing fails and every take-in of mesh-delivery is 'computed and not written' (journal 19:56:29: 'the directory modules/mesh-delivery-indexer/seatfiles is not in novox/mesh-delivery'). mesh-delivery #165 fixed that in the new indexer, which cannot run yet. The module comes back as it stood at 6bc232eb, imported by nothing, until the new indexer runs; the follow-up removes it. The operator's bootstrap of 2026-11-07. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- .../mesh-delivery-indexer/seatfiles/go.mod | 5 + .../mesh-delivery-indexer/seatfiles/go.sum | 2 + .../seatfiles/seatfiles.go | 181 ++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 modules/mesh-delivery-indexer/seatfiles/go.mod create mode 100644 modules/mesh-delivery-indexer/seatfiles/go.sum create mode 100644 modules/mesh-delivery-indexer/seatfiles/seatfiles.go diff --git a/modules/mesh-delivery-indexer/seatfiles/go.mod b/modules/mesh-delivery-indexer/seatfiles/go.mod new file mode 100644 index 00000000..4fc2f656 --- /dev/null +++ b/modules/mesh-delivery-indexer/seatfiles/go.mod @@ -0,0 +1,5 @@ +module git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/seatfiles + +go 1.26.0 + +require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104002338-5a38a1221659 diff --git a/modules/mesh-delivery-indexer/seatfiles/go.sum b/modules/mesh-delivery-indexer/seatfiles/go.sum new file mode 100644 index 00000000..60d9d4a6 --- /dev/null +++ b/modules/mesh-delivery-indexer/seatfiles/go.sum @@ -0,0 +1,2 @@ +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104002338-5a38a1221659 h1:tK7MzxVm/ECNg1Yblhh1ynVEthbZCg2mSPW0wQZC0b0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104002338-5a38a1221659/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= diff --git a/modules/mesh-delivery-indexer/seatfiles/seatfiles.go b/modules/mesh-delivery-indexer/seatfiles/seatfiles.go new file mode 100644 index 00000000..f28adb67 --- /dev/null +++ b/modules/mesh-delivery-indexer/seatfiles/seatfiles.go @@ -0,0 +1,181 @@ +// Package seatfiles is the indexer's one reading of the seat definitions a revision carries (novox/hq ADR 0633 §1, §6; +// ADR 0635, ADR 0636 §4): a manifest is judged against the seats of the revision it belongs to, never against the list +// the judge was built with. A definition is the contracts module's `seats//seat.json` (design 26 *A seat's +// definition*), with the parts it includes from `parts/.json`. This reader takes of it only what a manifest is +// judged by (the seat's scope, what it delivers, its verbs and events) and reads the rest leniently: the format's one +// strict reader (mesh-sdk seatdef) is not in the mesh-sdk the judge is built with, and the indexer of the revision keeps +// the definitions whole. +// +// It is the indexer's own package, a module of its own so that the checker's rules can require it (the lead's ruling on +// milestone 47/B0, ADR 0634 §1: shared code lives with its owner): the indexer's judge and the checker's seat list read +// definitions here and nowhere else. Milestone G's domain SDK reading replaces it. +package seatfiles + +import ( + "encoding/json" + "fmt" + + "git.novox.be/novox/mesh-sdk/go/catalogue" +) + +// Definition is the part of one seat's definition a manifest is judged by. +type Definition struct { + Format int `json:"format"` + Seat string `json:"seat"` + Scope string `json:"scope"` + Decision string `json:"decision"` + HolderDeclares bool `json:"holder-declares"` + Delivers string `json:"delivers"` + Replicated bool `json:"replicated"` + PulledByKind bool `json:"pulled-by-kind"` + Yields bool `json:"yields"` + HeldByTheEngine bool `json:"held-by-the-engine"` + Includes []string `json:"includes"` + Verbs []DefVerb `json:"verbs"` + Accepts []DefName `json:"accepts"` + Events []DefName `json:"events"` +} + +// DefVerb is a verb of a definition. +type DefVerb struct { + Name string `json:"name"` + Description string `json:"description"` + Optional bool `json:"optional"` + Replaces []string `json:"replaces"` +} + +// DefName is an accepted message or an event of a definition. +type DefName struct { + Name string `json:"name"` +} + +// Part is a part a definition includes: verbs and events written once for the seats that share them. +type Part struct { + Part string `json:"part"` + Verbs []DefVerb `json:"verbs"` + Events []DefName `json:"events"` +} + +// ParseDefinition reads one definition; a file that is not one names a seat and a scope. +func ParseDefinition(raw []byte) (Definition, error) { + var d Definition + if err := json.Unmarshal(raw, &d); err != nil { + return Definition{}, fmt.Errorf("a seat definition is not JSON: %w", err) + } + if d.Seat == "" || (d.Scope != catalogue.ScopeMesh && d.Scope != catalogue.ScopeNode) { + return Definition{}, fmt.Errorf("a seat definition names its seat and its scope (mesh or node), not %q and %q", d.Seat, d.Scope) + } + return d, nil +} + +// ParsePart reads one part. +func ParsePart(raw []byte) (Part, error) { + var p Part + if err := json.Unmarshal(raw, &p); err != nil { + return Part{}, fmt.Errorf("a part of a seat definition is not JSON: %w", err) + } + return p, nil +} + +// Resolve is the definition with the verbs and events of the parts it includes put first, as the indexer keeps it. +func (d Definition) Resolve(parts map[string]Part) (Definition, error) { + out := d + out.Includes, out.Verbs, out.Events = nil, nil, nil + for _, name := range d.Includes { + p, ok := parts[name] + if !ok { + return Definition{}, fmt.Errorf("the seat %s includes the part %q, which is not given", d.Seat, name) + } + out.Verbs = append(out.Verbs, p.Verbs...) + out.Events = append(out.Events, p.Events...) + } + out.Verbs = append(out.Verbs, d.Verbs...) + out.Events = append(out.Events, d.Events...) + return out, nil +} + +// Seats are the seats of resolved definitions, in the catalogue's types; a seat its holder declares itself is left out. +// A definition still naming parts it includes is refused: resolve it first. +func Seats(defs []Definition) ([]catalogue.Seat, error) { + var out []catalogue.Seat + for _, d := range defs { + if d.HolderDeclares { + continue + } + if len(d.Includes) > 0 { + return nil, fmt.Errorf("the seat %s names parts it includes: resolve it first", d.Seat) + } + s := catalogue.Seat{Name: d.Seat, Scope: d.Scope, Delivers: d.Delivers, Replicated: d.Replicated, + PulledByKind: d.PulledByKind, Yields: d.Yields, HeldByTheEngine: d.HeldByTheEngine, Decision: d.Decision} + for _, a := range d.Accepts { + s.Accepts = append(s.Accepts, a.Name) + } + for _, e := range d.Events { + s.Emits = append(s.Emits, e.Name) + } + for _, v := range d.Verbs { + s.Serves = append(s.Serves, catalogue.Verb{Name: v.Name, Description: v.Description, Optional: v.Optional, Replaces: v.Replaces}) + } + out = append(out, s) + } + return out, nil +} + +// SeatsOfFiles are the seats of resolved definitions given as files, as a request carries them. +func SeatsOfFiles(raws [][]byte) ([]catalogue.Seat, error) { + defs := make([]Definition, 0, len(raws)) + for _, raw := range raws { + d, err := ParseDefinition(raw) + if err != nil { + return nil, err + } + defs = append(defs, d) + } + return Seats(defs) +} + +// ResolveRaw is the definition raw resolved with the parts it includes, each read by part, every field it carries kept as +// written: the verbs and events of its parts put first, then its own, and no includes. It reads raw and every part as +// ParseDefinition and ParsePart do, and answers the definition read with it. It is how a request's `seats` carry a +// definition whole (ADR 0636 §4). +func ResolveRaw(raw []byte, part func(name string) ([]byte, error)) (Definition, json.RawMessage, error) { + d, err := ParseDefinition(raw) + if err != nil { + return Definition{}, nil, err + } + var whole map[string]json.RawMessage + if err := json.Unmarshal(raw, &whole); err != nil { + return Definition{}, nil, err + } + type listed struct{ Verbs, Events []json.RawMessage } + var verbs, events []json.RawMessage + for _, name := range d.Includes { + praw, err := part(name) + if err != nil { + return Definition{}, nil, fmt.Errorf("the seat %s includes the part %q: %w", d.Seat, name, err) + } + var l listed + if _, err := ParsePart(praw); err != nil { + return Definition{}, nil, fmt.Errorf("the part %q of the seat %s: %w", name, d.Seat, err) + } + _ = json.Unmarshal(praw, &l) + verbs, events = append(verbs, l.Verbs...), append(events, l.Events...) + } + var own listed + _ = json.Unmarshal(raw, &own) + verbs, events = append(verbs, own.Verbs...), append(events, own.Events...) + delete(whole, "includes") + for k, list := range map[string][]json.RawMessage{"verbs": verbs, "events": events} { + if len(list) == 0 { + delete(whole, k) + continue + } + b, err := json.Marshal(list) + if err != nil { + return Definition{}, nil, err + } + whole[k] = b + } + out, err := json.Marshal(whole) + return d, out, err +} -- 2.54.0