carlos / platform Public

console: the cut branch of ready checks supersession by stamp as the other waits do

A maintenance round the cut found outstanding and an operator round
superseded is abandoned, so restage writes the cut's own round instead of
waiting out the allowance. TestOperatorRoundSupersedesTheJobsWait/ready,
after the cut (RED before).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 703cc95bb829df24f0aeb6d8c8c9fbf42a8f9c8e parent bf78a26
2 files changed, +39 −0
  • internal/console/selfserve_job.go +6 −0
  • internal/e2e/selfserve/job_races_test.go +33 −0
diff --git a/internal/console/selfserve_job.go b/internal/console/selfserve_job.go
index 1ff64be53..aa0f00fd5 100644
--- a/internal/console/selfserve_job.go
+++ b/internal/console/selfserve_job.go
@@ -1285,6 +1285,12 @@ func (s *Server) selfServeReady(ctx context.Context, t *leaseToken, row *SelfSer
_, err := s.selfServeApply(ctx, t, row, request{Kind: "maintain", Phase: ssReady}, nil, selfServeRestageMut(row.ID))
return s.selfServePreOfflineRefusal(row, err, now)
}
+ // An operator round superseded the outstanding one: it is
+ // abandoned, and the next step finds none and restage writes the
+ // cut's own round.
+ if sup, err := s.selfServeSuperseded(row, mv); sup || err != nil {
+ return err
+ }
ev, err := s.readOfflineEvidence(ctx, row.Host, row.ID, mv)
if err != nil {
return err
diff --git a/internal/e2e/selfserve/job_races_test.go b/internal/e2e/selfserve/job_races_test.go
index b49e3d360..e3ce7c221 100644
--- a/internal/e2e/selfserve/job_races_test.go
+++ b/internal/e2e/selfserve/job_races_test.go
@@ -302,6 +302,39 @@ func TestOperatorRoundSupersedesTheJobsWait(t *testing.T) {
supersede(t, w, row.ID, "stage", true)
w.phase(row.ID, "ready", 60)
})
+ t.Run("ready, after the cut", func(t *testing.T) {
+ // The cut found the maintenance round outstanding and waits on it
+ // as the cut's first round; an operator round supersedes it. The
+ // job abandons it and restage writes the cut's own round.
+ w := newJobWorld(t)
+ row := w.admit()
+ w.watch = row.ID
+ w.phase(row.ID, "ready", 60)
+ w.advance(11 * time.Minute)
+ w.down()
+ w.consoleUntil(5, "a round confirmed", func() bool {
+ r := w.row(row.ID)
+ return r.Request.Kind == "maintain" && r.Request.Confirmed && !r.Request.Consumed
+ })
+ if !w.markCut(row.ID) {
+ t.Fatal("the cut was refused")
+ }
+ w.edit(func(rec *provision.Record) error {
+ return provision.OfflineRestage(row.ID, stamp(rec), provision.Receipt{})(rec)
+ })
+ w.up()
+ w.boxes() // the stager answers the operator's round only
+ w.down()
+ w.consoleUntil(4, "restage", func() bool { return w.row(row.ID).Phase == "restage" })
+ if r := w.row(row.ID); r.PhaseStep != "issue" {
+ t.Fatalf("the superseded round was adopted: %s", describe(r))
+ }
+ w.up()
+ w.phase(row.ID, "barrier", 20)
+ if n := w.nonces("stage", "restage"); len(n) != 1 {
+ t.Fatalf("restage's own rounds: %v", n)
+ }
+ })
t.Run("ready", func(t *testing.T) {
w := newJobWorld(t)
row := w.admit()