carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

| Earlier finding | Disposition at abd90b9f | |---|---| | R1.1 — Expiry signature accepted as production-off | Fixed: no signed expiry object. | | R1.2 — Existing data passes creation checks | Fixed: fresh row, empty-prefix checks, and rename/move restrictions. | | R1.3 — Keep/TTL races expiry | Fixed: settle, lock, and recheck expires_at > 0. | | R1.4 — Purge misses current deadline | Declined-with-reason acceptable: §11 explicitly defers the pre-existing box race; console early purge checks the marker timestamp. | | R1.5 — Expiry replay after keep | Fixed: no bucket expiry authority. | | R1.6 — Restore bypasses cap | Fixed: console restores reserve capacity; direct-mode exception explicitly accepted. | | R1.7 — Restrictions enforced only in CLI | Fixed for the reported paths: server enforcement specified. | | R1.8 — Browser/headless restore misses renewal | Fixed: shared restore helper and catch-up renewal. | | R1.9 — Interrupted purge loses short deadline | Fixed: deadline remains in the row. | | R1.10 — Migration disables expiry | Declined-with-reason acceptable: §4 describes direct migration and accepts its outcome. | | R2.1 — Rename brings permanent data under expiry | Fixed: shared rename operation refuses temporary apps. | | R2.2 — Early purge skips email teardown | Fixed: provisioning refusals and purge backstop. | | R2.3 — Failed restore cancels purge deadline | Fixed: deadline survives until successful row restore. | | R2.4 — Missing marker mistaken for restore | Fixed for the reported completed-purge case: empty prefix finalizes deletion. A separate safeguard regression remains below. | | R2.5 — Materialization omits deadline | Fixed: marker-bound deadline reconstruction specified. | | R2.6 — Instance provisioning bypasses store prohibition | Fixed: manifest and provisioning paths covered. | | R2.7 — Old console silently creates permanent app | Fixed: distinct endpoint and response verification; §7 now agrees. | | R2.8 — Expiry bypasses control-plane protection | Fixed: creation/setter restrictions and pre-trash check. | | R3.1 — Expiry condition matches kept app | Fixed: positive-expiry check under mutex. | | R3.2 — Account move brings permanent data under expiry | Fixed: shared MoveApp operation refuses temporary apps. | | R3.3 — expiring survives restore | Fixed: flag removed; settle-first recovery specified. | | R3.4 — Failed restore frees capacity | Fixed: §6 counts durable restoring reservations. | | R3.5 — Temporary app can become control plane | Fixed: setter restriction and expiry backstop. | | R3.6 — Listing command/API shape nonexistent | Fixed: new command and additive endpoint specified. |

  1. P1 — §§2, 5–6: absence-based recovery drops the existing empty-bucket safeguard. Both recovery branches prescribe PurgeAppRow when the marker and app objects are absent. But existing materialization deliberately refuses that conclusion when the entire apps/ listing is empty: a valid but misconfigured deployment bucket otherwise looks exactly like completed deletion (materialize.go:296, materialize.go:388). PurgeAppRow actually deletes the claim row (store.go:1491).

Start the console against an incorrect empty bucket with temporary apps awaiting purge. Startup materialization preserves their rows, but the proposed early-purge recovery then deletes them. Correcting the bucket leaves the real objects without their console claims or three-day purge authority.

Fix: carry the existing empty-bucket safeguard into settleApp and every absent-marker finalization path. Require a successful deployment-wide listing and preserve rows when it is empty. Test startup materialization followed by the expiry pass against an empty replacement bucket.

  1. P2 — §§5, 7: the existing Trash page promises recovery for 27 days after automatic destruction. The spec changes the CLI deletion output and adds a new listing, but leaves the existing browser deadline calculation unspecified. TrashedApps always calculates PurgesAt using the 30-day TrashCooloff (view.go:492). The template displays that date and explicitly promises 30 days (trash.html:10, trash.html:21). The trash API likewise returns the 30-day date as restorable_until (apicli.go:1295).

A temporary app can therefore be destroyed on day three while its recovery screen advertises day thirty.

Fix: specify one effective purge-deadline calculation using the persisted purge_after for temporary trash, with the existing 30-day calculation for permanent trash. Use it in the Trash view, deletion confirmation, API response, and CLI output; update the unconditional recovery promises. Test both app classes together.

Verdict: revise.