Bridge reads no mesh package from a go.sum

Only the indexer computes what a fingerprint takes (ADR 0593 §2): the merged and
pull messages carry no Packages, and the kept inputs no longer hold them (D6).

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-11-02 18:26:53 +01:00
parent fba98ee80f
commit 2aa43ec249
4 changed files with 32 additions and 62 deletions
@@ -423,9 +423,8 @@ func TestBridgeWritesMergedMessage(t *testing.T) { // 1236/AC4, 1448/AC5
want := delivery.Forge{V: delivery.Version, Kind: "merged", Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12, Head: head,
Merge: merge, Root: "r0", Trees: map[string]string{"": "t0", "modules/a": "t1"},
Manifests: map[string]string{"": string(manifest), "modules/a": string(manifest)},
Locks: map[string]map[string]string{"modules/a": {"go.sum": hex.EncodeToString(sha256Sum(sum))}},
// The mesh packages its go.sum names (ADR 0573 §2, issue 1585): this module's own go.sum names mesh-sdk.
Packages: map[string][]string{"modules/a": {"git.novox.be/novox/mesh-sdk/go"}}}
// No mesh package: only the indexer computes what a fingerprint takes (ADR 0593 §2).
Locks: map[string]map[string]string{"modules/a": {"go.sum": hex.EncodeToString(sha256Sum(sum))}}}
if !delivery.SameForge(f, want) {
t.Errorf("message = %+v, want %+v", f, want)
}
@@ -151,7 +151,7 @@ func ConvertKept(subject string, data []byte, trees Trees, files Files, kept Kep
if action == delivery.PullUpdated && p.ModuleDirsSaid {
f.Dirs, f.DirsKnown = p.ModuleDirs, true
var err error
if f.Manifests, f.Locks, f.Packages, _, err = inputsRead(p.Owner, p.Repo, p.HeadSHA, p.ModuleDirs, files); err != nil {
if f.Manifests, f.Locks, _, err = inputsRead(p.Owner, p.Repo, p.HeadSHA, p.ModuleDirs, files); err != nil {
return Message{}, fmt.Errorf("%w: %s: the manifests of %s/%s at the head %s: %v", ErrRetry, subject, p.Owner, p.Repo, p.HeadSHA, err)
}
}
@@ -190,15 +190,15 @@ type read struct {
// order the calls finish in, and the error is that of the first failing file in the order of dirs and LockFiles. After
// a failure no further file is asked for, and inputsOf returns only when every call it started has returned.
func inputsOf(owner, repo, ref string, dirs []string, files Files) (manifests map[string]string, locks map[string]map[string]string, err error) {
manifests, locks, _, _, err = inputsRead(owner, repo, ref, dirs, files)
manifests, locks, _, err = inputsRead(owner, repo, ref, dirs, files)
return manifests, locks, err
}
// inputsRead is inputsOf, and also answers the mesh packages each directory's go.sum names (delivery.MeshPackagesOf,
// novox/hq ADR 0573 §2; issue 1585) and, on a failure too, the directories whose manifest and lock files were all read,
// with their manifests and lock hashes: a merge keeps them by their tree, so its next try reads only the rest (issue
// 1126).
func inputsRead(owner, repo, ref string, dirs []string, files Files) (manifests map[string]string, locks map[string]map[string]string, packages map[string][]string, whole []string, err error) {
// inputsRead is inputsOf, and also answers, on a failure too, the directories whose manifest and lock files were all
// read, with their manifests and lock hashes: a merge keeps them by their tree, so its next try reads only the rest
// (issue 1126). It reads no mesh package a go.sum names: the indexer alone computes what a fingerprint takes (novox/hq
// ADR 0593 §2), so the message's Packages stay empty.
func inputsRead(owner, repo, ref string, dirs []string, files Files) (manifests map[string]string, locks map[string]map[string]string, whole []string, err error) {
at := func(dir, name string) string {
if dir == "" {
return name
@@ -242,7 +242,6 @@ func inputsRead(owner, repo, ref string, dirs []string, files Files) (manifests
wg.Wait()
manifests = map[string]string{}
locks = map[string]map[string]string{}
packages = map[string][]string{}
broken := map[string]bool{} // a directory with a file not read (failed, or never asked after a failure)
for _, r := range reads {
switch {
@@ -262,11 +261,6 @@ func inputsRead(owner, repo, ref string, dirs []string, files Files) (manifests
locks[r.dir] = map[string]string{}
}
locks[r.dir][path.Base(r.path)] = hex.EncodeToString(sum[:])
if path.Base(r.path) == "go.sum" {
if p := delivery.MeshPackagesOf(r.raw); len(p) > 0 {
packages[r.dir] = p
}
}
}
}
for _, dir := range dirs {
@@ -278,9 +272,8 @@ func inputsRead(owner, repo, ref string, dirs []string, files Files) (manifests
for dir := range broken {
delete(manifests, dir)
delete(locks, dir)
delete(packages, dir)
}
return manifests, locks, packages, whole, err
return manifests, locks, whole, err
}
if len(manifests) == 0 {
manifests = nil
@@ -288,10 +281,7 @@ func inputsRead(owner, repo, ref string, dirs []string, files Files) (manifests
if len(locks) == 0 {
locks = nil
}
if len(packages) == 0 {
packages = nil
}
return manifests, locks, packages, whole, nil
return manifests, locks, whole, nil
}
// merged is the merged message of a merge into the trunk: the trees of the merge commit, asked once.
@@ -312,19 +302,15 @@ func merged(subject string, p pull, trees Trees, files Files, kept Kept) (Messag
names = append(names, dir)
}
slices.Sort(names)
manifests, locks, packages, toRead := keptInputs(names, dirs, kept)
read, readLocks, readPackages, whole, err := inputsRead(p.Owner, p.Repo, p.MergeSHA, toRead, files)
manifests, locks, toRead := keptInputs(names, dirs, kept)
read, readLocks, whole, err := inputsRead(p.Owner, p.Repo, p.MergeSHA, toRead, files)
if err != nil {
keep(whole, dirs, read, readLocks, readPackages, kept) // what was read whole is not asked again at the next try
keep(whole, dirs, read, readLocks, kept) // what was read whole is not asked again at the next try
return Message{}, fmt.Errorf("%w: %s: the manifests of %s at the merge commit %s (%d of %d module directories read from the forge, the others kept by their tree): %v", ErrRetry, subject, repository, p.MergeSHA, len(toRead), len(names), err)
}
keep(toRead, dirs, read, readLocks, readPackages, kept)
keep(toRead, dirs, read, readLocks, kept)
maps.Copy(manifests, read)
maps.Copy(locks, readLocks)
maps.Copy(packages, readPackages)
if len(packages) == 0 {
packages = nil
}
if len(manifests) == 0 {
manifests = nil
}
@@ -336,7 +322,7 @@ func merged(subject string, p pull, trees Trees, files Files, kept Kept) (Messag
return Message{}, unreadable(subject, "%v", err)
}
f := delivery.Forge{Kind: delivery.ForgeMerged, Repository: repository, Branch: p.Head, Number: p.Number,
Head: p.HeadSHA, Merge: p.MergeSHA, Root: root, Trees: dirs, Manifests: manifests, Locks: locks, Packages: packages}
Head: p.HeadSHA, Merge: p.MergeSHA, Root: root, Trees: dirs, Manifests: manifests, Locks: locks}
if repository == CatalogRepository {
// The catalogue's predefined list at the merge (novox/hq ADR 0565 §2): the indexer keeps it for mesh-config.
raw, err := files(p.Owner, p.Repo, PredefinedFile, p.MergeSHA)
@@ -79,9 +79,9 @@ func TestMergedCarriesManifestsAndLocks(t *testing.T) { // 1448/AC1
if want := map[string]map[string]string{"modules/a": {"go.sum": hexSum(sum)}}; !reflect.DeepEqual(f.Locks, want) {
t.Errorf("locks = %v, want %v", f.Locks, want)
}
// Issue 1585 (ADR 0573 §2): and the mesh packages each go.sum names.
if want := map[string][]string{"modules/a": {"git.novox.be/novox/mesh-sdk/go"}}; !reflect.DeepEqual(f.Packages, want) {
t.Errorf("packages = %v, want %v", f.Packages, want)
// ADR 0593 §2 (D6): the bridge reads no mesh package; only the indexer computes what a fingerprint takes.
if len(f.Packages) != 0 || strings.Contains(string(msg.Data), `"packages"`) {
t.Errorf("the merged message carries packages: %v", f.Packages)
}
// ADR 0567: placement is the seat mesh-placement's: a merge of the catalogue carries no placement file and asks the
// forge for none.
@@ -134,8 +134,7 @@ func TestPullWithKnownDirsCarriesTheSameAtTheHead(t *testing.T) { // 1448/AC2
t.Fatal(err)
}
f, _ := delivery.DecodeForge(msg.Data)
if !reflect.DeepEqual(f.Manifests, map[string]string{"modules/a": string(manifest)}) || len(f.Locks) != 1 ||
!reflect.DeepEqual(f.Packages, map[string][]string{"modules/a": {"git.novox.be/novox/mesh-sdk/go"}}) {
if !reflect.DeepEqual(f.Manifests, map[string]string{"modules/a": string(manifest)}) || len(f.Locks) != 1 || len(f.Packages) != 0 {
t.Errorf("manifests %v locks %v packages %v", f.Manifests, f.Locks, f.Packages)
}
if len(forge.calls) == 0 || forge.calls[0].ref != head {
@@ -394,14 +393,5 @@ func mergeReadsOnlyChangedDirs(t *testing.T) {
if len(failing.calls) != 3 {
t.Errorf("the try after a failure read %d files, want only modules/b's 3", len(failing.calls))
}
// Issue 1585: inputs kept before the bridge read the packages are read again, so no module loses the packages it uses.
for tree, in := range kept2 {
in.Packages, in.PackagesRead = nil, false
kept2[tree] = in
}
failing.calls = nil
again, err = ConvertKept(mergedSubject, mergedBody(nil), treesOf(&calls, "r1", trees, nil), failing.get, kept2)
if err != nil || string(again.Data) != string(whole.Data) || len(failing.calls) != 6 {
t.Errorf("inputs kept without their packages: %d files read, want 6; same message: %v (%v)", len(failing.calls), string(again.Data) == string(whole.Data), err)
}
// ADR 0593 §2: inputs an older bridge kept with the packages it read are taken as they are; their packages are left.
}
@@ -7,14 +7,12 @@ package bridge
// every byte under it, so the manifest and the lock-file hashes read once at a tree hash are those of every later commit
// whose directory has the same tree: a merge reads the forge only for the directories it changed.
// Inputs is what the bridge read of one module directory at one tree hash: its manifest, its lock files' hex sha256
// by name (none when it has none) and the mesh packages its go.sum names (novox/hq ADR 0573 §2). PackagesRead says the
// packages were read: a value kept before the bridge read them is not taken, since its packages are not known.
// Inputs is what the bridge read of one module directory at one tree hash: its manifest and its lock files' hex sha256
// by name (none when it has none). The bridge reads no mesh package (novox/hq ADR 0593 §2: only the indexer computes
// what a fingerprint takes); a value kept with the packages an older bridge read is taken, and those are left.
type Inputs struct {
Manifest string `json:"manifest"`
Locks map[string]string `json:"locks,omitempty"`
Packages []string `json:"packages,omitempty"`
PackagesRead bool `json:"packages-read,omitempty"`
Manifest string `json:"manifest"`
Locks map[string]string `json:"locks,omitempty"`
}
// Kept holds Inputs by a directory's git tree hash (the bridge's state bucket inputs). Get answers false for a tree it
@@ -25,8 +23,8 @@ type Kept interface {
}
// keptInputs splits dirs into the inputs kept for their tree hash and the directories still to read from the forge.
func keptInputs(dirs []string, trees map[string]string, kept Kept) (manifests map[string]string, locks map[string]map[string]string, packages map[string][]string, read []string) {
manifests, locks, packages = map[string]string{}, map[string]map[string]string{}, map[string][]string{}
func keptInputs(dirs []string, trees map[string]string, kept Kept) (manifests map[string]string, locks map[string]map[string]string, read []string) {
manifests, locks = map[string]string{}, map[string]map[string]string{}
for _, dir := range dirs {
tree := trees[dir]
if kept == nil || tree == "" {
@@ -34,7 +32,7 @@ func keptInputs(dirs []string, trees map[string]string, kept Kept) (manifests ma
continue
}
in, ok := kept.Get(tree)
if !ok || !in.PackagesRead {
if !ok {
read = append(read, dir)
continue
}
@@ -42,22 +40,19 @@ func keptInputs(dirs []string, trees map[string]string, kept Kept) (manifests ma
if len(in.Locks) > 0 {
locks[dir] = in.Locks
}
if len(in.Packages) > 0 {
packages[dir] = in.Packages
}
}
return manifests, locks, packages, read
return manifests, locks, read
}
// keep puts what was read of each directory under its tree hash.
func keep(dirs []string, trees map[string]string, manifests map[string]string, locks map[string]map[string]string, packages map[string][]string, kept Kept) {
func keep(dirs []string, trees map[string]string, manifests map[string]string, locks map[string]map[string]string, kept Kept) {
if kept == nil {
return
}
for _, dir := range dirs {
raw, ok := manifests[dir]
if tree := trees[dir]; ok && tree != "" {
kept.Put(tree, Inputs{Manifest: raw, Locks: locks[dir], Packages: packages[dir], PackagesRead: true})
kept.Put(tree, Inputs{Manifest: raw, Locks: locks[dir]})
}
}
}