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
This commit is contained in:
2026-11-04 14:53:01 +01:00
parent fe83d66b62
commit 28ce325c2e
2 changed files with 42 additions and 1 deletions
+27
View File
@@ -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
+15 -1
View File
@@ -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()