Six of the seven LOW findings collected in #308; the seventh is
deliberately skipped (see below).
- SettingsViewModel: run the app-lock disable-path Keystore/DataStore
reseal off the main dispatcher (withContext(Dispatchers.Default)),
matching AppLockViewModel's threading policy - no Keystore crypto on
Main.
- AppLockGateHost: clear the covered app content out of the semantics
tree while locked so TalkBack can't traverse the occluded mailbox/
compose nodes behind the opaque cover; content stays composed so its
state still survives a re-lock.
- ReaderViewModel.toggleStar: reconcile the optimistic star on a failed
persist - roll it back and surface a one-shot StarFailed event instead
of leaving the star stuck in a state the store rejected.
- OnboardingViewModel: persist firstAddedAccountId in SavedStateHandle so
a process kill mid-onboarding still finishes onto the first account's
inbox rather than the unfiltered mailbox (preserves #30).
- RichTextEditor: memoize the formatting toolbar's parse + selection
scans with remember(value) so the per-keystroke hot path isn't
re-derived on every recomposition.
- AccountSetupViewModel.onOutlookResult: treat a normal OAuth cancel
(null result) as a no-op instead of surfacing an error snackbar.
Skipped: MailboxViewModel per-keystroke search re-paging - the finding
is documented-intentional and only a "could". The local pager narrows
cached results instantly as you type while the expensive server search
is already debounced (400ms); debouncing the local pager would add lag
for no clear win, and correct scoping (query only, not account/folder)
adds risk to a hot, well-tested path.
Each behavioral change ships a JVM/Robolectric test; the toolbar
memoization (a pure refactor) adds a toolbarStateOf test. PII-free
AppLog breadcrumbs added on the new fallback/state-change paths.
Closes#308