Send the announced base head with a check mesh-delivery asks
mesh/merge-gate pass: builds gitea, mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
mesh/delivery-group group feat/341-a-merge-check-judges-the-merge delivered: every member is delivered

The controller names it as the trunk commit of a verdict no build judges; a
recheck without it was answered with none (novox/hq ADR 0323).
This commit is contained in:
2026-10-12 17:52:20 +02:00
parent 434506f9b1
commit 41b6e10077
4 changed files with 38 additions and 3 deletions
@@ -279,6 +279,8 @@ type PullEvent struct {
ModuleDirs []string `json:"module_dirs,omitempty"`
ModuleDirsSaid bool `json:"module_dirs_said,omitempty"`
HeadChecks map[string]string `json:"head_checks,omitempty"`
// BaseSHA is the base branch's head when the head was announced (novox/hq ADR 0323).
BaseSHA string `json:"base_sha,omitempty"`
}
func (p PullEvent) repository() string { return strings.ToLower(p.Owner + "/" + p.Repo) }
@@ -300,7 +302,11 @@ func (h *Holder) PullUpdated(p PullEvent) {
if d := h.deliveries[id]; d != nil {
// Announced again: nothing new — but for a delivery whose verdict is out of date, the announcement is its
// check asked again against the trunk's head (the merge tool's ask, novox/hq ADR 0323 §5), and its bound
// runs from now (§7).
// runs from now (§7). The base head it was announced with is kept, for a check this owner asks after it.
if p.BaseSHA != "" && d.BaseSHA != p.BaseSHA {
d.BaseSHA = p.BaseSHA
h.keep(d)
}
if d.State == Proposed && d.outOfDate() != "" && d.AskedAgain == nil {
d.AskedAgain = &now
h.keep(d)
@@ -322,7 +328,7 @@ func (h *Holder) PullUpdated(p PullEvent) {
d := &Delivery{ID: id, Repository: p.repository(), Commit: p.HeadSHA, Number: p.Number, Title: p.Title,
Base: p.Base, Branch: p.Head, HTMLURL: p.HTMLURL, CloneURL: p.CloneURL, Paths: p.Paths,
PathsTruncated: p.PathsTruncated, Removed: p.Removed, ModuleDirs: p.ModuleDirs, ModuleDirsSaid: p.ModuleDirsSaid,
After: AfterIn(p.Body), Created: now}
After: AfterIn(p.Body), Created: now, BaseSHA: p.BaseSHA}
t, err := Apply(d, EvAnnounced, false, Facts{Now: now, Why: fmt.Sprintf("%s#%d's head announced", d.Repository, d.Number)})
if err != nil {
h.refuse(err)
@@ -438,3 +438,26 @@ func TestARebasedMemberAfterAnotherWasDeliveredIsComposedAgain(t *testing.T) {
t.Fatalf("the rebased member is held: %s", d.HeldWhy)
}
}
// A check this owner asks — a recheck — sends the base head the forge last announced the head with, so the
// controller's verdict for a head no build judges names its trunk commit (ADR 0323 §3, review of mesh-controller#271).
func TestARecheckSendsTheAnnouncedBaseHead(t *testing.T) {
const repo = "novox/mesh-tools-go"
const head = "1a2b3c4d5e6f7a8b"
w := newWorld(t)
p := pr(repo, 3, head, "x")
p.BaseSHA = trunk0
w.h.PullUpdated(p)
id := IDOf(repo, head)
if m := MemberOf(w.delivery(id)); m.BaseSHA != trunk0 {
t.Fatalf("the member sends base head %q", m.BaseSHA)
}
p.BaseSHA = merge1 // announced again, by the merge tool
w.h.PullUpdated(p)
if m := MemberOf(w.delivery(id)); m.BaseSHA != merge1 {
t.Fatalf("announced again, the member sends base head %q", m.BaseSHA)
}
if raw := string(mustJSON(MemberOf(w.delivery(id)))); !strings.Contains(raw, `"base_sha":"`+merge1+`"`) {
t.Fatalf("the member as the controller reads it: %s", raw)
}
}
@@ -53,6 +53,9 @@ type Delivery struct {
// into its trunk landed as (novox/hq ADR 0323 §7). A verdict that names another trunk commit is out of date.
// Empty until a merge into its trunk is heard after it was made.
TrunkNow string `json:"trunk_now,omitempty"`
// BaseSHA is the base branch's head the forge last announced its head with (ADR 0323): sent with a check this
// owner asks, for a verdict no build judges to name its trunk commit.
BaseSHA string `json:"base_sha,omitempty"`
// AskedAgain is when its head was announced again while its verdict was out of date — the merge tool's ask of
// its check against the trunk's head (ADR 0323 §5) — and no verdict has come since: its one-hour bound runs from
// then. Nil while nothing is asked, and cleared by a verdict or another move of its trunk.
@@ -31,13 +31,16 @@ type Member struct {
ModuleDirsSaid bool `json:"module_dirs_said,omitempty"`
CloneURL string `json:"clone_url,omitempty"`
After []string `json:"after,omitempty"`
// BaseSHA is the base branch's head the forge last announced the head with (novox/hq ADR 0323): the trunk commit
// the controller's verdict names when no build judges the head.
BaseSHA string `json:"base_sha,omitempty"`
}
// MemberOf is a delivery as a member.
func MemberOf(d *Delivery) Member {
return Member{ID: d.ID, Repository: d.Repository, Base: d.Base, Head: d.Commit, Number: d.Number, Paths: d.Paths,
PathsTruncated: d.PathsTruncated, Removed: d.Removed, ModuleDirs: d.ModuleDirs, ModuleDirsSaid: d.ModuleDirsSaid,
CloneURL: d.CloneURL, After: d.After}
CloneURL: d.CloneURL, After: d.After, BaseSHA: d.BaseSHA}
}
// Order is a group's order as the controller works it out: each pair with the rules it won over by