carlos / platform Public

Plan 4 (cleanup) rev 4: five decisions for Paul before it is built open

On branch self-serve-moves-3

Plan 4 rev 4 (docs/superpowers/plans/2026-10-22-self-serve-moves-4-cleanup.md on self-serve-moves-3) answers Astra's thirteen findings on rev 3 and three new ones. Its structural move: cleanup marks the lifted tombstone in the source bucket's lock.json by CAS with its own id before deleting anything, so no wake or writer can mint a version under the hibernate prefix while the mark holds; root removes its leftovers only through a host-level authority callback and root-owned acknowledgement files. Astra is reviewing rev 4 now. Five things only you can decide:

  1. The legacy move door takes no hostname reservation in rev 4. Your #343 ruling, as rev 3 recorded it, said placement and the legacy move door both reserve. Rev 4 exempts the legacy move because the mark tolerates it (the cleanup stops at its next key when the record carries a move, the wake cannot take the lease while the mark holds, and promotion then overwrites the prefix). Accept the exemption, or have the box reserve around MoveComplete (a box-side release and crash path, and a longer legacy-cut downtime while root answers)?
  2. Cleanup now runs 24 hours after every offline move, operator moves included, replacing the runbook's manual cleanup. Intended?
  3. A box switch CARLOSOFFLINECLEANUP=on gates the rollout (boxes before consoles). Acceptable?
  4. A move of the same host waits while its cleanup holds host/<hostname>, which can last until root answers (paged every 10 minutes). Acceptable?
  5. Copy: four door sentences, five page subjects and one page body, recorded pending you.

I will keep iterating with Astra on everything that is not one of these five; nothing is built until the plan is READY and you have ruled.

Rev 5 (0840937f) answers Astra's review of rev 4; Astra is reviewing it now. Three more decisions join the five above:

  1. The programs file (/data/programs/<host>.json) sits under the agent-writable /data on every built box, so under rev 5's rule (root removes nothing it cannot exclude other writers from) root never removes it and it stays the runbook's to clean. Acceptable, or should the agent remove it itself?
  2. Byte-identical versions: an If-Match delete cannot tell a re-promoted identical copy from the inventoried one, so after any run whose deletes were not all answered the mark and the reservation are held 15 minutes (QuietUntil), then a repair pass removes a cleanup delete marker that hides a newer version (the plan's one version delete, on a marker only). Acceptable?
  3. Rollback to a pre-plan-4 console needs no self-serve move in flight (the old console reads a move's new host key as a mismatch). Acceptable?

Rev 5's review (Astra, after236/selfserve-plan4-review5.md) found that the ETag-conditional delete with a quiet period and a repair pass cannot be made safe: a late HTTP attempt behind a successful SDK retry leaves no obligation to repair, and a delete marker's author cannot be identified, so the repair could undo a later owner's own deletion. Rev 6 therefore changes the primitive, and that is a spec amendment for you:

  1. Design §7.6 says cleanup uses plain deletes (markers) and never VersionId deletes, because deleting the current version would expose an older one. Rev 6 inventories EVERY version under the hibernate prefix at finish and deletes them by version id, oldest first, so nothing older is ever exposed and a late delete can only remove a version that was ours (a version id is unique; a later owner's version is never touched, whatever its bytes). The cost is recoverability of the retired source replica (a finished move's old copy, 24 hours on, which litestream retention would purge anyway). Accept the amendment? If not, the alternative is to leave the source replica in place and let retention handle it, with cleanup limited to root's leftovers.

Also from that review: root's leftover step will run inside the adopt pass (serialised with every root publisher by construction); the rollback becomes a procedure with a drain command on the new console before the old one starts; the CLI log gains lookup by move or cleanup id.

Rev 6 (the version-id deletes, question 9) is committed and with Astra. Question 7 (the 15-minute quiet period) is withdrawn: that mechanism is gone. One operational note from rev 6: a rollback to a pre-plan-4 console now has a procedure (disarm cleanup on every box and wait for every cleanup to end; run carlos system reservations drain on the new console, which fences each placement or create host and releases its entry and refuses while a self-serve move is in flight; stop the new console; start the old one).

Rev 8 (with Astra now) adds one more decision:

  1. Rollback to a pre-plan-4 console needs the new console's clean exit line after drained, and a 20-minute wait after any unanswered reservation write and after the last agent restart (S3 refuses a signed request older than 15 minutes; 5 minutes for skew). If the new console crashed or was killed after drained, roll forward, never back. To make "clean exit" exist, the console gains a SIGTERM handler (refuse new table writes, resend for up to 30 s, log clean or unclean, exit 0), which lengthens every console stop by up to 30 s. Question 5's copy now includes the wake's 503 text while a host's replica is being cleaned up and two drain lines.

Question 10 is replaced. Astra showed that a console rollback across the plan 4 boundary cannot be proven safe: a timed-out reservation write from a crashed new console can still land later, and AWS documents no bound on the interval between a request's authentication and its conditional commit, so no waiting period settles it. Rev 9 removes the mechanism instead of fencing it:

10 (new). Once a plan 4 console has written the reservation table in its new form, a pre-plan-4 console is never run against that deployment again; a defect is fixed by rolling forward. Rollback for plan 4 means disarming cleanup on the boxes (no new obligations; existing ones finish, supersede, or page). The drain command stays as an operator tool for stuck entries; the SIGTERM clean shutdown and the 20-minute wait are gone. Acceptable? (Plans 2 and 3 already had no console rollback across their table and schema changes.)

Also from rev 9: cleanup requires the agent not to run as root (the shipped carlos-edge.service already runs it as its own user); a root-run agent would publish without the exclusion root's step relies on, so arming refuses it with a page.

Rev 9 is with Astra. One more decision from it:

  1. A box whose agent runs as root never cleans up: arming refuses with a page, because a root-run agent would publish without the exclusion root's step relies on. The shipped carlos-edge.service runs the agent as its own user, so this only bites a hand-configured box. Acceptable?

Rev 10 is with Astra. One more:

  1. Once a box has been armed for cleanup, its root gate is permanent: from then on an adopt pass that cannot take the root pass lock publishes nothing that pass (it retries next pass), instead of today's fail-open adoption. The lock is root-only, so only a root-level fault trips it. Acceptable?

Plan 4 reached READY with Astra at rev 12 (84917d06) after nine rounds. I am starting its build on self-serve-moves-4 (stacked on plan 3), inert behind CARLOSOFFLINECLEANUP and plan 1's cutover, with the delete primitive isolated in one function so your answer to question 9 (version-id deletes versus leaving the replica to retention) switches cheaply. The twelve decisions above stay open; nothing merges without you.

Paul ruled 2026-10-24, taking every recommendation: 1 accept the legacy move door exemption; 2 cleanup 24 h after every offline move, operator moves included; 3 CARLOSOFFLINECLEANUP=on gates the rollout, boxes before consoles; 4 a same-host move waits on cleanup; 5 copy rows approved in one pass at the end of the build; 6 the programs file stays the runbook's; 7 withdrawn; 8 superseded by 10; 9 the §7.6 amendment accepted (version-id deletes, oldest first); 10 no console rollback across the plan 4 boundary, roll forward, drain stays an operator tool; 11 a root-run agent never cleans up; 12 the root gate is permanent after arming. The build continues on self-serve-moves-4; the census flips and the copy approval land with its last task.