diff --git a/modules/mesh-delivery-assigner/go.mod b/modules/mesh-delivery-assigner/go.mod index 53c72fc..18031e9 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc 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 add1f6c..c73ceee 100644 --- a/modules/mesh-delivery-assigner/go.sum +++ b/modules/mesh-delivery-assigner/go.sum @@ -8,6 +8,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c h1:rlUCZ5if git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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 e6b57b1..fa74794 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc 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 add1f6c..c73ceee 100644 --- a/modules/mesh-delivery-builder/go.sum +++ b/modules/mesh-delivery-builder/go.sum @@ -8,6 +8,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c h1:rlUCZ5if git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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/go.mod b/modules/mesh-delivery-checker/go.mod index 9e6d299..0340b63 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc 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 add1f6c..c73ceee 100644 --- a/modules/mesh-delivery-checker/go.sum +++ b/modules/mesh-delivery-checker/go.sum @@ -8,6 +8,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c h1:rlUCZ5if git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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 bad6d18..18df15f 100644 --- a/modules/mesh-delivery-checker/internal/check/examiner_test.go +++ b/modules/mesh-delivery-checker/internal/check/examiner_test.go @@ -944,8 +944,11 @@ 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 }{ {name: "accepted"}, + {name: "an untouched manifest's problem", untouched: "mail says no credential form"}, {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) @@ -960,7 +963,7 @@ 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: true}}}}}) + Manifests: map[string]composer.ManifestRead{"web": read, "mail": {Accepted: row.untouched == "", Refused: row.untouched}}}}}) result, _ := json.Marshal(delivery.ComposeAnswer{Key: "k", Verdict: verdict}) return delivery.Step{ID: a.ID, State: delivery.StepPassed, Result: result} } diff --git a/modules/mesh-delivery-checker/internal/check/manifests_judge.go b/modules/mesh-delivery-checker/internal/check/manifests_judge.go index 0568b29..d43301d 100644 --- a/modules/mesh-delivery-checker/internal/check/manifests_judge.go +++ b/modules/mesh-delivery-checker/internal/check/manifests_judge.go @@ -58,7 +58,8 @@ type TrunkManifests interface { } // 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 those modules, sorted. +// 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{} for module, digest := range trunk { @@ -73,7 +74,9 @@ func judgeRequest(trunk map[string]string, pulls []Pull) (req composer.Request, continue } manifests[module] = composer.Manifest{Digest: cm.Manifest} - seen[module] = true + if trunk[module] != cm.Manifest { + seen[module] = true + } } } return composer.Request{V: composer.Version, Cases: []composer.Case{{Name: JudgeCase, Manifests: manifests}}}, @@ -122,12 +125,23 @@ func (st *state) judgeManifests(ctx context.Context) *delivery.Verdict { if why != "" { return fail(goModule, fmt.Sprintf("the indexer's judge at %s did not judge the change's manifests: %s", at, why)) } - var refused []string - for _, module := range touched { - if r, ok := cv.Manifests[module]; ok && !r.Accepted { - refused = append(refused, fmt.Sprintf("the indexer's judge at %s refuses the manifest of %s: %s", at, module, - strings.ReplaceAll(r.Refused, "\n", "; "))) + // 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. + var refused, warned []string + for _, module := range slices.Sorted(maps.Keys(cv.Manifests)) { + r := cv.Manifests[module] + if r.Accepted || module == judgeBetween { + 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) + } + } + 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: " + diff --git a/modules/mesh-delivery-forge-bridge/go.mod b/modules/mesh-delivery-forge-bridge/go.mod index f455ac2..c13cd72 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc 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 89d9989..146b01e 100644 --- a/modules/mesh-delivery-forge-bridge/go.sum +++ b/modules/mesh-delivery-forge-bridge/go.sum @@ -4,6 +4,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101005726-af4e4e60a3a0 h1:q6ywchpT git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101005726-af4e4e60a3a0/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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 b8cdf7c..3159643 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go @@ -391,12 +391,13 @@ 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) + j, err := ix.JudgeAll(ctx, nil) if err != nil { logger.Printf("judging the manifests at start: %v", err) return } - logger.Printf("judged %d manifests at start: %d verdicts written, %d refused", j.Judged, j.Written, len(j.Refused)) + logger.Printf("judged %d manifests at start: %d verdicts written, %d with problems, of which outgrown and kept accepted: %v", + j.Judged, j.Written, len(j.Refused), j.Outgrown) }) // A build module says each time it wrote its toolchains key: its stream, and the indexer's durable consumer on it, are // made by the controller as the forge's are. Where the stream is not there yet the indexer says so and is started again diff --git a/modules/mesh-delivery-indexer/go.mod b/modules/mesh-delivery-indexer/go.mod index 34e6fb6..1f214cc 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc 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 add1f6c..c73ceee 100644 --- a/modules/mesh-delivery-indexer/go.sum +++ b/modules/mesh-delivery-indexer/go.sum @@ -8,6 +8,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c h1:rlUCZ5if git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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 b536ba5..a2ea532 100644 --- a/modules/mesh-delivery-indexer/internal/index/candidates.go +++ b/modules/mesh-delivery-indexer/internal/index/candidates.go @@ -138,7 +138,12 @@ 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). - if _, err := ix.JudgeAll(ctx); err != nil { + // 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 { 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/verdicts.go b/modules/mesh-delivery-indexer/internal/index/verdicts.go index 2605d21..b735730 100644 --- a/modules/mesh-delivery-indexer/internal/index/verdicts.go +++ b/modules/mesh-delivery-indexer/internal/index/verdicts.go @@ -29,12 +29,20 @@ var VerdictsBucket = delivery.VerdictsBucket type Judged struct { Judged, Written int Refused map[string][]string + // Outgrown are the modules accepted before whose problems are now warnings (ADR 0579 §1). + Outgrown []string } // JudgeAll judges every manifest the modules state names, as one catalogue with the predefined list the indexer keeps, // and writes what differs. A manifest the manifests state does not hold is left unjudged and said; a write that fails is // said and the pass goes on, so one key never costs the others their verdict. -func (ix *Indexer) JudgeAll(ctx context.Context) (Judged, error) { +// +// **A stricter judge never refuses what it accepted before** (novox/hq ADR 0579 §1): a manifest whose verdict under its +// digest is accepted, or which the indexer held before any verdict (taken as accepted, ADR 0578 §3) and is not in fresh, +// stays accepted, the new problems written as the verdict's Warnings and carried in its module's value, which the +// watchman raises as manifest-outgrown. A manifest in fresh (the digests a merge just took in) with no verdict yet, and +// one already refused, is judged in full. +func (ix *Indexer) JudgeAll(ctx context.Context, fresh map[string]bool) (Judged, error) { var out Judged if ix.Verdicts == nil || ix.Modules == nil || ix.Manifests == nil { return out, nil @@ -79,7 +87,12 @@ func (ix *Indexer) JudgeAll(ctx context.Context) (Judged, error) { out.Judged++ v := delivery.ManifestVerdict{Module: name, State: delivery.VerdictAccepted, Judge: id, At: ix.now()} if problems := refused[name]; len(problems) > 0 { - v.State, v.Problems = delivery.VerdictRefused, problems + if ix.acceptedBefore(ctx, digests[name], fresh) { + v.Warnings = problems + out.Outgrown = append(out.Outgrown, name) + } else { + v.State, v.Problems = delivery.VerdictRefused, problems + } } wrote, err := ix.writeVerdict(ctx, digests[name], v) if errors.Is(err, process.ErrNotYet) { @@ -105,6 +118,20 @@ func (ix *Indexer) JudgeAll(ctx context.Context) (Judged, error) { return out, nil } +// 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 { + value, _, found, err := ix.Verdicts.Get(ctx, digest) + if err != nil { + return false // unread: judged in full, said by the write that follows + } + if !found { + return !fresh[digest] + } + prev, err := delivery.DecodeManifestVerdict(value) + return err == nil && !prev.Refused() +} + // writeVerdict writes v under the digest's key unless the verdict held there says the same (ManifestVerdict.Same); wrote // says it was written. func (ix *Indexer) writeVerdict(ctx context.Context, digest string, v delivery.ManifestVerdict) (wrote bool, err error) { @@ -146,10 +173,10 @@ func (ix *Indexer) carryVerdict(ctx context.Context, name, digest string, v deli if v.Refused() { state = delivery.VerdictRefused } - if cur.Verdict == state && slices.Equal(cur.Problems, v.Problems) { + if cur.Verdict == state && slices.Equal(cur.Problems, v.Problems) && slices.Equal(cur.Warnings, v.Warnings) { return nil, false, nil } - cur.Verdict, cur.Problems = state, v.Problems + cur.Verdict, cur.Problems, cur.Warnings = state, v.Problems, v.Warnings if state == "" { cur.Problems = nil } diff --git a/modules/mesh-delivery-indexer/internal/index/verdicts_test.go b/modules/mesh-delivery-indexer/internal/index/verdicts_test.go index ae763bb..b1d8d1d 100644 --- a/modules/mesh-delivery-indexer/internal/index/verdicts_test.go +++ b/modules/mesh-delivery-indexer/internal/index/verdicts_test.go @@ -33,7 +33,8 @@ func TestTheIndexerJudgesEveryManifestOnce(t *testing.T) { } good := hold("good", `{"module":"good","version":"1"}`) bad := hold("bad", `{"module":"bad","version":"1","requires":["amqp"]}`) - j, err := ix.JudgeAll(ctx) + // Both taken in by a merge: judged in full. + j, err := ix.JudgeAll(ctx, map[string]bool{good: true, bad: true}) if err != nil || j.Judged != 2 || j.Written != 2 || len(j.Refused) != 1 { t.Fatalf("the first pass: %+v %v", j, err) } @@ -70,20 +71,41 @@ func TestTheIndexerJudgesEveryManifestOnce(t *testing.T) { } // A new judge that judges alike writes nothing and says nothing again. ix.JudgeID = "judge-2" - if j, err := ix.JudgeAll(ctx); err != nil || j.Written != 0 || len(said) != 1 { + if j, err := ix.JudgeAll(ctx, nil); err != nil || j.Written != 0 || len(said) != 1 { t.Fatalf("a judge that judges alike: %+v %v, said %d", j, err, len(said)) } // The manifest mended: its new digest is accepted and the module's refusal goes. - hold("bad", `{"module":"bad","version":"2"}`) - if j, err := ix.JudgeAll(ctx); err != nil || j.Written != 1 || len(j.Refused) != 0 { + mended := hold("bad", `{"module":"bad","version":"2"}`) + if j, err := ix.JudgeAll(ctx, map[string]bool{mended: true}); err != nil || j.Written != 1 || len(j.Refused) != 0 { t.Fatalf("mended: %+v %v", j, err) } if m := module("bad"); m.Verdict != "" || m.Problems != nil { t.Fatalf("modules.bad still carries %+v", m) } + // ADR 0579 §1: a manifest held before any verdict (taken in under the judges until then), which this judge finds a + // problem in, stays accepted with the problem as a warning, carried in its value and said nowhere as a refusal; the + // same problem in a manifest a merge takes in now, or in one whose digest changed, is refused in full. + old := hold("old", `{"module":"old","version":"1","requires":["amqp"]}`) + if j, err := ix.JudgeAll(ctx, nil); err != nil || len(j.Outgrown) != 1 || j.Outgrown[0] != "old" { + t.Fatalf("an outgrown manifest: %+v %v", j, err) + } + if v := verdict(old); v.Refused() || len(v.Warnings) != 1 || !strings.Contains(v.Warnings[0], `requires "amqp"`) { + t.Fatalf("old: %+v", v) + } + if m := module("old"); m.Verdict != "" || len(m.Warnings) != 1 || len(said) != 1 { + t.Fatalf("modules.old carries %+v, said %d", m, len(said)) + } + changed := hold("old", `{"module":"old","version":"2","requires":["amqp"]}`) + fresh := hold("new", `{"module":"new","version":"1","requires":["amqp"]}`) + if j, err := ix.JudgeAll(ctx, map[string]bool{changed: true, fresh: true}); err != nil || len(j.Outgrown) != 0 { + t.Fatalf("a changed and a new manifest: %+v %v", j, err) + } + 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)) + } // Without the verdicts state the indexer judges nothing. ix.Verdicts = nil - if j, err := ix.JudgeAll(ctx); err != nil || j.Judged != 0 { + if j, err := ix.JudgeAll(ctx, nil); err != nil || j.Judged != 0 { t.Fatalf("without the state: %+v %v", j, err) } } diff --git a/modules/mesh-delivery-teller/go.mod b/modules/mesh-delivery-teller/go.mod index 25265f2..33b04d1 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc 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 3698fd6..8ddea19 100644 --- a/modules/mesh-delivery-teller/go.sum +++ b/modules/mesh-delivery-teller/go.sum @@ -2,6 +2,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030201806-eff81f1220e2 h1:RF73nVI1 git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030201806-eff81f1220e2/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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 42a0754..19f92b5 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc 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 a0e8771..d957e2b 100644 --- a/modules/mesh-delivery-view/go.sum +++ b/modules/mesh-delivery-view/go.sum @@ -2,6 +2,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031084708-acbf80ad910a h1:DcSvfEmd git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261031084708-acbf80ad910a/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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 e81f2f6..37013cd 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc 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 9e928c1..aeb9ad3 100644 --- a/modules/mesh-delivery-watchman/go.sum +++ b/modules/mesh-delivery-watchman/go.sum @@ -8,6 +8,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c h1:rlUCZ5if git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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-delivery-watchman/internal/watch/findings_test.go b/modules/mesh-delivery-watchman/internal/watch/findings_test.go index 582d23b..b067f4a 100644 --- a/modules/mesh-delivery-watchman/internal/watch/findings_test.go +++ b/modules/mesh-delivery-watchman/internal/watch/findings_test.go @@ -50,6 +50,14 @@ func TestTheWatchmansFindings(t *testing.T) { } w.ModuleVerdict("nats", value(""), Put) w.ModuleVerdict("power", nil, Delete) + // ADR 0579 §1: an accepted manifest a stricter judge outgrew is a warning until mended. + outgrown, _ := delivery.EncodeModule(delivery.Module{FP2: "src2:" + strings.Repeat("a", 64), Manifest: strings.Repeat("b", 64), + Repository: "novox/mesh-tools", Commit: "c", Warnings: []string{"says no credential form"}}) + w.ModuleVerdict("node-tools", outgrown, Put) + if f := o.findings[FindingKey(ScopeModule, "node-tools", KindManifestOutgrown)]; f.Severity != Warning || !strings.Contains(f.Summary, "says no credential form") { + t.Fatalf("findings = %v", o.findings) + } + w.ModuleVerdict("node-tools", value(""), Put) }, map[string]string{}}, {"WM4: a stuck key not moved is its finding, and its move deletes it", func(t *testing.T, w *Watchman, o *fakeOut, c *clock) { w.Step("go", "n1.ask1", runningStep("ask1", t0), Put) diff --git a/modules/mesh-delivery-watchman/internal/watch/verdicts.go b/modules/mesh-delivery-watchman/internal/watch/verdicts.go index 1839f3f..1a80e65 100644 --- a/modules/mesh-delivery-watchman/internal/watch/verdicts.go +++ b/modules/mesh-delivery-watchman/internal/watch/verdicts.go @@ -10,14 +10,20 @@ import ( // it, composing nothing of its own, until a mended manifest is merged. const KindManifestRefused = "manifest-refused" +// KindManifestOutgrown is a module whose manifest the indexer accepted before and a stricter judge finds problems in +// (novox/hq ADR 0579 §1): it stays accepted and runs; the warning stands until the manifest is mended. +const KindManifestOutgrown = delivery.ManifestOutgrown + // ModuleVerdict is a change of the indexer's value of a module (delivery.ModulesBucket): a value carrying a refusal is a // finding, one that does not (accepted, or not judged yet) or a deleted key ends it. func (w *Watchman) ModuleVerdict(module string, value []byte, op Op) { w.mu.Lock() defer w.mu.Unlock() key := FindingKey(ScopeModule, module, KindManifestRefused) + outgrown := FindingKey(ScopeModule, module, KindManifestOutgrown) if op != Put { w.unfind(key) + w.unfind(outgrown) return } m, err := delivery.DecodeModule(value) @@ -28,8 +34,20 @@ func (w *Watchman) ModuleVerdict(module string, value []byte, op Op) { v := m.VerdictOf(module) if refused, _ := delivery.TakeManifestVerdict(v); !refused { w.unfind(key) + if v == nil || len(v.Warnings) == 0 { + w.unfind(outgrown) + return + } + w.find(Finding{Scope: ScopeModule, ID: module, Kind: KindManifestOutgrown, Severity: Warning, Source: delivery.ModulesBucket, + Summary: "the manifest of " + module + " breaks rules the indexer's judge holds it to now: " + strings.Join(v.Warnings, "; "), + Said: strings.Join(v.Warnings, "; "), + Headline: "The manifest of " + module + " needs mending", + Explanation: "A stricter judge found problems in a manifest it accepted before; " + module + " stays accepted and " + + "runs, and its next change is refused until they are mended (novox/hq ADR 0579).", + Needs: "a pull request that mends the manifest of " + module}) return } + w.unfind(outgrown) w.find(Finding{Scope: ScopeModule, ID: module, Kind: KindManifestRefused, Severity: Warning, Source: delivery.ModulesBucket, Summary: v.Why(), Said: strings.Join(v.Problems, "; "), diff --git a/modules/mesh-grants/go.mod b/modules/mesh-grants/go.mod index 2b8f837..0dce6d1 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.20261101014012-a20bdf795236 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014310-19c204273bdc github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-grants/go.sum b/modules/mesh-grants/go.sum index 00620bf..05021a6 100644 --- a/modules/mesh-grants/go.sum +++ b/modules/mesh-grants/go.sum @@ -8,6 +8,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c h1:rlUCZ5if git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101013319-25a2cecd989c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261101014012-a20bdf795236 h1:JEkSnrmHs5QIRJGhtrghLC1+C0AxqMpef8a32d/BLPg= 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= 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=