The composer reads a case's verdict as the running checker hands it, version 0 and no judge (issue 1966)

A case's verdicts are the checker's account of the indexer's modules values, not the indexer's kept values: the
trunk's checker writes them with version 0 and no judge, so the kept value's strict reader refused every one
and the group try failed composition on all four nodes ("the verdict of f1-circuits is not one: ... version 0").
The composer now takes version 0 (or none) and keys.Version, its judge optional, and still refuses a later
version, a value that does not decode and an unknown state. The bus reader of kept verdicts stays strict.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-06 10:05:27 +01:00
parent a397492503
commit 3b700886f2
2 changed files with 41 additions and 1 deletions
+25 -1
View File
@@ -13,6 +13,7 @@ import (
"git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient"
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-networking/modules/mesh-wireguard/go/overlay"
"slices"
"sort"
@@ -20,6 +21,7 @@ import (
sdk "git.novox.be/novox/mesh-delivery/sdk/composer"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/keys"
"git.novox.be/novox/mesh-sdk/go/sdkversion"
"github.com/novox/mesh-host/internal/compose"
@@ -105,7 +107,7 @@ func parse(m map[string]Manifest, reads map[string]ManifestRead, verdicts map[st
if err != nil {
read.Refused = fmt.Sprintf("the manifest of %s does not decode: %v", name, err)
} else if raw, ok := verdicts[name]; ok {
v, verr := meshcatalogclient.DecodeVerdict(raw)
v, verr := caseVerdict(raw)
if verr != nil {
return nil, fmt.Errorf("composer: the verdict of %s is not one: %w", name, verr)
}
@@ -128,6 +130,28 @@ func parse(m map[string]Manifest, reads map[string]ManifestRead, verdicts map[st
return out, nil
}
// caseVerdict reads a verdict a case carries. It is not the indexer's kept value but the checker's account of it, made
// from the indexer's modules value: a checker of the trunk writes it with version 0 and no judge, one of the registry
// milestone with no version and no judge, and the composer of a group's head is asked by the checker that runs. So it is
// read in the kept value's form at version 0 (or none) and at keys.Version, its judge optional; a later version, a value
// that does not decode and a state no reader knows are refused, never taken as accepted.
func caseVerdict(raw json.RawMessage) (meshcatalog.VerdictsByDigestValue, error) {
var v meshcatalog.VerdictsByDigestValue
if err := json.Unmarshal(raw, &v); err != nil {
return v, fmt.Errorf("the value does not fit a verdict's shape: %w", err)
}
if v.V != 0 && v.V != keys.Version {
return v, fmt.Errorf("%w: the value is version %d, this reader reads 0 and %d", keys.ErrUnknownVersion, v.V, keys.Version)
}
switch v.State {
case meshcatalogclient.VerdictAccepted, meshcatalogclient.VerdictRefused, meshcatalogclient.VerdictManifestWaits:
default:
return v, fmt.Errorf("the state %q is none of %s, %s and %s", v.State, meshcatalogclient.VerdictAccepted,
meshcatalogclient.VerdictRefused, meshcatalogclient.VerdictManifestWaits)
}
return v, nil
}
func runCase(r Request, c Case, depth string) (CaseVerdict, error) {
cv := CaseVerdict{Name: c.Name, Manifests: map[string]ManifestRead{}}
shelf, err := parse(c.Manifests, cv.Manifests, c.Verdicts)
+16
View File
@@ -3,6 +3,7 @@ package composer
import (
"context"
"encoding/json"
"errors"
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
@@ -15,6 +16,7 @@ import (
"testing"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/keys"
"github.com/novox/mesh-host/internal/compose"
"github.com/novox/mesh-host/internal/compose/frombus"
"github.com/novox/mesh-host/internal/compose/frombus/frombustest"
@@ -262,4 +264,18 @@ func TestTheComposerTakesTheVerdictsOfItsInput(t *testing.T) {
!strings.Contains(got.Refused, "the indexer's judge (src2:j) refused the manifest of xterm: falls back to a key not listed") {
t.Fatalf("xterm refused by its verdict: %+v, want refused in the verdict's words", got)
}
// The verdict as the trunk's checker hands it, version 0 and no judge (group try 1793952957648659842 failed on all
// four nodes: "the verdict of f1-circuits is not one: ... the value is version 0"), holds the module too; a later
// version is refused.
r.Cases[0].Verdicts = map[string]json.RawMessage{"xterm": json.RawMessage(`{"v":0,"module":"xterm","state":"refused","problems":["falls back to a key not listed"],"judge":"","at":"0001-01-01T00:00:00Z"}`)}
if v, err = Run(r); err != nil {
t.Fatalf("a version-0 verdict of the trunk's checker: %v", err)
}
if got := v.Cases[0].Manifests["xterm"]; got.Accepted || !strings.Contains(got.Refused, "refused the manifest of xterm: falls back") {
t.Fatalf("xterm refused by a version-0 verdict: %+v, want refused", got)
}
r.Cases[0].Verdicts["xterm"] = json.RawMessage(`{"v":2,"module":"xterm","state":"refused","problems":["p"]}`)
if _, err = Run(r); !errors.Is(err, keys.ErrUnknownVersion) {
t.Fatalf("a version-2 verdict: %v, want keys.ErrUnknownVersion", err)
}
}