Add temp-file and Cargo-unit cleaners, reclaiming without external tools - #7
Merged
Conversation
Vacuum could not cover the workflow it was built to replace. Reclaiming disk space still meant dropping to a shell for `sudo rm -r /tmp/*`, a system-wide `nix-collect-garbage`, a time-based `journalctl --vacuum`, and cargo-sweep. Two cleaners close that gap, both reading the filesystem directly and executing nothing: * `temp-files` (new category) offers an entry under /tmp, /var/tmp, or $TMPDIR only when it is not a symlink, is owned by the invoking user, has gone untouched for seven days, and is not live session state. Root-owned leftovers stay out of scope and are reclaimed through a printed `sudo systemd-tmpfiles --clean` instead. * `cargo-prune` removes dead build units from inside a Cargo target/ directory, so a project still in use keeps its warm cache rather than paying for a full rebuild. Units are grouped by the compiler hash Cargo records in each fingerprint; the group with the most recent activity is the toolchain in use and the rest are leftovers. Freshness comes from the invoked.timestamp file Cargo writes for the purpose, never from access time, which relatime does not update when Cargo reuses an artifact. The incremental cache is offered separately, and a profile whose .cargo-lock is held by a running build is skipped entirely. Package-manager GC also gains system-wide `nix-collect-garbage -d --verbose`, `journalctl --vacuum-time=7d`, and `systemd-tmpfiles --clean`, all as printed sudo lines; Vacuum still never escalates. Three changes to the core contract were required rather than incidental: * `Cleaner::extra_roots` lets a cleaner declare delete roots beyond the scan roots. /tmp is outside $HOME, so every temp candidate would otherwise be refused as outside the allowed roots. The widened set applies only when that cleaner is selected; the protected-prefix and symlink guards are untouched. * `Candidate::trash_ok` marks candidates the trash cannot help. A path under /tmp trashes into /tmp/.Trash-$uid on the same filesystem and frees nothing, and a batch of hundreds of regenerable files would scatter as many trash entries. These purge instead, with the reason reported rather than applied silently. --apply is still required. * `Target::Batch` carries the many scattered paths one logical reclaim covers. The safety gate runs over every path before any is removed, so a batch is refused whole rather than half-applied. Sizes now count a hardlinked inode once. Cargo hardlinks its final artifacts into place, so summing file lengths naively over-reported this workspace by about 12% and would have claimed the same bytes twice. Frontends group by category rather than by cleaner, so a category appears once however many cleaners feed it, and --cleaner selects a single one where their candidates deliberately overlap. --stale-days and a stale_days config key set the staleness window. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FCJbPm4FWS99y9utbUe13z
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Vacuum could not cover the workflow it was built to replace. Reclaiming disk space still meant dropping to a shell for
sudo rm -r /tmp/*, a system-widenix-collect-garbage, a time-basedjournalctl --vacuum, andcargo-sweep.Two new cleaners, both executing nothing
temp-files(new category) is the safe counterpart tosudo rm -r /tmp/*, which destroys other users' files and the live state of running processes. An entry under/tmp,/var/tmp, or$TMPDIRis offered only when it is not a symlink, is owned by the invoking user, has gone untouched for seven days, and is not session state (.X11-unix,systemd-private-*,.Trash-*, …). Root-owned leftovers stay out of scope and go to a printedsudo systemd-tmpfiles --clean.cargo-pruneremoves dead build units from inside a Cargotarget/, so a project still in use keeps its warm cache. It replaces acargo-sweepshell-out that could never have worked here:cargo-sweepis unmaintained (its README says so).cargo metadata,rustup toolchain list, andrustc -vV, none of which run on a Nix- or Guix-managed toolchain —cargo-sweep sweep --dry-run --installederrors out on the maintainer's own machine.--timemode reads atime, its oldest open bug: underrelatimeCargo does not bump atime on artifacts it reuses, so it deletes warm caches.Instead: target dirs are found by
CACHEDIR.TAG; units are grouped by the"rustc"hash Cargo writes into each fingerprint, and the group with the most recent activity is the toolchain in use; freshness comes frominvoked.timestamp. The incremental cache — ~30% of a target dir's bytes and somethingcargo-sweepnever reclaims — is offered separately. A profile whose.cargo-lockis held by a running build is skipped.Package-manager GC also gains system-wide
nix-collect-garbage -d --verbose,journalctl --vacuum-time=7d, andsystemd-tmpfiles --clean, all as printed sudo lines.Core contract changes, required rather than incidental
Cleaner::extra_roots—/tmpis outside$HOME, so every temp candidate would have been refused withOUTSIDE_ROOTS. The widened set applies only when that cleaner is selected; denylist and symlink guards untouched.Candidate::trash_ok— a path under/tmptrashes into/tmp/.Trash-$uidon the same filesystem and frees nothing. These purge instead, with the reason reported, never silently.--applystill required.Target::Batch— one logical reclaim spanning many paths. The safety gate runs over every path before any is removed, so a batch is refused whole rather than half-applied.Sizes now count a hardlinked inode once; Cargo hardlinks its artifacts, so naive summing over-reported this workspace by ~12% and would have claimed the same bytes twice.
Frontends group by category rather than by cleaner (a latent bug: a second cleaner in a category produced duplicate JSON groups and duplicate headings).
--cleanerselects one where candidates deliberately overlap;--stale-daysand astale_daysconfig key set the window.Verification
The toolchain-hash correspondence was proven before anything depended on it: hashing the
-vVtext Cargo stores in.rustc_info.jsonreproduces the fingerprint value exactly —9571511559510505644for rustc 1.95.0 and125055456796404090for 1.97.1, matching every fingerprint in the respective target dirs.End to end, a crate was built with 1.95.0 then 1.97.1 to create two real toolchain groups in one target dir. Pruning the stale group left the live group and the unhashed binary intact, and the rebuild reported
Fresh itoa/Fresh e2e— zero recompilation.Gate:
cargo fmt --check,clippy --workspace --all-targets -D warnings, 82 tests,reuse lint,makeinfowith no warnings.🤖 Generated with Claude Code
https://claude.ai/code/session_01FCJbPm4FWS99y9utbUe13z