Hear the gate's transitions beside today's delivery module's in mesh-issues, and name the gate among the owners (issue 1350)

This commit is contained in:
2026-10-20 00:58:01 +00:00
parent 6a39da5485
commit d16fa1e052
5 changed files with 52 additions and 9 deletions
@@ -45,10 +45,16 @@ func (n *notified) notify(key, severity, summary, headline, explanation, resolve
}
func transition(t *testing.T, h *harness, number int, to, why, mergedAs string, at time.Time) {
t.Helper()
transitionOn(t, h, "mesh-delivery.transition", number, to, why, mergedAs, at)
}
// transitionOn is a delivery's transition said under an event's name: today's module's, or the gate's.
func transitionOn(t *testing.T, h *harness, event string, number int, to, why, mergedAs string, at time.Time) {
t.Helper()
raw, _ := json.Marshal(map[string]any{"repository": "novox/mesh-catalog", "number": number, "to": to, "why": why,
"merged_as": mergedAs, "at": at})
if err := h.OnTransition(stdio.Envelope{Key: "mesh-delivery.transition", Body: raw}); err != nil {
if err := h.OnTransition(stdio.Envelope{Key: event, Body: raw}); err != nil {
t.Fatal(err)
}
}
@@ -333,3 +339,26 @@ func TestAResolvedIssueEndsItsStuckWarningAndIsNeverSaidStuck(t *testing.T) {
t.Fatalf("a resolved issue said stuck: %q", note.said)
}
}
// Issue 1350 AC1: the handler takes a transition under either name, today's module's or the gate's, and keeps the same fact.
func TestATransitionIsTakenUnderEitherNameOfTheDeliverysOwner(t *testing.T) {
for _, event := range transitionEvents {
t.Run(event, func(t *testing.T) {
h := newHarness(t)
n := h.open(t, "the counter skips a number")
if _, err := h.Move(MoveAsk{Number: n, Status: StatusLocated, LocatedIn: []string{"mesh-catalog modules/records"},
FixedBy: []string{"mesh-catalog#9"}}); err != nil {
t.Fatal(err)
}
at := time.Date(2026, 10, 20, 9, 0, 0, 0, time.UTC)
transitionOn(t, h, event, 9, "delivered", "the walk is done", "abc123abc123", at)
v, err := h.View(n)
if err != nil {
t.Fatal(err)
}
if v.Delivery != "delivered to every machine it should reach" {
t.Fatalf("the issue's delivery line is %q, heard as %s; wanted delivered", v.Delivery, event)
}
})
}
}
+12 -4
View File
@@ -145,15 +145,23 @@ func start(t *Tracker) {
}
}
// transitionEvents are the events a delivery's transitions are said under: today's module's, and the gate's that takes its place
// (novox/hq ADR 0450 §8, issue 1350). The old name stays until today's module is forgotten; the handler takes either.
var transitionEvents = []string{"mesh-delivery.transition", "mesh-delivery-gate.transition"}
// hearOpened subscribes the triage to the tracker's own `opened` and `moved` events and to mesh-delivery's
// `transition` (ADR 0278 §1, ADR 0279 §3), each asked again until the runtime takes it: the runtime answers only
// `transition`, and the gate's (ADR 0278 §1, ADR 0279 §3), each asked again until the runtime takes it: the runtime answers only
// once Serve runs.
func hearOpened(t *Tracker) {
for _, s := range []struct {
type hearing struct {
pattern string
handle func(stdio.Envelope) error
}{{"mesh-issues." + EventOpened, t.OnOpened}, {"mesh-issues." + EventMoved, t.OnMoved},
{"mesh-delivery.transition", t.OnTransition}} {
}
hearings := []hearing{{"mesh-issues." + EventOpened, t.OnOpened}, {"mesh-issues." + EventMoved, t.OnMoved}}
for _, event := range transitionEvents {
hearings = append(hearings, hearing{event, t.OnTransition})
}
for _, s := range hearings {
for wait := 2 * time.Second; ; wait = min(wait*2, time.Minute) {
time.Sleep(wait)
err := stdio.Subscribe(s.pattern, s.handle)
@@ -102,9 +102,14 @@ func TestItDeclaresAndHoldsTheIssueTrackerSeatOnceForTheMesh(t *testing.T) {
d[3].Name != ReservationsState || d[3].History > 1 || d[3].TTLSeconds != 0 {
t.Fatalf("state %+v", d)
}
// The delivery's transitions are heard from today's module and from the gate that takes its place (novox/hq ADR 0450 §8,
// issue 1350); the old name stays until today's module is forgotten. The subscriptions are the same list.
if !reflect.DeepEqual(transitionEvents, []string{"mesh-delivery.transition", "mesh-delivery-gate.transition"}) {
t.Fatalf("the triage subscribes to %v", transitionEvents)
}
// The duplicate check hears the tracker's own opens and asks the agent module on its node (ADR 0278 §1, §5);
// the import and a reservation read the record through the records module (issue 424).
if !reflect.DeepEqual(m.Consumes, []string{"mesh-issues." + EventOpened, "mesh-issues." + EventMoved, "mesh-delivery.transition"}) ||
if !reflect.DeepEqual(m.Consumes, []string{"mesh-issues." + EventOpened, "mesh-issues." + EventMoved, "mesh-delivery.transition", "mesh-delivery-gate.transition"}) ||
!reflect.DeepEqual(m.Invokes, []string{"claude-code.claude_code_judge", "seat:mesh-delivery.checks", "seat:operator-channel.notify",
"gitea.gitea_pull_reviews", "records.records_status", "records.records_list", "records.records_read", "mesh-knowledge.knowledge_highest"}) {
t.Fatalf("consumes %v, invokes %v", m.Consumes, m.Invokes)
+1 -1
View File
@@ -58,7 +58,7 @@
"mesh-catalog modules/memory-pressure",
"mesh-catalog modules/mesh-catalog",
"mesh-catalog modules/mesh-cli",
"mesh-catalog modules/mesh-delivery",
"mesh-catalog modules/mesh-delivery-gate",
"mesh-catalog modules/mesh-issues",
"mesh-catalog modules/mesh-vault",
"mesh-catalog modules/mesh-watcher",
+3 -2
View File
@@ -57,7 +57,8 @@
"consumes": [
"mesh-issues.opened",
"mesh-issues.moved",
"mesh-delivery.transition"
"mesh-delivery.transition",
"mesh-delivery-gate.transition"
],
"invokes": [
"claude-code.claude_code_judge",
@@ -151,7 +152,7 @@
"mesh-catalog modules/memory-pressure",
"mesh-catalog modules/mesh-catalog",
"mesh-catalog modules/mesh-cli",
"mesh-catalog modules/mesh-delivery",
"mesh-catalog modules/mesh-delivery-gate",
"mesh-catalog modules/mesh-issues",
"mesh-catalog modules/mesh-vault",
"mesh-catalog modules/mesh-watcher",