diff --git a/modules/mesh-delivery/cmd/mesh-delivery/issue451_test.go b/modules/mesh-delivery/cmd/mesh-delivery/issue451_test.go new file mode 100644 index 00000000..832c53b7 --- /dev/null +++ b/modules/mesh-delivery/cmd/mesh-delivery/issue451_test.go @@ -0,0 +1,48 @@ +package main + +import ( + "encoding/json" + "strings" + "testing" + "time" +) + +// **An unanswered line says the state of the delivery it holds, as data** (novox/hq issue 451): for a delivery in +// ready or rejected, whose check answered once, the controller advises a recheck rather than the build queue, and +// it reads that state from the line, never from its words. A head with no delivery held says none. +func TestAnUnansweredLineNamesTheStateOfItsDelivery(t *testing.T) { + at := time.Date(2026, 10, 11, 0, 43, 0, 0, time.UTC) + for _, c := range []struct { + gate string + want State + }{{"pass", Ready}, {"fail", Rejected}} { + w := newWorldOver(t, newMemStore(), newFakeController(), newFakeForge(), at) + id := heldIn(t, w, c.gate, c.want) + w.forge.open = openPullsOf(t, readyRepoCheckPending) + w.h.SeeOpenPulls() + w.later(Bounds[Proposed].For + time.Minute) + w.h.SeeOpenPulls() + lines := w.h.Stalled() + if len(lines) != 1 || lines[0].ID != id || lines[0].State != Unanswered || lines[0].Held != c.want { + t.Fatalf("a %s delivery's unanswered line does not say its state as data: %+v", c.want, lines) + } + raw, _ := json.Marshal(lines[0]) + if !strings.Contains(string(raw), `"held":"`+string(c.want)+`"`) { + t.Fatalf("the line the controller reads carries no held state: %s", raw) + } + } + + // The same head with no delivery held here: unanswered, and no state to say. + w := newWorldOver(t, newMemStore(), newFakeController(), newFakeForge(), at) + w.forge.open = openPullsOf(t, readyRepoCheckPending) + w.h.SeeOpenPulls() + w.later(Bounds[Proposed].For + time.Minute) + w.h.SeeOpenPulls() + lines := w.h.Stalled() + if len(lines) != 1 || lines[0].State != Unanswered || lines[0].Held != "" { + t.Fatalf("a head with no delivery held says a state: %+v", lines) + } + if raw, _ := json.Marshal(lines[0]); strings.Contains(string(raw), `"held"`) { + t.Fatalf("a line with no delivery held carries a held field: %s", raw) + } +} diff --git a/modules/mesh-delivery/cmd/mesh-delivery/unchecked.go b/modules/mesh-delivery/cmd/mesh-delivery/unchecked.go index 2d5069c1..0db11ed5 100644 --- a/modules/mesh-delivery/cmd/mesh-delivery/unchecked.go +++ b/modules/mesh-delivery/cmd/mesh-delivery/unchecked.go @@ -250,8 +250,8 @@ func (h *Holder) uncheckedLines(now time.Time) []StalledLine { h2 = "none: its delivery is " + string(u.held) + ", a state the operator moves — the operator's" } out = append(out, StalledLine{ID: id, Number: u.pull.Number, State: state, For: now.Sub(u.first).Round(time.Second).String(), - Bound: u.bound().String(), - H2: h2, Says: says, Waiting: waiting}) + Bound: u.bound().String(), Held: u.held, + H2: h2, Says: says, Waiting: waiting}) } return out } diff --git a/modules/mesh-delivery/cmd/mesh-delivery/verbs.go b/modules/mesh-delivery/cmd/mesh-delivery/verbs.go index fa80497b..45b0903c 100644 --- a/modules/mesh-delivery/cmd/mesh-delivery/verbs.go +++ b/modules/mesh-delivery/cmd/mesh-delivery/verbs.go @@ -199,10 +199,14 @@ type StalledLine struct { // the operator's own zone, which it cannot do inside a finished sentence (novox/hq issue 443). Waiting stays // beside them for a controller that does not read them yet. Checks []WaitingCheck `json:"checks,omitempty"` - For string `json:"for"` - Bound string `json:"bound"` - H2 string `json:"h2"` - Says string `json:"says"` + // Held is the state of the delivery a line of the state Unanswered is about, ready or rejected, when one is held + // here (novox/hq issues 441, 451): its check answered once, so the controller advises a recheck of it rather than + // the build queue, reading the state here and never from Says. Empty for a head with no delivery. + Held State `json:"held,omitempty"` + For string `json:"for"` + Bound string `json:"bound"` + H2 string `json:"h2"` + Says string `json:"says"` } // Stalled is the `stalled` verb: what the controller's self-check raises as `delivery..stalled`.