carlos / platform Public

sim: a rollback keeps the source region the files home until the source's return, and a dirty return's install park retires the pre-move generations (moves a5e8c13f, D6)

L18 finding 1 as moves fixed it (branch moves-restoring-source-protection,
movessources.go's restoring case): the source region stays authoritative
while the target releases (releasing, released; the model's returned,
SrcPhase waiting), and from the source's return CAS the newest admission
decides. A clean return names op.SrcGen again on the source; after a
dirty one the target's admission decides until the source's probe is
admitted into the return generation (dirtyReturned), so filesHome no
longer counts the source region, even as op.exec's, and the return's
install park (the model's install-park, moves' D6) retires the pre-move
generations with their files instead of keeping them for the sweep.
servableRegions leaves the window out: the source's next install comes
from the return lane, not a pinned manifest, so INV-30 does not count the
pre-move generations. TestDirtyReturnParksPreMoveFiles shows the
pre-move manifest retired by the install park and the return serving; it
fails under the files-home-move-phases mutation, which keeps it.

No invariant shows D6 retiring anything the source still needs (3,000
two-box seeds, 0 failed; one dirty rollback reached). One-box COUNTERS
and traces unchanged; the 300-seed two-box COUNTERS are unchanged too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev eaa08175dc75f5cb85597257ab62a54dd133abcf parent 372f7a4
3 files changed, +74 −6
  • internal/sim/hostlife/regionha_completion_witness_test.go +45 −0
  • internal/sim/hostlife/regionha_test.go +28 −5
  • internal/sim/hostlife/rules_test.go +1 −1

CI passed — run details

CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok  	amadan.net/carlos/platform.git/launchctx	0.012s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok  	github.com/carlosframework/platform	338.732s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	3.180s
ok  	github.com/carlosframework/platform/cmd/moveprobe	92.219s
ok  	github.com/carlosframework/platform/internal/activator	209.481s
ok  	github.com/carlosframework/platform/internal/agentsock	0.019s
ok  	github.com/carlosframework/platform/internal/alerting	0.003s
ok  	github.com/carlosframework/platform/internal/analytics	0.019s
ok  	github.com/carlosframework/platform/internal/appconfig	2.266s
ok  	github.com/carlosframework/platform/internal/apperr	0.071s
ok  	github.com/carlosframework/platform/internal/applog	0.131s
ok  	github.com/carlosframework/platform/internal/awsinv	0.004s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.004s
ok  	github.com/carlosframework/platform/internal/boxid	0.851s
?   	github.com/carlosframework/platform/internal/boxid/boxidtest	[no test files]
?   	github.com/carlosframework/platform/internal/boxid/internal/signtest	[no test files]
ok  	github.com/carlosframework/platform/internal/boxvolumes	0.008s
ok  	github.com/carlosframework/platform/internal/caps	0.011s
ok  	github.com/carlosframework/platform/internal/certwarm	0.002s
ok  	github.com/carlosframework/platform/internal/cgroup	0.191s
ok  	github.com/carlosframework/platform/internal/changelog	0.239s
ok  	github.com/carlosframework/platform/internal/clamd	0.057s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
ok  	github.com/carlosframework/platform/internal/console	357.452s
?   	github.com/carlosframework/platform/internal/controlname	[no test files]
?   	github.com/carlosframework/platform/internal/controlsock	[no test files]
ok  	github.com/carlosframework/platform/internal/dbdoor	6.444s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.009s
ok  	github.com/carlosframework/platform/internal/dbverify	0.004s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.004s
ok  	github.com/carlosframework/platform/internal/docsite	0.005s
ok  	github.com/carlosframework/platform/internal/domains	1.993s
ok  	github.com/carlosframework/platform/internal/doorkey	0.006s
ok  	github.com/carlosframework/platform/internal/economics	0.007s
ok  	github.com/carlosframework/platform/internal/edge	25.917s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.006s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.137s
ok  	github.com/carlosframework/platform/internal/gate	1.657s
ok  	github.com/carlosframework/platform/internal/genselect	0.014s
ok  	github.com/carlosframework/platform/internal/geo	0.013s
ok  	github.com/carlosframework/platform/internal/geoip	0.029s
ok  	github.com/carlosframework/platform/internal/host	55.459s
ok  	github.com/carlosframework/platform/internal/hostactors	0.352s
ok  	github.com/carlosframework/platform/internal/hostbox	1.390s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.245s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.019s
ok  	github.com/carlosframework/platform/internal/hostexec	0.317s
ok  	github.com/carlosframework/platform/internal/hostfence	0.031s
ok  	github.com/carlosframework/platform/internal/hostfiles	4.149s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.022s
ok  	github.com/carlosframework/platform/internal/hostguard	0.139s
ok  	github.com/carlosframework/platform/internal/hostops	1.401s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.021s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.021s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.141s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.021s
ok  	github.com/carlosframework/platform/internal/infraview	0.021s
ok  	github.com/carlosframework/platform/internal/instancectl	1.704s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.701s
ok  	github.com/carlosframework/platform/internal/instancefs	0.024s
ok  	github.com/carlosframework/platform/internal/instancekey	0.021s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.007s
ok  	github.com/carlosframework/platform/internal/layout	1.341s
ok  	github.com/carlosframework/platform/internal/lease	9.426s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.005s
ok  	github.com/carlosframework/platform/internal/ledger	0.011s
ok  	github.com/carlosframework/platform/internal/mail	0.164s
ok  	github.com/carlosframework/platform/internal/maintenance	0.077s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.480s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.082s
ok  	github.com/carlosframework/platform/internal/metrics	0.005s
ok  	github.com/carlosframework/platform/internal/moves	20.974s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.744s
ok  	github.com/carlosframework/platform/internal/objstore	0.174s
ok  	github.com/carlosframework/platform/internal/ops	0.168s
ok  	github.com/carlosframework/platform/internal/park	0.192s
ok  	github.com/carlosframework/platform/internal/privatefile	0.003s
ok  	github.com/carlosframework/platform/internal/provision	3.201s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.006s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	3.666s
ok  	github.com/carlosframework/platform/internal/relay	7.227s
ok  	github.com/carlosframework/platform/internal/release	18.103s
ok  	github.com/carlosframework/platform/internal/rollout	3.245s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.004s
ok  	github.com/carlosframework/platform/internal/rootaws	0.010s
ok  	github.com/carlosframework/platform/internal/s3compat	0.004s
ok  	github.com/carlosframework/platform/internal/safefile	0.003s
ok  	github.com/carlosframework/platform/internal/schedule	2.212s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	8.460s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	4.258s
ok  	github.com/carlosframework/platform/internal/status	0.039s
ok  	github.com/carlosframework/platform/internal/steering	0.005s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.133s
ok  	github.com/carlosframework/platform/internal/svcrole	0.004s
ok  	github.com/carlosframework/platform/internal/telemetry	0.004s
ok  	github.com/carlosframework/platform/internal/termfence	0.005s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.004s
ok  	github.com/carlosframework/platform/internal/unixdial	0.004s
ok  	github.com/carlosframework/platform/internal/updatewords	0.003s
ok  	github.com/carlosframework/platform/internal/usage	0.004s
ok  	github.com/carlosframework/platform/internal/version	0.001s
ok  	github.com/carlosframework/platform/internal/vet	1.528s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
  minio: cached, digest verified
  mc: cached, digest verified
  minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
  buckets and scoped user ready
=== RUN   TestDeliveredContractMinio
=== RUN   TestDeliveredContractMinio/put
=== RUN   TestDeliveredContractMinio/get_round-trips_the_body
=== RUN   TestDeliveredContractMinio/list_shows_the_key
=== RUN   TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN   TestDeliveredContractMinio/delete_removes_it
=== RUN   TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
    --- PASS: TestDeliveredContractMinio/put (0.00s)
    --- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
    --- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
    --- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
    --- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
    --- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok  	github.com/carlosframework/platform/internal/objstore	0.023s
=== RUN   TestT4StorageProbeMinio
2026/10/12 19:06:56 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DDDDB99BB113BC, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.01s)
PASS
ok  	github.com/carlosframework/platform/internal/host	0.021s
minio contract: PASS
diff --git a/internal/sim/hostlife/regionha_completion_witness_test.go b/internal/sim/hostlife/regionha_completion_witness_test.go
index 0d2b2d8d6..166577cbc 100644
--- a/internal/sim/hostlife/regionha_completion_witness_test.go
+++ b/internal/sim/hostlife/regionha_completion_witness_test.go
@@ -679,3 +679,48 @@ func TestTerminatedCompletionBoxBlocksAPurge(t *testing.T) {
t.Fatalf("the box was declared lost: %s", w.state())
}
}
+
+// TestDirtyReturnParksPreMoveFiles (moves a5e8c13f, L18 finding 1; D6):
+// a move to region 1 rolls back dirty after its target wrote. While the
+// target releases, the source region keeps the files live set; from the
+// source's return CAS the newest admission (the target's) decides, so the
+// return's install park retires the pre-move generation's manifest in the
+// source region rather than keeping it for the at-rest sweep. INV-30 does
+// not count it, the return serves on the source, and every invariant
+// holds to quiescence.
+func TestDirtyReturnParksPreMoveFiles(t *testing.T) {
+ w := newWitness()
+ w.twoBox()
+ src, sib := w.boxes[0], w.boxes[3]
+ w.serve(src, 1, app0)
+ w.running(t, src)
+ w.published(t, src)
+ loc := w.claim.Holder.Location
+ mk, _, _, _, m := w.newestManifest(src.Region, loc, w.claim.Holder.Gen)
+ if m == nil {
+ t.Fatalf("no pre-move manifest: %s", w.state())
+ }
+ w.moveTo(1)
+ tgt := w.boxByID(w.claim.Op.Tgt)
+ w.reach(t, 600, "the target's probe runs, before the commit", func() bool {
+ c := w.claim
+ return c.Op.Kind == "landing" && !c.Op.Committed && hasMain(tgt) && w.rec.MoveState == "landing"
+ }, func() { w.worker(src); w.reconcile(src); w.worker(tgt); w.reconcile(tgt); w.runConsole() })
+ w.mustHold(t)
+ w.appWrite(tgt) // the rollback is dirty
+ w.drive(w.claim.Op.CutDeadline+1-w.now, never, w.act(sib))
+ w.reach(t, 1500, "the dirty return's install park done", func() bool {
+ c := w.claim
+ return c.Op.Kind == "restoring" && c.Op.Dirty && c.Op.SrcPhase != "waiting" && c.Op.SrcPhase != "claimed" && c.Op.SrcPhase != "install-park" || c.Op.Kind == "serving"
+ }, w.actAll(), w.recoveryPass, w.runConsole)
+ w.mustHold(t)
+ if w.stats["rollback-dirty"] == 0 && w.claim.Op.Kind != "restoring" {
+ t.Fatalf("the rollback was not dirty: %s", w.state())
+ }
+ if v := w.buckets[src.Region].current(mk); v != nil {
+ t.Fatalf("the dirty return's install park kept the pre-move manifest %s (sweep owed %v): %s", mk, v.SweepOwed, w.state())
+ }
+ w.reach(t, 1500, "the dirty return serving on the source", func() bool { c := w.claim; return c.Op.Kind == "serving" && c.Op.Exec == src.ID }, w.actAll(), w.recoveryPass, w.runConsole, func() { w.wake(src) })
+ w.finish(t)
+ w.mustHold(t)
+}
diff --git a/internal/sim/hostlife/regionha_test.go b/internal/sim/hostlife/regionha_test.go
index 89f175ae6..1b815e678 100644
--- a/internal/sim/hostlife/regionha_test.go
+++ b/internal/sim/hostlife/regionha_test.go
@@ -2146,10 +2146,17 @@ func (w *world) filesLiveSet(c *claim, r int, k string) bool {
// follows it in the CAS that queues the source region's
// whole-location park-superseded, so the source stays home until
// that park owns it;
-// - restoring, every phase, released with op.exec none included: the
-// source's region (a clean return names op.SrcGen again there; a
-// dirty return's target region is retired by the release's
-// whole-location park, which never consults the files live set);
+// - restoring: the source's region while the target releases
+// (releasing, released, the dirty lane's returned: SrcPhase waiting,
+// op.exec the target or none), until the source's return CAS (moves
+// a5e8c13f, L18 finding 1). After a clean return the source is op.exec
+// and names op.SrcGen again. After a dirty return the newest admission
+// (the target's) decides until the source's probe is admitted into the
+// return generation: the source region is not the files home then,
+// even as op.exec's, so the return's install park (moves' D6,
+// install-park) retires the pre-move generations with their files
+// (dirtyReturned). The target region is retired by the release's
+// whole-location park, which never consults the files live set;
// - creating, deleting, parking, purging, free: none (op.exec is none
// in each, and no case below names them).
//
@@ -2158,6 +2165,9 @@ func (w *world) filesLiveSet(c *claim, r int, k string) bool {
// commit, and in a rollback's released phase, a park-generation queued
// during the move archives the live manifest in the source region.
func (w *world) filesHome(c *claim, r int) bool {
+ if dirtyReturned(c) && r == c.Op.SrcRegion && on("files-home-move-phases") {
+ return false // D6: the dirty return's install parks the pre-move generations
+ }
if e := w.execBox(c); e != nil && e.Region == r {
return true
}
@@ -2197,7 +2207,10 @@ type pinWatch struct {
// op.SrcGen again there); a rollback's source region. Creating,
// deleting, parking and purging have no executor and name none.
func (w *world) servableRegions(c *claim) []int {
- if c == nil || c.Holder == nil {
+ if c == nil || c.Holder == nil || dirtyReturned(c) {
+ // after a dirty return the source's next install comes from the
+ // return lane, never from a pinned manifest of the pre-move
+ // generations, which D6 retires by design (moves a5e8c13f)
return nil
}
var out []int
@@ -2617,3 +2630,13 @@ func (w *world) relocJournalRelease(b *box, c *claim, pending bool) {
w.logf("relocation %s: box %d released the host at TXID %d (flushed=%v)%s", op.ID, b.ID, r.FinalTX, r.Flushed, note)
}
}
+
+// dirtyReturned: a rollback whose source has taken the host back dirty
+// (its return CAS written: SrcPhase past waiting) and whose probe has not
+// yet been admitted into the return generation. From that CAS the newest
+// admission decides (moves' movesAuthoritative, a5e8c13f): the target's,
+// so the source region is not authoritative, and the return's install
+// park retires the pre-move generations (D6).
+func dirtyReturned(c *claim) bool {
+ return c != nil && c.Holder != nil && c.Op.Kind == "restoring" && c.Op.Dirty && c.Op.SrcPhase != "waiting" && c.Holder.Gen != c.Op.RollbackID
+}
diff --git a/internal/sim/hostlife/rules_test.go b/internal/sim/hostlife/rules_test.go
index 134ffae25..5d1ebacee 100644
--- a/internal/sim/hostlife/rules_test.go
+++ b/internal/sim/hostlife/rules_test.go
@@ -209,7 +209,7 @@ var allRules = []string{
"park-evidence-every-box", // final review Important 2 (cf566b41; hostops RetirementEvidence.VerifyObligation, collectEvidence over the inventory; claim §6.2a): in a two-box region a park-replica or park-superseded run by a box that is not the executor waits for every live box of the region not on lost to publish its stop evidence for the operation; the mutation parks on the owner's evidence alone, and the sibling writer's replicator ships into the parked region (INV-10, seed 819; witness)
// Astra's joint round 1 on the two-box model
"files-live-region", // joint round 1 finding 1, claim §6.2a (the live set is {authoritative region, location, generation}; region HA §5.6, L13): the files live set is region-qualified: holder.generation's manifests, the pin's, P's, the chain's newest and the objects they or a box's files directory name are live only in the files home (filesHome: the authoritative region, and while a relocation runs its source region holds the whole files live set), and the newest manifest is read only in the key's own region; the mutation reads every region, so a manifest PUT landing in the former region after a move's cleanup protects itself and the object it names, and the sweep never retires them (INV-10; witness)
- "files-home-move-phases", // joint round 1 review I1, claim §6.2a and moves §5.1's authoritative-region table: the files home also holds the source region while moving (op.exec none from stopped on), while landing until the move commits (op.Committed: admission does not rule out a clean rollback, joint round 2), and while restoring (released included); the mutation keeps op.exec's region and a relocation's source only, so a park-generation queued during the move archives the live manifest in stopped, or after the target's admission before a clean rollback, and the source-region failover pins no keyed manifest (INV-30, INV-3 with INV-30 off; wMoveStoppedKeepsLiveFiles, wRollbackKeepsSourceFiles)
+ "files-home-move-phases", // joint round 1 review I1, claim §6.2a and moves §5.1's authoritative-region table: the files home also holds the source region while moving (op.exec none from stopped on), while landing until the move commits (op.Committed: admission does not rule out a clean rollback, joint round 2), and while restoring until the source's return CAS (releasing and released; after a dirty return the newest admission decides until the source's probe is admitted, so the return's install park retires the pre-move generations, moves' D6, a5e8c13f: TestDirtyReturnParksPreMoveFiles); the mutation keeps op.exec's region and a relocation's source only, so a park-generation queued during the move archives the live manifest in stopped, or after the target's admission before a clean rollback, and the source-region failover pins no keyed manifest (INV-30, INV-3 with INV-30 off; wMoveStoppedKeepsLiveFiles, wRollbackKeepsSourceFiles)
// L18 joint round findings 2 to 4 (regionha_completion_test.go; 1b-next 596fa8b1 and a092ddc2)
"reloc-probe-stop", // finding 2, ruling 49 (Participant's EndProbe, RecordProbeStop, CompleteRelocation's probeStopped): the passed probe's session is stopped with root's receipt and the stop journaled before the serving CAS, bounded by ruling 67; the mutation completes over the running probe, which strands its session across the kind change (INV-31; wProbeStopBeforeServing)
"reloc-probe-fence-owner", // finding 2, ruling 75 (relocationStopProbe's FenceAndStopAs, relocationReleaseHold's LiftFenceAs): the probe stop installs its owned activator fence only on an unfenced host or meets its own, and refuses any other owner's with nothing stopped; the lift lifts only its own; the mutation installs over, and lifts, any fence (INV-32; wProbeStopMeetsAnotherFence, wLiftAfterRestartMeetsAnotherFence)