carlos / platform Public

self-serve job: participant pinning and report reads are judged at the pass's clock (fix round 2, item 7)

Diagnosis of CI's TestClaimsOffInstanceIDIsPublished failure (d0227a68):
the witness moves the boxes' clock and the console's clock on together by
61 s, but two of the job's freshness judgements read the wall clock, not
the pass's: pinParticipants (offlineReportCurrent) and readOfflineEvidence
(ValidateOfflineReportFor's future skew). Reached less than a second after
the advance, as on a fast runner, the box reports stamped 61 s ahead were a
minute in the future at wall time: "the box ie-box has not reported
recently" every round. On a slower box enough wall time passed to hide
it. In production both clocks are the wall clock (selfServeNow is
time.Now() with no test clock), so no production answer changes; the two
judgements now use selfServeNow like the job's others, and a door's read,
outside the pass, still reads the wall clock.

The witness advances two minutes, past OfflineRequestSkew, so it fails on
every machine without the fix. RED (this box): "after 60 rounds, still
not in staging" with "waiting: the box ie-box has not reported recently",
then with pinning fixed "offline report: written 1m58s in the future";
GREEN with both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev e6e4bb7b427be38c114b86f87907a24bcd30502c parent bbaccf9
2 files changed, +18 −5
  • internal/console/apiinstancemove_offline.go +9 −2
  • internal/e2e/selfserve/selfserve_operator_test.go +9 −3
diff --git a/internal/console/apiinstancemove_offline.go b/internal/console/apiinstancemove_offline.go
index e55ce4324..070b12896 100644
--- a/internal/console/apiinstancemove_offline.go
+++ b/internal/console/apiinstancemove_offline.go
@@ -232,7 +232,10 @@ func (e offlineEvidence) byRole(role string) []offlineReport {
// a request-bound report's future skew), logged. m nil is the legacy
// validation alone. A listing or a read that fails is the error.
func (s *Server) readOfflineEvidence(ctx context.Context, host, id string, m *provision.MoveState) (offlineEvidence, error) {
- now := time.Now()
+ // The reports are judged at the pass's clock when the job reads them
+ // (selfServeNow), as pinning is; a door's read is the wall clock (fix
+ // round 2, item 7).
+ now := s.selfServeNow()
prefix := moves.OfflineReportPrefix(host, id)
if prefix == "" {
return offlineEvidence{}, nil
@@ -657,7 +660,11 @@ func (s *Server) pinParticipants(ctx context.Context, sel *offlineSelection, req
return ref
}
}
- now := time.Now()
+ // The reports are judged at the pass's clock when the self-serve
+ // job pins (selfServeNow: its tick's clock, which a deadline witness
+ // moves with the boxes'), as the job's other freshness judgements are;
+ // a door's request reads the wall clock (fix round 2, item 7).
+ now := s.selfServeNow()
sel.Source = nil
for _, b := range sel.Fleet.boxesAt(sel.From) {
p, ref := s.pinParticipant(ctx, sel.Fleet, b, s.homeRegionIn(b.Placement), requireIDs, id, now)
diff --git a/internal/e2e/selfserve/selfserve_operator_test.go b/internal/e2e/selfserve/selfserve_operator_test.go
index f6c3aabd0..7bb2a2fdb 100644
--- a/internal/e2e/selfserve/selfserve_operator_test.go
+++ b/internal/e2e/selfserve/selfserve_operator_test.go
@@ -325,12 +325,18 @@ func TestClaimsOffInstanceIDIsPublished(t *testing.T) {
t.Fatalf("the move pinned %+v", r.Participants)
}
// The moves worker asks again a minute on (the boxes' clock and the
- // console's moved together), and the source takes its role.
+ // console's moved together), and the source takes its role. Two
+ // minutes, so the boxes' stamps are past OfflineRequestSkew ahead of
+ // the wall clock: every judgement the console's pass makes of them
+ // must be at the pass's clock (moved with the boxes'), never the wall
+ // clock (fix round 2, item 7: a fast runner reached staging's
+ // participant pinning less than a second after a 61 s advance and
+ // judged the reports a minute in the future).
if got := src.InstanceID(); got != "" {
t.Fatalf("the worker re-read the instance id inside its minute: %q", got)
}
- w.e.Clock().Advance(61 * time.Second)
- w.advance(61 * time.Second)
+ w.e.Clock().Advance(2 * time.Minute)
+ w.advance(2 * time.Minute)
if got := src.InstanceID(); got != "i-e2e-ie-box" {
t.Fatalf("the worker's instance id a minute on: %q", got)
}