Files
zeekay e872b1907a the weekly prune could delete the build production was about to serve
A publish writes its objects first and its manifest last. The prune listed
manifests, then listed objects, so a build finishing between the two had its
chunks present and claimed by nothing -- and both forge mirrors publish on
every push, so that window is not narrow. It ran --apply on a cron, unattended,
and would have removed the unique chunks of the build the pin moved to moments
later: exactly the 404 this origin exists to prevent, reintroduced by its own
housekeeping.

Two independent guards, because one is a race and races deserve a backstop.
The manifests are re-read AFTER the object listing and anything new counts as
referenced. And nothing younger than a day is ever deleted whatever the
manifests say -- age used only to PROTECT, never to condemn, since expiring by
age would delete the most stable chunks first.

Two more ways it could delete too much. The clock is the runner's while the
timestamps are the store's, so a runner running fast made every manifest look
expired at once and collapsed retention to a single build; a floor of ten
builds bounds that. And an orphan set approaching the size of the tree means a
premise is wrong -- a truncated manifest, a half-read listing -- so it refuses
rather than deletes. Per-key delete errors are reported instead of counted as
successes.

Separately, the publish trusted a length to decide a hashed name was already
published. The name claims specific bytes, so it now checks the bytes against
the store's ETag and fails the build on a mismatch. A file that merely LOOKS
content-addressed -- logo-DARKMODE.svg fits the shape -- would otherwise be
served from the first build forever, or replaced under clients holding it for a
year, and both happened silently.

The S3 credential moves off the docker command line to a passed-through
variable, and both halves are masked rather than just the secret.

Tests are mutation-checked: removing either prune guard fails its case. They
run in ci-python beside the other bin/ script tests, skipped when the file is
absent so the new step cannot fail an unrebased PR.
2026-08-08 23:10:13 -07:00
..