Judge a walk's machine on its last send, enter a resumed tier anew, and resume a walk lost to silence (issue 1886)

R4e, resumed at tier 1 after a stop of five hours, named ace and shanks silent within seconds of tier 1's send: the
walk judged each machine on every send it had made it, tier 0's of 00:23 too, held against tier 1's templates, and
measured the bound from it. A send carries the whole declaration, so the last one is what a machine is judged on.
The resume kept the tier's entry of before the stop, so the plan read waiting for 5h48m. And plans retry refused a
walk failed only by the gate's silence, though its machines had since applied.
This commit is contained in:
2026-10-23 08:02:11 +02:00
parent 219fdcd7f0
commit b7d996878b
5 changed files with 299 additions and 28 deletions
+6
View File
@@ -675,6 +675,12 @@ func tierLeftOf(old *Plan, p Plan, now time.Time) (time.Time, *Duration) {
since = old.Created
}
wasOpen := old.State == PlanBuilding || old.State == PlanRolling
if !wasOpen && p.Open() {
// **A walk set open again enters its tier anew** (issue 1886): a resume or a retry at the tier it ended at. Kept,
// the entry of before its stop measured the tier from hours before the resume — R4e read "waiting … for 5h48m53s"
// seven minutes after it was resumed — and the watchdog of its progress (S3) found it late at once.
return now, nil
}
if !wasOpen || (old.Tier == p.Tier && p.Open()) {
return since, nil // still in the tier, or already ended
}
@@ -0,0 +1,32 @@
package inventory
import (
"testing"
"time"
)
// Issue 1886: R4e entered tier 1 at 02:01, was stopped, and was resumed at 07:43 at the same tier. Its save kept the
// tier's entry of 02:01, so at 07:50 the plan read "waiting for mesh-build-image on shanks to be applied for 5h48m53s"
// and its progress watchdog measured the tier from before the stop. A walk set open again at the tier it ended at
// enters that tier anew: its time in the tier runs from the resume.
func TestAWalkResumedAtItsTierEntersItAnew(t *testing.T) {
entered := time.Date(2026, 10, 23, 0, 1, 7, 0, time.UTC)
resumed := time.Date(2026, 10, 23, 5, 43, 30, 0, time.UTC)
old := &Plan{ID: "plan-r4e", State: PlanFailed, Tier: 1, Tiers: [][]string{{"a"}, {"b"}, {"c"}},
Created: entered.Add(-2 * time.Hour), TierEntered: entered}
p := *old
p.State = PlanBuilding
got, d := tierLeftOf(old, p, resumed)
if !got.Equal(resumed) || d != nil {
t.Fatalf("1886: a walk resumed at tier 1 entered it at %s (duration %v); want %s, no tier left", got, d, resumed)
}
// A walk still in its tier keeps its entry; one that ended keeps it too.
open := *old
open.State = PlanRolling
if got, _ := tierLeftOf(&open, open, resumed); !got.Equal(entered) {
t.Fatalf("an open walk still in its tier was stamped anew: %s", got)
}
if got, _ := tierLeftOf(old, *old, resumed); !got.Equal(entered) {
t.Fatalf("a failed walk saved again was stamped anew: %s", got)
}
}