From c84a2e58e88ca4207c44de3aa3f4f41576e25087 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 16:37:56 +0100 Subject: [PATCH] Delete go/composer: delivery sdk's composer holds it, no importer left on any join head (S-7, B9f; issue 1966) Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- go/composer/composer.go | 209 ----------------------------------- go/composer/composer_test.go | 58 ---------- go/packages_list_test.go | 2 +- 3 files changed, 1 insertion(+), 268 deletions(-) delete mode 100644 go/composer/composer.go delete mode 100644 go/composer/composer_test.go diff --git a/go/composer/composer.go b/go/composer/composer.go deleted file mode 100644 index 8a33b1a..0000000 --- a/go/composer/composer.go +++ /dev/null @@ -1,209 +0,0 @@ -// Package composer is the shape of a composition over a plain request (novox/hq ADR 0482 clause 4, issue 1621, as ADR -// 0593 §7 changes it): the request the command mesh-compose reads and the verdict it answers. The command and the -// composer that runs it are the node-engine's (mesh-host cmd/mesh-compose), built from its repository at a group's -// head, so a change to its composition code is judged by that code. -// -// **It reads its request and nothing else.** No bus, store, network, file or environment: the manifests come inside the -// request (raw, as their repository holds them), the rest of the mesh is a compose.Input, and the answer is a value. -// A manifest named only by its digest in the delivery indexer's bucket is filled before the run by Fill, by whoever -// holds the bus (the Go build module's compose job); Run refuses a digest left unfilled. -package composer - -import ( - "encoding/json" - "errors" - "fmt" - "slices" - - "git.novox.be/novox/mesh-sdk/go/catalogue" - "git.novox.be/novox/mesh-sdk/go/delivery" -) - -// Version is the version of a Request and of a Verdict; a reader refuses another. -const Version = 1 - -// Package is the import path of the command, which a compose job builds at a commit of the node-engine's repository -// (novox/hq ADR 0593 §7). -const Package = "github.com/novox/mesh-host/cmd/mesh-compose" - -// The depths of a composition. -const ( - // DepthResolve resolves each machine's set over the rest of the mesh (compose.Resolve): what the assigner judges - // while the facts snapshot is not on the bus (issue 1461). - DepthResolve = "resolve" - // DepthCompose composes each machine's whole declaration and runs the node-engine's validator over it - // (compose.Compose). - DepthCompose = "compose" -) - -// Request is everything one run composes: the rest of the mesh, one or more cases of manifests composed over it, and -// the machines composed in each case. -type Request struct { - V int `json:"v"` - // Input is the composition's input without its catalogue (and without each node's kept manifests): the cases give - // those. A Catalogue or Kept it holds is ignored. It is the node-engine's composition input as JSON, which only the - // composer decodes (novox/hq ADR 0593 §7), so this shape imports no composition. - Input json.RawMessage `json:"input"` - // Cases are composed one after the other over Input, each with its own manifests; their names are unique. - Cases []Case `json:"cases"` - // Machines are the nodes composed in every case; none means the manifests are only read. - Machines []string `json:"machines,omitempty"` - // Depth is DepthResolve or DepthCompose; empty is DepthCompose. - Depth string `json:"depth,omitempty"` - // HoldingsFromClaims adds to Input's holdings every seat an assigned module claims, held on its node (the - // assigner's input, which has no holdings of record). - HoldingsFromClaims bool `json:"holdings_from_claims,omitempty"` - // Declarations asks each composed machine's declared paths (DepthCompose only). - Declarations bool `json:"declarations,omitempty"` - // Predefined is the list of predefined keys at the catalogue's root (predefined.json, novox/hq ADR 0348 §7). No - // longer read: the composer judges no manifest (novox/hq ADR 0578 §5), the indexer's judge does, and its verdicts - // come in each case's Verdicts. Kept so an asker that still sends it is read. - Predefined catalogue.PredefinedList `json:"predefined,omitempty"` - // Packages are the known packages a manifest's `packages` may name, each by its name in its own ecosystem (design 32: - // a Go package's go.mod module path, a TypeScript package's package.json name), for the indexer's judge (novox/hq ADR - // 0578 §5, ADR 0606 §4); the composer does not read them. Nil is no list at hand, and the judge judges no name; an - // empty list is a list, and every name is unknown to it. - Packages []string `json:"packages,omitzero"` -} - -// Case is one catalogue composed: every module's manifest by name, and per node the manifests kept at the build it runs. -// -// Placements are, by node, the placement the case composes that node with in place of what Input says of it: a change's -// own placement files, so a check composes a catalogue pull request's placement (issue 1126, frombus.Placed); a null -// placement leaves nothing placed on the node. A case without them composes Input's. -// -// Verdicts are, by module, the indexer's judge's verdict on its manifest in Manifests (novox/hq ADR 0578 §5), put in by -// the assigner or the checker that runs the composer: a refused one is held, composed as its stub so what it offers -// still shapes the rest, and named in the case's verdict. A module without one is accepted (delivery.TakeManifestVerdict). -type Case struct { - Name string `json:"name"` - Manifests map[string]Manifest `json:"manifests"` - Verdicts map[string]delivery.ManifestVerdict `json:"verdicts,omitempty"` - Kept map[string]map[string]Manifest `json:"kept,omitempty"` - Placements map[string]*delivery.Placement `json:"placements,omitempty"` -} - -// Manifest is a module's manifest as its repository holds it (Raw, the bytes), or the digest the delivery indexer keeps -// it under (Digest), which Fill replaces by the bytes. On the wire it is the manifest itself, or {"digest": "..."}. -type Manifest struct { - Raw json.RawMessage - Digest string -} - -// MarshalJSON writes the raw manifest, or {"digest": ...} when only the digest is known. -func (m Manifest) MarshalJSON() ([]byte, error) { - if len(m.Raw) > 0 { - return m.Raw, nil - } - return json.Marshal(map[string]string{"digest": m.Digest}) -} - -// UnmarshalJSON reads {"digest": "..."} (an object with that one key) as a digest, and any other value as the raw -// manifest. -func (m *Manifest) UnmarshalJSON(raw []byte) error { - var probe map[string]json.RawMessage - if json.Unmarshal(raw, &probe) == nil && len(probe) == 1 { - if d, ok := probe["digest"]; ok { - var digest string - if err := json.Unmarshal(d, &digest); err != nil { - return fmt.Errorf("a manifest's digest is not a string: %w", err) - } - *m = Manifest{Digest: digest} - return nil - } - } - *m = Manifest{Raw: slices.Clone(raw)} - return nil -} - -// Verdict is what one run answers: the mesh-sdk the command was built with, and each case's verdict in the order asked. -type Verdict struct { - V int `json:"v"` - SDK string `json:"sdk"` - Cases []CaseVerdict `json:"cases"` -} - -// CaseVerdict is one case's: every manifest read or refused, every machine composed or not. -type CaseVerdict struct { - Name string `json:"name"` - Manifests map[string]ManifestRead `json:"manifests"` - Machines map[string]Machine `json:"machines,omitempty"` -} - -// ManifestRead is one manifest as this mesh-sdk reads it: accepted, or refused with why: the indexer's verdict -// (Case.Verdicts) or bytes that do not decode. A field the decoder does not know is set aside, named in SetAside, and -// the manifest still accepted (novox/hq ADR 0578 §6). -type ManifestRead struct { - Accepted bool `json:"accepted"` - Refused string `json:"refused,omitempty"` - SetAside string `json:"set_aside,omitempty"` -} - -// Machine is one machine composed: whether it composes (resolves, at DepthResolve), why not, the node-engine -// validator's problems and the composition's refusals (DepthCompose), and its declared paths when asked. NotJudged says, -// in words, why the composer did not judge the machine (an older node-engine's input that does not say how it composes, -// novox/hq ADR 0593 §9): the machine is then neither composed nor refused, and a reader decides by this field alone, -// never by the words of Reason. -type Machine struct { - Composes bool `json:"composes"` - NotJudged string `json:"not_judged,omitempty"` - Reason string `json:"reason,omitempty"` - Problems []string `json:"problems,omitempty"` - Refusals []string `json:"refusals,omitempty"` - Declared []Path `json:"declared,omitempty"` -} - -// Path is one path a declaration puts on the machine, with its kind (the resource's type), owner, group and mode as -// declared, and the module it is of. -type Path struct { - Path string `json:"path"` - Kind string `json:"kind"` - Owner string `json:"owner,omitempty"` - Group string `json:"group,omitempty"` - Mode string `json:"mode,omitempty"` - Module string `json:"module,omitempty"` -} - -// ErrUnfilled is a manifest named by a digest that nobody replaced by its bytes. -var ErrUnfilled = errors.New("composer: a manifest is named by its digest and was not filled") - -// Decode reads a request; it refuses one that is not JSON or of another version. -func Decode(data []byte) (Request, error) { - var r Request - if err := json.Unmarshal(data, &r); err != nil { - return Request{}, fmt.Errorf("composer: the request is not JSON of its shape: %w", err) - } - if r.V != Version { - return Request{}, fmt.Errorf("composer: a request of version %d, this composer reads %d", r.V, Version) - } - return r, nil -} - -// Fill replaces every manifest named by its digest by the bytes get answers for that digest; a digest get cannot answer -// stops it, named. -func Fill(r *Request, get func(digest string) ([]byte, error)) error { - fill := func(m map[string]Manifest) error { - for name, man := range m { - if len(man.Raw) > 0 { - continue - } - raw, err := get(man.Digest) - if err != nil { - return fmt.Errorf("composer: the manifest of %s (digest %s): %w", name, man.Digest, err) - } - m[name] = Manifest{Raw: raw} - } - return nil - } - for _, c := range r.Cases { - if err := fill(c.Manifests); err != nil { - return err - } - for _, kept := range c.Kept { - if err := fill(kept); err != nil { - return err - } - } - } - return nil -} diff --git a/go/composer/composer_test.go b/go/composer/composer_test.go deleted file mode 100644 index 9ee1c40..0000000 --- a/go/composer/composer_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package composer - -import ( - "encoding/json" - "errors" - "strings" - "testing" -) - -const demo = `{"module": "demo", "version": "1", "claims": [{"name": "node-files"}]}` - -// A manifest named by its digest goes on the wire as {"digest": ...}, and Fill puts the bytes the indexer keeps in its -// place; a raw manifest goes as itself. -func TestAManifestIsItsBytesOrItsDigest(t *testing.T) { - r := Request{V: Version, Cases: []Case{{Name: "change", Manifests: map[string]Manifest{ - "demo": {Digest: "d1"}, "raw": {Raw: json.RawMessage(`{"module":"raw","version":"1"}`)}}, - Kept: map[string]map[string]Manifest{"laptop": {"demo": {Digest: "d1"}}}}}} - wire, err := json.Marshal(r) - if err != nil { - t.Fatal(err) - } - if !strings.Contains(string(wire), `"demo":{"digest":"d1"}`) || !strings.Contains(string(wire), `"raw":{"module":"raw","version":"1"}`) { - t.Fatalf("on the wire: %s", wire) - } - back, err := Decode(wire) - if err != nil { - t.Fatal(err) - } - asked := 0 - if err := Fill(&back, func(d string) ([]byte, error) { - asked++ - if d != "d1" { - return nil, errors.New("no such digest") - } - return []byte(demo), nil - }); err != nil { - t.Fatal(err) - } - if asked != 2 || string(back.Cases[0].Manifests["demo"].Raw) != demo || string(back.Cases[0].Kept["laptop"]["demo"].Raw) != demo { - t.Fatalf("filled %d times: %+v", asked, back.Cases[0]) - } - // The known packages: none is no list (not on the wire), an empty list is a list, and a list reads back whole. - for _, known := range [][]string{nil, {}, {"git.novox.be/novox/mesh-sdk", "@novox/mesh-sdk"}} { - wire, err := json.Marshal(Request{V: Version, Packages: known}) - if err != nil { - t.Fatal(err) - } - back, err := Decode(wire) - if err != nil || (back.Packages == nil) != (known == nil) || strings.Join(back.Packages, ",") != strings.Join(known, ",") || - known == nil && strings.Contains(string(wire), "packages") { - t.Errorf("the known packages %#v went as %s and read back %#v (%v)", known, wire, back.Packages, err) - } - } - bad := Request{V: Version, Cases: []Case{{Name: "c", Manifests: map[string]Manifest{"x": {Digest: "nope"}}}}} - if err := Fill(&bad, func(string) ([]byte, error) { return nil, errors.New("not there") }); err == nil || !strings.Contains(err.Error(), "nope") { - t.Fatalf("a digest the indexer does not hold: %v", err) - } -} diff --git a/go/packages_list_test.go b/go/packages_list_test.go index c160b28..857ca19 100644 --- a/go/packages_list_test.go +++ b/go/packages_list_test.go @@ -24,7 +24,7 @@ import ( // request: does every module share it, or is it a shape more than one part reads? var meshSDKPackages = []string{ ".", "announce", "announcetest", "buildstep", "buscalls", "buscompose", "busstate", "busretry", "calladdress", "catalogue", "channel", - "composer", "declaration", "delivery", "forgekey", "handacts", "health", "internal/beside", "keys", "kvlimit", "membership", "motion", + "declaration", "delivery", "forgekey", "handacts", "health", "internal/beside", "keys", "kvlimit", "membership", "motion", "objectlink", "outward", "overlay", "parts", "pernode", "process", "provider", "provider/harness", "provisions", "sdkversion", "seal", "seatdef", "seatlist", "seatlist/seatlisttest", "testbus", "views", "views/viewstest", }