Skip to content

Drive write lease Phase 3 (part 1): native-document backups - #1669

Merged
newhoggy merged 3 commits into
mainfrom
issue-1664-drive-write-lease
Sep 11, 2026
Merged

Drive write lease Phase 3 (part 1): native-document backups#1669
newhoggy merged 3 commits into
mainfrom
issue-1664-drive-write-lease

Conversation

@newhoggy

Copy link
Copy Markdown
Contributor

Summary

First slice of Phase 3 of issue #1664 (ADR-0080), landing after Phase 2 (#1667): drive lease acquire now backs up native documents (Sheets/Docs/Slides) too, not just binary files.

  • A Google-native document has no bytes for Phase 2's byte-backup path to hash and copy locally, so it backs up instead as a lossless Drive-side files.copy into a new per-account lease_backup_folder_id setting — restorable by a human in the Drive UI even without this tool.
  • Without that setting configured, a native-document target is still refused outright, before authenticating at all — same behavior as before, now conditional rather than unconditional.
  • LeaseRecord's backup field is now the LeaseBackup enum (Bytes { path, sha256, size } / DriveCopy { file_id }) instead of the three flat, always-required fields Phase 2 shipped — the two forms are mutually exclusive by construction, which independently-optional fields would only have documented.
  • New FilesApi::copy (files.copy) with its own CopyForBackup scope hint (always the unrestricted drive scope, since leasing exists precisely to back up files this tool did not create).

Sheets/Docs write verbs still don't require a lease — only drive edit does, unchanged from Phase 2. Wiring --lease into SheetsWrite/SheetsStructure/SheetsDelete/SheetsProtection/DocsWrite is the remainder of Phase 3, in a follow-up PR (5 separate insertion points, one per engine, already scoped).

A real test-isolation gap caught along the way

AcquireCommand::execute now calls the new settings-reading helper (active_account_lease_backup_folder_id), which meant two existing wiremock-only CLI tests started implicitly depending on this machine's real settings.json instead of being fully isolated. Fixed by giving both the same EnvGuard::clear_credentials() isolation drive::test_support's other settings-reading tests already use.

Test plan

  • cargo test --lib -- drive:: — 1253 passed (18 new)
  • cargo clippy --all-targets --features mcp -- -D warnings — clean
  • cargo fmt --check — clean
  • cargo doc --no-deps --document-private-items — exits 0 (verified locally after the previous PR's cross-platform doc-link failures)
  • Full cargo test --lib — 9263 passed
  • cargo insta test — no snapshot drift (no CLI flags changed)
  • docs/drive.md, CHANGELOG.md, ADR-0080's landed-so-far note updated

Phase 3 (in progress) of ADR-0080 (issue #1664): a Google Sheet/Doc/
Slide has no bytes for the Phase 2 byte-backup path to hash and copy
locally, so it now backs up as a lossless Drive-side files.copy into
a new per-account lease_backup_folder_id setting instead.

- FilesApi::copy (files.copy) with its own CopyForBackup scope hint
  (always the unrestricted `drive` scope -- unlike edit_content there
  is no app-created-it case, since leasing exists precisely to back
  up files omni-dev did not create).
- LeaseRecord's backup field is now the LeaseBackup enum (Bytes {
  path, sha256, size } / DriveCopy { file_id }) instead of the three
  flat, always-required fields Phase 2 shipped -- the two forms are
  mutually exclusive by construction, which a handful of
  independently-optional fields would only have documented.
- acquire() branches on target.is_google_native(): refuses outright,
  before authenticating at all, when native and no backup folder is
  configured (the pre-existing refusal path, now conditional); when a
  folder is configured, backs up via files.copy after authentication
  succeeds, same ordering as the byte-backup path.
- DriveAccountSettings.lease_backup_folder_id (per-account, same
  reason write_permissions is: a folder id only means something
  inside the one Drive it was minted in), resolved by a new
  active_account_lease_backup_folder_id() helper mirroring
  active_account_rules().

Caught during implementation: AcquireCommand::execute now calls the
new settings-reading helper, which made two existing wiremock-only
CLI tests implicitly depend on this machine's real settings.json
instead of being fully isolated. Fixed by giving both the same
EnvGuard::clear_credentials() isolation drive::test_support's other
settings-reading tests already use.

