Plan B Phase 4 wanted "every mutating handler must call
refresh_groups_cache" to be a compile-time invariant, with all
callers funneled through Vault::after_manifest_change. The
mutating-handler sweep happened, but two read-side callsites
(commands/list.rs and commands/get.rs) still called the public
helper directly for opportunistic shell-completion cache freshness.
Closes the gap:
- helpers::refresh_groups_cache demoted from pub to pub(crate).
- list.rs and get.rs drop their explicit calls. Cache freshness
between mutations is unaffected: every mutating handler still
funnels through after_manifest_change. The minor staleness
window (manifest changed externally via git pull, no local
mutation since) is the trade-off the spec accepts in exchange
for the compile-time invariant.
The Plan B done-criterion "grep refresh_groups_cache outside
session.rs returns zero" now passes apart from the function
definition itself, which lives in helpers.rs (the natural place
for a flat utility). The visibility scoping achieves the
architectural intent.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 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.
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.
The groups-cache opt-out is a developer debugging knob, not a
user-facing config. Gating the env-var lookup behind cfg!(debug_assertions)
makes release builds ignore the variable; the optimiser removes the
lookup entirely, so the variable name doesn't appear in release binary
strings output.
Doc-comments updated to reflect the new behaviour.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Control characters (newlines, tabs) in item titles corrupted git log
output. Now strips control chars and truncates to 50 chars.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Locks the singular vs plural transition (1 minute ago vs 2 minutes
ago) and each bucket boundary (59→60s minutes, 3599→3600s hours,
86400→86400×2 days, etc.) so future tweaks can't silently regress
the user-facing labels.
Reads .relicario/last_backup (written by cmd_backup_export). Format:
'never' for fresh vaults, '4 days ago' otherwise. Closes the
'is my backup stale?' question without leaving the terminal.