From 1a3ef8101f9dedf5431c29c605c82848fab17fe4 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 14:25:25 +0100 Subject: [PATCH] J3 (A2f findings 2, 4): a leaving module's accepted event is named by its module; a user naming no series accepts the first numbered one ManifestAccepted.EventID names the module when the digest is empty, so the indexer's own id for a module leaving the trunk can go and the id lives in one place. Manifest.AcceptedSeries takes the series the package has a numbered version in and answers the lowest, the series of its first numbered version (design 32), DefaultSeries while none is. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- go/catalogue/config_test.go | 1 + go/catalogue/manifest.go | 34 ++++++++++++++++++++++++++++++---- go/delivery/verdicts.go | 6 +++++- go/delivery/verdicts_test.go | 5 +++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/go/catalogue/config_test.go b/go/catalogue/config_test.go index cc482dc..01e6925 100644 --- a/go/catalogue/config_test.go +++ b/go/catalogue/config_test.go @@ -145,6 +145,7 @@ func TestAConfigManifestIsWrittenBackInItsOwnForm(t *testing.T) { } if m.Settings["font"].RenamedFrom != "font-name" || m.AcceptedSeries("mesh-sdk") != "0.2" || m.AcceptedSeries("mesh-contracts") != DefaultSeries || m.Build.Artifacts[0].SeriesOf() != "0.2" || + m.AcceptedSeries("mesh-sdk", "0.1") != "0.2" || m.AcceptedSeries("mesh-contracts", "0.10", "x", "0.3", "1.0") != "0.3" || m.Build.Artifacts[0].GeneratedFrom != "seats" || m.Build.Artifacts[1].SeriesOf() != "0.1" { t.Fatalf("read %+v", m) } diff --git a/go/catalogue/manifest.go b/go/catalogue/manifest.go index 5aecd73..94046c1 100644 --- a/go/catalogue/manifest.go +++ b/go/catalogue/manifest.go @@ -1243,13 +1243,39 @@ type Artifact struct { GeneratedFrom string `json:"generated-from,omitempty"` } -// AcceptedSeries is the series of the package of that name this module accepts: the one Packages names, or -// DefaultSeries. -func (m Manifest) AcceptedSeries(name string) string { +// AcceptedSeries is the series of the package of that name this module accepts: the one Packages names, or else the +// series of the package's first numbered version (design 32), the lowest of numbered, the series the package has a +// numbered version in (the indexer's to say; a series is only ever raised); DefaultSeries while none is numbered. +func (m Manifest) AcceptedSeries(name string, numbered ...string) string { if s := m.Packages[name]; s != "" { return s } - return DefaultSeries + first := "" + for _, s := range numbered { + if seriesOrder(s) != nil && (first == "" || compareSeries(s, first) < 0) { + first = s + } + } + if first == "" { + return DefaultSeries + } + return first +} + +// seriesOrder is a series' major and minor, or nil when it is no `.`. +func seriesOrder(s string) []int { + major, minor, ok := strings.Cut(s, ".") + a, err1 := strconv.Atoi(major) + b, err2 := strconv.Atoi(minor) + if !ok || err1 != nil || err2 != nil || a < 0 || b < 0 { + return nil + } + return []int{a, b} +} + +// compareSeries orders two well-formed series by major, then minor. +func compareSeries(a, b string) int { + return slices.Compare(seriesOrder(a), seriesOrder(b)) } // DefaultSeries is the series of an artifact or an accepted package that names none (design 32). diff --git a/go/delivery/verdicts.go b/go/delivery/verdicts.go index a6cede6..f54c0f3 100644 --- a/go/delivery/verdicts.go +++ b/go/delivery/verdicts.go @@ -234,7 +234,11 @@ func AcceptedOf(digest string, v ManifestVerdict, m catalogue.Manifest) Manifest } // EventID is the event's id, one per manifest digest and judge, so the bus takes it once: -// `manifest-accepted--`. +// `manifest-accepted--`. The empty one of a module leaving the trunk carries no digest, so it is named by +// its module: `manifest-accepted-left--`. func (a ManifestAccepted) EventID() string { + if a.Digest == "" { + return ManifestAcceptedEvent + "-left-" + a.Module + "-" + a.Judge + } return ManifestAcceptedEvent + "-" + a.Digest + "-" + a.Judge } diff --git a/go/delivery/verdicts_test.go b/go/delivery/verdicts_test.go index 90af075..dc48d77 100644 --- a/go/delivery/verdicts_test.go +++ b/go/delivery/verdicts_test.go @@ -103,6 +103,11 @@ func TestAManifestVerdictIsTheContractReadersTake(t *testing.T) { if id := heard.EventID(); id != "manifest-accepted-"+digest+"-src2:j" { t.Fatalf("the accepted event's id is %q", id) } + // The empty one of a module leaving the trunk has no digest: its id names the module, so two modules' differ. + if a, b := (ManifestAccepted{Module: "a", Judge: "j"}).EventID(), (ManifestAccepted{Module: "b", Judge: "j"}).EventID(); a == b || + a != "manifest-accepted-left-a-j" { + t.Fatalf("the ids of two modules leaving are %q and %q", a, b) + } // Both verdict events carry the indexer's revision (ADR 0622 ยง2), and one without it reads as revision 0. for _, ev := range []any{ManifestAccepted{Module: "m", Revision: 41}, ManifestRefused{Module: "m", Revision: 41}} { raw, _ := json.Marshal(ev)