DLV-I6 ruling: a package is named in its own ecosystem, and its series key with it

At each trunk merge the indexer reads every package artifact's name from the merge commit (a Go package's go.mod module
path, a TypeScript package's package.json name) and writes it into the series key as name, beside the module and artifact
it is built from, so the builder numbers it under that name before its first version. Series keys, accepted series and
indexer_package_users go by the name (the lead's design limit for design 32, replacing the builder-name fallback).

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 14:44:38 +01:00
parent f7a6118e61
commit 8d7e8d3cb9
5 changed files with 150 additions and 23 deletions
@@ -136,7 +136,7 @@ func packageUsersTool(state func(string) views.Source) stdio.Tool {
"packages, or the default), that series' newest published version and the version its fingerprint is pinned to, so a " +
"user left on an older series is seen. Give package (such as mesh-sdk/go) for one.",
Input: map[string]any{"type": "object", "properties": map[string]any{
"package": map[string]any{"type": "string", "description": "one package by the builder's name, <module>/<artifact>; all when absent"}}},
"package": map[string]any{"type": "string", "description": "one package by its name in its own ecosystem (a Go module path, an npm package name); all when absent"}}},
Run: func(args map[string]any) (any, error) {
only, _ := args["package"].(string)
got, err := index.PackageUsersOf(state("modules"), state("manifests"), state(delivery.PackagesState), only)
@@ -199,6 +199,10 @@ func (ix *Indexer) ApplyMessage(ctx context.Context, f delivery.Forge) (Applied,
if err := ix.ApplySeatDefinitions(ctx, f); err != nil {
return applied, err
}
// Each package's name in its own ecosystem, which the builder numbers it under (design 32).
if err := ix.KeepPackageNames(ctx, f); err != nil {
return applied, err
}
}
var digests map[string]string
var held map[string]delivery.Module
@@ -296,24 +296,44 @@ func TestTheControlRecords(t *testing.T) {
})
t.Run("packages per series", func(t *testing.T) {
ctx := context.Background()
ix := &Indexer{Packages: newMem()}
sdkGo := "git.novox.be/novox/mesh-sdk/go"
// The lead's design limit (design 32): a merge names each package by its name in its own ecosystem, read at the
// merge commit: the Go package by its go.mod's module path, the TypeScript one by its package.json name.
files := map[string]string{"go/go.mod": "module " + sdkGo + "\n\ngo 1.26\n", "package.json": `{"name":"@novox/mesh-sdk","version":"0.0.0"}`}
ix := &Indexer{Packages: newMem(), Logf: t.Logf, ReadFile: func(_ context.Context, r, sha, p string) ([]byte, error) {
if v, ok := files[p]; ok && r == "novox/mesh-sdk" && sha == "m1" {
return []byte(v), nil
}
return nil, nil
}}
sdk := `{"module":"mesh-sdk","version":"1","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"]},` +
`{"name":"ts","kind":"package","language":"typescript","paths":["src","package.json"],"series":"0.3"}]}}`
if err := ix.KeepPackageNames(ctx, delivery.Forge{Kind: delivery.ForgeMerged, Repository: "novox/mesh-sdk", Merge: "m1",
Manifests: map[string]string{delivery.RootPath: sdk}}); err != nil {
t.Fatal(err)
}
named := ix.SeriesAll(ctx)
if g, ts := named[SeriesKey(sdkGo, "0.1")], named[SeriesKey("@novox/mesh-sdk", "0.3")]; g.Name != sdkGo || g.Package != "mesh-sdk/go" || g.GoModule != sdkGo ||
ts.Name != "@novox/mesh-sdk" || ts.Package != "mesh-sdk/ts" || ts.GoModule != "" || len(named) != 2 {
t.Errorf("names at the merge = %+v", named)
}
delete(ix.Packages.(*memStore).vals, SeriesKey("@novox/mesh-sdk", "0.3"))
for _, h := range []HeardVersion{
{Package: "mesh-sdk/go", Seat: "go-package-registry", Version: "0.1.15", Commit: "c15"},
{Package: "mesh-sdk/go", Seat: "go-package-registry", Version: "0.1.15", Commit: "c15", Digest: "h1:a", GoModule: sdkGo, Published: true},
{Package: "mesh-sdk/go", Version: "0.1.16", Commit: "c16"},
{Package: "mesh-sdk/go", Version: "0.1.14", Commit: "c14", Published: true},
{Package: "mesh-sdk/go", Version: "0.1.17-feat-x.1", Tag: "feat-x", Published: true},
{Package: "mesh-sdk/go", Version: "0.2.0", Commit: "c20", Published: true},
{Package: sdkGo, Seat: "go-package-registry", Version: "0.1.15", Commit: "c15"},
{Package: sdkGo, Seat: "go-package-registry", Version: "0.1.15", Commit: "c15", Digest: "h1:a", GoModule: sdkGo, Published: true},
{Package: sdkGo, Version: "0.1.16", Commit: "c16"},
{Package: sdkGo, Version: "0.1.14", Commit: "c14", Published: true},
{Package: sdkGo, Version: "0.1.17-feat-x.1", Tag: "feat-x", Published: true},
{Package: sdkGo, Version: "0.2.0", Commit: "c20", Published: true},
} {
if _, err := ix.HearVersion(ctx, h); err != nil {
t.Fatal(err)
}
}
all := ix.SeriesAll(ctx)
one, two := all[SeriesKey("mesh-sdk/go", "0.1")], all[SeriesKey("mesh-sdk/go", "0.2")]
one, two := all[SeriesKey(sdkGo, "0.1")], all[SeriesKey(sdkGo, "0.2")]
if one.Newest == nil || one.Newest.Version != "0.1.15" || one.Newest.Digest != "h1:a" || one.Numbered == nil || one.Numbered.Version != "0.1.16" ||
one.GoModule != sdkGo || one.Seat != "go-package-registry" || len(all) != 2 || two.Newest == nil || two.Newest.Version != "0.2.0" {
one.GoModule != sdkGo || one.Name != sdkGo || one.Package != "mesh-sdk/go" || one.Seat != "go-package-registry" || len(all) != 2 || two.Newest == nil || two.Newest.Version != "0.2.0" {
t.Errorf("series = %+v", all)
}
if got := ix.publishedAll(ctx); len(got) != 0 {
@@ -334,15 +354,15 @@ func TestTheControlRecords(t *testing.T) {
}
s[key] = &views.Entry{Value: raw, Revision: 1}
}
putJSON(packages, SeriesKey("mesh-sdk/go", "0.1"), SeriesPackage{V: 1, Package: "mesh-sdk/go", Series: "0.1", GoModule: sdkGo,
putJSON(packages, SeriesKey(sdkGo, "0.1"), SeriesPackage{V: 1, Name: sdkGo, Package: "mesh-sdk/go", Series: "0.1", GoModule: sdkGo,
Newest: &SeriesVersion{Version: "0.1.15"}})
putJSON(packages, SeriesKey("mesh-sdk/go", "0.2"), SeriesPackage{V: 1, Package: "mesh-sdk/go", Series: "0.2", GoModule: sdkGo})
putJSON(packages, SeriesKey(sdkGo, "0.2"), SeriesPackage{V: 1, Name: sdkGo, Package: "mesh-sdk/go", Series: "0.2", GoModule: sdkGo})
for name, u := range map[string]struct {
repo, manifest string
pkgs, imports []string
pinned string
}{
"new": {"novox/mesh-catalog", `{"module":"new","version":"1","packages":{"mesh-sdk/go":"0.2","web/ui":"1.0"}}`, nil, nil, ""},
"new": {"novox/mesh-catalog", `{"module":"new","version":"1","packages":{"git.novox.be/novox/mesh-sdk/go":"0.2","@novox/ui":"1.0"}}`, nil, nil, ""},
"old": {"novox/mesh-catalog", `{"module":"old","version":"1"}`, []string{sdkGo}, nil, "v0.1.15"},
"imports": {"novox/mesh-tools", `{"module":"imports","version":"1"}`, nil, []string{sdkGo + "/views"}, ""},
"sibling": {"novox/mesh-sdk", `{"module":"sibling","version":"1"}`, []string{sdkGo}, nil, ""},
@@ -363,16 +383,16 @@ func TestTheControlRecords(t *testing.T) {
}
got, err := PackageUsersOf(modules, manifests, packages, "")
want := []PackageUses{
{Package: "mesh-sdk/go", GoModule: sdkGo, Series: map[string]string{"0.1": "0.1.15", "0.2": ""}, Users: []PackageUser{
{Package: "@novox/ui", Series: map[string]string{}, Users: []PackageUser{{Module: "new", Accepts: "1.0", Named: true}}},
{Package: sdkGo, GoModule: sdkGo, Series: map[string]string{"0.1": "0.1.15", "0.2": ""}, Users: []PackageUser{
{Module: "imports", Accepts: "0.1", Newest: "0.1.15"},
{Module: "new", Accepts: "0.2", Named: true},
{Module: "old", Accepts: "0.1", Newest: "0.1.15", Pinned: "v0.1.15"}}},
{Package: "web/ui", Series: map[string]string{}, Users: []PackageUser{{Module: "new", Accepts: "1.0", Named: true}}},
}
if err != nil || !reflect.DeepEqual(got, want) {
t.Errorf("indexer_package_users = %+v (%v), want %+v", got, err, want)
}
if one, err := PackageUsersOf(modules, manifests, packages, "web/ui"); err != nil || len(one) != 1 || one[0].Package != "web/ui" {
if one, err := PackageUsersOf(modules, manifests, packages, "@novox/ui"); err != nil || len(one) != 1 || one[0].Package != "@novox/ui" {
t.Errorf("one package = %+v (%v)", one, err)
}
if _, err := PackageUsersOf(modules, manifests, packages, "nobody/x"); err == nil {
@@ -67,7 +67,7 @@ func PackageUsersOf(modules, manifests, packages views.Source, only string) ([]P
if err := json.Unmarshal(e.Value, &s); err != nil {
return nil, fmt.Errorf("packages.%s holds what is not a series: %w", key, err)
}
p := get(s.Package)
p := get(s.Name)
if s.GoModule != "" {
p.GoModule = s.GoModule
}
@@ -3,8 +3,10 @@ package index
import (
"context"
"encoding/json"
"errors"
"fmt"
"maps"
"path"
"slices"
"strings"
"time"
@@ -25,8 +27,12 @@ import (
// SeriesPackage is the value of a series key.
type SeriesPackage struct {
V int `json:"v"`
// Package is the builder's name of the package, `<module>/<artifact>`; Series its `<major>.<minor>`.
Package string `json:"package"`
// Name is the package's name in its own ecosystem (design 32, the lead's design limit of 2026-11-04): a Go package's
// module path in its go.mod at the commit, an npm package's package.json name. The series key is named by it, and a
// user's `packages` names it. Package is the module and artifact it is built from, `<module>/<artifact>`, by which
// the builder finds the name to number under; Series its `<major>.<minor>`.
Name string `json:"name"`
Package string `json:"package,omitempty"`
Series string `json:"series"`
// Seat is the registry seat it is published through; GoModule the Go module path a Go package is fetched under (from
// go-package-registry's published), empty for any other.
@@ -67,7 +73,7 @@ func SeriesOf(version string) (string, bool) {
return fmt.Sprintf("%d.%d", p.core[0], p.core[1]), true
}
// HeardVersion is what the indexer takes of a numbered or a published: the package's name, the seat, the version and its
// HeardVersion is what the indexer takes of a numbered or a published: the package's name in its ecosystem, the seat, the version and its
// tag, the commit, digest and fingerprint, and the Go module path a published names.
type HeardVersion struct {
Package, Seat, Version, Tag, Commit, Digest, Fingerprint, GoModule string
@@ -104,14 +110,17 @@ func (ix *Indexer) HearVersion(ctx context.Context, h HeardVersion) (bool, error
}
kept := false
err := write(ctx, ix.Packages, SeriesKey(h.Package, series), func(value []byte, found bool) ([]byte, bool, error) {
s := SeriesPackage{V: delivery.Version, Package: h.Package, Series: series}
s := SeriesPackage{V: delivery.Version, Name: h.Package, Series: series}
if found {
if err := json.Unmarshal(value, &s); err != nil {
ix.logf("packages.%s holds what is not a series, written anew: %v", SeriesKey(h.Package, series), err)
s = SeriesPackage{V: delivery.Version, Package: h.Package, Series: series}
s = SeriesPackage{V: delivery.Version, Name: h.Package, Series: series}
}
}
kept = false
if s.Name == "" {
s.Name, kept = h.Package, true
}
if newer(s.Numbered) {
s.Numbered, kept = &numbered, true
}
@@ -175,7 +184,7 @@ func acceptedSeries(series map[string]SeriesPackage, m catalogue.Manifest, repos
var pins delivery.PackageVersions
for _, key := range slices.Sorted(maps.Keys(series)) {
s := series[key]
if s.GoModule == "" || s.Newest == nil || s.Newest.Contents == nil || m.AcceptedSeries(s.Package) != s.Series {
if s.GoModule == "" || s.Newest == nil || s.Newest.Contents == nil || m.AcceptedSeries(s.Name) != s.Series {
continue
}
if repo, _, ok := delivery.PackageRepository(s.GoModule); !ok || delivery.SameRepository(repo, repository) {
@@ -194,3 +203,97 @@ func acceptedSeries(series map[string]SeriesPackage, m catalogue.Manifest, repos
}
return out, pins
}
// KeepPackageNames writes, at a trunk merge, each package artifact's name in its own ecosystem into its series key
// (design 32): a Go package's module path from the go.mod under its paths at the merge commit, a TypeScript package's
// package.json name, each read through the forge. The key keeps what it held and gains the name and the module and
// artifact it is built from, so the builder numbers it under that name before its first version. A name the forge does
// not answer is said and left for the next merge; a manifest that does not decode is the judge's to refuse.
func (ix *Indexer) KeepPackageNames(ctx context.Context, f delivery.Forge) error {
if ix.Packages == nil || ix.ReadFile == nil || f.Kind != delivery.ForgeMerged || f.Merge == "" {
return nil
}
var errs []error
for _, dir := range slices.Sorted(maps.Keys(f.Manifests)) {
m, err := catalogue.Decode([]byte(f.Manifests[dir]))
if err != nil || m.Build == nil {
continue
}
for _, a := range m.Build.Artifacts {
if a.Kind != catalogue.ArtifactPackage {
continue
}
name, err := ix.packageName(ctx, f.Repository, f.Merge, dir, a)
if err != nil {
ix.logf("packages: the name of %s/%s at %.12s is not read, so it is numbered under no name until the next merge: %v", m.Module, a.Name, f.Merge, err)
continue
}
series, built := a.SeriesOf(), m.Module+"/"+a.Name
errs = append(errs, write(ctx, ix.Packages, SeriesKey(name, series), func(value []byte, found bool) ([]byte, bool, error) {
s := SeriesPackage{V: delivery.Version, Series: series}
if found && json.Unmarshal(value, &s) != nil {
s = SeriesPackage{V: delivery.Version, Series: series}
}
if s.Name == name && s.Package == built && (a.Language != "go" || s.GoModule == name) {
return nil, false, nil
}
s.Name, s.Package = name, built
if a.Language == "go" {
s.GoModule = name
}
s.At = ix.now()
next, err := json.Marshal(s)
return next, false, err
}))
}
}
return errors.Join(errs...)
}
// packageName is a package artifact's name in its ecosystem, read at commit from the first of its paths (or the module's
// directory) that holds its go.mod (Go) or package.json (TypeScript).
func (ix *Indexer) packageName(ctx context.Context, repository, commit, dir string, a catalogue.Artifact) (string, error) {
file := "package.json"
if a.Language == "go" {
file = "go.mod"
}
base := dir
if base == delivery.RootPath {
base = ""
}
roots := a.Paths
if len(roots) == 0 {
roots = []string{"."}
}
for _, r := range roots {
p := path.Join(base, r, file)
if strings.HasSuffix(r, file) {
p = path.Join(base, r)
}
raw, err := ix.ReadFile(ctx, repository, commit, p)
if err != nil {
return "", fmt.Errorf("%s: %w", p, err)
}
if raw == nil {
continue
}
if a.Language == "go" {
for _, line := range strings.Split(string(raw), "\n") {
if rest, ok := strings.CutPrefix(strings.TrimSpace(line), "module "); ok {
if name := strings.Trim(strings.TrimSpace(rest), `"`); name != "" {
return name, nil
}
}
}
return "", fmt.Errorf("%s names no module", p)
}
var pj struct {
Name string `json:"name"`
}
if err := json.Unmarshal(raw, &pj); err != nil || pj.Name == "" {
return "", fmt.Errorf("%s names no name (%v)", p, err)
}
return pj.Name, nil
}
return "", fmt.Errorf("no %s under %v", file, roots)
}