Two lifecycle-consistency fixes from PR #45's review (issue #101).
1. Grace across Back/recreation. The AppLockGate state machine was a field of
the Activity-scoped AppLockViewModel, so Back on the task root (which finishes
the Activity and clears its ViewModelStore on API 29/30) dropped the grace
marker and re-armed a fresh LOCKED gate, demanding full re-auth on return —
unlike leaving via Home. Provide AppLockGate as an application-scoped @Singleton
(SecurityModule) and inject it into the ViewModel, so the same instance is
reused across recreation and the 30s grace behaves identically for Back and
Home. A genuine cold start (process death) still constructs a fresh, LOCKED gate.
2. PassphraseSession eviction. The KDoc promised the passphrase is "cleared on
lock, timeout," but nothing re-locked it on grace expiry and full eviction is
not achievable without a DB close/reopen (provideDatabase runs once per process;
owned by #93 / #111). Correct the KDoc to state the process-lifetime limitation
explicitly and add a code comment at the timeout re-lock deferring full eviction
to #93 / #111. We deliberately do NOT call session.lock() on timeout: it is the
only separately-held copy but also drives EncryptedCacheGuard, so clearing it
while merely locked (not exited) would stall background sync/push even though the
DB stays open — not a correct partial eviction. No DatabaseModule changes.
Tests (JVM): extend AppLockGateTest to cover grace surviving a reused-instance
recreation within and beyond the window, and a fresh gate starting LOCKED; add
AppLockViewModelTest asserting the gate is an injected dependency the ViewModel
delegates to (onBackground/onAuthError).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>