mesh/merge-gate pass: builds 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 covered after its failure: a later walk that contains it was delivered: plan-1791711936761655786 (novox/mesh-catalog at 83c64dcd into main,…
A stop is final, so a later merge whose queued walk would carry the modules now holds it off; a stopped one stays in stalled until a later delivered merge carries them. A walk counts only modules it sent, and an old walk record is read again rather than taken as building nothing.
212 lines
8.9 KiB
Go
212 lines
8.9 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// novox/hq issue 474: a merge batched behind a later one of its branch (ADR 0276) whose walk ended done before
|
|
// any word was asked — the controller judged the batch's files moved nothing it holds, "nothing to walk" — was
|
|
// left published, waiting for "its walk's start", which a done walk never has. Its walk's record settles it: it
|
|
// is delivered when that walk moved its modules, covered when a later delivered merge carried them (ADR 0270),
|
|
// and otherwise stopped, saying why — never left waiting.
|
|
//
|
|
// The shape is the night's own (2026-10-11): mesh-controller #215 (merged as fbc7bc97, its check's plan moving
|
|
// build-agent) and #217 (merged as 8305e4e3 a minute later, moving nothing) in one batch, walked as
|
|
// plan-1791681624611167730 at 8305e4e3: done, no tier, still saying it awaits mesh-delivery.
|
|
|
|
const (
|
|
ctl474 = "novox/mesh-controller"
|
|
head215 = "d2e9dc61590f950b"
|
|
merge215 = "fbc7bc976b3c115b"
|
|
head217 = "68fbd99e894443ce"
|
|
merge217 = "8305e4e3d126594d"
|
|
head219 = "ef551fdfb6b29d4a"
|
|
merge219 = "0a2e58c0704ccc09"
|
|
batchWalk = "plan-1791681624611167730"
|
|
walk219 = "plan-1791686195314774150"
|
|
nothingNote = "moved nothing the mesh holds: nothing to walk"
|
|
)
|
|
|
|
var at215 = time.Date(2026, 10, 11, 1, 20, 19, 0, time.UTC)
|
|
|
|
// theNight is #215 and #217 checked and merged, and their batch's walk done with no tier, as the controller said it.
|
|
func theNight(t *testing.T) *world {
|
|
w := newWorldOver(t, newMemStore(), newFakeController(), newFakeForge(), at215.Add(-time.Minute))
|
|
p215 := pr(ctl474, 215, head215, "fix/431-make-check-runs-what-the-gate-runs", "Makefile")
|
|
p217 := pr(ctl474, 217, head217, "fix/432-a-test-reads-what-it-carries", "cmd/mesh-controller/check_test.go")
|
|
w.h.PullUpdated(p215)
|
|
w.h.PullUpdated(p217)
|
|
w.h.Checked(verdictMoving(ctl474, 215, head215, "build-agent"))
|
|
w.h.Checked(verdictMoving(ctl474, 217, head217))
|
|
w.later(time.Minute)
|
|
w.h.PullMerged(mergedAt(p215, merge215, at215))
|
|
w.later(time.Minute)
|
|
w.h.PullMerged(mergedAt(p217, merge217, at215.Add(56*time.Second)))
|
|
w.later(90 * time.Second)
|
|
cut := w.now
|
|
w.h.WalkMoved(w.ctl.put(Walk{ID: batchWalk, Repository: ctl474, Branch: "main", Commit: merge217, Created: cut,
|
|
Updated: cut, State: walkDone, Tiers: [][]string{}, Modules: map[string]*WalkModule{}, Note: nothingNote,
|
|
Delivery: &WalkDelivery{Awaits: "mesh-delivery", Merges: []WalkMerge{
|
|
{Repository: ctl474, Commit: merge215, Carried: merge217}, {Repository: ctl474, Commit: merge217}}}}))
|
|
w.settleAll()
|
|
return w
|
|
}
|
|
|
|
// The live delivery: its walk ended without moving build-agent, and a later merge (#219) delivered build-agent
|
|
// everywhere from main holding its merge — covered, from the walk's record and the forge's word, never waiting.
|
|
func TestAMergeWhoseBatchWalkEndedIsCoveredByALaterDeliveredMerge(t *testing.T) {
|
|
w := theNight(t)
|
|
id := IDOf(ctl474, head215)
|
|
if s := w.state(IDOf(ctl474, head217)); s != Delivered {
|
|
t.Fatalf("#217, which moves nothing, is %s", s)
|
|
}
|
|
w.forge.contains[merge215+" in "+merge219] = true
|
|
deliverLater(w, ctl474, 219, "main", head219, merge219, at215.Add(76*time.Minute), walkDone, "build-agent")
|
|
w.later(2 * time.Minute)
|
|
w.settleAll()
|
|
d := w.delivery(id)
|
|
if d.State != Covered {
|
|
t.Fatalf("#215, its walk %s done with nothing moved and build-agent delivered later by #219, is %s, waiting "+
|
|
"for %q", batchWalk, d.State, w.h.waitsFor(d))
|
|
}
|
|
if why := d.Transitions[len(d.Transitions)-1].Why; !strings.Contains(why, IDOf(ctl474, head219)) {
|
|
t.Fatalf("covered, but not by #219: %q", why)
|
|
}
|
|
if len(w.h.Stalled()) != 0 {
|
|
t.Fatalf("stalled still lists %+v", w.h.Stalled())
|
|
}
|
|
}
|
|
|
|
// With nothing later to carry its modules, it is stopped once the cover test says so and the walk ended past the
|
|
// grace — saying which walk ended without moving what — and not before. Stopped, it stays loud: `stalled` lists it
|
|
// until a later delivered merge carries its modules.
|
|
func TestAMergeWhoseBatchWalkEndedAndNothingCoversItIsStoppedSayingWhy(t *testing.T) {
|
|
w := theNight(t)
|
|
id := IDOf(ctl474, head215)
|
|
w.later(5 * time.Minute)
|
|
w.settleAll()
|
|
if s := w.state(id); s != Published {
|
|
t.Fatalf("within the grace it is %s", s)
|
|
}
|
|
w.later(strandedAfter)
|
|
w.settleAll()
|
|
d := w.delivery(id)
|
|
if d.State != Stopped {
|
|
t.Fatalf("its walk done without build-agent and nothing later carrying it, it is %s, waiting for %q", d.State,
|
|
w.h.waitsFor(d))
|
|
}
|
|
why := d.Transitions[len(d.Transitions)-1].Why
|
|
for _, want := range []string{"never reached the machines", batchWalk, "build-agent", nothingNote} {
|
|
if !strings.Contains(why, want) {
|
|
t.Fatalf("its stop says %q, not %q", why, want)
|
|
}
|
|
}
|
|
lines := w.h.Stalled()
|
|
if len(lines) != 1 || lines[0].ID != id || !strings.Contains(lines[0].Says, "never reached the machines") {
|
|
t.Fatalf("stopped, it is not listed loudly: %+v", lines)
|
|
}
|
|
// A later merge that delivers build-agent ends the listing; the stop itself stays, final.
|
|
w.forge.contains[merge215+" in "+merge219] = true
|
|
deliverLater(w, ctl474, 219, "main", head219, merge219, w.now, walkDone, "build-agent")
|
|
w.later(2 * time.Minute)
|
|
w.settleAll()
|
|
if d := w.delivery(id); d.State != Stopped || d.CoveredBy == "" {
|
|
t.Fatalf("after #219 it is %s, covered by %q", d.State, d.CoveredBy)
|
|
}
|
|
if lines := w.h.Stalled(); len(lines) != 0 {
|
|
t.Fatalf("stalled still lists %+v", lines)
|
|
}
|
|
}
|
|
|
|
// A later merge whose walk is still queued, and whose plan moves what this one's walk left unbuilt, holds the stop
|
|
// off: a stop is final, and that merge may yet cover it (review of issue 474).
|
|
func TestAMergeWhoseBatchWalkEndedWaitsForALaterMergeOnItsWay(t *testing.T) {
|
|
w := theNight(t)
|
|
id := IDOf(ctl474, head215)
|
|
p := pr(ctl474, 219, head219, "fix/334", "internal/builder/builder.go")
|
|
w.h.PullUpdated(p)
|
|
w.h.Checked(verdictMoving(ctl474, 219, head219, "build-agent", "mesh-controller"))
|
|
w.later(3 * time.Minute)
|
|
w.h.PullMerged(mergedAt(p, merge219, w.now))
|
|
w.settleAll()
|
|
w.later(strandedAfter)
|
|
w.settleAll()
|
|
if d := w.delivery(id); d.State != Published {
|
|
t.Fatalf("with #219 on its way it is %s (%s)", d.State, d.Stranded)
|
|
}
|
|
// #219's walk delivers build-agent: covered.
|
|
w.forge.contains[merge215+" in "+merge219] = true
|
|
walk := aWalk(walk219, ctl474, merge219, true)
|
|
walk.Modules = map[string]*WalkModule{"build-agent": {}, "mesh-controller": {}}
|
|
w.h.WalkMoved(w.ctl.put(walk))
|
|
w.settleAll()
|
|
walk = w.ctl.walk(walk219)
|
|
sent := w.now
|
|
for m := range walk.Modules {
|
|
walk.Modules[m] = &WalkModule{State: "built", SentAt: &sent}
|
|
}
|
|
walk.State = walkDone
|
|
w.h.WalkMoved(w.ctl.put(walk))
|
|
w.later(2 * time.Minute)
|
|
w.settleAll()
|
|
if s := w.state(IDOf(ctl474, head219)); s != Delivered {
|
|
t.Fatalf("#219 is %s", s)
|
|
}
|
|
if d := w.delivery(id); d.State != Covered {
|
|
t.Fatalf("after #219 delivered build-agent it is %s, waiting for %q", d.State, w.h.waitsFor(d))
|
|
}
|
|
}
|
|
|
|
// A walk that ended before its word built nothing: a module its record names with why it was not sent is not read
|
|
// as delivered. A merge whose plan moves nothing, answered by such a walk, is delivered.
|
|
func TestAWalkThatEndedBeforeItsWordBuiltNothing(t *testing.T) {
|
|
for _, moves := range [][]string{{"build-agent"}, nil} {
|
|
w := newWorldOver(t, newMemStore(), newFakeController(), newFakeForge(), at215.Add(-time.Minute))
|
|
p := pr(ctl474, 215, head215, "fix/x", "cmd/mesh-builder/main.go")
|
|
w.h.PullUpdated(p)
|
|
w.h.Checked(verdictMoving(ctl474, 215, head215, moves...))
|
|
w.later(time.Minute)
|
|
w.h.PullMerged(mergedAt(p, merge215, at215))
|
|
w.later(2 * time.Minute)
|
|
w.h.WalkMoved(w.ctl.put(Walk{ID: batchWalk, Repository: ctl474, Branch: "main", Commit: merge215, Created: w.now,
|
|
Updated: w.now, State: walkDone, Tiers: [][]string{{"build-agent"}},
|
|
Modules: map[string]*WalkModule{"build-agent": {Why: "not sent: its walk never started"}},
|
|
Delivery: &WalkDelivery{Awaits: "mesh-delivery", Merges: []WalkMerge{{Repository: ctl474, Commit: merge215}}}}))
|
|
w.settleAll()
|
|
d := w.delivery(IDOf(ctl474, head215))
|
|
want := Published
|
|
if len(moves) == 0 {
|
|
want = Delivered
|
|
}
|
|
if d.State != want {
|
|
t.Fatalf("moving %v: it is %s, waiting for %q, not %s", moves, d.State, w.h.waitsFor(d), want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A walk record kept before it named the modules it built, with tiers, is not read as building nothing: the walk is
|
|
// asked for again, and what it answers settles the delivery.
|
|
func TestAnOldWalkRecordIsAskedForAgain(t *testing.T) {
|
|
w := theNight(t)
|
|
id := IDOf(ctl474, head215)
|
|
w.h.mu.Lock()
|
|
d := w.h.deliveries[id]
|
|
d.Walk.Moved, d.Walk.MovedSaid, d.Walk.Tiers = nil, false, 1
|
|
w.h.mu.Unlock()
|
|
if got := w.h.waitsFor(w.delivery(id)); !strings.Contains(got, "walk's record") {
|
|
t.Fatalf("an old record waits for %q", got)
|
|
}
|
|
w.later(time.Minute)
|
|
w.settleAll()
|
|
if d := w.delivery(id); !d.Walk.MovedSaid || d.Walk.Tiers != 0 {
|
|
t.Fatalf("the walk was not read again: %+v", d.Walk)
|
|
}
|
|
w.later(strandedAfter)
|
|
w.settleAll()
|
|
if s := w.state(id); s != Stopped {
|
|
t.Fatalf("read again, it is %s", s)
|
|
}
|
|
}
|