retention + cleanup: channel retention policy, store GC, deletion audit (P5.3)

Subplan 02 Slice 4. Channel gains retention_keep (0 = keep everything;
N = keep the newest N published releases of the channel's lineage), set
via the new `dist channel set --retention-keep`. The new `dist store
cleanup` prunes lineage-expired releases — never one any channel points
at, so cross-promoted releases survive — drops their artifact rows,
GCs objects/ files no surviving artifact references, and sweeps stale
staging/ leftovers; every deletion writes an audit event. The pruned
model is saved before any unlink, so a crash leaves orphan blobs (next
run catches them), never dangling references.

repo.publish no longer replaces an existing channel row wholesale: only
the pointer moves, so policy/rollout/retention survive every publish
(previously each publish silently reset them to defaults).

std.fs has no directory listing, so cleanup.sx carries a local
opendir/readdir/closedir shim, like publish.sx's time(2) shim.

dist.db channels gains the retention_keep column (idempotent ALTER for
pre-retention stores); db.json import treats it as optional.

tests/retention_cleanup.sx pins the whole scenario; the repo.publish
assertion fails on the pre-fix code. make test 23/23 green.
This commit is contained in:
agra
2026-06-12 19:35:52 +03:00
parent 7ec1e10f6e
commit dc6908dee7
9 changed files with 783 additions and 5 deletions

3
.gitignore vendored
View File

@@ -3,6 +3,9 @@
# build artifacts from `make build`
build/
# a root-level `sx build -o dist` convenience binary
/dist
# scratch store roots / fixtures created by tests (never /tmp)
.sx-tmp/