carlos / platform Public

lifetimes witnesses: the synthetic (1, 9) is explained, and the interleave observes the slot retired when the tombstone's delete is issued

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev e00e1d6c779db1acee28a5999e2432a09a1a81cb parent 806f03c
2 files changed, +28 −6
  • internal/console/lifetimes_e2e_test.go +21 −3
  • internal/host/movesoffline_lifetime_test.go +7 −3
diff --git a/internal/console/lifetimes_e2e_test.go b/internal/console/lifetimes_e2e_test.go
index c04e20ffd..37599927a 100644
--- a/internal/console/lifetimes_e2e_test.go
+++ b/internal/console/lifetimes_e2e_test.go
@@ -385,11 +385,29 @@ func TestDelayedCreateReplayMoveAndRetirementInterleave(t *testing.T) {
t.Fatalf("the uk box minted a row after the late create: %+v", rt)
}
- // Recovery: the slot retired, then the tombstone removed.
+ // Recovery: the slot retired, then the tombstone removed. The slot
+ // is read at the moment each conditional delete of the key is
+ // issued, so the order is observed, not inferred from the end state.
+ var atDelete []string
+ l.bucket.setHook(func(op, k string) error {
+ if op == "delete-if-match" && k == key {
+ slot, _, err := provision.ReadHostIncarnation(context.Background(), l.fx.rel, ltOne)
+ switch {
+ case err != nil:
+ atDelete = append(atDelete, "unreadable")
+ case slot == nil:
+ atDelete = append(atDelete, "absent")
+ default:
+ atDelete = append(atDelete, slot.State)
+ }
+ }
+ return nil
+ })
l.recoveredTo(t, r.OpID, lifetimeCommitted)
+ l.bucket.setHook(nil)
requireRetired(t, l.fx.rel, ltApp, ltOne, 1)
- if ops := l.bucket.opsOf(key); !contains(ops, "delete-if-match") {
- t.Fatalf("record ops = %v, want the tombstone deleted on its ETag", ops)
+ if len(atDelete) != 1 || atDelete[0] != provision.SlotRetired {
+ t.Fatalf("the slot when the tombstone's delete was issued: %v, want one delete, under the retired slot", atDelete)
}
// The hostname's next lifetime is 2.
if rec := l.mustCreate(t, ltOne); rec.Incarnation != 2 {
diff --git a/internal/host/movesoffline_lifetime_test.go b/internal/host/movesoffline_lifetime_test.go
index 331436394..9aa37e7a5 100644
--- a/internal/host/movesoffline_lifetime_test.go
+++ b/internal/host/movesoffline_lifetime_test.go
@@ -92,9 +92,13 @@ func requireFoldWinner(t *testing.T, why string, a, b []byte, want string) {
// syntheticReadyPublication is a SYNTHETIC fold input: box's edges file as
// the real writer publishes a ready row for the host at pair, built over a
-// scratch registry. This plan's writer never publishes the row it stands
-// for (a held row is not published; plan 3's holding entry is), so no box
-// of the world produces it.
+// scratch registry. It has two uses. In the abort witnesses it stands for
+// the target's landing row at (1, 1): this plan's writer never publishes
+// that row (a held row is not published; plan 3's holding entry is), so no
+// box of the world produces it. In TestRecreatedHostOutranksItsPreviousLifetime
+// it is the brief's synthetic retained (1, 9), from a box the world does not
+// run, kept beside the real (1, 9) that uk-b publishes there: the real one
+// is the primary witness, the synthetic one the form the brief asked for.
func syntheticReadyPublication(t *testing.T, box string, pair registry.Pair) []byte {
t.Helper()
reg := edgesTestRegistry(t)