carlos / platform Public

hostclaim: the orphan predicate (record first, then claim; no claim = legacy)

Co-Authored-By: Codex (gpt-6-astra) <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
codex agent, operated by paul@keymail.dev pushed by claude agent, operated by paul@keymail.dev 4d91a7448a648d22cd7e22327a88f5b21a7fec4b parent 5aaa1ac
2 files changed, +39 −0
  • internal/hostclaim/orphan.go +16 −0
  • internal/hostclaim/orphan_test.go +23 −0
diff --git a/internal/hostclaim/orphan.go b/internal/hostclaim/orphan.go
new file mode 100644
index 000000000..072728710
--- /dev/null
+++ b/internal/hostclaim/orphan.go
@@ -0,0 +1,16 @@
+package hostclaim
+
+// IsOrphan is §5.1 step 5. The caller reads the record FIRST and this
+// claim AFTER it (R3-2). Each incarnation gets exactly one claim attempt,
+// and a record exists only after that attempt succeeded, so a claim read
+// after the record that does not name recordInc means recordInc can never
+// become the holder. A creating holder is named by Holder, so it is
+// covered. An absent claim is a legacy host (Q2), never an orphan. The
+// caller deletes only with DeleteIfMatch on the ETag it read.
+func IsOrphan(recordInc string, claimReadAfterRecord Observed) bool {
+ if claimReadAfterRecord.Absent || claimReadAfterRecord.Claim == nil {
+ return false
+ }
+ h := claimReadAfterRecord.Claim.Holder
+ return h == nil || h.Incarnation != recordInc
+}
diff --git a/internal/hostclaim/orphan_test.go b/internal/hostclaim/orphan_test.go
new file mode 100644
index 000000000..1dc8c7a4d
--- /dev/null
+++ b/internal/hostclaim/orphan_test.go
@@ -0,0 +1,23 @@
+package hostclaim
+
+import "testing"
+
+func TestIsOrphan(t *testing.T) {
+ if IsOrphan("A", Observed{Absent: true}) {
+ t.Fatal("no claim = legacy, never orphan")
+ }
+ held := Observed{Claim: &Claim{Holder: &Holder{Incarnation: "A"}, Op: Op{Kind: KindServing}}}
+ if IsOrphan("A", held) {
+ t.Fatal("the holder's own record is not an orphan")
+ }
+ creating := Observed{Claim: &Claim{Holder: &Holder{Incarnation: "A"}, Op: Op{Kind: KindCreating, ID: "c"}}}
+ if IsOrphan("A", creating) {
+ t.Fatal("a record of the creating incarnation is not an orphan")
+ }
+ if !IsOrphan("A", Observed{Claim: &Claim{Holder: &Holder{Incarnation: "B"}, Op: Op{Kind: KindServing}}}) {
+ t.Fatal("another incarnation's claim makes A an orphan")
+ }
+ if !IsOrphan("A", Observed{Claim: &Claim{}}) {
+ t.Fatal("a free claim makes A an orphan")
+ }
+}