From f5d4b2f86ed47430eb369cea6a4b4bf180c94ed5 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 28 Oct 2026 11:12:21 +0100 Subject: [PATCH] Gate: take a retargeted pull request's new base, so a retarget to main is seeded and asked and one away from main is not (issue 1422) A re-announcement updated the base head but never the base: a pull request retargeted from nox-mesh to main stayed 'into nox-mesh' and was never asked or seeded, and one retargeted away from main kept its open ask. --- .../cmd/mesh-delivery-gate/holder.go | 23 ++++++++++++++ .../issue1726_trunk_test.go | 31 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder.go index c8c3d02a..b2337024 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder.go @@ -352,6 +352,8 @@ func (h *Holder) pullUpdated(p PullEvent) (isNew bool) { // A delivery made from its verdict before its head was heard (issue 1726) holds none of what an announcement // carries: filled from this one, so its base is known and the operator is asked. h.fillFromAnnouncement(d, p) + // A pull request retargeted to another base (review of issue 1422): what it merges into is the forge's word now. + h.retarget(d, p) // The review record, corrected in the description without a push, is read again (ADR 0421 ยง5). if by, at := ReviewIn(p.Body); p.Body != "" && (by != d.ReviewedBy || at != d.ReviewedAt) { d.ReviewedBy, d.ReviewedAt = by, at @@ -453,6 +455,27 @@ func (h *Holder) fillFromAnnouncement(d *Delivery, p PullEvent) { h.Poke() } +// retarget takes the base a live, unmerged delivery's pull request was announced again with when it differs from the one +// kept: into main it is asked and merged as any other, and source.main may be seeded from it (seedTrunks); out of main an +// ask nobody answered yet is taken back, and the next look says it is not asked (ready.go). A delivery whose base is not yet +// known is fillFromAnnouncement's. Said in the log. Under the lock. +func (h *Holder) retarget(d *Delivery, p PullEvent) { + if p.Base == "" || d.Base == "" || p.Base == d.Base || d.State.Final() || d.MergedAs != "" || + (d.Ready != nil && d.Ready.Merged != nil) { + return + } + from := d.Base + d.Base = p.Base + if r := d.Ready; r != nil { + r.OtherBase = "" + if r.AskID != "" && r.Approved == nil { + h.cancels = append(h.cancels, h.withdrawAsk(r.AskID, fmt.Sprintf("%s was retargeted from %s to %s", d.ID, from, p.Base))) + } + } + h.Logf("[mesh-delivery] %s: its pull request was retargeted from %s to %s", d.ID, from, p.Base) + h.keep(d) +} + // PullEdited takes a pull request's edit (novox/hq issue 1079): its description is read again for the review record, the // `after:` lines and the issues it names, exactly as an announcement reads them. An Approve ask still unanswered whose words // the new record changes is withdrawn and asked again with it read, without a word on the pull request: nothing was diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/issue1726_trunk_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/issue1726_trunk_test.go index 0f3bea30..4848162d 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/issue1726_trunk_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/issue1726_trunk_test.go @@ -98,6 +98,37 @@ func TestStartingWritesTheNewestMergeOfEachRepositoryNotYetInSourceMain(t *testi t.Fatalf("seeded with %s; wanted %s, the base head of the latest announcement", got, trunkF) } + // A green pull request retargeted from nox-mesh to main is seeded and asked; one retargeted from main to nox-mesh has + // its ask taken back and is not merged (review round 2). + retarget := func(from, to string) (*world, int) { + w := newWorld(t) + w.op.auto = false + p := pr(never, 7, readyHead, "feat/x", "src/a.go") + p.Base, p.BaseSHA = from, trunkN + w.h.PullUpdated(p) + w.h.Checked(judgedAt(never, 7, readyHead, "pass", "t1t1t1t1t1t1")) + open := openPull(never, 7, readyHead) + open.Base = from + w.forge.open = []OpenPull{open} + w.h.SeeOpenPulls() + w.settleAll() + before := len(w.op.open()) + p.Base, open.Base = to, to + w.forge.open = []OpenPull{open} + w.h.PullUpdated(p) + w.h.SeeOpenPulls() + w.settleAll() + w.later(2 * time.Minute) + w.settleAll() + return w, before + } + if r, before := retarget("nox-mesh", "main"); before != 0 || r.source.main == nil || r.source.main.Heads[never] != trunkN || len(r.op.open()) != 1 { + t.Fatalf("retargeted to main: %d asks before, source.main %+v, open asks %v; wanted none before, then seeded at %s and asked", before, r.source.main, r.op.open(), trunkN) + } + if r, before := retarget("main", "nox-mesh"); before != 1 || len(r.op.open()) != 0 || len(r.forge.merges) != 0 || !strings.Contains(r.logs(), "retargeted") { + t.Fatalf("retargeted to nox-mesh: %d asks before, open asks %v, merges %v; wanted one before, then none and the retarget said", before, r.op.open(), r.forge.merges) + } + o := newWorld(t) o.source.main = &sdk.Source{V: sdk.Version, Group: "newer", Heads: map[string]string{i1726Repo: i1726Merge2}} o.h.PullUpdated(pr(i1726Repo, 471, head2, "fix/b", "modules/app/b.go"))