Skip to content

Drive write lease Phase 3 (part 2): require --lease on every Sheets/Docs write verb - #1670

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

Drive write lease Phase 3 (part 2): require --lease on every Sheets/Docs write verb#1670
newhoggy merged 2 commits into
mainfrom
issue-1664-drive-write-lease

Conversation

@newhoggy

Copy link
Copy Markdown
Contributor

Summary

Continues ADR-0080 Phase 3 (issue #1664): wires the --lease requirement into every remaining content-mutating Sheets/Docs write verb, closing the gap left after PR #1669 (native-document backup support) where only drive edit actually required a lease.

Verbs gated in this PR:

  • drive sheets write/append/clear (SheetsWrite)
  • add-sheet/rename-sheet/insert-rows/insert-columns/duplicate-sheet/reorder-sheet/hide-sheet/show-sheet (SheetsStructure) and delete-sheet/delete-rows/delete-columns/delete-range (SheetsDelete)
  • format-cells/merge-cells/unmerge-cells/update-borders/update-dimension-properties/auto-resize-columns and set-data-validation/clear-data-validation (both SheetsStructure)
  • protect-range/update-protection/unprotect-range (SheetsProtection)
  • drive docs replace/append (DocsWrite)

Design notes

  • One shared fail-closed check, not seven copies. src/drive/lease/check.rs factors the token-verification sequence (present, unexpired, bound to the right file, not stale) out of drive edit's original implementation — the first PR to add a lease check anywhere. A security-critical refusal path copied seven times is exactly the kind of drift a shared function exists to prevent.
  • The staleness check is a fresh files.get immediately before each mutating call, on every surface — never a reuse of metadata fetched before the ancestor-chain walk that decides the folder-permission gate, which can itself take long enough to let the lease go stale first. This mirrors the TOCTOU fix already landed for drive edit in PR Drive write lease Phase 2: drive lease acquire, --lease on drive edit #1667.
  • Docs keeps two separate, unrelated staleness checks. ADR-0076's own requiredRevisionId check (guards the batchUpdate call itself against a concurrent edit) and this ADR's lease-version check (guards the lease ledger's recorded Drive version) both run; neither replaces the other.
  • Since neither the Sheets values/batchUpdate APIs nor the Docs batchUpdate API return Drive metadata in their response, refreshing a lease's recorded version after a successful write costs one extra files.get on these surfaces — the same trade-off ADR-0076 already accepted for the Docs revision lease.
  • Fixed sheets::target_gate::TargetGateOutcome::Gated to actually thread through the requires_lease flag it was computing but discarding — every engine wired in this PR depends on it.

Test plan

  • cargo build --lib --tests --bin omni-dev
  • cargo test --lib (9298 passed)
  • cargo clippy --all-targets --features mcp -- -D warnings (clean)
  • cargo fmt --check (clean)
  • cargo insta test — reviewed and accepted (13 new --lease <TOKEN> flags in the CLI help snapshot, matching exactly the verbs listed above)
  • New lease-refusal tests per engine (no lease / unknown token / wrong file / stale version / require_lease: false opt-out)

Wires ADR-0080 §9's Drive write lease into every content-mutating
Sheets/Docs verb, not just `drive edit`: sheets write/append/clear,
add-sheet/rename-sheet/insert-rows/insert-columns/duplicate-sheet/
reorder-sheet/hide-sheet/show-sheet, delete-sheet/delete-rows/
delete-columns/delete-range, format-cells/merge-cells/unmerge-cells/
update-borders/update-dimension-properties/auto-resize-columns,
set-data-validation/clear-data-validation, protect-range/
update-protection/unprotect-range, and docs replace/append.

The fail-closed lease-check sequence (acquire the ledger lock, verify
the token is present/live/bound to the right file/not stale) is
factored out of drive edit's original implementation into
src/drive/lease/check.rs, shared by all seven engines rather than
copied per engine. The staleness check is a fresh files.get
immediately before each mutating call, never a reuse of metadata
fetched before the ancestor-chain walk that decides the
folder-permission gate. Docs keeps its own, separate revisionId
staleness check (ADR-0076) alongside the new one; neither replaces
the other.

Also fixes target_gate::TargetGateOutcome::Gated to thread through
the requires_lease flag it was previously computing but discarding,
which every one of these engines now depends on.
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

Coverage

Total: 97.24% ⚪ 0.03 pp vs main

Comparing 4a3544b..ea2bc5e (merge-base → PR head)

File Before After Δ
src/drive/lease/check.rs 100% 🆕 new
src/drive/sheets/format.rs 99.66% 99.71% 🟢 0.05 pp
src/cli/drive/sheets/structure.rs 99.5% 99.56% 🟢 0.05 pp
src/cli/drive/sheets/protection.rs 99.04% 99.1% 🟢 0.06 pp
src/drive/sheets/protection.rs 99.61% 99.69% 🟢 0.08 pp
src/drive/sheets/write.rs 99.54% 99.63% 🟢 0.09 pp
src/drive/content_edit.rs 99.29% 99.4% 🟢 0.1 pp
src/drive/sheets/validation.rs 99.56% 99.68% 🟢 0.12 pp
src/drive/sheets/structure.rs 98.22% 98.34% 🟢 0.12 pp
src/cli/drive/sheets/validation.rs 99.03% 99.15% 🟢 0.12 pp
src/cli/drive/sheets/write.rs 97.52% 97.84% 🟢 0.32 pp
src/cli/drive/docs/write.rs 90.76% 91.73% 🟢 0.97 pp
src/drive/docs/write.rs 95.34% 96.69% 🟢 1.36 pp

Patch coverage

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

File Patch Uncovered new lines
src/cli/drive.rs 100% (28/28)
src/cli/drive/docs/write.rs 100% (16/16)
src/cli/drive/sheets/format.rs 100% (24/24)
src/cli/drive/sheets/protection.rs 100% (18/18)
src/cli/drive/sheets/structure.rs 100% (36/36)
src/cli/drive/sheets/validation.rs 100% (17/17)
src/cli/drive/sheets/write.rs 100% (51/51)
src/drive/content_edit.rs 100% (29/29)
src/drive/docs/write.rs 100% (307/307)
src/drive/lease/check.rs 100% (136/136)
src/drive/sheets/format.rs 100% (327/327)
src/drive/sheets/protection.rs 100% (425/425)
src/drive/sheets/structure.rs 100% (232/232)
src/drive/sheets/target_gate.rs 100% (1/1)
src/drive/sheets/validation.rs 100% (341/341)
src/drive/sheets/write.rs 100% (220/220)

📦 Full per-file coverage summary · run summary

Adds the lease-flow tests (NoLease/Expired/WrongFile/Stale/Failed,
pre-write staleness-refetch failure, and the require_lease:false
skip path) that sheets write/structure/format/validation/protection
and docs write were each missing at least one of, plus the
lease-ledger-unreadable and native-refresh-refetch-failure paths in
the newly extracted drive/lease/check.rs, the describe()/log_status()
arms for the lease refusal variants, and resolve_ledger_path's
non-dry-run branch across the six CLI entry points.

check.rs's ledger-unreadable warn! now binds ledger_path.display()
to a local before the macro call (the daemon/services/worktrees.rs
load_pr_cache precedent) so the branch is coverage-visible whether or
not a tracing subscriber is installed, rather than relying on a
per-test subscriber to force the lazy field evaluation.
@newhoggy
newhoggy merged commit b870d4f into main Sep 11, 2026
18 checks passed
@newhoggy
newhoggy deleted the issue-1664-drive-write-lease branch September 11, 2026 14:55
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