Keep a merge a cut made history, and build a batch's walk at the branch (hq ADR 0276 review)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without …
mesh/repo-check pass: its merge-check.sh passed

This commit is contained in:
jochen
2026-10-10 13:48:31 +02:00
parent 2887691414
commit 0e0e143055
6 changed files with 118 additions and 10 deletions
+24 -2
View File
@@ -153,7 +153,11 @@ func (f following) hearMerge(ctx context.Context, m link.SourceMoved, now time.T
return notNow(err)
}
}
if from, packaging, already := mergeCandidates(m, entries, read); len(from) == 0 && len(packaging) == 0 && len(later) == 0 {
owed, err := owedLate(ctx, inv, m, merged, touches)
if err != nil {
return notNow(err)
}
if from, packaging, already := mergeCandidates(m, entries, read); len(from) == 0 && len(packaging) == 0 && len(later) == 0 && !owed {
// "Already built from it" and "nothing reads it" are different facts, and reading the first
// as the second sends somebody looking for a broken trigger when the mesh is up to date.
if already > 0 {
@@ -216,6 +220,23 @@ func (f following) hearMerge(ctx context.Context, m link.SourceMoved, now time.T
return cutBatchesHeld(ctx, f.open, now)
}
// owedLate says a merge that reads as history is still the record's to answer (review of this change): a cut
// marks every module it moved seen at that moment, so a merge of that branch the bus lost and the catch-up
// hands over later — made before the cut, heard after it — reads as history and was dropped. One that touches
// a module the mesh holds, of a branch whose merges the controller keeps, made since it began keeping them,
// is kept; one made before it began is old news, as before.
func owedLate(ctx context.Context, inv *inventory.Inventory, m link.SourceMoved, merged time.Time,
touches []inventory.Entry) (bool, error) {
if len(touches) == 0 {
return false, nil
}
kept, first, err := inv.KeptOnBranch(ctx, m.Owner+"/"+m.Repo, m.Base)
if err != nil || !kept {
return false, err
}
return !merged.Before(first.Add(-mergeGrace)), nil
}
// answeredByALaterMerge is the plan that answers a merge heard after a later merge of its branch was put in
// one (ADR 0276 decision 4): a batch not yet cut, whose walk plans every file of the repository's merges; an
// open walk, or a walk done, that builds every module this merge would move — built from the branch after the
@@ -595,7 +616,7 @@ func walkAlone(ctx context.Context, open *stores, m inventory.BatchedMerge, now
p := inventory.Plan{ID: fmt.Sprintf("plan-%d", now.UnixNano()), Repository: m.Repository, Branch: m.Branch,
Commit: m.Commit, Merged: m.Merged, Created: now, State: inventory.PlanQueued, Tiers: [][]string{},
Modules: map[string]*inventory.PlanModule{}, Note: "walked alone after a failed walk carried it",
Delivery: &inventory.PlanDelivery{}}
Delivery: &inventory.PlanDelivery{Alone: true}}
if err := inv.SavePlan(ctx, &p); err != nil {
return err
}
@@ -678,6 +699,7 @@ func planBatch(ctx context.Context, open *stores, batch *inventory.Plan, carry [
plan.Delivery = &inventory.PlanDelivery{}
}
plan.Delivery.Merges = named
plan.Delivery.Alone = batch.Delivery != nil && batch.Delivery.Alone
if len(moved) == 0 {
plan.State = inventory.PlanDone
plan.Tiers = [][]string{}