The indexer reads its view of the trunk from the newest trunk revision (milestone 47/G W4.3, second part: the view's read)

The lead's ruling on finding 1: modules becomes an in-memory view of the newest
trunk revision read from catalog-revisions at start, one form of the fact
(ADR 0656 §6). TrunkView gives that view as a store in memory and the revision
it was read at; an empty bucket is an empty view. Not yet wired in main.

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 11:45:56 +01:00
parent 9ab20f14f8
commit e0d5488138
2 changed files with 49 additions and 0 deletions
@@ -9,6 +9,7 @@ import (
"strconv"
"strings"
"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-delivery/sdk/graph"
"git.novox.be/novox/mesh-sdk/go/process"
@@ -224,3 +225,34 @@ func (ix *Indexer) keepTrunkRevision(ctx context.Context, edges map[string]graph
}
return nil
}
// TrunkView is the indexer's view of the trunk read from the newest trunk revision the bucket keeps (the lead's ruling
// on milestone 47/G finding 1; ADR 0656 §6, one form of the fact): a store in memory holding each module's value as
// ModuleValueOf reads it back from its entry, under its modules key, and the revision it was read at (0 when the bucket
// keeps none yet: the view starts empty and the first merge taken in fills it). A lost entry is an error naming the
// module (ReadRevision), never a module left out of the view.
func TrunkView(ctx context.Context, o Objects) (Store, int64, error) {
view := newOverlay(nil)
newest, any, err := newestTrunk(ctx, o)
if err != nil || !any {
return view, 0, err
}
_, entries, err := ReadRevision(ctx, o, RevisionObject(newest))
if err != nil {
return nil, 0, err
}
for _, e := range entries {
key, err := meshcatalogclient.ModulesKey(e.Module)
if err != nil {
return nil, 0, fmt.Errorf("trunk.%d: the entry of %q: %w", newest.Revision, e.Module, err)
}
data, err := meshcatalogclient.EncodeModule(ModuleValueOf(e))
if err != nil {
return nil, 0, fmt.Errorf("trunk.%d: the entry of %s: %w", newest.Revision, e.Module, err)
}
if err := view.Create(ctx, key, data); err != nil {
return nil, 0, fmt.Errorf("trunk.%d: the entry of %s: %w", newest.Revision, e.Module, err)
}
}
return view, newest.Revision, nil
}
@@ -352,6 +352,23 @@ func TestBasesAreComputedFirstAndNoFingerprintTakesTheBuilder(t *testing.T) { //
!slices.ContainsFunc(got, func(e meshcatalog.CatalogEntry) bool { return e.Module == "mesh-tools" && e.Fp == tools.Fp }) {
t.Errorf("trunk.2 read back = %+v, %d entries, %v", back, len(got), err)
}
// W4.3's second part: the view of the trunk read at start is the newest trunk revision, each module's value as its
// entry gives it back; a bucket that keeps none is an empty view at 0.
view, at, err := TrunkView(context.Background(), kept)
if err != nil || at != 2 {
t.Fatalf("the view is read at %d (%v), want 2", at, err)
}
for name, e := range moved {
key, _ := meshcatalogclient.ModulesKey(name)
raw, _, found, _ := view.Get(context.Background(), key)
got, err := meshcatalogclient.DecodeModule(raw)
if want := ModuleValueOf(e); !found || err != nil || !reflect.DeepEqual(got, want) {
t.Errorf("the view's %s = %+v (%v), want %+v", name, got, err, want)
}
}
if empty, at, err := TrunkView(context.Background(), newMemObjects(0)); err != nil || at != 0 || func() bool { k, _ := empty.Keys(context.Background()); return len(k) != 0 }() {
t.Errorf("an empty bucket's view is read at %d (%v), want an empty view at 0", at, err)
}
_ = kept.Delete(context.Background(), EntryObject(next.Entries[0].Digest))
if _, _, err := ReadRevision(context.Background(), kept, "trunk.2"); err == nil || !strings.Contains(err.Error(), next.Entries[0].Module) {
t.Errorf("trunk.2 with a lost entry read without error naming it: %v", err)