Skip to content

feat(drive,cli): add drive lease prune command (#1678) - #1684

Merged
newhoggy merged 4 commits into
mainfrom
issue-1678-drive-lease-prune
Sep 14, 2026
Merged

newhoggy merged 4 commits into
mainfrom
issue-1678-drive-lease-prune

Conversation

@newhoggy

Copy link
Copy Markdown
Contributor

Summary

  • Adds drive lease prune, bounding the lease ledger's and the backup directory/folder's unbounded growth — the ADR-0080 Consequences fast-follow named at drive: gate content writes behind a Touch ID-authorised backup lease, with a fail-closed audit log #1664's close.
  • Mirrors omni-dev log prune's shape (--older-than/--max-size/--dry-run, at least one bound required, age filter then size filter). A live lease is never a removal candidate, regardless of bounds.
  • A ledger row is only ever dropped together with the backup it points at: a Bytes backup is deleted from local disk, a DriveCopy backup is moved to Drive Trash via a new FilesApi::trash (files.update with trashed: true) — the integration's first delete-adjacent capability, deliberately reversible (Trash, not permanent files.delete).
  • A backup-deletion failure skips just that row (left for a future prune run) rather than failing the whole command.

Test plan

  • cargo build
  • cargo fmt --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test (full suite green; the two pre-existing daemon_test.rs lifecycle flakes are unrelated — no daemon files touched)
  • update-snapshots skill run — help_all snapshot diff reviewed and accepted, adds only the new prune subcommand's help text
  • Manual smoke test against the live Drive test account (not yet run)

Bounds the lease ledger's and the backup directory/folder's unbounded
growth, the ADR-0080 Consequences fast-follow named at #1664's close.
Mirrors `omni-dev log prune`'s shape (--older-than/--max-size/--dry-run)
but a live lease is never a candidate, and a ledger row is dropped only
together with the backup it points at: a Bytes backup is deleted from
disk, a DriveCopy backup is moved to Drive Trash via a new
FilesApi::trash (the integration's first delete-adjacent capability,
deliberately reversible rather than a permanent files.delete).
@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown

Coverage

Total: 97.28% ⚪ 0.01 pp vs main

Comparing 0ac2256..6ba86ae (merge-base → PR head)

File Before After Δ
src/drive/lease/prune.rs 100% 🆕 new
src/cli/drive/lease.rs 98.96% 99.23% 🟢 0.27 pp
🔇 5 ignored region(s), 0 tolerated region(s)

ignore removes the lines from both reports; tolerate keeps them in the reported percentage but scores them against the baseline, so a cross-run flip cannot move a delta. Regions are read from each revision's own source.

File Kind Lines Rev Reason
src/cli/drive/lease.rs ignore 408-412 both a sheet restore's fresh backup is always a DriveCopy — restore.rs's own recheck refuses unless the target is still a spreadsheet, and acquire() only ever takes a Bytes backup of a non-native target; this arm exists solely for exhaustiveness over the shared LeaseBackup enum
src/cli/drive/lease.rs ignore 882-885 both guards this test helper against misuse; every call site below passes an acquire subcommand
src/cli/drive/lease.rs ignore 896-899 head guards this test helper against misuse; every call site below passes a prune subcommand
src/drive/lease/ledger.rs ignore 666-670 both mutate_locked refuses before ever calling the closure, so its body never runs — a hit here is a regression, not a coverage gap
src/drive/lease/restore.rs ignore 977-981 both every test using this double refuses before authenticating; a hit here is a regression, not a coverage gap

Patch coverage

Patch: 100% (1179/1179 new lines covered)

File Patch Uncovered new lines
src/cli/drive/lease.rs 100% (205/205)
src/drive/files_api.rs 100% (64/64)
src/drive/lease/ledger.rs 100% (26/26)
src/drive/lease/prune.rs 100% (884/884)

📦 Full per-file coverage summary · run summary

Persist the ledger immediately after each row's backup is cleared,
inside the removal loop, instead of once at the end — a crash mid-run
now strands at most the one row it was working on rather than the
whole batch. Align the --older-than cutoff boundary with
request_log::keep_by_age's inclusive semantics (a row expiring exactly
at the cutoff survives). Fold WriteCapability::Trash into Metadata,
since they gate the identical scope and only differed in wording.
Document drive lease prune in docs/drive.md's Lease section and update
ADR-0080's Consequences/Sync-obligation text now that it has landed.
…rune

Fix a real correctness bug: --max-size alone could trash a zero-byte
DriveCopy backup when it sorted behind an oversized Bytes backup that
alone exceeded the budget, contradicting the documented "DriveCopy is
only reachable via --older-than" contract. The size filter now sets
DriveCopy rows aside before computing the byte budget, so they never
participate in it regardless of position.

Shrink the ledger lock's hold time: rather than one continuous lock
across the whole run (including every sequential Drive trash call),
the lock is now taken once to snapshot removal candidates and once per
row (via the existing mutate_locked) to persist that row's own
removal — the same brief load-mutate-save discipline every other lease
command already uses, so a large prune no longer blocks concurrent
acquire/restore/writes for the run's full duration.

Log and audit every removal attempt: a clear_backup failure now
carries its token/file id/error into a tracing::warn! instead of
collapsing into an opaque count, and every attempt (pruned or failed)
writes its own best-effort audit.jsonl record, mirroring acquire's
audit trail so "why is this backup gone" stays answerable per lease.

Smaller fixes: the CLI summary no longer claims backups were
"trashed"/bytes were "freed" in past tense under --dry-run; a
pre-existing acquire.rs comment claiming prune reclaims the
AlreadyLeased race's orphaned backup is corrected (prune only ever
walks existing ledger rows, so that orphan is a known, separate gap);
a ledger-save failure immediately after a backup was cleared now
surfaces a loud, actionable error naming the stranded token instead of
propagating a generic one.
Adds direct/targeted tests for the lines PR #1684's coverage bot flagged:
backup_size's DriveCopy arm, PruneOutcome's JsonlSerialize impl, a Bytes
backup's NotFound-tolerated and non-NotFound-failure clear_backup paths,
the best-effort audit-write failure being warned and swallowed, the
--max-size-only/older_than:None CLI branch dispatched through
LeaseCommand::execute, the dry-run and non-dry-run human-readable table
summaries, and the "cleared the backup but failed to persist its ledger
removal" dangling-row error path (forced deterministically via a delayed
mock response plus a background thread that revokes the ledger
directory's write permission mid-run).
@newhoggy
newhoggy merged commit fe55042 into main Sep 14, 2026
18 checks passed
@newhoggy
newhoggy deleted the issue-1678-drive-lease-prune branch September 14, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant