carlos / platform Public

design: self-serve offline moves, status: Astra READY at rev 16, awaiting Paul

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 516e35eb8ae36bc1a2ea545f21db5ce35f63ada5 parent 0e1eb66
1 file changed, +1 −1
  • docs/superpowers/specs/2026-10-21-self-serve-offline-moves-design.md +1 −1

CI passed — run details

CI log
=== gate ===
.amadan/test-gate.sh
=== launchctx (1 s)
ok  	amadan.net/carlos/platform.git/launchctx	0.015s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
=== vet (2 s)
=== gofmt (1 s)
gofmt -l .: clean
=== rest (144 s)
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	5.128s
ok  	github.com/carlosframework/platform/cmd/moveprobe	12.679s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.833s
ok  	github.com/carlosframework/platform/internal/activator	60.467s
ok  	github.com/carlosframework/platform/internal/agentsock	0.042s
ok  	github.com/carlosframework/platform/internal/alerting	0.017s
ok  	github.com/carlosframework/platform/internal/analytics	0.059s
ok  	github.com/carlosframework/platform/internal/appconfig	4.296s
ok  	github.com/carlosframework/platform/internal/apperr	0.373s
ok  	github.com/carlosframework/platform/internal/applog	0.279s
ok  	github.com/carlosframework/platform/internal/awsinv	0.012s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.009s
ok  	github.com/carlosframework/platform/internal/boxid	1.336s
?   	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.012s
ok  	github.com/carlosframework/platform/internal/caps	0.036s
ok  	github.com/carlosframework/platform/internal/certwarm	0.008s
ok  	github.com/carlosframework/platform/internal/cgroup	0.833s
ok  	github.com/carlosframework/platform/internal/changelog	0.643s
ok  	github.com/carlosframework/platform/internal/claimactivation	1.953s
ok  	github.com/carlosframework/platform/internal/clamd	0.060s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
?   	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	5.904s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.015s
ok  	github.com/carlosframework/platform/internal/dbverify	0.005s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.005s
ok  	github.com/carlosframework/platform/internal/docsite	0.031s
ok  	github.com/carlosframework/platform/internal/domains	3.718s
ok  	github.com/carlosframework/platform/internal/doorkey	0.017s
ok  	github.com/carlosframework/platform/internal/economics	0.008s
ok  	github.com/carlosframework/platform/internal/edge	46.683s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.029s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.220s
ok  	github.com/carlosframework/platform/internal/gate	2.269s
ok  	github.com/carlosframework/platform/internal/genselect	0.039s
ok  	github.com/carlosframework/platform/internal/geo	0.019s
ok  	github.com/carlosframework/platform/internal/geoip	0.042s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.481s
ok  	github.com/carlosframework/platform/internal/host	140.455s
ok  	github.com/carlosframework/platform/internal/hostactors	8.438s
ok  	github.com/carlosframework/platform/internal/hostactors/awsmembership	0.005s
ok  	github.com/carlosframework/platform/internal/hostbox	4.212s
ok  	github.com/carlosframework/platform/internal/hostclaim	1.580s
ok  	github.com/carlosframework/platform/internal/hostenv	0.008s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.012s
ok  	github.com/carlosframework/platform/internal/hostexec	7.679s
ok  	github.com/carlosframework/platform/internal/hostfence	0.057s
ok  	github.com/carlosframework/platform/internal/hostfiles	11.002s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.033s
ok  	github.com/carlosframework/platform/internal/hostguard	1.121s
ok  	github.com/carlosframework/platform/internal/hostops	2.999s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.003s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.006s
ok  	github.com/carlosframework/platform/internal/hostrelocate	1.290s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.306s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.005s
ok  	github.com/carlosframework/platform/internal/infraview	0.008s
ok  	github.com/carlosframework/platform/internal/instancectl	5.295s
ok  	github.com/carlosframework/platform/internal/instanceexec	3.001s
ok  	github.com/carlosframework/platform/internal/instancefs	0.006s
ok  	github.com/carlosframework/platform/internal/instancekey	0.004s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.017s
ok  	github.com/carlosframework/platform/internal/layout	2.969s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.006s
ok  	github.com/carlosframework/platform/internal/lease	10.194s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.918s
ok  	github.com/carlosframework/platform/internal/ledger	0.016s
ok  	github.com/carlosframework/platform/internal/mail	0.258s
ok  	github.com/carlosframework/platform/internal/maintenance	0.122s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.983s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.169s
ok  	github.com/carlosframework/platform/internal/metrics	0.007s
ok  	github.com/carlosframework/platform/internal/moves	23.000s
ok  	github.com/carlosframework/platform/internal/moves/archive	1.566s
ok  	github.com/carlosframework/platform/internal/objstore	0.366s
ok  	github.com/carlosframework/platform/internal/ops	0.202s
ok  	github.com/carlosframework/platform/internal/park	0.289s
ok  	github.com/carlosframework/platform/internal/plans	0.018s
ok  	github.com/carlosframework/platform/internal/privatefile	0.009s
ok  	github.com/carlosframework/platform/internal/provision	16.327s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.008s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	6.612s
ok  	github.com/carlosframework/platform/internal/relay	7.263s
ok  	github.com/carlosframework/platform/internal/release	28.049s
ok  	github.com/carlosframework/platform/internal/rollout	4.663s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.009s
ok  	github.com/carlosframework/platform/internal/rootaws	0.010s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.075s
ok  	github.com/carlosframework/platform/internal/rootstate	0.033s
ok  	github.com/carlosframework/platform/internal/runs	0.547s
ok  	github.com/carlosframework/platform/internal/s3compat	0.012s
ok  	github.com/carlosframework/platform/internal/safefile	0.007s
ok  	github.com/carlosframework/platform/internal/schedule	3.156s
ok  	github.com/carlosframework/platform/internal/seeds	0.017s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	17.204s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	3.416s
ok  	github.com/carlosframework/platform/internal/status	0.096s
ok  	github.com/carlosframework/platform/internal/steering	0.009s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.261s
ok  	github.com/carlosframework/platform/internal/svcrole	0.003s
ok  	github.com/carlosframework/platform/internal/telemetry	0.009s
ok  	github.com/carlosframework/platform/internal/termfence	0.008s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.007s
ok  	github.com/carlosframework/platform/internal/unixdial	0.005s
ok  	github.com/carlosframework/platform/internal/updatewords	0.007s
ok  	github.com/carlosframework/platform/internal/usage	0.010s
ok  	github.com/carlosframework/platform/internal/version	0.002s
ok  	github.com/carlosframework/platform/internal/vet	1.976s
=== internal_console@0 (86 s)
ok  	github.com/carlosframework/platform/internal/console	83.367s
=== internal_console@1 (40 s)
ok  	github.com/carlosframework/platform/internal/console	37.273s
=== internal_console@2 (45 s)
ok  	github.com/carlosframework/platform/internal/console	41.668s
=== internal_console@3 (40 s)
ok  	github.com/carlosframework/platform/internal/console	36.778s
=== internal_console@4 (40 s)
ok  	github.com/carlosframework/platform/internal/console	37.141s
=== internal_console@5 (38 s)
ok  	github.com/carlosframework/platform/internal/console	34.612s
=== internal_console@6 (39 s)
ok  	github.com/carlosframework/platform/internal/console	36.119s
=== internal_console@7 (46 s)
ok  	github.com/carlosframework/platform/internal/console	43.119s
=== root@0 (36 s)
ok  	github.com/carlosframework/platform	33.441s
=== root@1 (46 s)
ok  	github.com/carlosframework/platform	43.252s
=== root@2 (43 s)
ok  	github.com/carlosframework/platform	40.963s
=== 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
linux/arm64 carlos binary: 50069666 bytes (ceiling 52000000)
--- 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.025s
=== RUN   TestT4StorageProbeMinio
2026/10/21 23:37:58 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E0AFBD28FA9B7A, 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.022s
minio contract: PASS
diff --git a/docs/superpowers/specs/2026-10-21-self-serve-offline-moves-design.md b/docs/superpowers/specs/2026-10-21-self-serve-offline-moves-design.md
index 2ee162eda..23b26d23e 100644
--- a/docs/superpowers/specs/2026-10-21-self-serve-offline-moves-design.md
+++ b/docs/superpowers/specs/2026-10-21-self-serve-offline-moves-design.md
@@ -1,6 +1,6 @@
# Self-serve offline moves: a tenant's customer moves their own instance
-Rev 16, 2026-10-22. Status: design for review (Paul). Branch `design-self-serve-offline-moves`. Rev 1 (062387a1) was reviewed by Astra (`after236/selfserve-spec-review.md`, NOT READY, 14 findings) and by a cold builder check (7 questions); rev 2 (0e58e51d) answered them and was reviewed again (`selfserve-spec-review2.md`, NOT READY, 12 findings). Rev 2's automatic return after `open` was the source of most of round 2: whether the new region ever accepted a write cannot be proven from the bucket (litestream releases the lease even when its final sync failed; file-only changes leave no LTX; an absent lock does not tell "never taken" from "released"), and a safe post-open revert would need a new admission barrier and stop protocol for the target, which is Phase 1's work. **Rev 3 narrows the promise to what is provable**: every failure before `open` returns the account to the old region automatically and losslessly, everything checkable is checked before `open`, and a failure after `open` is paged with the holding page kept until the app answers, the return being an operator's run of the same offline move the other way. This changes one of Paul's rulings and is §10 Q3. Rev 3 (b77c01a9) was reviewed again (`selfserve-spec-review3.md`, NOT READY, 8 findings, the abort boundary accepted); rev 4 answers them: which failures stay after `open` (§7.5), the job's durable admission order (§7), cleanup's own reservation and the root handoff (§7.6), the holding entries' causal ordering (§6.4), the probe mode (§7.4), deadlines as initiate-and-retry with blocked states (§7.2), and the stale contradictions. Rev 4 (20fdcf18) was reviewed again (`selfserve-spec-review4.md`, NOT READY, 2 P1 and 2 P2: holding entries ordered by move identity cannot order consecutive moves or show an abort completed; a timed-out root cleanup request could run after its reservation was released; the queued handshake's producers; stale text). Rev 5 answers them with a per-host `move_seq` published with every route and holding entry (§6.4), a root request that ends only with root's `done` or acknowledged `cancelled` (§7.6), queued defined as no copying with the participants' reports allowed (§7), and the stale passages aligned. Rev 5 (89e0f37a) was reviewed again (`selfserve-spec-review5.md`, NOT READY, 1 P1 and 2 P2: a serving row rebuilt by ordinary reconciliation after a finished move would carry sequence 0 and lose to a stale holding entry, and a recreated host's publications need separating from a previous life's; the queued table order; two stale sentences). Rev 6 answers them: reconciliation persists the record's sequence with a row it mints after a move (§6.4), a per-record `move_lineage` separates lives (§6.4), the table order and the two sentences are fixed. Rev 6 (bc5e2d97) was reviewed again (`selfserve-spec-review6.md`, NOT READY, 1 P1: a random lineage identifies but does not order lifetimes, and a lineage-less publication retained by an edge through a read failure can coexist with the first move's holding entry). Rev 7 replaces the lineage with a durable per-hostname incarnation number the console allocates and never reuses, so every publication orders as (incarnation, sequence) and a publication without one loses (§6.4). Rev 7 (f2feed6c) was reviewed again (`selfserve-spec-review7.md`, NOT READY, 1 P1: a custom domain repointed from one instance to another compares two unrelated counters). Rev 8 gives alias bindings their own durable revision from the domains record, published with the target hostname; edges pick the newest binding first and compare the target's pair within it (§6.4). Rev 8 (05f4c353) was reviewed again (`selfserve-spec-review8.md`, NOT READY, 1 P1: aliases derived from a wildcard catch-all have no binding record to carry a revision; 1 P2: retaining a detached domains record changes what a claim listing means). Rev 9 narrows: holding entries list explicit aliases only and derived aliases keep today's answer during a move; the binding revision lives in a console table that survives the domains record, so detach stays a delete (§6.4). Rev 9 (3bcea70e) was reviewed again (`selfserve-spec-review9.md`, NOT READY: a detached explicit alias can fall back to a derived one beneath a retained explicit holding entry; the revision table and the bucket write need a crash-safe protocol). Every remaining round-8 and round-9 finding was about a holding entry an edge keeps as last-good through a read failure and later orders against a new binding. **Rev 10 stops tolerating that case instead of ordering it:** a holding entry is never last-good, it is honoured only from a publication fetched within the last five minutes, and the alias revision machinery is dropped; the incarnation number is allocated durably before it is exposed (§6.4). Rev 10 (82023a80) was reviewed again (`selfserve-spec-review10.md`, NOT READY, 2 P1 and 1 P2: two concurrent creates of one hostname can commit incarnations out of order; a fresh hold must not displace a retained ready route for an alias that now names another instance; alias publications need the target hostname). Rev 11 answers them: creates and deletes of a hostname are serialised through a pending allocation that only a completed record write commits (§6.4); for an alias, any eligible ready route naming a different target wins over a holding entry, pairs are compared only within one target, and the five-minute bound is stated as a bound on reader disconnection, not publisher death (§6.4); every alias publication carries its target hostname (§6.4). Rev 11 (9ae3902f) was reviewed again (`selfserve-spec-review11.md`, NOT READY, 1 P1: recovery after a console crash could abandon an outstanding create whose record write later lands, because the record's "absent" precondition recurs after a deletion). Rev 12 makes the incarnation an authoritative bucket slot that never returns to absent and that every consumer checks the record against, so a late write materialises nothing (§6.4). Rev 12 (35bd391d) was reviewed again (`selfserve-spec-review12.md`, NOT READY, 2 P1: recovery that advances the slot would invalidate a create whose record did land; the ordinary delete is unconditional and a delayed one could erase a successor). Rev 13 answers them: recovery replays the create's own intent to completion at its allocated number and never advances a slot to abandon; deletes are conditional on the observed record ETag and retire only the captured slot revision, with persisted intent (§6.4). Rev 13 (696cb374) was reviewed again (`selfserve-spec-review13.md`, NOT READY, 1 P1: a retirement that exposes the record's absence before the slot is retired lets a late original create restore an obsolete record, and an ETag difference alone was read as a successor; 1 P2: allocation recovery omitted the crash before the slot write and a stale record under the key). Rev 14 answers them: retirement first replaces the record with a non-servable tombstone, so absence is never exposed while the slot is live, and every recovery branch of the create and the retirement is listed (§6.4). Rev 14 (bb05348f) was reviewed again (`selfserve-spec-review14.md`, NOT READY, 1 P1: a record without the incarnation field was accepted beside any existing slot, so an old console's outstanding create could land after a retirement; 1 P2: two retirement recovery states had no terminal disposition). Rev 15 answers them: a fieldless record beside a slot is valid only under a live migration intent bound to that record's key and ETag, and both retirement states get terminal outcomes (§6.4). Rev 15 (dd094a27) was reviewed again (`selfserve-spec-review15.md`, NOT READY, 1 P1: an ordinary record write that wins between the migration's read and its CAS left a legitimate fieldless record that the new rule treated as stale). Rev 16 answers it: migration first marks the record by CAS, which fences every writer, and binds the slot to the marked record (§6.4).
+Rev 16, 2026-10-22. Status: Astra READY at rev 16 (`after236/selfserve-spec-review16.md`, sixteen rounds); awaiting Paul's approval, with §10 Q3 the one ruling it changes. Built behind `CARLOS_SELF_SERVE_MOVES` meanwhile, off by default. Branch `design-self-serve-offline-moves`. Rev 1 (062387a1) was reviewed by Astra (`after236/selfserve-spec-review.md`, NOT READY, 14 findings) and by a cold builder check (7 questions); rev 2 (0e58e51d) answered them and was reviewed again (`selfserve-spec-review2.md`, NOT READY, 12 findings). Rev 2's automatic return after `open` was the source of most of round 2: whether the new region ever accepted a write cannot be proven from the bucket (litestream releases the lease even when its final sync failed; file-only changes leave no LTX; an absent lock does not tell "never taken" from "released"), and a safe post-open revert would need a new admission barrier and stop protocol for the target, which is Phase 1's work. **Rev 3 narrows the promise to what is provable**: every failure before `open` returns the account to the old region automatically and losslessly, everything checkable is checked before `open`, and a failure after `open` is paged with the holding page kept until the app answers, the return being an operator's run of the same offline move the other way. This changes one of Paul's rulings and is §10 Q3. Rev 3 (b77c01a9) was reviewed again (`selfserve-spec-review3.md`, NOT READY, 8 findings, the abort boundary accepted); rev 4 answers them: which failures stay after `open` (§7.5), the job's durable admission order (§7), cleanup's own reservation and the root handoff (§7.6), the holding entries' causal ordering (§6.4), the probe mode (§7.4), deadlines as initiate-and-retry with blocked states (§7.2), and the stale contradictions. Rev 4 (20fdcf18) was reviewed again (`selfserve-spec-review4.md`, NOT READY, 2 P1 and 2 P2: holding entries ordered by move identity cannot order consecutive moves or show an abort completed; a timed-out root cleanup request could run after its reservation was released; the queued handshake's producers; stale text). Rev 5 answers them with a per-host `move_seq` published with every route and holding entry (§6.4), a root request that ends only with root's `done` or acknowledged `cancelled` (§7.6), queued defined as no copying with the participants' reports allowed (§7), and the stale passages aligned. Rev 5 (89e0f37a) was reviewed again (`selfserve-spec-review5.md`, NOT READY, 1 P1 and 2 P2: a serving row rebuilt by ordinary reconciliation after a finished move would carry sequence 0 and lose to a stale holding entry, and a recreated host's publications need separating from a previous life's; the queued table order; two stale sentences). Rev 6 answers them: reconciliation persists the record's sequence with a row it mints after a move (§6.4), a per-record `move_lineage` separates lives (§6.4), the table order and the two sentences are fixed. Rev 6 (bc5e2d97) was reviewed again (`selfserve-spec-review6.md`, NOT READY, 1 P1: a random lineage identifies but does not order lifetimes, and a lineage-less publication retained by an edge through a read failure can coexist with the first move's holding entry). Rev 7 replaces the lineage with a durable per-hostname incarnation number the console allocates and never reuses, so every publication orders as (incarnation, sequence) and a publication without one loses (§6.4). Rev 7 (f2feed6c) was reviewed again (`selfserve-spec-review7.md`, NOT READY, 1 P1: a custom domain repointed from one instance to another compares two unrelated counters). Rev 8 gives alias bindings their own durable revision from the domains record, published with the target hostname; edges pick the newest binding first and compare the target's pair within it (§6.4). Rev 8 (05f4c353) was reviewed again (`selfserve-spec-review8.md`, NOT READY, 1 P1: aliases derived from a wildcard catch-all have no binding record to carry a revision; 1 P2: retaining a detached domains record changes what a claim listing means). Rev 9 narrows: holding entries list explicit aliases only and derived aliases keep today's answer during a move; the binding revision lives in a console table that survives the domains record, so detach stays a delete (§6.4). Rev 9 (3bcea70e) was reviewed again (`selfserve-spec-review9.md`, NOT READY: a detached explicit alias can fall back to a derived one beneath a retained explicit holding entry; the revision table and the bucket write need a crash-safe protocol). Every remaining round-8 and round-9 finding was about a holding entry an edge keeps as last-good through a read failure and later orders against a new binding. **Rev 10 stops tolerating that case instead of ordering it:** a holding entry is never last-good, it is honoured only from a publication fetched within the last five minutes, and the alias revision machinery is dropped; the incarnation number is allocated durably before it is exposed (§6.4). Rev 10 (82023a80) was reviewed again (`selfserve-spec-review10.md`, NOT READY, 2 P1 and 1 P2: two concurrent creates of one hostname can commit incarnations out of order; a fresh hold must not displace a retained ready route for an alias that now names another instance; alias publications need the target hostname). Rev 11 answers them: creates and deletes of a hostname are serialised through a pending allocation that only a completed record write commits (§6.4); for an alias, any eligible ready route naming a different target wins over a holding entry, pairs are compared only within one target, and the five-minute bound is stated as a bound on reader disconnection, not publisher death (§6.4); every alias publication carries its target hostname (§6.4). Rev 11 (9ae3902f) was reviewed again (`selfserve-spec-review11.md`, NOT READY, 1 P1: recovery after a console crash could abandon an outstanding create whose record write later lands, because the record's "absent" precondition recurs after a deletion). Rev 12 makes the incarnation an authoritative bucket slot that never returns to absent and that every consumer checks the record against, so a late write materialises nothing (§6.4). Rev 12 (35bd391d) was reviewed again (`selfserve-spec-review12.md`, NOT READY, 2 P1: recovery that advances the slot would invalidate a create whose record did land; the ordinary delete is unconditional and a delayed one could erase a successor). Rev 13 answers them: recovery replays the create's own intent to completion at its allocated number and never advances a slot to abandon; deletes are conditional on the observed record ETag and retire only the captured slot revision, with persisted intent (§6.4). Rev 13 (696cb374) was reviewed again (`selfserve-spec-review13.md`, NOT READY, 1 P1: a retirement that exposes the record's absence before the slot is retired lets a late original create restore an obsolete record, and an ETag difference alone was read as a successor; 1 P2: allocation recovery omitted the crash before the slot write and a stale record under the key). Rev 14 answers them: retirement first replaces the record with a non-servable tombstone, so absence is never exposed while the slot is live, and every recovery branch of the create and the retirement is listed (§6.4). Rev 14 (bb05348f) was reviewed again (`selfserve-spec-review14.md`, NOT READY, 1 P1: a record without the incarnation field was accepted beside any existing slot, so an old console's outstanding create could land after a retirement; 1 P2: two retirement recovery states had no terminal disposition). Rev 15 answers them: a fieldless record beside a slot is valid only under a live migration intent bound to that record's key and ETag, and both retirement states get terminal outcomes (§6.4). Rev 15 (dd094a27) was reviewed again (`selfserve-spec-review15.md`, NOT READY, 1 P1: an ordinary record write that wins between the migration's read and its CAS left a legitimate fieldless record that the new rule treated as stale). Rev 16 answers it: migration first marks the record by CAS, which fences every writer, and binds the slot to the marked record (§6.4).
Builds on the offline operator move (`docs/superpowers/plans/2026-10-20-offline-operator-move.md`, shipped in v0.28.5 to v0.28.7) and its runbook (`docs/superpowers/plans/2026-10-21-offline-move-runbook.md`). Supersedes nothing: Phase 1 (`docs/superpowers/specs/2026-10-02-move-any-app-design.md`) stays the live-move path for claims-on deployments, and the M1 to M18 rulings stand. M18 ("Tito waits; move mutations are operator-only") is answered here for one door, not lifted: §5 says exactly what the customer's request may do.