fix(planning): replan a plan once when reconsiderations race

Retracting a plan's assumption replanned it and withdrew its pending
approval. An executor tick holding the old plan then saw the withdrawn
approval, treated it as expired and replanned a second time. Entering
replanning is now a compare-and-set, so only the first reconsideration
replans and a stale one does nothing. A regression test fails with three
plans without the guard.
This commit is contained in:
Andy Dixon
2026-10-04 20:07:12 +01:00
parent edcc6580b6
commit b6538600d1
3 changed files with 33 additions and 2 deletions
+19
View File
@@ -538,3 +538,22 @@ func TestStaleStepCopyCannotMoveStepBackwards(t *testing.T) {
t.Fatalf("stale copy overwrote a claimed step: %s", got.Status)
}
}
// Two reconsiderations holding the same plan (an event reaction and an
// executor tick) must replan it once, not twice.
func TestConcurrentReconsiderationReplansOnce(t *testing.T) {
f := newFixture(t)
f.allow("service.restart")
it := f.intention(false)
p := f.start(it)
trig := volition.Trigger{Kind: volition.TriggerStepFailed, Detail: "test"}
if err := f.exec.reconsider(f.ctx, p, it, trig); err != nil {
t.Fatal(err)
}
if err := f.exec.reconsider(f.ctx, p, it, trig); err != nil { // stale copies of the plan and intention
t.Fatal(err)
}
if ps, _ := f.plans.ForIntention(f.ctx, nil, it.ID); len(ps) != 2 {
t.Fatalf("want one replan, got %d plans", len(ps))
}
}