From 28ce325c2e843c74e813bed80ef1ce2c3760e4fe Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 14:53:01 +0100 Subject: [PATCH] J4 (A2j finding 2): a verdict names each series change the heads make, so none is lost when the status line is cut delivery.Verdict carries series, each {module, artifact (or the package's ecosystem name), from, to}, refused when a field is empty, it changes nothing, or one artifact is named twice. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- go/delivery/checker.go | 27 +++++++++++++++++++++++++++ go/delivery/checker_test.go | 16 +++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/go/delivery/checker.go b/go/delivery/checker.go index b5f5bad..d0a9f9d 100644 --- a/go/delivery/checker.go +++ b/go/delivery/checker.go @@ -179,6 +179,20 @@ type Verdict struct { // carried over, by repository (ADR 0548 ยง5): the verdict was rewritten for Trunk and Beside by running only the packages // whose digest the moves changed. Absent from a verdict judged whole, and from one still running. CarriedFrom map[string]string `json:"carried-from,omitempty"` + // Series names each change of an artifact's series the checked heads make against the trunk's manifests (design 32; + // the checker's DLV-C4), each once, in the order the check found them; the pull request's mesh/check line says the + // same, and a reader that cuts that line still has every one here. Absent when the heads change no series, and in a + // value written before it. + Series []SeriesChange `json:"series,omitempty"` +} + +// SeriesChange is one change of a series: the module, its artifact (or the package's ecosystem name, as the module's +// packages name it), the series on trunk and the series the heads give it. +type SeriesChange struct { + Module string `json:"module"` + Artifact string `json:"artifact"` + From string `json:"from"` + To string `json:"to"` } func (c *Composition) check() error { @@ -243,6 +257,19 @@ func (v Verdict) check() error { return fmt.Errorf("delivery: the verdict's carried-from names the commits it is judged on: nothing moved, so nothing was carried") } } + changed := map[string]bool{} + for _, c := range v.Series { + if c.Module == "" || c.Artifact == "" || c.From == "" || c.To == "" { + return fmt.Errorf("delivery: the verdict's series change %+v names no module, artifact, from or to", c) + } + if c.From == c.To { + return fmt.Errorf("delivery: the verdict's series change of %s/%s is from %s to the same series", c.Module, c.Artifact, c.From) + } + if changed[c.Module+"/"+c.Artifact] { + return fmt.Errorf("delivery: the verdict names the series change of %s/%s twice", c.Module, c.Artifact) + } + changed[c.Module+"/"+c.Artifact] = true + } for level, r := range v.Levels { if err := checkLevel(level); err != nil { return err diff --git a/go/delivery/checker_test.go b/go/delivery/checker_test.go index b20af2e..e923ccd 100644 --- a/go/delivery/checker_test.go +++ b/go/delivery/checker_test.go @@ -33,8 +33,11 @@ func verdictSamples() map[string]Verdict { carried.Beside = map[string]string{"novox/mesh-catalog": "cccc"} carried.CarriedFrom = map[string]string{"novox/mesh-sdk": "9999", "novox/mesh-control": "bbbb", "novox/mesh-catalog": "cccc"} carried.Composition = &Composition{State: CompositionPass, SDK: carried.SDK, Machines: []string{"ace"}, FPSet: "fps1:00"} + series := passVerdict() + series.Series = []SeriesChange{{Module: "mesh-sdk", Artifact: "go", From: "0.1", To: "0.2"}, + {Module: "mesh-contracts", Artifact: "@novox/mesh-contracts", From: "0.1", To: "1.0"}} return map[string]Verdict{"running": running, "pass": passVerdict(), "fail by a test": failTest, "fail by a machine": failMachine, - "carried": carried} + "carried": carried, "naming its series changes": series} } func keptSample() Kept { @@ -93,6 +96,11 @@ func TestVerdictRoundTrip(t *testing.T) { } } data, _ := EncodeVerdict(passVerdict()) + data, _ = EncodeVerdict(verdictSamples()["naming its series changes"]) + if !strings.Contains(string(data), `"series":[{"module":"mesh-sdk","artifact":"go","from":"0.1","to":"0.2"},`) { + t.Errorf("the series changes are not each {module, artifact, from, to}: %s", data) + } + data, _ = EncodeVerdict(passVerdict()) for _, field := range []string{`"queue":1200`, `"clone":800`, `"unit":950`, `"integration-setup":2100`, `"integration-run":7400`, `"composition":3300`, `"run":12`, `"kept":301`, `"machines":["ace","g14","novox","shanks"]`, `"sdk":"v0.1.14`} { if !strings.Contains(string(data), field) { t.Errorf("the value lacks %s: %s", field, data) @@ -314,6 +322,12 @@ func TestVerdictNewFieldsChecked(t *testing.T) { "carried from an empty commit": {func(v *Verdict) { v.CarriedFrom = map[string]string{"novox/mesh-sdk": ""} }, []string{"carried-from", "mesh-sdk"}}, "carried over no move": {func(v *Verdict) { v.CarriedFrom = map[string]string{"novox/mesh-sdk": "aaaa"} }, []string{"nothing moved"}}, "a running verdict carried": {func(v *Verdict) { v.State, v.CarriedFrom = VerdictRunning, map[string]string{"novox/mesh-sdk": "9999"} }, []string{"running", "carried"}}, + // Design 32: each series change whole, once, and a change. + "a series change with no from": {func(v *Verdict) { v.Series = []SeriesChange{{Module: "m", Artifact: "go", To: "0.2"}} }, []string{"series", "from"}}, + "a series change to itself": {func(v *Verdict) { v.Series = []SeriesChange{{Module: "m", Artifact: "go", From: "0.2", To: "0.2"}} }, []string{"m/go", "same series"}}, + "a series change named twice": {func(v *Verdict) { + v.Series = []SeriesChange{{Module: "m", Artifact: "go", From: "0.1", To: "0.2"}, {Module: "m", Artifact: "go", From: "0.2", To: "0.3"}} + }, []string{"m/go", "twice"}}, } for name, c := range cases { v := passVerdict()