From e8a74765f8a6f1815b86fbdf9ea47cf6a8453f60 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 18 Oct 2026 01:18:13 +0200 Subject: [PATCH] A composed check that lands judged against a trunk that has moved is out of date and asked again (issue 1047, review round 1) --- .../cmd/mesh-delivery/group_test.go | 41 +++++++++++++++++++ .../mesh-delivery/cmd/mesh-delivery/holder.go | 13 ++++++ 2 files changed, 54 insertions(+) diff --git a/modules/mesh-delivery/cmd/mesh-delivery/group_test.go b/modules/mesh-delivery/cmd/mesh-delivery/group_test.go index 35a91e24..faa9dbf4 100644 --- a/modules/mesh-delivery/cmd/mesh-delivery/group_test.go +++ b/modules/mesh-delivery/cmd/mesh-delivery/group_test.go @@ -316,4 +316,45 @@ func TestATrunkMoveAsksTheCheckOfAGroupMemberWhoseApproveIsNotAskedYet(t *testin if len(w.ctl.checks[asked:]) != 1 { t.Fatalf("asked twice: %v", w.ctl.checks[asked:]) } + // The composed check that was on its way when the trunk moved answers, judged against the old trunk (ADR 0403 §6): it is + // out of date as it lands, so the group does not become ready on it and the operator is not asked. + g := w.h.groups["feat/g"] + w.later(time.Minute) + w.h.Checked(CheckedEvent{Group: "feat/g", ID: g.Check.Asked, Verdict: "pass", Summary: "composed on the old trunk", + 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() + if w.op.count() != 0 { + t.Fatalf("the operator was asked on a composed pass judged against the old trunk: %d", w.op.count()) + } + // The moved member's fresh verdict arrives: the composed check is asked again, and only its pass asks the operator. + w.later(time.Minute) + composedBefore := len(w.ctl.checks) + w.h.Checked(judged(gCat, 2, gHk, "pass", gT1)) + w.settleAll() + last := w.ctl.checks[len(w.ctl.checks)-1] + if len(w.ctl.checks) == composedBefore || !strings.HasPrefix(last, "feat/g:") { + t.Fatalf("the composed check was not asked again on the fresh verdict: %v", w.ctl.checks[composedBefore:]) + } + if w.op.count() != 0 { + t.Fatalf("the operator was asked before the new composed check passed: %d", w.op.count()) + } + g = w.h.groups["feat/g"] + w.h.Checked(CheckedEvent{Group: "feat/g", ID: g.Check.Asked, Verdict: "pass", Summary: "composed on the new trunk", + 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: gT1[:12]}}}) + w.settleAll() + if w.op.count() != 1 { + t.Fatalf("the operator is asked once, on the new pass: %d", w.op.count()) + } } diff --git a/modules/mesh-delivery/cmd/mesh-delivery/holder.go b/modules/mesh-delivery/cmd/mesh-delivery/holder.go index 386d2729..77f7013b 100644 --- a/modules/mesh-delivery/cmd/mesh-delivery/holder.go +++ b/modules/mesh-delivery/cmd/mesh-delivery/holder.go @@ -829,6 +829,19 @@ func (h *Holder) Checked(c CheckedEvent) { } } } + // A composed check that was on its way when a trunk moved lands judged against the old trunk: out of date as it lands + // (novox/hq ADR 0403 §6, issue 1047), and asked again once a member is judged again. + if g.Check.OutOfDate == "" { + for id, judged := range g.Check.Trunks { + if d := h.deliveries[id]; d != nil && d.MergedAs == "" && !d.State.Final() && d.TrunkNow != "" && !sameCommit(judged, d.TrunkNow) { + g.Check.OutOfDate = fmt.Sprintf("composed check out of date: %s was judged against trunk %s, the trunk is now %s", + id, shortOf(judged, 12), shortOf(d.TrunkNow, 12)) + g.Check.OutOfDateAt = now + h.Logf("[mesh-delivery] group %s: %s", g.ID, g.Check.OutOfDate) + break + } + } + } g.Updated = now h.keepGroup(g) h.Logf("[mesh-delivery] group %s composed: %s — %s", g.ID, c.Verdict, c.Summary)