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>
3 files changed,
+74
−6
internal/sim/hostlife/regionha_completion_witness_test.go+45 −0internal/sim/hostlife/regionha_test.go+28 −5internal/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) |