Sheets/Docs write verbs still don't require a lease -- only `drive
edit` does, unchanged from Phase 2. Wiring --lease into
SheetsWrite/SheetsStructure/SheetsDelete/SheetsProtection/DocsWrite
is the remainder of Phase 3.
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

Coverage

Total: 97.21% ⚪ 0.01 pp vs main

Comparing c614268..f467545 (merge-base → PR head)

File Before After Δ
src/drive/content_edit.rs 99.2% 99.29% 🟢 0.09 pp
src/cli/drive/permissions/check.rs 95.42% 97.12% 🟢 1.7 pp

Patch coverage

Patch: 99.03% (611/617 new lines covered)

File Patch Uncovered new lines
src/cli/drive/helpers.rs 100% (45/45)
src/cli/drive/lease.rs 100% (29/29)
src/cli/drive/permissions/check.rs 100% (51/51)
src/drive/account.rs 100% (2/2)
src/drive/content_edit.rs 100% (106/106)
src/drive/files_api.rs 100% (43/43)
src/drive/folder_ancestry.rs 100% (5/5)
src/drive/lease/acquire.rs 97.83% (271/277) 682, 746, 787, 916, 941-942
src/drive/lease/ledger.rs 100% (14/14)
src/drive/write_gate.rs 100% (45/45)
Uncovered new lines (6)
  • src/drive/lease/acquire.rs:682
  • src/drive/lease/acquire.rs:746
  • src/drive/lease/acquire.rs:787
  • src/drive/lease/acquire.rs:916
  • src/drive/lease/acquire.rs:941
  • src/drive/lease/acquire.rs:942

Indirect coverage changes

🔴 0 lines lost coverage, 🟢 6 lines gained coverage on unchanged code.

Indirect changes
  • src/cli/drive/permissions/check.rs:233 🟢 uncovered → covered
  • src/cli/drive/permissions/check.rs:234 🟢 uncovered → covered
  • src/cli/drive/permissions/check.rs:235 🟢 uncovered → covered
  • src/cli/drive/permissions/check.rs:236 🟢 uncovered → covered
  • src/cli/drive/permissions/check.rs:237 🟢 uncovered → covered
  • src/cli/drive/permissions/check.rs:238 🟢 uncovered → covered

📦 Full per-file coverage summary · run summary

newhoggy added a commit that referenced this pull request Sep 11, 2026
Cover the paths the PR #1669 coverage report listed as uncovered new
lines, all failure branches or print arms added by the previous two
commits:

- `active_account_lease_backup_folder_id`: unconfigured account, a
  configured account with no folder set, and one with a folder set.
- `drive lease acquire`: a `files.copy` failure for a native document,
  a failed post-backup metadata re-fetch, and a post-backup fetch that
  omits `version` — each must report `Failed` and write no ledger row.
  The pre-auth `files.get` is mounted once (priority 1) so the second,
  post-backup call falls through to the failing responder, the same
  shape the existing TOCTOU regression test uses.
- `drive edit`: a failed pre-lease metadata re-fetch reports `Failed`
  with no media PATCH issued.
- `print_result`'s `DriveCopy` arm, `print_report`'s "not required"
  lease line, and `append_write_scope_hint`'s `CopyForBackup` hint.

The remaining lines the report names in `acquire.rs` (682, 746, 787,
813-814) are `panic!` arms inside the tests themselves and are not
reachable by design.
…e match

Code review of #1664 found the staleness check comparing against a
version snapshot taken before the permission gate's own files.get
calls, and drive lease acquire recording a version/modifiedTime taken
before the (up to 120s) Touch ID prompt and the backup itself — both
re-fetch fresh metadata at the right point now. decided_rule_requires_lease
also matched rules by folder/depth alone, letting an unrelated rule for a
different operation override an explicit require_lease opt-out; it now
filters by the operation being checked. drive permissions check now
surfaces the requires_lease it already computed instead of dropping it.

Also sanitizes the Drive-controlled file name reaching the Touch ID
prompt and the printed backup path, moves the blocking authenticate()
call onto block_in_place so it can't stall a shared multi-thread
runtime, and reuses the existing hex-encoding helper instead of a
second implementation. Corrects ADR-0080 to stop describing a
CLI-flag/env/settings resolution and global lease settings that
haven't landed yet.
Cover the paths the PR #1669 coverage report listed as uncovered new
lines, all failure branches or print arms added by the previous two
commits:

- `active_account_lease_backup_folder_id`: unconfigured account, a
  configured account with no folder set, and one with a folder set.
- `drive lease acquire`: a `files.copy` failure for a native document,
  a failed post-backup metadata re-fetch, and a post-backup fetch that
  omits `version` — each must report `Failed` and write no ledger row.
  The pre-auth `files.get` is mounted once (priority 1) so the second,
  post-backup call falls through to the failing responder, the same
  shape the existing TOCTOU regression test uses.
- `drive edit`: a failed pre-lease metadata re-fetch reports `Failed`
  with no media PATCH issued.
- `print_result`'s `DriveCopy` arm, `print_report`'s "not required"
  lease line, and `append_write_scope_hint`'s `CopyForBackup` hint.

The remaining lines the report names in `acquire.rs` (682, 746, 787,
813-814) are `panic!` arms inside the tests themselves and are not
reachable by design.
@newhoggy
newhoggy force-pushed the issue-1664-drive-write-lease branch from 0b363a7 to f467545 Compare September 11, 2026 11:12
@newhoggy
newhoggy merged commit 4a3544b into main Sep 11, 2026
18 checks passed
@newhoggy
newhoggy deleted the issue-1664-drive-write-lease branch September 11, 2026 11:36
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