feat(cli): report quota usage deltas around permanent purge batches - #36
Merged
Conversation
Wraps the three permanent-cleanup batches — direct `clean --purge`, clean expiry purge, and explicit `trash purge` — with a read-only quota observation: a pre snapshot, the mutation, a post snapshot, and a signed per-scope usage delta. Observation is strictly best-effort and never blocks or alters the mutation: a failed pre canonicalization, provider error, or non-absolute request is reported as unavailable, and the mutation result and exit code are preserved regardless. A delta is reported only when the before/after snapshots are comparable — provider, data source, filesystem, mount identity (device major/minor and source), subject, and observation anchor must all match — so a mount replaced between the two probes yields an explicit incomparable result rather than a fabricated delta. Anchors resolving to the same scope fold to one probe. Signed deltas use i128 and are never clamped; JSON carries the full signed values and human output states the observed change is not attributed to degu. Empty entry plans still run, and observe, the aged-claim-marker housekeeping the human path previously skipped. The observation report is additive: default JSON/human output and exit codes are otherwise unchanged. A subject with privileged mount-namespace control can still stage an ABA mount swap within a single probe to influence a reporting-only delta; that grants no mutation authority and never changes deletion selection or exit code, and remains the existing hostile-root boundary.
FeathBow
force-pushed
the
feat/quota-observed-delta
branch
from
August 9, 2026 16:44
14716ca to
5874c5d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.
Suppressed comments (2)
crates/degu/src/commands/trash/purge.rs:29
plan.has_housekeeping_scope()is effectively alwaystrueforplan_purge_all():trash_roots()always yields at least the state root (lifecycle/storage.rs:173-186), andplan_all_trash()always pushes one batch per root (lifecycle/purge.rs:103-125). That makes theTRASH_IS_EMPTYearly-return and thenot_attempted_action(..., NotStartedReason::Empty)branch below effectively unreachable, but they still add branching complexity to the command.
Consider simplifying by removing the has_housekeeping_scope() checks here and always going through the planned+coordinate observation path (or otherwise keying the “empty” fast-path off a condition that can actually occur).
if !plan.has_housekeeping_scope() {
return stdoutln!("{}", super::output::TRASH_IS_EMPTY);
}
crates/degu/src/lifecycle/purge/plan.rs:70
has_housekeeping_scope()currently returns!self.batches.is_empty(), but batches are always populated becausetrash_roots()always returns at least one root (lifecycle/storage.rs:173-186) and the purge planners always push a batch per root (lifecycle/purge.rs:64-85 and 103-125). As a result, this helper does not actually distinguish “nothing to do” cases and can mislead callers into thinking the value is meaningful.
Either remove this helper (and inline the invariant where needed) or redefine it to reflect the condition you actually care about (e.g., presence of any purge entries or housekeeping work).
/// Even an entry-empty batch may purge aged numeric claim markers.
pub(crate) fn has_housekeeping_scope(&self) -> bool {
!self.batches.is_empty()
}
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.
Summary
Wires the action/batch result contract and the reusable quota probe into the three permanent-cleanup batches — direct
clean --purge, clean expiry purge, and explicittrash purge— so each one reports a read-only quota usage delta measured around the mutation. It takes a pre snapshot, runs the mutation, takes a post snapshot, and reports a signed per-scope delta. The observation report is additive: default JSON/human output and exit codes are otherwise unchanged.Guarantees
unavailable; the mutation still runs and its result and exit code are preserved. Observation failures cannot change what is deleted or the process exit status.incomparabledimension rather than a fabricated delta. Anchors that resolve to the same scope fold to a single probe.i128and are never clamped or sign-flipped; JSON carries the full signed values (viaarbitrary_precision), and human output states plainly that the observed change is not attributed to degu.Trash is empty.return.Residual boundary
A subject with privileged mount-namespace control can still stage an ABA mount swap within a single probe window to influence a reporting-only delta. This grants no mutation authority, never changes deletion selection or exit code, and is the same hostile-root boundary degu already documents; closing it would require Linux 5.14+
quotactl_fdor an inherited-FD execution model and is out of scope here.Validation
cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warningson the host and forx86_64-unknown-linux-musl: clean.cargo test --workspaceon the host and on a non-root Linux host underumask 002: green. Unit tests pin the signed-delta boundaries and JSON round-trip, the pre→execute→post ordering, non-blocking behavior on canonicalization/provider/relative-request failure, per-identity folding (including refusal to fold across filesystems or a replaced mount), a partially-failed batch still being observed, and non-causal, escape-safe human copy; integration tests cover direct/expiry/trash purge end-to-end and the empty-plan housekeeping observation.