- session.rs: drop save_manifest_raw — its only caller was
after_manifest_change itself; the pub(crate) advertised the exact
bypass-the-cache-refresh footgun the wrapper exists to eliminate.
Inline the encrypt + atomic_write pair.
- session.rs: into_kdf_params(self) → to_kdf_params(&self). Body just
copies three u32s; the consume-self had no ownership benefit and
forced the round-trip test to rebuild a ParamsFile field-by-field.
- helpers.rs: add git_rm(repo, paths, context) wrapper around git_run
+ the load-bearing --ignore-unmatch flag. Replaces two near-identical
three-line "build rm_args, extend, git_run" blocks in trash.rs.
- trash.rs: purge_item_filesystem drops the if x.exists() pre-checks
(TOCTOU + redundant stat per item per trash-empty iteration). Uses
ErrorKind::NotFound swallow on remove_file/remove_dir_all instead.
- basic_flows.rs: trim trash_empty_batches_into_one_commit's sleep
comment to just the WHY.
Renames purge_item to purge_item_filesystem — body becomes filesystem-only
(remove item.enc, remove attachments/<id>/, manifest.remove). Returns the
relative paths it removed. cmd_purge and cmd_trash_empty accumulate the
paths and fire ONE git rm + ONE git add + ONE git commit per invocation.
A 50-item trash empty now produces 3 git subprocesses regardless of N
(was N+2). New regression test trash_empty_batches_into_one_commit asserts
the one-commit invariant via git rev-list --count.
Adds the canonical post-mutation funnel: save_manifest_raw + groups.cache
refresh in one method. Converts nine commands/*.rs mutation callsites from
the manual save_manifest + refresh_groups_cache pair to a single
vault.after_manifest_change(&manifest)?. save_manifest renamed to
save_manifest_raw (pub(crate)) so future commands cannot accidentally
bypass the cache refresh. Four of the nine sites (attach.rs add/detach,
import.rs LastPass, trash.rs cmd_trash_empty's per-item save) previously
skipped the cache refresh — the wrapper fixes them. refresh_groups_cache
moves from main.rs to helpers.rs so the read-side warmup callers in
get.rs/list.rs still reach it.