design: self-serve offline moves, status: Astra READY at rev 16, awaiting Paul
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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. |