Begin the record of merges at the earliest merge kept on any trunk, not per trunk (ADR 0463 section 2, issue 1336)
mesh/merge-gate head 9f0d9098b735 merged into trunk d163304e3ff1 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 9f0d9098b735 merged into trunk d163304e3ff1 — pass: its merge-check.sh passed
mesh/delivery delivered

This commit is contained in:
2026-10-20 14:49:10 +00:00
parent 3045af4722
commit 9f0d9098b7
6 changed files with 116 additions and 39 deletions
+10
View File
@@ -75,6 +75,16 @@ func (i *Inventory) KeptOnBranch(ctx context.Context, repository, branch string)
return i.chainKeptOnBranch(ctx, c, repository, branch)
}
// EarliestKept says the controller keeps a merge of some repository's branch, and when it heard the first of all: where its record of
// merges begins on the whole mesh (novox/hq ADR 0463). A merge made before then is history on every trunk.
func (i *Inventory) EarliestKept(ctx context.Context) (bool, time.Time, error) {
c, err := i.theChain(ctx)
if err != nil {
return false, time.Time{}, err
}
return i.chainEarliestKept(ctx, c)
}
// AloneMerges is every merge waiting to be walked on its own commit, the newest first (ADR 0276 decision 3).
func (i *Inventory) AloneMerges(ctx context.Context) ([]BatchedMerge, error) {
c, err := i.theChain(ctx)
+18
View File
@@ -437,6 +437,24 @@ func (i *Inventory) chainKeptOnBranch(ctx context.Context, c *chain.Chain, repos
return true, first.UTC(), nil
}
// chainEarliestKept is when the controller heard the first merge it holds a record of, on any repository and branch.
func (i *Inventory) chainEarliestKept(ctx context.Context, c *chain.Chain) (bool, time.Time, error) {
w, err := i.chainWinners(ctx, c)
if err != nil {
return false, time.Time{}, err
}
var first time.Time
for _, h := range w {
if first.IsZero() || h.m.Heard.Before(first) {
first = h.m.Heard
}
}
if first.IsZero() {
return false, time.Time{}, nil
}
return true, first.UTC(), nil
}
func (i *Inventory) chainAloneMerges(ctx context.Context, c *chain.Chain) ([]BatchedMerge, error) {
w, err := i.chainWinners(ctx, c)
if err != nil {
+25
View File
@@ -347,3 +347,28 @@ func TestThePlansAreReadFromAViewKeptByOneWatch(t *testing.T) {
return len(open) == 3
})
}
// Where the record of merges begins is the earliest merge kept on any repository and branch (novox/hq ADR 0463 §2).
func TestTheRecordOfMergesBeginsAtTheEarliestMergeKeptOnAnyTrunk(t *testing.T) {
inv, c := anInventoryOnTheChain(t)
ctx := context.Background()
if kept, _, err := inv.chainEarliestKept(ctx, c); err != nil || kept {
t.Fatalf("an inventory with no merge kept says it keeps one: %v %v", kept, err)
}
if err := inv.chainSavePlans(ctx, c, []*Plan{aBatch("plan-1", plansAt), aBatch("plan-2", plansAt)}, nil); err != nil {
t.Fatal(err)
}
later := aMerge("m-late", "plan-1")
later.Repository, later.Branch, later.Heard = "novox/mesh-sdk", "main", plansAt.Add(48*time.Hour)
earlier := aMerge("m-early", "plan-2")
earlier.Repository, earlier.Branch, earlier.Heard = "novox/photos", "nox-mesh", plansAt.Add(time.Hour)
for _, m := range []BatchedMerge{later, earlier} {
if added, err := inv.chainAddMerge(ctx, c, m); err != nil || !added {
t.Fatalf("%s: %v %v", m.Commit, added, err)
}
}
kept, first, err := inv.chainEarliestKept(ctx, c)
if err != nil || !kept || !first.Equal(plansAt.Add(time.Hour)) {
t.Fatalf("the record begins at %v (%v, %v), want %v", first, kept, err, plansAt.Add(time.Hour))
}
}