carlos / platform Public

moves: TargetLocationScope and ResidueScope; the rollback's target release and the landing's residue parks use them (Task 2 fix round 1)

Both build holder.location, the holder's incarnation and the 120 s
request limit from their genuine bound (the target's stop report's
CheckedAt; the flip), as the two call sites did by hand. Behaviour is
unchanged; TestScopeHelpersUseTheGenuineBound covers both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 2db9c06bec8ee933d5e262c0c52d9a833bb0a145 parent 7deabe4
4 files changed, +26 −4
  • internal/host/movesinstall.go +1 −1
  • internal/host/movesrollback.go +1 −2
  • internal/moves/parkscope.go +20 −1
  • internal/moves/parkscope_test.go +4 −0
diff --git a/internal/host/movesinstall.go b/internal/host/movesinstall.go
index 5d1f4e8f8..f90509e38 100644
--- a/internal/host/movesinstall.go
+++ b/internal/host/movesinstall.go
@@ -424,7 +424,7 @@ func (ir *installRun) parking(ctx context.Context) (bool, error) {
if ir.rec.Move.FlippedAt == nil {
return false, fmt.Errorf("move %s: the record has no flip time", id)
}
- scope := hostclaim.ParkScope{Location: location, Incarnation: holder.Incarnation, EvidenceAt: ir.rec.Move.FlippedAt.UTC(), RequestLimit: landingRequestLimit}
+ scope := moves.ResidueScope(holder, *ir.rec.Move.FlippedAt)
if obs, err = hostclaim.Update(ctx, ir.s, obs, hostclaim.QueueResidueParks(id, cfg.Region, scope, gens, obligationID)); err != nil {
return false, fmt.Errorf("queue the residue parks: %w", err)
}
diff --git a/internal/host/movesrollback.go b/internal/host/movesrollback.go
index 7a0b1734a..d52afdedc 100644
--- a/internal/host/movesrollback.go
+++ b/internal/host/movesrollback.go
@@ -582,8 +582,7 @@ func (r *rollbackRun) claimReleased(ctx context.Context, evidence string) error
return err
}
holder := obs.Claim.Holder
- scope := hostclaim.ParkScope{Location: strings.TrimSuffix(holder.Location, "/"), Incarnation: holder.Incarnation,
- EvidenceAt: report.CheckedAt.UTC(), RequestLimit: cutRequestLimit}
+ scope := moves.TargetLocationScope(*holder, report.CheckedAt)
if _, err := hostclaim.Update(ctx, r.s, obs, hostclaim.TargetReleased(r.g.ID, cfg.Box, cfg.Region, evidence, scope, obligationID)); err != nil {
return fmt.Errorf("the claim half of the release: %w", err)
}
diff --git a/internal/moves/parkscope.go b/internal/moves/parkscope.go
index daedeac6d..f1611d6f0 100644
--- a/internal/moves/parkscope.go
+++ b/internal/moves/parkscope.go
@@ -26,7 +26,26 @@ const ParkRequestLimit = 120 * time.Second
// comes after ExecToNone, after which the source never writes its location
// again.
func SourceLocationScope(holder hostclaim.Holder, flippedAt time.Time) hostclaim.ParkScope {
- return hostclaim.ParkScope{Location: strings.TrimSuffix(holder.Location, "/"), Incarnation: holder.Incarnation, EvidenceAt: flippedAt.UTC(), RequestLimit: ParkRequestLimit}
+ return locationScope(holder, flippedAt)
+}
+
+// TargetLocationScope is a rolled-back target region's location park, queued
+// when the target releases (hostclaim.TargetReleased): holder.location and
+// the holder's incarnation. Its horizon is the target's stop report's
+// CheckedAt, after which the target writes its location no more.
+func TargetLocationScope(holder hostclaim.Holder, stopCheckedAt time.Time) hostclaim.ParkScope {
+ return locationScope(holder, stopCheckedAt)
+}
+
+// ResidueScope is the location a landing's earlier-stay residue parks
+// (hostclaim.QueueResidueParks) are scoped to: holder.location and the
+// holder's incarnation. Its horizon starts at the flip, like the source's.
+func ResidueScope(holder hostclaim.Holder, flippedAt time.Time) hostclaim.ParkScope {
+ return locationScope(holder, flippedAt)
+}
+
+func locationScope(holder hostclaim.Holder, bound time.Time) hostclaim.ParkScope {
+ return hostclaim.ParkScope{Location: strings.TrimSuffix(holder.Location, "/"), Incarnation: holder.Incarnation, EvidenceAt: bound.UTC(), RequestLimit: ParkRequestLimit}
}
// StagingScope is the park of a move's own staging lane, moves/<host>/<op>,
diff --git a/internal/moves/parkscope_test.go b/internal/moves/parkscope_test.go
index c20bef500..5c3ee56e1 100644
--- a/internal/moves/parkscope_test.go
+++ b/internal/moves/parkscope_test.go
@@ -28,6 +28,10 @@ func TestScopeHelpersUseTheGenuineBound(t *testing.T) {
}{
{"source location, holder with a slash", SourceLocationScope(hostclaim.Holder{Location: "instances/" + host + "/" + inc + "/", Incarnation: inc}, bound), "instances/" + host + "/" + inc},
{"source location, holder without one", SourceLocationScope(hostclaim.Holder{Location: "instances/" + host, Incarnation: inc}, bound), "instances/" + host},
+ {"target location, holder with a slash", TargetLocationScope(hostclaim.Holder{Location: "instances/" + host + "/" + inc + "/", Incarnation: inc}, bound), "instances/" + host + "/" + inc},
+ {"target location, holder without one", TargetLocationScope(hostclaim.Holder{Location: "instances/" + host, Incarnation: inc}, bound), "instances/" + host},
+ {"residue, holder with a slash", ResidueScope(hostclaim.Holder{Location: "instances/" + host + "/" + inc + "/", Incarnation: inc}, bound), "instances/" + host + "/" + inc},
+ {"residue, holder without one", ResidueScope(hostclaim.Holder{Location: "instances/" + host, Incarnation: inc}, bound), "instances/" + host},
{"staging", StagingScope(host, "move-1", inc, bound), "moves/" + host + "/move-1"},
{"abort", AbortScope(host, "move-1", inc, bound), "moves/" + host + "/move-1"},
{"return lane", ReturnLaneScope(host, "rb-1", inc, bound), "moves/" + host + "/rb-1"},