carlos / platform Public

hostclaim: persisted park scope and the holderless sweep (claim rev 6.15; Plan 4 Task 1)

Co-Authored-By: Codex <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 2bc302f663feb864f696f8e2e8edd09ec9d88d6a parent 8c40aca
2 files changed, +72 −10
  • internal/hostclaim/claim.go +40 −10
  • internal/hostclaim/park_test.go +32 −0
diff --git a/internal/hostclaim/claim.go b/internal/hostclaim/claim.go
index 66153a317..7280b2ea5 100644
--- a/internal/hostclaim/claim.go
+++ b/internal/hostclaim/claim.go
@@ -105,15 +105,23 @@ type InventoryEntry struct {
Paths map[string]uint64 `json:"paths"` // path -> stamp
}
+type ParkScope struct {
+ Location string `json:"location"`
+ Incarnation string `json:"incarnation"`
+ EvidenceAt time.Time `json:"evidence_at"`
+ RequestLimit time.Duration `json:"request_limit_ns"`
+}
+
type Obligation struct {
- ID string `json:"id"`
- Op string `json:"op"`
- Kind string `json:"kind"` // "park-replica" | "park-local" | "park-generation"
- Region string `json:"region,omitempty"`
- Generation string `json:"generation,omitempty"`
- Box string `json:"box,omitempty"`
- Retirement string `json:"retirement"`
- Owner string `json:"owner"` // "region:<r>" | "box:<b>"
+ Park *ParkScope `json:"park,omitempty"`
+ ID string `json:"id"`
+ Op string `json:"op"`
+ Kind string `json:"kind"` // "park-replica" | "park-local" | "park-generation"
+ Region string `json:"region,omitempty"`
+ Generation string `json:"generation,omitempty"`
+ Box string `json:"box,omitempty"`
+ Retirement string `json:"retirement"`
+ Owner string `json:"owner"` // "region:<r>" | "box:<b>"
}
type CancelledCreate struct {
@@ -199,10 +207,10 @@ func (c *Claim) Validate() error {
return fmt.Errorf("%w: op %q without id", ErrMalformed, c.Op.Kind)
}
if c.Holder == nil {
- if c.Op.Kind != "" && c.Op.Kind != KindPurging { // a purge fence has no holder (CC21)
+ if c.Op.Kind != "" && c.Op.Kind != KindPurging && !holderlessSweep(c) { // a purge fence has no holder (CC21)
return fmt.Errorf("%w: op %q without holder", ErrMalformed, c.Op.Kind)
}
- if len(c.Obligations) > 0 {
+ if len(c.Obligations) > 0 && !holderlessSweep(c) {
return fmt.Errorf("%w: a free claim with outstanding obligations", ErrMalformed)
}
} else {
@@ -238,6 +246,16 @@ func (c *Claim) Validate() error {
if o.ID == "" || o.Op == "" || o.Retirement == "" || seen[o.ID] {
return fmt.Errorf("%w: obligation ids", ErrMalformed)
}
+ if o.Park != nil {
+ p := o.Park
+ if (o.Kind != "park-replica" && o.Kind != "park-generation") || !isHex64(p.Incarnation) || p.EvidenceAt.IsZero() || p.RequestLimit <= 0 || p.RequestLimit > time.Duration(1<<63-1)-15*time.Minute {
+ return fmt.Errorf("%w: park scope", ErrMalformed)
+ }
+ base := "instances/" + c.Host
+ if p.Location != base && p.Location != base+"/"+p.Incarnation {
+ return fmt.Errorf("%w: park location", ErrMalformed)
+ }
+ }
seen[o.ID] = true
switch o.Kind {
case "park-generation":
@@ -258,3 +276,15 @@ func (c *Claim) Validate() error {
}
return nil
}
+
+func holderlessSweep(c *Claim) bool {
+ if c.Holder != nil || c.Op.Kind != KindParking || c.Op.ID == "" || c.Op.Intent["park_sweep"] != "v1" || c.Op.Exec != "" || len(c.Obligations) == 0 {
+ return false
+ }
+ for _, o := range c.Obligations {
+ if o.Op != c.Op.ID || o.Park == nil || (o.Kind != "park-replica" && o.Kind != "park-generation") {
+ return false
+ }
+ }
+ return true
+}
diff --git a/internal/hostclaim/park_test.go b/internal/hostclaim/park_test.go
new file mode 100644
index 000000000..74a6f914b
--- /dev/null
+++ b/internal/hostclaim/park_test.go
@@ -0,0 +1,32 @@
+package hostclaim
+
+import (
+ "testing"
+ "time"
+)
+
+func TestParkScopeAndHolderlessSweep(t *testing.T) {
+ c := &Claim{V: 2, Host: "shop.bab.example.com", Gen: 1, Op: Op{Kind: KindParking, ID: "sweep", Intent: map[string]string{"park_sweep": "v1"}}}
+ c.Obligations = []Obligation{{ID: "ob", Op: "sweep", Kind: "park-replica", Region: "ie", Retirement: "rt", Owner: "region:ie", Park: &ParkScope{Location: "instances/" + c.Host + "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Incarnation: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", EvidenceAt: time.Now(), RequestLimit: time.Second}}}
+ if err := c.Validate(); err != nil {
+ t.Fatal(err)
+ }
+ for _, change := range []func(*Claim){
+ func(c *Claim) { c.Obligations[0].Park = nil },
+ func(c *Claim) { c.Op.Exec = "box" },
+ func(c *Claim) { c.Obligations[0].Op = "another" },
+ func(c *Claim) { c.Op.Intent = nil },
+ func(c *Claim) { c.Obligations[0].Park.RequestLimit = 0 },
+ func(c *Claim) { c.Obligations[0].Park.EvidenceAt = time.Time{} },
+ func(c *Claim) { c.Obligations[0].Park.Location = "instances/other.example" },
+ } {
+ copy := *c
+ copy.Obligations = append([]Obligation(nil), c.Obligations...)
+ scope := *c.Obligations[0].Park
+ copy.Obligations[0].Park = &scope
+ change(&copy)
+ if err := copy.Validate(); err == nil {
+ t.Fatal("invalid sweep admitted")
+ }
+ }
+}