Addresses the below-cut data-core review nits from #313:
- SignatureRepository.delete: wrap delete + default-promotion in one SignatureDao
@Transaction (deletePromotingDefault) so a crash between them can't leave an
account with signatures but no default; log the promotion (PII-free).
- SignatureRepository.create: move the count-then-default check-then-act into a
SignatureDao @Transaction (insertMakingFirstDefault) so two concurrent
first-creates can't both become default.
- AccountSettingsRepository.update: route the read-modify-write through an
AccountSettingsDao @Transaction (readModifyWrite) so concurrent per-field
setters can't clobber each other.
- MailRepositoryImpl expunge/move/move-by-role: chunk the unbounded
getRoutingByIds/deleteByIds IN(:ids) queries (500/chunk) like MailPruner,
removing the latent SQLITE_MAX_VARIABLE_NUMBER crash.
- MessageDao.observeSummaries: remove the dead whole-table projection (superseded
by Paging #124/#214); migrate test/debug-probe callers to getById or the paged
query (which now guards the #51 CursorWindow regression).
- AccountDataMigrator: fix stale KDoc (schema is v2 with sortOrder, not v1).
- DatabaseEncryption.migrate: also sweep the stale -journal sidecar (journal_mode
= DELETE), matching AccountDataMigrator's sweep.
Unit tests updated for the repository delegations; instrumented DAO tests cover
the new @Transaction behaviour; MailRepositoryImplTest covers the chunk split;
DatabaseEncryptionTest covers the -journal sweep.
Closes#313
Address the Phase-3 review nits collected in #298:
- perf(HtmlToText): hoist the 4 per-call Regex literals in convert() to
private vals so each compiles once, not once per fetched HTML body.
- fix(ReportStore): write reports via temp-file + atomic rename so a crash
mid-write can't truncate a .json that scan() then silently drops. Temp uses
a non-.json suffix so it is never scanned.
- fix(RichTextEditing): applyLink now splits partially-overlapping links
(keeping the non-overlapping remainder) instead of un-linking it whole,
mirroring subtractRange.
- perf(GraphSender): guard attachment size before readBytes() so an oversized
file can't OOM or blow Graph sendMail's ~4 MB request cap; fails
mayHaveSent=false so the outbox falls back to SMTP (which streams).
- perf(RichText): mergeSameValueSpans is O(n) via a last-run-per-style map
instead of O(n^2) indexOfLast; output is identical.
- fix(DiagnosticsCollector): bucket provider labels by DNS-label boundary, not
raw substring, so mail.notgmail.example no longer reads as Gmail.
Adds/updates unit tests for each behavioural change; pure-perf nits keep their
existing green coverage plus a direct mergeSameValueSpans equivalence test.