From edf7b3c0bf3a4e8debedf7b8e8568c704293044d Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 1 Nov 2026 03:12:09 +0100 Subject: [PATCH] Answer review round 1: fresh is only what a take-in newly wrote, a merge keeps the verdict, the check compares with the trunk (ADR 0578, 0579) Issue 1585, R1: fresh holds the digests a take-in newly wrote (a new module, a changed manifest); a registered or taken-in module is judged too; a start pass that fails is judged again at the next message heard, no timer. R2: a module's verdict, problems and warnings are carried while its digest stands and are no part of the compare. R3: the checker judges the trunk beside the change and fails on every problem the change case adds, an untouched manifest's included. R4: the judge reads the indexer's kept predefined keys, or the catalogue member's when it changes them. A log line names a package no held module publishes. Every module pins mesh-sdk 62cddf4. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL --- modules/mesh-delivery-assigner/go.mod | 2 +- modules/mesh-delivery-assigner/go.sum | 2 + modules/mesh-delivery-builder/go.mod | 2 +- modules/mesh-delivery-builder/go.sum | 2 + .../cmd/mesh-delivery-checker/judge.go | 30 +++- .../cmd/mesh-delivery-checker/main.go | 2 + modules/mesh-delivery-checker/go.mod | 2 +- modules/mesh-delivery-checker/go.sum | 2 + .../internal/check/examiner_test.go | 22 ++- .../internal/check/judge.go | 4 + .../internal/check/manifests_judge.go | 130 ++++++++++++++---- .../mesh-delivery-checker/manifest_test.go | 2 +- modules/mesh-delivery-checker/module.json | 1 + modules/mesh-delivery-forge-bridge/go.mod | 2 +- modules/mesh-delivery-forge-bridge/go.sum | 2 + .../cmd/mesh-delivery-indexer/main.go | 6 +- modules/mesh-delivery-indexer/go.mod | 2 +- modules/mesh-delivery-indexer/go.sum | 2 + .../internal/index/candidates.go | 36 +++-- .../internal/index/order.go | 10 ++ .../internal/index/registered.go | 7 +- .../internal/index/source.go | 1 + .../internal/index/toolchains.go | 26 +++- .../internal/index/verdicts.go | 20 +++ .../internal/index/verdicts_test.go | 15 ++ modules/mesh-delivery-teller/go.mod | 2 +- modules/mesh-delivery-teller/go.sum | 2 + modules/mesh-delivery-view/go.mod | 2 +- modules/mesh-delivery-view/go.sum | 2 + modules/mesh-delivery-watchman/go.mod | 2 +- modules/mesh-delivery-watchman/go.sum | 2 + modules/mesh-grants/go.mod | 2 +- modules/mesh-grants/go.sum | 2 + 33 files changed, 277 insertions(+), 71 deletions(-) diff --git a/modules/mesh-delivery-assigner/go.mod b/modules/mesh-delivery-assigner/go.mod index 18031e9..b3749c2 100644 --- a/modules/mesh-delivery-assigner/go.mod +++ b/modules/mesh-delivery-assigner/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-assigner go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-assigner/go.sum b/modules/mesh-delivery-assigner/go.sum index c73ceee..eaa1d08 100644 --- a/modules/mesh-delivery-assigner/go.sum +++ b/modules/mesh-delivery-assigner/go.sum @@ -10,6 +10,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-delivery-builder/go.mod b/modules/mesh-delivery-builder/go.mod index fa74794..bc8a565 100644 --- a/modules/mesh-delivery-builder/go.mod +++ b/modules/mesh-delivery-builder/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-builder go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-builder/go.sum b/modules/mesh-delivery-builder/go.sum index c73ceee..eaa1d08 100644 --- a/modules/mesh-delivery-builder/go.sum +++ b/modules/mesh-delivery-builder/go.sum @@ -10,6 +10,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/judge.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/judge.go index f5243b6..bee45f7 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/judge.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/judge.go @@ -2,6 +2,8 @@ package main import ( "context" + "encoding/json" + "errors" "fmt" "sort" "strings" @@ -31,7 +33,31 @@ type judgeDeps struct { compositions check.CompositionReader // nodes names every node with a record; nil hands no names. nodes *nodeNames - logf func(format string, args ...any) + // predefined is the indexer's kept list of predefined keys; nil reads none. + predefined check.PredefinedList + logf func(format string, args ...any) +} + +// keptPredefined reads the indexer's key predefined.list (delivery.PredefinedKept), bound once its state appears. +type keptPredefined struct{ b *process.AwaitedBucket } + +func (k keptPredefined) PredefinedList(ctx context.Context) (json.RawMessage, error) { + kv := k.b.KV() + if kv == nil { + return nil, process.ErrNotYet + } + e, err := kv.Get(ctx, delivery.PredefinedListKey) + if errors.Is(err, jetstream.ErrKeyNotFound) { + return nil, nil + } + if err != nil { + return nil, err + } + var kept delivery.PredefinedKept + if err := json.Unmarshal(e.Value(), &kept); err != nil { + return nil, err + } + return kept.List, nil } // nodeNames reads every node's name from the node-engine's have, one key `.node` per node with a record (novox/hq @@ -69,7 +95,7 @@ func newJudge(d judgeDeps) check.Judge { nodes = d.nodes } return &check.Examiner{Nodes: nodes, Compositions: d.compositions, Asks: d.asks, Toolchains: d.tools, - Kept: check.Kept{Store: d.kept}, Previews: d.previews, Modules: d.repos, Packages: d.repos, Published: d.repos, Forge: d.forge, Runs: d.repos, Builds: d.builds, Announce: d.announce, Trunks: d.trunks, Manifests: d.repos, Logf: d.logf} + Kept: check.Kept{Store: d.kept}, Previews: d.previews, Modules: d.repos, Packages: d.repos, Published: d.repos, Forge: d.forge, Runs: d.repos, Builds: d.builds, Announce: d.announce, Trunks: d.trunks, Manifests: d.repos, Predefined: d.predefined, Logf: d.logf} } // manifestKV reads a manifest by its digest. diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go index 732bdfb..ded10d8 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go @@ -202,6 +202,8 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro repos: repos, builds: builds, forge: forgeReads{call: caller.Call}, logf: logger.Printf, announce: announce, compositions: compositions, nodes: openNodeNames(ctx, js, logger.Printf), + predefined: keptPredefined{process.AwaitBucket(ctx, js, conn, delivery.IndexerModule+"_"+delivery.IndexerPredefinedState, + "a change's manifests are judged with no predefined keys, each fallback as its value declares it", logger.Printf)}, }) ch.Announce = announce tryer.Judge = ch.Judge diff --git a/modules/mesh-delivery-checker/go.mod b/modules/mesh-delivery-checker/go.mod index 0340b63..eabcc0c 100644 --- a/modules/mesh-delivery-checker/go.mod +++ b/modules/mesh-delivery-checker/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-checker go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-checker/go.sum b/modules/mesh-delivery-checker/go.sum index c73ceee..eaa1d08 100644 --- a/modules/mesh-delivery-checker/go.sum +++ b/modules/mesh-delivery-checker/go.sum @@ -10,6 +10,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-delivery-checker/internal/check/examiner_test.go b/modules/mesh-delivery-checker/internal/check/examiner_test.go index 18df15f..55020ce 100644 --- a/modules/mesh-delivery-checker/internal/check/examiner_test.go +++ b/modules/mesh-delivery-checker/internal/check/examiner_test.go @@ -930,6 +930,13 @@ func TestASecondCheckOfTheSameHeadsKeepsWhatTheFirstPassed(t *testing.T) { } } +// keptList is the indexer's kept predefined list as a test gives it. +type keptList string + +func (k keptList) PredefinedList(context.Context) (json.RawMessage, error) { + return json.RawMessage(k), nil +} + // fixedDigests are the indexer's manifest digests as a test gives them. type fixedDigests map[string]string @@ -944,16 +951,20 @@ func TestTheIndexersJudgeJudgesTheChangesManifests(t *testing.T) { name string refused string fails string - // untouched is the judge's refusal of mail, which the change leaves as it is: a warning, never a failure (ADR 0579 §2) - untouched string + // untouched is the judge's refusal of mail, which the change leaves as it is, and trunkMail its refusal on the trunk: + // a problem the trunk had is a warning, one the change causes fails (ADR 0579 §2) + untouched, trunkMail string }{ {name: "accepted"}, - {name: "an untouched manifest's problem", untouched: "mail says no credential form"}, + {name: "an untouched manifest's problem the trunk had", untouched: "mail says no credential form", trunkMail: "mail says no credential form"}, + {name: "a problem the change causes in an untouched manifest", untouched: "mail reads a state web no longer keeps", + fails: "refuses the manifest of mail: mail reads a state web no longer keeps"}, {name: "refused", refused: "web requires \"amqp\"\nweb has no id", fails: "the indexer's judge at mesh-delivery's trunk " + strings.Repeat("d", 40) + ` refuses the manifest of web: web requires "amqp"; web has no id`}, } { b := newBench(t) b.ex.Manifests = fixedDigests{"mail": strings.Repeat("a", 64), "web": strings.Repeat("b", 64)} b.ex.Trunks = fixedTrunks{heads: map[string]string{DeliveryRepository: trunk}} + b.ex.Predefined = keptList(`{"font-size": {"type": "number", "description": "the size"}}`) var judged []delivery.ComposeJob answer := b.asks.Answer b.asks.Answer = func(a delivery.StepAsk) delivery.Step { @@ -963,7 +974,8 @@ func TestTheIndexersJudgeJudgesTheChangesManifests(t *testing.T) { judged = append(judged, *a.Compose) read := composer.ManifestRead{Accepted: row.refused == "", Refused: row.refused} verdict, _ := json.Marshal(composer.Verdict{V: composer.Version, SDK: "exe:1", Cases: []composer.CaseVerdict{{Name: JudgeCase, - Manifests: map[string]composer.ManifestRead{"web": read, "mail": {Accepted: row.untouched == "", Refused: row.untouched}}}}}) + Manifests: map[string]composer.ManifestRead{"web": read, "mail": {Accepted: row.untouched == "", Refused: row.untouched}}}, + {Name: JudgeTrunk, Manifests: map[string]composer.ManifestRead{"web": {Accepted: true}, "mail": {Accepted: row.trunkMail == "", Refused: row.trunkMail}}}}}) result, _ := json.Marshal(delivery.ComposeAnswer{Key: "k", Verdict: verdict}) return delivery.Step{ID: a.ID, State: delivery.StepPassed, Result: result} } @@ -977,6 +989,8 @@ func TestTheIndexersJudgeJudgesTheChangesManifests(t *testing.T) { } var req composer.Request if err := json.Unmarshal(judged[0].Request, &req); err != nil || req.Cases[0].Manifests["web"].Digest != web || + req.Cases[1].Name != JudgeTrunk || req.Cases[1].Manifests["web"].Digest != strings.Repeat("b", 64) || + req.Predefined["font-size"].Type != "number" || req.Cases[0].Manifests["mail"].Digest != strings.Repeat("a", 64) { t.Fatalf("%s: the request %+v: %v", row.name, req, err) } diff --git a/modules/mesh-delivery-checker/internal/check/judge.go b/modules/mesh-delivery-checker/internal/check/judge.go index 7478a7a..1c2226f 100644 --- a/modules/mesh-delivery-checker/internal/check/judge.go +++ b/modules/mesh-delivery-checker/internal/check/judge.go @@ -114,6 +114,8 @@ type Examiner struct { // Manifests are the indexer's manifest digests by module: the catalogue a change's manifests are judged in by the // indexer's judge (manifests_judge.go, novox/hq ADR 0578 §2); nil judges no manifest. Manifests TrunkManifests + // Predefined reads the catalogue's predefined keys the indexer keeps (manifests_judge.go); nil reads none. + Predefined PredefinedList // Compositions answers the assigner's composition check of a check by the check's key (delivery.CompositionKey), // waiting until the assigner wrote a verdict that does not wait, or ctx ends (ADR 0528 clause 5, issue 1297); nil // leaves the composition not run. @@ -225,6 +227,8 @@ type state struct { // judgeHead is the group's mesh-delivery member head when its merge changes the judge (ADR 0578 §2); empty judges // with mesh-delivery's trunk. judgeHead string + // predefinedHead is the catalogue member's head when its merge changes predefined.json: the list is read there. + predefinedHead string } // Judge runs the case (it is the Judge of a Checker). A failure of the change, of a merge or of a step is a verdict; an error is only a case that cannot diff --git a/modules/mesh-delivery-checker/internal/check/manifests_judge.go b/modules/mesh-delivery-checker/internal/check/manifests_judge.go index d43301d..c3114fd 100644 --- a/modules/mesh-delivery-checker/internal/check/manifests_judge.go +++ b/modules/mesh-delivery-checker/internal/check/manifests_judge.go @@ -9,6 +9,7 @@ import ( "strings" "git.novox.be/novox/mesh-sdk/go/buildstep" + "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/composer" "git.novox.be/novox/mesh-sdk/go/delivery" ) @@ -25,8 +26,10 @@ const ( DeliveryRepository = "novox/mesh-delivery" JudgeDir = "modules/mesh-delivery-indexer" JudgePackage = "mesh-delivery-indexer/cmd/mesh-judge" - // JudgeCase is the one case a judge request holds: the catalogue as the change leaves it. - JudgeCase = "change" + // JudgeCase is the case of a judge request holding the catalogue as the change leaves it, and JudgeTrunk the one + // holding it as the trunk holds it: a problem the change case has and the trunk case lacks is the change's (ADR 0579 §2). + JudgeCase = "change" + JudgeTrunk = "trunk" // judgeBetween is how the judge names the problems between manifests that no one module's removal ends. judgeBetween = "(between manifests)" ) @@ -57,14 +60,21 @@ type TrunkManifests interface { ManifestDigests() map[string]string } +// PredefinedList is the catalogue's list of predefined keys the indexer keeps (its state predefined, key list), the one a +// config value falling back to a key is judged against (ADR 0348 §7); nil when it keeps none. +type PredefinedList interface { + PredefinedList(ctx context.Context) (json.RawMessage, error) +} + // judgeRequest is the judge's request over the catalogue as the case leaves it: every trunk manifest by its digest, each // one a pull request touches replaced by the digest its candidate names; touched are the modules whose manifest the // change adds or changes (a digest the trunk does not hold for it), sorted (novox/hq ADR 0579 §2). func judgeRequest(trunk map[string]string, pulls []Pull) (req composer.Request, touched []string) { - manifests := map[string]composer.Manifest{} + manifests, before := map[string]composer.Manifest{}, map[string]composer.Manifest{} for module, digest := range trunk { if digest != "" { manifests[module] = composer.Manifest{Digest: digest} + before[module] = composer.Manifest{Digest: digest} } } seen := map[string]bool{} @@ -79,8 +89,8 @@ func judgeRequest(trunk map[string]string, pulls []Pull) (req composer.Request, } } } - return composer.Request{V: composer.Version, Cases: []composer.Case{{Name: JudgeCase, Manifests: manifests}}}, - slices.Sorted(maps.Keys(seen)) + return composer.Request{V: composer.Version, Cases: []composer.Case{{Name: JudgeCase, Manifests: manifests}, + {Name: JudgeTrunk, Manifests: before}}}, slices.Sorted(maps.Keys(seen)) } // judgeManifests runs the indexer's judge over the case's manifests and fails the check for each touched manifest it @@ -104,6 +114,12 @@ func (st *state) judgeManifests(ctx context.Context) *delivery.Verdict { if commit == "" { return fail(goModule, "the change's manifests cannot be judged: the trunk head of "+DeliveryRepository+" is not known") } + // The list of predefined keys the manifests are judged against (ADR 0578 §2): the catalogue member's predefined.json + // when the change alters it, else the one the indexer keeps. + if list, from := st.predefinedList(ctx); list != nil { + req.Predefined = list + st.c.note("the manifests are judged against the predefined keys of " + from) + } raw, err := json.Marshal(req) if err != nil { return fail(goModule, err.Error()) @@ -121,32 +137,45 @@ func (st *state) judgeManifests(ctx context.Context) *delivery.Verdict { if err != nil { return fail(goModule, "the judge of the change's manifests: "+err.Error()) } - cv, why := judgeAnswer(step) + cv, trunkCase, why := judgeAnswer(step) if why != "" { return fail(goModule, fmt.Sprintf("the indexer's judge at %s did not judge the change's manifests: %s", at, why)) } - // Only a new or changed manifest is refused (ADR 0579 §2); one the change leaves as it is is named as a warning, never - // a failure: it is not refused for a rule it already broke. + // A new or changed manifest is judged in full; of one the change leaves as it is, a problem the trunk already had is a + // warning (it is not refused for a rule it already broke), and a problem the change causes in it, which the trunk case + // lacks, fails the check as the change's (ADR 0579 §2). var refused, warned []string for _, module := range slices.Sorted(maps.Keys(cv.Manifests)) { r := cv.Manifests[module] - if r.Accepted || module == judgeBetween { + if r.Accepted || r.Refused == "" { continue } - words := strings.ReplaceAll(r.Refused, "\n", "; ") - if slices.Contains(touched, module) { - refused = append(refused, fmt.Sprintf("the indexer's judge at %s refuses the manifest of %s: %s", at, module, words)) - } else { - warned = append(warned, module+": "+words) + had := map[string]bool{} + for _, p := range strings.Split(trunkCase.Manifests[module].Refused, "\n") { + had[p] = true + } + var caused, old []string + for _, p := range strings.Split(r.Refused, "\n") { + if slices.Contains(touched, module) || !had[p] { + caused = append(caused, p) + } else { + old = append(old, p) + } + } + if len(caused) > 0 { + what := "the manifest of " + module + if module == judgeBetween { + what = "the catalogue" + } + refused = append(refused, fmt.Sprintf("the indexer's judge at %s refuses %s: %s", at, what, strings.Join(caused, "; "))) + } + if len(old) > 0 { + warned = append(warned, module+": "+strings.Join(old, "; ")) } } if len(warned) > 0 { st.c.note("warnings of the indexer's judge on manifests the change leaves as they are (ADR 0579 §2): " + strings.Join(warned, " | ")) } - if between := cv.Manifests[judgeBetween]; between.Refused != "" { - st.c.note("the indexer's judge names problems between manifests that no one module's removal ends: " + - strings.ReplaceAll(between.Refused, "\n", "; ")) - } if len(refused) > 0 { return fail(goModule, strings.Join(refused, "\n")) } @@ -154,30 +183,77 @@ func (st *state) judgeManifests(ctx context.Context) *delivery.Verdict { return nil } -// judgeAnswer reads the judge's case from its step, or why it cannot. -func judgeAnswer(step delivery.Step) (composer.CaseVerdict, string) { +// judgeAnswer reads the judge's change and trunk cases from its step, or why it cannot. +func judgeAnswer(step delivery.Step) (change, trunk composer.CaseVerdict, why string) { if step.State != delivery.StepPassed { - return composer.CaseVerdict{}, fmt.Sprintf("its step %s: %s", step.State, step.Reason) + return change, trunk, fmt.Sprintf("its step %s: %s", step.State, step.Reason) } var a delivery.ComposeAnswer if err := json.Unmarshal(step.Result, &a); err != nil { - return composer.CaseVerdict{}, "its step's answer cannot be read: " + err.Error() + return change, trunk, "its step's answer cannot be read: " + err.Error() } var v composer.Verdict if err := json.Unmarshal(a.Verdict, &v); err != nil || v.V != composer.Version { - return composer.CaseVerdict{}, fmt.Sprintf("its verdict cannot be read (version %d): %v", v.V, err) + return change, trunk, fmt.Sprintf("its verdict cannot be read (version %d): %v", v.V, err) } + found := 0 for _, c := range v.Cases { - if c.Name == JudgeCase { - return c, "" + switch c.Name { + case JudgeCase: + change, found = c, found+1 + case JudgeTrunk: + trunk, found = c, found+1 } } - return composer.CaseVerdict{}, "its verdict holds no case " + JudgeCase + if found != 2 { + return change, trunk, "its verdict does not hold both the cases " + JudgeCase + " and " + JudgeTrunk + } + return change, trunk, "" } -// judgedByMember notes the group's mesh-delivery member head as the judge's when its merge changes the judge. +// predefinedList is the list of predefined keys a case's manifests are judged against, and where it comes from: the +// catalogue member's predefined.json at its head when its merge changes it, else the one the indexer keeps; nil when +// neither can be read, said, and the judge then takes each fallback as its value declares it. +func (st *state) predefinedList(ctx context.Context) (catalogue.PredefinedList, string) { + read := func(raw []byte, from string) (catalogue.PredefinedList, string) { + list, err := catalogue.ParsePredefined(raw) + if err != nil { + st.c.note("the predefined keys of " + from + " cannot be read, so fallbacks are judged as their values declare them: " + err.Error()) + return nil, "" + } + return list, from + } + if st.predefinedHead != "" && st.j.Forge != nil { + text, err := st.j.Forge.File(ctx, catalogueRepository, catalogue.PredefinedFile, st.predefinedHead) + if err == nil { + return read([]byte(text), "the catalogue member's head "+st.predefinedHead) + } + st.c.note("the catalogue member's predefined.json cannot be read: " + err.Error()) + } + if st.j.Predefined == nil { + return nil, "" + } + raw, err := st.j.Predefined.PredefinedList(ctx) + if err != nil || raw == nil { + if err != nil { + st.c.note("the indexer's predefined keys cannot be read: " + err.Error()) + } + return nil, "" + } + return read(raw, "the indexer's kept list") +} + +// judgedByMember notes the group's mesh-delivery member head as the judge's when its merge changes the judge, and the +// catalogue member's head as where the predefined keys are read when its merge changes predefined.json. func (st *state) judgedByMember(p Pull, merged []Fetched) { if changesJudge(p.Repository, merged) { st.judgeHead = p.Head } + if p.Repository == catalogueRepository { + for _, f := range merged { + if slices.Contains(f.Changed, catalogue.PredefinedFile) { + st.predefinedHead = p.Head + } + } + } } diff --git a/modules/mesh-delivery-checker/manifest_test.go b/modules/mesh-delivery-checker/manifest_test.go index 218d358..3e6ac73 100644 --- a/modules/mesh-delivery-checker/manifest_test.go +++ b/modules/mesh-delivery-checker/manifest_test.go @@ -103,7 +103,7 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1 } if want := []string{"mesh-build-go.steps", "mesh-build-go.toolchains", "mesh-build-node.steps", "mesh-build-node.toolchains", "mesh-delivery-assigner.compositions", "mesh-delivery-assigner.placement", "mesh-delivery-builder.builds", "mesh-delivery-gate.source", "mesh-delivery-gate.turns", "mesh-delivery-indexer.candidates", - "mesh-delivery-indexer.manifests", "mesh-delivery-indexer.modules", "mesh-delivery-watchman.presence", "node-engine.have"}; !reflect.DeepEqual(sorted(m.Reads), want) { + "mesh-delivery-indexer.manifests", "mesh-delivery-indexer.modules", "mesh-delivery-indexer.predefined", "mesh-delivery-watchman.presence", "node-engine.have"}; !reflect.DeepEqual(sorted(m.Reads), want) { t.Errorf("reads = %v, want %v", m.Reads, want) } // 1887/A5b: the checker reads nothing of the controller's plans-building (ADR 0483 superseded by ADR 0525 §7), calls no diff --git a/modules/mesh-delivery-checker/module.json b/modules/mesh-delivery-checker/module.json index f020663..a8adc46 100644 --- a/modules/mesh-delivery-checker/module.json +++ b/modules/mesh-delivery-checker/module.json @@ -42,6 +42,7 @@ "mesh-delivery-indexer.candidates", "mesh-delivery-indexer.manifests", "mesh-delivery-indexer.modules", + "mesh-delivery-indexer.predefined", "mesh-delivery-watchman.presence", "node-engine.have" ], diff --git a/modules/mesh-delivery-forge-bridge/go.mod b/modules/mesh-delivery-forge-bridge/go.mod index c13cd72..1babc4a 100644 --- a/modules/mesh-delivery-forge-bridge/go.mod +++ b/modules/mesh-delivery-forge-bridge/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-forge-bridge go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-forge-bridge/go.sum b/modules/mesh-delivery-forge-bridge/go.sum index 146b01e..3b19944 100644 --- a/modules/mesh-delivery-forge-bridge/go.sum +++ b/modules/mesh-delivery-forge-bridge/go.sum @@ -6,6 +6,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go index 3159643..b0d9a8a 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go @@ -391,9 +391,11 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro // Every manifest judged at the start, so a judge that changed with this program judges every one again (ADR 0578 // §1), once the verdicts state is there; each merge judges them again (index.ApplyMessage). verdicts.Ready(func(jetstream.KeyValue) { - j, err := ix.JudgeAll(ctx, nil) + // A pass that fails (a read the bus did not answer) is judged again at the next message or source revision the + // indexer hears (index.kickJudge): no timer. + j, err := ix.JudgeAtStart(ctx) if err != nil { - logger.Printf("judging the manifests at start: %v", err) + logger.Printf("judging the manifests at start: %v; judged again at the next message heard", err) return } logger.Printf("judged %d manifests at start: %d verdicts written, %d with problems, of which outgrown and kept accepted: %v", diff --git a/modules/mesh-delivery-indexer/go.mod b/modules/mesh-delivery-indexer/go.mod index 1f214cc..872e6ec 100644 --- a/modules/mesh-delivery-indexer/go.mod +++ b/modules/mesh-delivery-indexer/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-indexer go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-indexer/go.sum b/modules/mesh-delivery-indexer/go.sum index c73ceee..eaa1d08 100644 --- a/modules/mesh-delivery-indexer/go.sum +++ b/modules/mesh-delivery-indexer/go.sum @@ -10,6 +10,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-delivery-indexer/internal/index/candidates.go b/modules/mesh-delivery-indexer/internal/index/candidates.go index a2ea532..b0b2025 100644 --- a/modules/mesh-delivery-indexer/internal/index/candidates.go +++ b/modules/mesh-delivery-indexer/internal/index/candidates.go @@ -78,19 +78,20 @@ type Indexer struct { // closed message the indexer missed heals at the next one (issue 1126); nil asks none. ReadOpen func(ctx context.Context) ([]delivery.OpenPull, error) - mu sync.Mutex // guards source, toolchains and previews, which the watches and the forge messages all reach - source sourceNow - previews map[string]delivery.Preview // by previews key (previews.go) - toolchains map[string]map[string]delivery.Toolchain // language, then node (toolchains.go) - tried map[string]bool // repository, commit and directory a registered module's manifest was tried at (registered.go) - unread map[string]bool // registered modules whose last read the forge could not answer, read again by RetryUnread - retrying atomic.Bool // a RetryUnread runs in the background (kickRetry) - retryAgain atomic.Bool // an event asked for one more RetryUnread after the running one - retryWG sync.WaitGroup // the background RetryUnread, for tests to wait on - recompute sync.Mutex // one Recompute at a time - untaken map[string]string // repository to the latest merge applied while the taken state was missing (taken.go) - filling atomic.Bool // a FillTaken runs in the background (KickFill) - fillAgain atomic.Bool // a kick arrived while it ran + mu sync.Mutex // guards source, toolchains and previews, which the watches and the forge messages all reach + source sourceNow + previews map[string]delivery.Preview // by previews key (previews.go) + toolchains map[string]map[string]delivery.Toolchain // language, then node (toolchains.go) + tried map[string]bool // repository, commit and directory a registered module's manifest was tried at (registered.go) + unread map[string]bool // registered modules whose last read the forge could not answer, read again by RetryUnread + retrying atomic.Bool // a RetryUnread runs in the background (kickRetry) + retryAgain atomic.Bool // an event asked for one more RetryUnread after the running one + retryWG sync.WaitGroup // the background RetryUnread, for tests to wait on + recompute sync.Mutex // one Recompute at a time + untaken map[string]string // repository to the latest merge applied while the taken state was missing (taken.go) + filling atomic.Bool // a FillTaken runs in the background (KickFill) + fillAgain atomic.Bool // a kick arrived while it ran + judgePending atomic.Bool // the start's judge pass failed: judged again at the next message (verdicts.go) } func (ix *Indexer) logf(format string, args ...any) { @@ -111,6 +112,7 @@ func (ix *Indexer) Apply(ctx context.Context, f delivery.Forge) error { // ApplyMessage is Apply, and answers which modules a merged message wrote and kept (for the indexer's heard state). Each // applied merged message is said in one line: its repository, commit and pull request, and the modules written. func (ix *Indexer) ApplyMessage(ctx context.Context, f delivery.Forge) (Applied, error) { + ix.kickJudge(ctx) var applied Applied if f.Kind == delivery.ForgeOpen { return applied, ix.applyOpen(ctx, f) @@ -138,12 +140,8 @@ func (ix *Indexer) ApplyMessage(ctx context.Context, f delivery.Forge) (Applied, return applied, err } // Every manifest judged again: a rule between manifests can turn on the one this merge moved (ADR 0578 §1). - // The merge's own manifests are fresh: one with no verdict yet is judged in full (ADR 0579 §1). - fresh := map[string]bool{} - for _, raw := range f.Manifests { - fresh[delivery.ManifestDigest([]byte(raw))] = true - } - if _, err := ix.JudgeAll(ctx, fresh); err != nil { + // What this take-in newly wrote is fresh: judged in full (ADR 0579 §1). + if _, err := ix.JudgeAll(ctx, w.fresh); err != nil { ix.logf("judge: the manifests are not judged after %s#%d: %v", f.Repository, f.Number, err) } } diff --git a/modules/mesh-delivery-indexer/internal/index/order.go b/modules/mesh-delivery-indexer/internal/index/order.go index 5aec9fe..5fac94d 100644 --- a/modules/mesh-delivery-indexer/internal/index/order.go +++ b/modules/mesh-delivery-indexer/internal/index/order.go @@ -34,6 +34,16 @@ type mergeWrite struct { late string // the taken merge this one is older than, or "" asked string // the taken merge the order was asked against, or "" when none was asked written []string + // fresh are the manifest digests this take-in newly wrote: a new module's, or a changed one's (ADR 0579 §1). + fresh map[string]bool +} + +// taken marks a digest this take-in newly wrote. +func (w *mergeWrite) taken(digest string) { + if w.fresh == nil { + w.fresh = map[string]bool{} + } + w.fresh[digest] = true } func newMergeWrite(f delivery.Forge) *mergeWrite { diff --git a/modules/mesh-delivery-indexer/internal/index/registered.go b/modules/mesh-delivery-indexer/internal/index/registered.go index 01a79b7..4d8b3ab 100644 --- a/modules/mesh-delivery-indexer/internal/index/registered.go +++ b/modules/mesh-delivery-indexer/internal/index/registered.go @@ -134,9 +134,14 @@ func (ix *Indexer) indexAt(ctx context.Context, r Registration, force, reread bo if p := read.Packages[dir]; len(p) > 0 { f.Packages = map[string][]string{dir: p} } - if _, err := ix.applyManifests(ctx, f, false); err != nil { + w, err := ix.applyManifests(ctx, f, false) + if err != nil { return t, err } + // A module registered or taken in is judged as a merge's are (ADR 0578 §1, ADR 0579 §1). + if _, err := ix.JudgeAll(ctx, w.fresh); err != nil { + ix.logf("judge: the manifests are not judged after taking in %s: %v", r.Module, err) + } if _, _, found, err := ix.Modules.Get(ctx, key); err != nil { return t, fmt.Errorf("modules.%s: %w", key, err) } else if !found { diff --git a/modules/mesh-delivery-indexer/internal/index/source.go b/modules/mesh-delivery-indexer/internal/index/source.go index 9b3e409..f71a367 100644 --- a/modules/mesh-delivery-indexer/internal/index/source.go +++ b/modules/mesh-delivery-indexer/internal/index/source.go @@ -37,6 +37,7 @@ func (ix *Indexer) srcOf(repository, merge string) uint64 { // when it is written, applyMerged), and a repository none of the indexer's modules is built from writes nothing. The // modules of a repository are those the modules bucket holds of it (ADR 0501 clause 6). func (ix *Indexer) ApplySource(ctx context.Context, rev uint64, s delivery.Source) error { + ix.kickJudge(ctx) ix.mu.Lock() if rev < ix.source.rev { ix.mu.Unlock() diff --git a/modules/mesh-delivery-indexer/internal/index/toolchains.go b/modules/mesh-delivery-indexer/internal/index/toolchains.go index 567f944..0a4c1cf 100644 --- a/modules/mesh-delivery-indexer/internal/index/toolchains.go +++ b/modules/mesh-delivery-indexer/internal/index/toolchains.go @@ -256,7 +256,10 @@ func (ix *Indexer) recomputeModules(ctx context.Context, overlay map[string]deli } // The latest published version of each mesh package the module uses: the trunk merge of the module that holds it, // as this recompute holds it, so a package's merge moves every dependent in the same recompute (ADR 0573 §2). - versions, _ := delivery.PackageVersionsOf(kept.Packages, held) + versions, unpublished := delivery.PackageVersionsOf(kept.Packages, held) + if len(unpublished) > 0 { + ix.logf("%s: no module the indexer holds publishes %s, so its fingerprint takes no version of it", module, strings.Join(unpublished, ", ")) + } var r result switch { case k.contexts: @@ -348,10 +351,19 @@ func (ix *Indexer) writeOverlay(ctx context.Context, overlay, finals map[string] wrote := false err := write(ctx, ix.Modules, key, func(value []byte, found bool) ([]byte, bool, error) { wrote = false - if found { - if cur, err := delivery.DecodeModule(value); err == nil && sameButWhere(cur, next) { - return nil, false, nil - } + next := next + cur, err := delivery.DecodeModule(value) + held := found && err == nil + if held && cur.Manifest == next.Manifest { + // The judge's verdict stands while the digest does (ADR 0578 §3, ADR 0579 §1): a recompute never writes a + // refused or outgrown module as accepted; only the judge changes it. + next.Verdict, next.Problems, next.Warnings = cur.Verdict, cur.Problems, cur.Warnings + } + if held && sameButWhere(cur, next) { + return nil, false, nil + } + if next.Manifest != "" && (!held || cur.Manifest != next.Manifest) { + w.taken(next.Manifest) // a new module, or a changed manifest: fresh, judged in full (ADR 0579 §1) } data, err := delivery.EncodeModule(next) wrote = err == nil @@ -367,10 +379,12 @@ func (ix *Indexer) writeOverlay(ctx context.Context, overlay, finals map[string] return nil } -// sameButWhere says the held value cur is next but for where it was computed (Commit and Src): the module is unchanged. +// sameButWhere says the held value cur is next but for where it was computed (Commit and Src) and the judge's verdict, +// which only the judge writes: the module is unchanged. func sameButWhere(cur, next delivery.Module) bool { next = withV(next) next.Commit, next.Src = cur.Commit, cur.Src + next.Verdict, next.Problems, next.Warnings = cur.Verdict, cur.Problems, cur.Warnings return reflect.DeepEqual(cur, next) } diff --git a/modules/mesh-delivery-indexer/internal/index/verdicts.go b/modules/mesh-delivery-indexer/internal/index/verdicts.go index b735730..291562d 100644 --- a/modules/mesh-delivery-indexer/internal/index/verdicts.go +++ b/modules/mesh-delivery-indexer/internal/index/verdicts.go @@ -118,6 +118,26 @@ func (ix *Indexer) JudgeAll(ctx context.Context, fresh map[string]bool) (Judged, return out, nil } +// JudgeAtStart is JudgeAll of every manifest held, none fresh, as the indexer starts (a judge that changed judges every +// one again); one that fails is judged again at the next message or source revision heard (kickJudge). +func (ix *Indexer) JudgeAtStart(ctx context.Context) (Judged, error) { + j, err := ix.JudgeAll(ctx, nil) + ix.judgePending.Store(err != nil) + return j, err +} + +// kickJudge judges again a start pass that failed, when the indexer hears something: never on a timer. +func (ix *Indexer) kickJudge(ctx context.Context) { + if !ix.judgePending.Load() { + return + } + if j, err := ix.JudgeAtStart(ctx); err != nil { + ix.logf("judge: the manifests are still not judged: %v", err) + } else { + ix.logf("judge: judged %d manifests again after a failed start: %d verdicts written", j.Judged, j.Written) + } +} + // acceptedBefore says whether the manifest of digest was accepted before this judgement: its verdict says accepted, or // it has none and is not fresh (held before any verdict, so taken in under the judges that ran until then). func (ix *Indexer) acceptedBefore(ctx context.Context, digest string, fresh map[string]bool) bool { diff --git a/modules/mesh-delivery-indexer/internal/index/verdicts_test.go b/modules/mesh-delivery-indexer/internal/index/verdicts_test.go index b1d8d1d..df57baa 100644 --- a/modules/mesh-delivery-indexer/internal/index/verdicts_test.go +++ b/modules/mesh-delivery-indexer/internal/index/verdicts_test.go @@ -103,6 +103,21 @@ func TestTheIndexerJudgesEveryManifestOnce(t *testing.T) { if !verdict(changed).Refused() || !verdict(fresh).Refused() || module("old").Verdict != delivery.VerdictRefused || len(module("old").Warnings) != 0 { t.Fatalf("the changed and the new manifest are not refused: %+v %+v", verdict(changed), verdict(fresh)) } + // A merge's write of a module whose digest it leaves as it is carries the verdict over, and nothing is fresh; a changed + // digest is fresh, judged in full (review R1, R2). + w := &mergeWrite{} + same := delivery.Module{FP2: "src2:" + strings.Repeat("c", 64), Manifest: changed, Repository: "novox/mesh-catalog", Commit: "c2"} + if err := ix.writeOverlay(ctx, map[string]delivery.Module{"old": same}, nil, w); err != nil { + t.Fatal(err) + } + if m := module("old"); m.Verdict != delivery.VerdictRefused || len(m.Problems) == 0 || len(w.fresh) != 0 { + t.Fatalf("a merge rewrote a refused module as %+v, fresh %v", m, w.fresh) + } + other := strings.Repeat("d", 64) + same.Manifest = other + if err := ix.writeOverlay(ctx, map[string]delivery.Module{"old": same}, nil, w); err != nil || !w.fresh[other] { + t.Fatalf("a changed digest is not fresh: %v %v", w.fresh, err) + } // Without the verdicts state the indexer judges nothing. ix.Verdicts = nil if j, err := ix.JudgeAll(ctx, nil); err != nil || j.Judged != 0 { diff --git a/modules/mesh-delivery-teller/go.mod b/modules/mesh-delivery-teller/go.mod index 33b04d1..18360a1 100644 --- a/modules/mesh-delivery-teller/go.mod +++ b/modules/mesh-delivery-teller/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-teller go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-teller/go.sum b/modules/mesh-delivery-teller/go.sum index 8ddea19..38d5056 100644 --- a/modules/mesh-delivery-teller/go.sum +++ b/modules/mesh-delivery-teller/go.sum @@ -4,6 +4,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-delivery-view/go.mod b/modules/mesh-delivery-view/go.mod index 19f92b5..0d6aadc 100644 --- a/modules/mesh-delivery-view/go.mod +++ b/modules/mesh-delivery-view/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-view go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-view/go.sum b/modules/mesh-delivery-view/go.sum index d957e2b..e1a3b79 100644 --- a/modules/mesh-delivery-view/go.sum +++ b/modules/mesh-delivery-view/go.sum @@ -4,6 +4,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-delivery-watchman/go.mod b/modules/mesh-delivery-watchman/go.mod index 37013cd..f0d4d8f 100644 --- a/modules/mesh-delivery-watchman/go.mod +++ b/modules/mesh-delivery-watchman/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-watchman go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-watchman/go.sum b/modules/mesh-delivery-watchman/go.sum index aeb9ad3..60bda77 100644 --- a/modules/mesh-delivery-watchman/go.sum +++ b/modules/mesh-delivery-watchman/go.sum @@ -10,6 +10,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.8.0-default-no-op h1:1BOWQJweNyvZMlpAHXGLiZQn9S+QXGcz3xh94lC0w6E= github.com/antithesishq/antithesis-sdk-go v0.8.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-grants/go.mod b/modules/mesh-grants/go.mod index 0dce6d1..5ef5119 100644 --- a/modules/mesh-grants/go.mod +++ b/modules/mesh-grants/go.mod @@ -3,7 +3,7 @@ module mesh-grants go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-grants/go.sum b/modules/mesh-grants/go.sum index 05021a6..144efc9 100644 --- a/modules/mesh-grants/go.sum +++ b/modules/mesh-grants/go.sum @@ -10,6 +10,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmH git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc h1:kVq7sS4I0EnL8DcAA2a3YstnXS6WmYYzJz7eK7IZ9RY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62 h1:R5C8ICjiaDITHRFR/uIATwARPPXKxssUC70m0svstb0= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101020750-62cddf4efc62/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=