From c0bee5d042d465c77efddd7db903e19f638ceb42 Mon Sep 17 00:00:00 2001 From: mesh-admin Date: Wed, 14 Oct 2026 05:45:48 +0000 Subject: [PATCH] Judge every manifest against the list of predefined keys of its tree, and carry the list from a build Mesh and node config may hold values only for keys on a fixed, reviewed list (hq ADR 0348 clause 7, design 57 package K2, issue 661). A build reads predefined.json at the root of the repository at the commit it builds and carries it with the commit's generation; module check and the merge gate read it from the tree. (Part 1 of 2: the changes to existing files.) Co-Authored-By: Claude Opus 5.5 --- cmd/mesh-builder/main.go | 5 +++ cmd/mesh-controller/build.go | 38 ++++++++++++++++++++-- cmd/mesh-controller/check.go | 26 ++++++++++++++- cmd/mesh-controller/merge_gate.go | 15 +++++++-- cmd/mesh-controller/stores.go | 6 ++++ internal/builder/builder.go | 53 +++++++++++++++++++++++++++++-- internal/builder/builder_test.go | 2 ++ internal/builder/source.go | 21 +++++++++++- internal/catalogue/config.go | 23 ++++---------- internal/catalogue/config_test.go | 19 +++++------ internal/catalogue/manifest.go | 16 ++++++++++ internal/catalogue/template.go | 2 +- internal/link/build.go | 14 ++++++++ testdata/store-tests | 3 ++ 14 files changed, 206 insertions(+), 37 deletions(-) diff --git a/cmd/mesh-builder/main.go b/cmd/mesh-builder/main.go index 7bf565be..df5735ae 100644 --- a/cmd/mesh-builder/main.go +++ b/cmd/mesh-builder/main.go @@ -418,6 +418,11 @@ func answer(ctx context.Context, publisher builder.Publisher, on, workspace stri for _, s := range built.Sources { result.Sources = append(result.Sources, link.BuildSource{Repository: s.Repository, Ref: s.Ref, Paths: s.Paths}) } + // The list of predefined keys the manifest was judged against, for the controller to publish + // (novox/hq ADR 0348 §7). + if p := built.Predefined; p != nil { + result.Predefined = &link.PredefinedList{Keys: p.Keys, Generation: p.Generation} + } say("built", built.Manifest.Module+" from "+short(built.Commit)) } } diff --git a/cmd/mesh-controller/build.go b/cmd/mesh-controller/build.go index d7eb9c2d..ae337692 100644 --- a/cmd/mesh-controller/build.go +++ b/cmd/mesh-controller/build.go @@ -183,8 +183,10 @@ func buildFrom(result link.BuildResult) inventory.Build { kept.Manifest = recordedManifest(result.Manifest, announced) // The module name comes from the manifest, which only exists when the build got that far. if len(kept.Manifest) > 0 { - if m, err := catalogue.ParseManifest(kept.Manifest); err == nil { - kept.Module = m.Module + if list, err := judgedWith(result); err == nil { + if m, err := catalogue.ParseManifestWith(kept.Manifest, list); err == nil { + kept.Module = m.Module + } } } return kept @@ -603,6 +605,11 @@ func saysWhenThePolicyActs(ctx context.Context, inv *inventory.Inventory, module // written with the same values. func takeIn(ctx context.Context, inv *inventory.Inventory, result link.BuildResult) ( catalogue.Manifest, inventory.Build, error) { + // The list of predefined keys as the store keeps it now: another controller process may have kept a newer one. + if err := loadPredefined(ctx, inv); err != nil { + return catalogue.Manifest{}, inventory.Build{}, fmt.Errorf("the list of predefined keys kept cannot be "+ + "read: %w", err) + } kept := buildFrom(result) if err := inv.RecordBuild(ctx, kept); err != nil { return catalogue.Manifest{}, kept, err @@ -613,7 +620,15 @@ func takeIn(ctx context.Context, inv *inventory.Inventory, result link.BuildResu return catalogue.Manifest{}, kept, fmt.Errorf("%s could not build %s:\n%s", result.On, result.Repository, result.Failed) } - manifest, err := catalogue.ParseManifest(kept.Manifest) + // Judged first against the list the build was judged against: the catalogue's at the commit built (novox/hq + // ADR 0348 §7), so a module naming a key its own commit adds is read; and again below against the list the + // mesh publishes, before it is registered. + list, err := judgedWith(result) + if err != nil { + return catalogue.Manifest{}, kept, fmt.Errorf("%s built %s (%s), recorded and not registered: %w", result.On, + result.Repository, short(result.Commit), err) + } + manifest, err := catalogue.ParseManifestWith(kept.Manifest, list) if err != nil { return catalogue.Manifest{}, kept, fmt.Errorf("%s built %s and what came back is not a manifest: %w", result.On, result.Repository, err) @@ -687,6 +702,23 @@ func takeIn(ctx context.Context, inv *inventory.Inventory, result link.BuildResu "back: it is recorded and not registered again — a newer build is", result.On, manifest.Module, short(result.Commit)) } + // **The newest trunk build's list of predefined keys is published** (novox/hq ADR 0348 §7): a build of the + // catalogue that may be registered keeps its list when its commit is newer than the one kept. Then the module + // is judged against the list the mesh publishes, as every manifest read again is: one naming a key that list + // does not hold, or of another type, is refused here and never enters the catalogue. + if carried, err := carriedList(result); err != nil { + return manifest, kept, err + } else if carried != nil { + if err := publishPredefined(ctx, inv, result, carried); err != nil { + fmt.Printf("%s: the list of predefined keys it carried is not published: %v\n", result.ID, err) + } + } + if manifest.NamesPredefined() { + if _, err := catalogue.ParseManifest(kept.Manifest); err != nil { + return manifest, kept, fmt.Errorf("%s built %s (%s), recorded and not registered: judged against the "+ + "list of predefined keys the mesh publishes, %w", result.On, manifest.Module, short(result.Commit), err) + } + } // **A build whose source is unchanged is never a move** (novox/hq issue 280): a rebuild made from // what the build the mesh stands on was made from registers that build's artifacts at the new // commit, so no machine is sent a new digest for a source nobody changed — an image is not diff --git a/cmd/mesh-controller/check.go b/cmd/mesh-controller/check.go index 64b04429..cd7d57bc 100644 --- a/cmd/mesh-controller/check.go +++ b/cmd/mesh-controller/check.go @@ -47,6 +47,24 @@ func moduleCheckFor(paths []string, longestMachine int, out io.Writer) error { shelf := catalogue.Shelf{} faulted := map[string]bool{} failed := 0 + // The list of predefined keys at the root of the repository above each manifest (novox/hq ADR 0348 §7), read + // once per repository: a manifest naming a key is judged against the list its own tree holds. + lists := map[string]catalogue.PredefinedList{} + listOf := func(path string) (catalogue.PredefinedList, error) { + root := catalogue.RepositoryRoot(filepath.Dir(path)) + if root == "" { + return nil, nil + } + if list, read := lists[root]; read { + return list, nil + } + list, err := catalogue.ReadPredefined(root) + if err != nil { + return nil, err + } + lists[root] = list + return list, nil + } for _, path := range paths { raw, err := os.ReadFile(path) if err != nil { @@ -54,7 +72,13 @@ func moduleCheckFor(paths []string, longestMachine int, out io.Writer) error { failed++ continue } - m, err := catalogue.ParseManifest(raw) + list, err := listOf(path) + if err != nil { + fmt.Fprintf(out, "%s: %v\n", path, err) + failed++ + continue + } + m, err := catalogue.ParseManifestWith(raw, list) if err == nil { // Each template's text read from the tree beside the manifest, and judged as inline content is // (novox/hq ADR 0349 §2): a missing template is refused by name. diff --git a/cmd/mesh-controller/merge_gate.go b/cmd/mesh-controller/merge_gate.go index 78b5332a..b555e4e5 100644 --- a/cmd/mesh-controller/merge_gate.go +++ b/cmd/mesh-controller/merge_gate.go @@ -550,8 +550,17 @@ func shelfWithChange(base map[string]catalogue.Manifest, sources map[string]inve out[k] = m } var failures []string + // The list of predefined keys at the tree's root (novox/hq ADR 0348 §7): what the build of this tree judges each + // manifest against and registration publishes, so what the gate judges each against too. A list that is + // wrong is refused by name, and every manifest is then judged against none. + predefined, err := catalogue.ReadPredefined(tree) + if err != nil { + failures = append(failures, fmt.Sprintf("%s: the controller judging this (%s) refuses it — %s", + catalogue.PredefinedFile, version, oneLine(err.Error()))) + predefined = nil + } found := map[string]string{} // module → its directory in the tree - err := filepath.WalkDir(tree, func(path string, d fs.DirEntry, err error) error { + err = filepath.WalkDir(tree, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } @@ -575,7 +584,7 @@ func shelfWithChange(base map[string]catalogue.Manifest, sources map[string]inve if err != nil { return err } - m, err := catalogue.ParseManifest(raw) + m, err := catalogue.ParseManifestWith(raw, predefined) if err == nil { // Each template's text from the tree beside the manifest, as the build reads it back from the archive it // packs (novox/hq ADR 0349 §2): so a change to a template alone is a change to its module. @@ -610,7 +619,7 @@ func shelfWithChange(base map[string]catalogue.Manifest, sources map[string]inve // As registration reads a built manifest: strictly, again (takeIn). var raw []byte if raw, err = json.Marshal(resolved); err == nil { - resolved, err = catalogue.ParseManifest(raw) + resolved, err = catalogue.ParseManifestWith(raw, predefined) } } if err == nil { diff --git a/cmd/mesh-controller/stores.go b/cmd/mesh-controller/stores.go index aeea12c1..f2db129a 100644 --- a/cmd/mesh-controller/stores.go +++ b/cmd/mesh-controller/stores.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "os" "time" "github.com/novox/mesh-controller/internal/catalogue" @@ -124,6 +125,11 @@ func openInventory(ctx context.Context) (*inventory.Inventory, error) { if aliases, err := inv.Aliases(ctx); err == nil { catalogue.UseAliases(aliases) } + // And the list of predefined keys the mesh publishes (novox/hq ADR 0348 §7), which every manifest read again + // is judged against. A store a migration has not reached yet keeps none, and nothing names a key then. + if err := loadPredefined(ctx, inv); err != nil { + fmt.Fprintf(os.Stderr, "the list of predefined keys kept cannot be read: %v\n", err) + } return inv, nil } diff --git a/internal/builder/builder.go b/internal/builder/builder.go index c2c1cbf9..0729a351 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -108,6 +108,17 @@ type Result struct { // recipe and the package it compiles; for any other, nothing is said and the whole of what the build // sees stays its source, as before. Sources []BuildSource + + // Predefined is the list of predefined keys at the root of the repository at the commit built (novox/hq ADR + // 0348 §7), which the manifest was judged against; nil for a repository that holds none. + Predefined *Predefined +} + +// Predefined is the list of predefined keys a build read: its text, canonical (catalogue.PredefinedList), and +// the generation of the commit built — how many commits it stands on, which orders two builds of one trunk. +type Predefined struct { + Keys []byte + Generation int } // BuildSource is the build source a build read in one repository (novox/hq ADR 0267). @@ -261,11 +272,28 @@ func build(ctx context.Context, run Runner, publish Publisher, "%s has no %s at %s, so there is nothing saying what it is: %w", repository, ManifestName, describe(path), err) } - manifest, err := catalogue.ParseManifest(raw) + // **The list of predefined keys at the commit built** (novox/hq ADR 0348 §7): predefined.json at the + // repository's root, read strictly; a list that is wrong fails the build, and the manifest is judged against + // the list of its own tree, never against one the mesh published from another commit. + predefined, err := catalogue.ReadPredefined(tree) + if err != nil { + say("predefined", "INVALID: %v", err) + return Result{}, err + } + manifest, err := catalogue.ParseManifestWith(raw, predefined) if err != nil { say("manifest", "INVALID: %v", err) return Result{}, err } + var listed *Predefined + if predefined != nil { + text, err := predefined.Canonical() + if err != nil { + return Result{}, err + } + listed = &Predefined{Keys: text, Generation: generationOf(ctx, run, tree, commit)} + say("predefined", "%d key(s) at %s, generation %d", len(predefined), short(commit), listed.Generation) + } // What is judged only where a definition is written (novox/hq ADR 0326 §3, issue 574): a build is // where a changed definition is first read, so it is refused here before anything is made of it. // Judged with each template's text in its file, as written in the tree (novox/hq ADR 0349 §2): a template the @@ -291,6 +319,11 @@ func build(ctx context.Context, run Runner, publish Publisher, if src.tree, err = gitTree(ctx, run, tree, path); err != nil { src.notPinned("its tree could not be named: " + err.Error()) } + // A module falling back to a predefined key was judged against the list, so a change to the list is a change + // to it: the planner rebuilds it, and its build publishes the list (ADR 0348 §7). + if predefined != nil && manifest.NamesPredefined() { + src.rootHas(catalogue.PredefinedFile) + } // A build-time credential, written into the build context as .npmrc, but ONLY for a module that // asks for it: a `package` artifact (which publishes), or an image whose Dockerfile COPYs .npmrc. @@ -451,7 +484,23 @@ func build(ctx context.Context, run Runner, publish Publisher, return Result{Manifest: resolved, Commit: commit, Built: built, Against: against(within, manifest, stoodOn), Read: readBy(manifest), Source: fingerprint, Trunk: trunk, OnTrunk: onTrunk, Branches: branches, Sources: sources, - ReuseKey: key.Key, ReuseParts: key.Parts, NoKey: key.Why, Reused: reused, Why: builtWhy}, nil + ReuseKey: key.Key, ReuseParts: key.Parts, NoKey: key.Why, Reused: reused, Why: builtWhy, + Predefined: listed}, nil +} + +// generationOf is how many commits the commit built stands on, itself included: of two commits on one trunk the +// later has more, so the controller keeps the list of the newer one whatever order it hears them in (ADR 0348 +// §7). 0 when git could not say. +func generationOf(ctx context.Context, run Runner, tree, commit string) int { + out, err := run(ctx, tree, "git", "rev-list", "--count", commit) + if err != nil { + return 0 + } + n, err := strconv.Atoi(strings.TrimSpace(out)) + if err != nil || n < 0 { + return 0 + } + return n } // takeACheckBuild is the walk's look for a check build of its key (novox/hq ADR 0350 §2, §3): the key taken by diff --git a/internal/builder/builder_test.go b/internal/builder/builder_test.go index afe57572..4bfe447f 100644 --- a/internal/builder/builder_test.go +++ b/internal/builder/builder_test.go @@ -83,6 +83,8 @@ func (r *recorded) run(_ context.Context, dir, name string, args ...string) (str return r.tree + "\n", nil case name == "git" && len(args) > 0 && args[0] == "rev-parse": return "c0ffeec0ffeec0ffeec0ffeec0ffeec0ffeec0ff\n", nil + case name == "git" && len(args) > 1 && args[0] == "rev-list" && args[1] == "--count": + return "42\n", nil } return "", nil } diff --git a/internal/builder/source.go b/internal/builder/source.go index 7d9b9c0e..4d033a33 100644 --- a/internal/builder/source.go +++ b/internal/builder/source.go @@ -58,6 +58,9 @@ type sourceInputs struct { // whole when an artifact's is not known (novox/hq ADR 0267). own map[string]bool ownIsAll bool + // root is the module's build source in its own repository outside its directory, relative to the + // repository's root: the list of predefined keys a manifest naming one was judged against (ADR 0348 §7). + root map[string]bool // read is, per context (repository and ref), the build source read there; nil for one read whole. read map[string]map[string]bool readAs map[string]catalogue.ArtifactContext @@ -86,6 +89,9 @@ func (s *sourceInputs) copy() *sourceInputs { for k, v := range s.own { c.own[k] = v } + for k, v := range s.root { + c.root[k] = v + } for k, v := range s.read { if v == nil { c.read[k] = nil @@ -112,7 +118,7 @@ func (s *sourceInputs) contextOffHead() { func newSourceInputs(module string) *sourceInputs { return &sourceInputs{module: module, contexts: map[string]string{}, toolchains: map[string]string{}, - own: map[string]bool{}, read: map[string]map[string]bool{}, readAs: map[string]catalogue.ArtifactContext{}, + own: map[string]bool{}, root: map[string]bool{}, read: map[string]map[string]bool{}, readAs: map[string]catalogue.ArtifactContext{}, contextsAtHead: true} } @@ -126,6 +132,16 @@ func (s *sourceInputs) ownHas(entries ...string) { } } +// rootHas adds entries, relative to the repository's root, to the module's build source in its own repository. +func (s *sourceInputs) rootHas(entries ...string) { + if s == nil { + return + } + for _, e := range entries { + s.root[e] = true + } +} + // ownWhole says an artifact's build source in the module's own repository is not known: the module's // whole directory is its source, as it was before. func (s *sourceInputs) ownWhole() { @@ -178,6 +194,9 @@ func (s *sourceInputs) buildSources() []BuildSource { for e := range s.own { own = append(own, withPrefix(s.prefix, e)) } + for e := range s.root { + own = append(own, e) + } sort.Strings(own) out = append(out, BuildSource{Paths: compactSorted(own)}) } diff --git a/internal/catalogue/config.go b/internal/catalogue/config.go index 90e9ea71..6feaee70 100644 --- a/internal/catalogue/config.go +++ b/internal/catalogue/config.go @@ -61,18 +61,6 @@ const ( // accountFacts are what the mesh places for a module's account, never config values (ADR 0348 §11). var accountFacts = map[string]bool{"puid": true, "pgid": true} -// PredefinedKey is one key of the catalogue's list of predefined keys, as far as a module's value falling back -// to it is judged here: its type, and whether it is trusted (ADR 0348 §7, §8). -type PredefinedKey struct { - Type string - Trusted bool -} - -// predefinedKeys is the list of predefined keys, nil until it is present. The list (`predefined.json` at the -// catalogue's root) arrives with design 57's package K2 (issue 661); until then a value naming one is refused -// saying the list is not yet present. -var predefinedKeys map[string]PredefinedKey - // configForms is which of the two forms a manifest was written in, by the words of each it uses. type configForms struct { config, settings []string @@ -259,12 +247,13 @@ func ConfigProblems(m Manifest) []string { "(novox/hq ADR 0348 §4, ADR 0112)", word) } if d.Predefined != "" { - p, listed := predefinedKeys[d.Predefined] + p, listed := m.predefined[d.Predefined] switch { - case predefinedKeys == nil: - say("falls back to the predefined key %q, and the list of predefined keys (the catalogue's "+ - "predefined.json, design 57 package K2) is not yet present: no value can fall back to one "+ - "until it is; leave \"predefined\" out for now (novox/hq ADR 0348 §7)", d.Predefined) + case m.predefined == nil: + say("falls back to the predefined key %q, and the list of predefined keys (%s at the root of "+ + "the catalogue's repository) is not present where it is judged: a value falls back only to a "+ + "key on that list; judge it in the catalogue, or leave \"predefined\" out (novox/hq ADR 0348 §7)", + d.Predefined, PredefinedFile) case !listed: say("falls back to %q, which is not on the list of predefined keys: name a key on it, or leave "+ "\"predefined\" out (novox/hq ADR 0348 §7)", d.Predefined) diff --git a/internal/catalogue/config_test.go b/internal/catalogue/config_test.go index 1a2d8499..2cbaeea1 100644 --- a/internal/catalogue/config_test.go +++ b/internal/catalogue/config_test.go @@ -157,20 +157,20 @@ func TestConfigRefusesAnObjectOrListNotTrusted(t *testing.T) { } } -// withPredefined is the list of predefined keys as package K2 will give it, for the length of a test. -func withPredefined(t *testing.T, keys map[string]PredefinedKey) { +// withPredefined is the list of predefined keys the mesh published, for the length of a test. +func withPredefined(t *testing.T, keys PredefinedList) { t.Helper() - was := predefinedKeys - predefinedKeys = keys - t.Cleanup(func() { predefinedKeys = was }) + was := ReplacePublished(keys) + t.Cleanup(func() { ReplacePublished(was) }) } -// Rule 5: a predefined key not on the list, or of another type. Until the list is present (package K2), a value -// naming one is refused saying so. +// Rule 5: a predefined key not on the list, or of another type. Where no list is present, a value naming one is +// refused saying so. func TestConfigRefusesAPredefinedKeyNotOnTheListOrOfAnotherType(t *testing.T) { + withPredefined(t, nil) m := inConfigForm() declaration(m, "font-size")["predefined"] = "font-size" - refused(t, m, `"font-size"`, "the list of predefined keys", "not yet present") + refused(t, m, `"font-size"`, "the list of predefined keys", "is not present") withPredefined(t, map[string]PredefinedKey{"font-size": {Type: "number"}, "accent": {Type: "string"}}) if _, err := parseRaw(t, m); err != nil { @@ -186,9 +186,10 @@ func TestConfigRefusesAPredefinedKeyNotOnTheListOrOfAnotherType(t *testing.T) { // Rule 6: an untrusted value falling back to a trusted predefined key. func TestConfigRefusesAnUntrustedValueFallingBackToATrustedKey(t *testing.T) { + withPredefined(t, nil) m := inConfigForm() declaration(m, "theme")["predefined"] = "theme" - refused(t, m, `"theme"`, "not yet present") + refused(t, m, `"theme"`, "is not present") withPredefined(t, map[string]PredefinedKey{"theme": {Type: "string", Trusted: true}}) refused(t, m, `"theme"`, "trusted", "is not") diff --git a/internal/catalogue/manifest.go b/internal/catalogue/manifest.go index 49866bdd..480ce761 100644 --- a/internal/catalogue/manifest.go +++ b/internal/catalogue/manifest.go @@ -541,6 +541,10 @@ type Manifest struct { // machine's declaration by name until the controller is updated (LeftOut). unknown string + // predefined is the list of predefined keys it was judged against (predefined.go, novox/hq ADR 0348 §7): + // read again — its templates filled from the tree — it is judged against the same list. + predefined PredefinedList + // bestEffort marks the view of a left-out module that only its backup lines are made from // (backupView, novox/hq ADR 0262): a line of it that cannot be placed is said in the plan's list of // what could not be placed, never an error that would cost the whole machine its declaration. @@ -1600,6 +1604,17 @@ func SecretLocal(to, local string) string { func (m Manifest) UnknownField() string { return m.unknown } func ParseManifest(raw []byte) (Manifest, error) { + return parseManifest(raw, PublishedPredefined()) +} + +// ParseManifestWith is ParseManifest judging every value that falls back to a predefined key against the list +// given — the one read from the tree the manifest is in (predefined.go, novox/hq ADR 0348 §7) — where +// ParseManifest judges against the list the mesh published. Nil is no list: a value naming a key is refused. +func ParseManifestWith(raw []byte, predefined PredefinedList) (Manifest, error) { + return parseManifest(raw, predefined) +} + +func parseManifest(raw []byte, predefined PredefinedList) (Manifest, error) { var m Manifest // Strictly. **An unknown key is refused**, which is the discipline the host's declaration // parser has and manifests lacked (novox/hq 04-ISSUES/003): a `scope:` key survived in five @@ -1627,6 +1642,7 @@ func ParseManifest(raw []byte) (Manifest, error) { } return Manifest{}, fmt.Errorf("this is not a module manifest: %w", err) } + m.predefined = predefined var problems []string if !name.MatchString(m.Module) { diff --git a/internal/catalogue/template.go b/internal/catalogue/template.go index 2a363bf2..d4aa104a 100644 --- a/internal/catalogue/template.go +++ b/internal/catalogue/template.go @@ -312,7 +312,7 @@ func WithTemplatesFrom(m Manifest, dir string) (Manifest, error) { if err != nil { return Manifest{}, err } - return ParseManifest(raw) + return parseManifest(raw, m.predefined) } // HeldTemplateProblem is why a manifest cannot be held by the mesh because it still names a template, or "": the diff --git a/internal/link/build.go b/internal/link/build.go index 6eb16588..a8d56c18 100644 --- a/internal/link/build.go +++ b/internal/link/build.go @@ -334,6 +334,12 @@ type BuildResult struct { ReusedFrom *ReusedFrom `json:"reused-from,omitempty"` BuiltWhy string `json:"built-why,omitempty"` + // Predefined is the list of predefined keys at the root of the repository at the commit built (novox/hq ADR + // 0348 §7), which the manifest was judged against: the controller publishes the catalogue's from a trunk + // build, the newest by Generation winning. Absent for a repository holding none, and from a builder that + // predates the list. + Predefined *PredefinedList `json:"predefined,omitempty"` + // Source is the request's, echoed: the seat form of the repository, for whoever registers. Source *SourceOnSeat `json:"source,omitempty"` @@ -346,6 +352,14 @@ type BuildResult struct { Checked *CheckRequest `json:"check,omitempty"` } +// PredefinedList is the list of predefined keys a build read (novox/hq ADR 0348 §7): Keys its canonical text, a +// JSON object of keys, and Generation how many commits the commit built stands on — of two commits on one trunk +// the later has more, which is what orders two builds' lists; 0 when the builder could not say. +type PredefinedList struct { + Keys json.RawMessage `json:"keys"` + Generation int `json:"generation,omitempty"` +} + // ReadRepository is a repository a build read source from besides the module's own, at the branch, // tag or commit it read. Spelled here as well as in the catalogue and the inventory, for the reason // MadeArtifact is: one direction of dependency. diff --git a/testdata/store-tests b/testdata/store-tests index d3af9659..344072f4 100644 --- a/testdata/store-tests +++ b/testdata/store-tests @@ -64,3 +64,6 @@ ./internal/inventory TestPlacementMovesOutOfTheSettingsLayers ./internal/inventory TestASettingRoutesPlacementBesideTheLayer ./cmd/mesh-controller TestADeclarationIsTheSameAfterPlacementMoves +./internal/inventory TestANewerBuildsListReplacesTheKeptOneAndAnOlderOneHeardLaterDoesNot +./cmd/mesh-controller TestTheMergeGateJudgesEachManifestAgainstTheListOfTheTree +./cmd/mesh-controller TestRegistrationPublishesTheNewestTrunkListAndJudgesEveryModuleAgainstIt