Ask a group member's own repo-check when the group is held before its Approve for it (issue 1792)
mesh/merge-gate waiting in the build queue since 23:13
mesh/delivery superseded: a newer head of the same pull request

This commit is contained in:
2026-10-21 23:11:55 +02:00
parent 3793175894
commit f436d1dace
2 changed files with 54 additions and 3 deletions
@@ -380,18 +380,19 @@ func (h *Holder) approval(d *Delivery, now time.Time) (string, func()) {
// A group is not asked while a member's required status is not a success at its head (issue 1275): the group
// says which, and is asked once every member may merge.
var holdMember, holdSays string
var holdOwn bool
var readErr error
for _, m := range reads {
if gid == "" {
break
}
says, _, err := h.notASuccess(m)
says, ownOnly, err := h.notASuccess(m)
if err != nil {
readErr = err
break
}
if says != "" {
holdMember, holdSays = m.id, says
holdMember, holdSays, holdOwn = m.id, says, ownOnly
break
}
}
@@ -421,7 +422,18 @@ func (h *Holder) approval(d *Delivery, now time.Time) (string, func()) {
if readErr != nil || holdSays != "" {
stale()
h.mu.Lock()
defer h.mu.Unlock()
var own func()
// A member's own repository check pending or missing (a sibling's new head put it out of date, issue 1792) is
// asked again here, once for the head: nothing else asks it, and the composed check sets no repository check.
if d := h.deliveries[holdMember]; holdOwn && d != nil && d.Ready != nil {
own = h.askOwnAfterRefusal(d)
}
defer func() {
h.mu.Unlock()
if own != nil {
own()
}
}()
undo()
if g := h.groups[gid]; g != nil && holdSays != "" {
if g.Hold == nil || g.Hold.Kind != "unasked" || g.Hold.Says != holdSays || g.Hold.Member != holdMember {
@@ -0,0 +1,39 @@
package main
import "testing"
// Issue 1792: a sibling's new head put a member's own mesh/repo-check back to pending ("out of date: judged beside ..."),
// and nothing asked it again. The composed check passed, the group stayed held ("unasked") on the member's pending
// status, and the operator's Approve was never asked. The group's hold on a member's own pending repository check asks
// that check, once for the head, with no composed check (ADR 0382 §2: each member's own check stays required).
func TestAGroupHeldBeforeItsAskOnAMembersOwnPendingRepoCheckAsksThatCheckOnce(t *testing.T) {
w := newWorld(t)
w.op.auto = false
w.h.PullUpdated(pr(gCtl, 1, gHc, "feat/g", "internal/a.go"))
w.h.PullUpdated(pr(gCat, 2, gHk, "feat/g", "modules/app/module.json"))
w.h.Checked(judged(gCtl, 1, gHc, "pass", gTrunk0))
w.h.Checked(judged(gCat, 2, gHk, "pass", gTrunk0))
w.settleAll()
g := w.h.groups["feat/g"]
setChecks(w, gCat, gHk, "success", "success")
setChecks(w, gCtl, gHc, "success", "pending")
asked := len(w.ctl.checks)
w.h.Checked(CheckedEvent{Group: "feat/g", ID: g.Check.Asked, Verdict: "pass", Summary: "composed",
Members: []struct {
Owner string `json:"owner"`
Repo string `json:"repo"`
Commit string `json:"commit"`
Trunk string `json:"trunk,omitempty"`
}{{Owner: "novox", Repo: "mesh-controller", Commit: gHc, Trunk: gTrunk0[:12]},
{Owner: "novox", Repo: "mesh-catalog", Commit: gHk, Trunk: gTrunk0[:12]}}})
w.settleAll()
w.later(time30s)
w.settleAll()
own, composed := ownChecks(w, asked)
if len(composed) != 0 || len(own) != 1 || own[0] != ":"+IDOf(gCtl, gHc) {
t.Fatalf("own checks %v, composed %v; wanted the member's own check asked once", own, composed)
}
if w.op.count() != 0 {
t.Fatalf("the operator was asked with a required status pending: %d", w.op.count())
}
}