Duplicate of #111 (2026-09-06, the flagship filling its root and failing every push to amadan.net) — same gap, found independently on a different box two days later, with the same evidence: retention prunes the bucket, nothing removes the extracted copy, and ensureStaged re-fetches a missing one so removing it is eviction rather than a retention decision.
Fixed on forge main as caf60d1. The keep set is exactly the one this issue derived: every version any /opt/carlos/live/* symlink names, plus every version and prev_version in the registry, with a 6-hour grace on newest-file mtime for everything else (CARLOS_VERSIONS_GRACE overrides).
Two places the shipped version differs from the shape proposed here, both of them things #111's review turned up:
It is not in the agent's retention tick. It cannot be: carlos-edge.service runs the agent unprivileged under ProtectSystem=strict with ReadWritePaths=/data /run/carlos, so it cannot write /opt at all — which is why staging is already relayed to root through carlos-stage.path. The sweep runs in the root carlos adopt pass instead, as step 0: after the registry opens and deliberately before openDeployStore, the pubkey parse and release.Pass. All three are fatal, and release.Pass writes its bake clock into /data — so on a box that has actually filled, a sweep placed after any of them never runs and the box cannot free itself.
Live-link pins are read from the links, not from the registry. The two agree on a healthy box. The case where they disagree is the one that matters: PointLive succeeded, reg.SetVersions then failed on a full /data, and the link is the only record of what is running.
The rest — the flock that serialises the pass against carlos-stage.service, the journald cap, and the root-free-bytes item that is still open — is on #111. Closing this one; the discussion there carries the follow-ups.
Note for annie-welding specifically: nothing sweeps until the box is rolled onto caf60d1. Until then the hand cleanup in this issue is still the only sweep it gets.