diff --git a/cmd/mesh-controller/delivery.go b/cmd/mesh-controller/delivery.go index 4c4df022..4055e730 100644 --- a/cmd/mesh-controller/delivery.go +++ b/cmd/mesh-controller/delivery.go @@ -89,20 +89,29 @@ func waitingSaid(p inventory.Plan, lines []stalledLine) string { if len(lines) == 0 { return waitingNote(p) } - var held, other []string + var held, unheld, other []string for _, l := range lines { - if l.State == "held" { + switch l.State { + case "held": held = append(held, l.Says) - } else { + case "unheld": + unheld = append(unheld, l.Says) + default: other = append(other, l.Says) } } - if len(held) > 0 { + switch { + case len(held) > 0: return fmt.Sprintf("published; its walk waits for %s's word, and a delivery it carries is held for a person: %s", p.Delivery.Awaits, strings.Join(held, "; ")) + case len(other) > 0: + // Not at its turn: it goes when its turn comes. A hand start here would skip its group's order (review of + // novox/hq issue 529), so none is offered. + return fmt.Sprintf("published; its walk waits for %s's word: %s", p.Delivery.Awaits, strings.Join(other, "; ")) } + // Held by nothing: nothing will give it its word, so the hand start is the way on. return fmt.Sprintf("published; its walk waits for %s's word: %s — `plans go %s --why …` starts it by hand", - p.Delivery.Awaits, strings.Join(other, "; "), p.ID) + p.Delivery.Awaits, strings.Join(unheld, "; "), p.ID) } // letGo gives a waiting walk its word: by the delivery's owner, or a person. The next advance asks its diff --git a/cmd/mesh-controller/issue529_test.go b/cmd/mesh-controller/issue529_test.go index 858672db..d8cc2fb1 100644 --- a/cmd/mesh-controller/issue529_test.go +++ b/cmd/mesh-controller/issue529_test.go @@ -51,8 +51,26 @@ func TestAWalkWaitingOnADeliveryNotAtItsTurnSaysSo(t *testing.T) { p := waitingWalk(now) waits := walkWaits{p.ID: {{ID: "novox/mesh-sdk@9747f3e1b1ac", State: "published", Walk: p.ID, Says: "not let go yet: novox/mesh-tools@3f0c1a6cec18 (published) carried by it not at its turn"}}} - if line := planLineSaid(p, now, pauseView{}, tierAtLeast, nil, waits); !strings.Contains(line, "not at its turn") || - !strings.Contains(line, "novox/mesh-tools@3f0c1a6cec18") { + line := planLineSaid(p, now, pauseView{}, tierAtLeast, nil, waits) + if !strings.Contains(line, "not at its turn") || !strings.Contains(line, "novox/mesh-tools@3f0c1a6cec18") { + t.Fatalf("the line reads %q", line) + } + // No hand start offered: it would skip the group's order (review of issue 529). + if strings.Contains(line, "plans go") { + t.Fatalf("a walk not at its turn points at a hand start: %q", line) + } +} + +// A walk nothing holds — the nzbgeek case, its merges' deliveries ended as nothing-to-move — is said so, and the hand +// start is the way on. +func TestAWalkHeldByNothingSaysWhyAndTheHandStart(t *testing.T) { + now := time.Date(2026, 10, 11, 20, 30, 0, 0, time.UTC) + p := waitingWalk(now) + waits := walkWaits{p.ID: {{ID: "novox/mesh-media-catalog@cdcdcdcd0000", State: "unheld", Walk: p.ID, + Says: "held by nothing: its merges' deliveries ended as nothing-to-move (novox/mesh-media-catalog@abababab0000), " + + "yet the walk moves nzbgeek, nzbget — nothing gives it its word"}}} + line := planLineSaid(p, now, pauseView{}, tierAtLeast, nil, waits) + if !strings.Contains(line, "ended as nothing-to-move") || !strings.Contains(line, "plans go "+p.ID) { t.Fatalf("the line reads %q", line) } }