The message-open path emits no AppLog breadcrumbs, so the 35–74 s open stalls this perf
run measured are invisible in field diagnostics — they could only be caught by adb-driving a
physical device. Instrument the reader / openMessage path with PII-free latency breadcrumbs so
this class of regression is observable from Problem Reports and so the fixes in #355/#356/#357 can be
verified in the field.
Evidence (physical-device perf run, 2026-07-05)
Opening an uncached message took avg 48.4 s (35–74 s) on real Gmail over LTE (Pixel 10 Pro
XL, origin/main 6118b6d) — see #355 for full data and correlation with backfill.
The perf write-up explicitly notes: "in-app navigation / message-open (ReaderViewModel) emits no AppLog breadcrumbs — AppLog is only wired into sync/backfill/prune workers, IMAP IDLE
push, app-lock, DB encryption, and 'Application created'." The only reason this stall is
quantified at all is external adb instrumentation (am start -W, uiautomator polling).
This also aligns with the maintainer DoD rule that no app source change is done without
appropriate PII-free AppLog logging.
Root cause
ReaderViewModel.init and MailRepositoryImpl.openMessage() do the body fetch with no timing
log around the network round-trip; there is no signal distinguishing a cache hit (instant) from a
cold fetch (seconds–minutes), nor any record of contention with backfill.
Proposed approach
Add PII-free AppLog breadcrumbs around openMessage():
open requested / cache-hit vs cold-fetch / body-fetch duration (ms) / attachment-meta count /
result (success|error). No subject, sender, addresses, or body — only ids/durations/flags
(e.g. a hashed or account-scoped opaque id, folder name, byte/kb size bucket).
Emit a breadcrumb when backfill yields to / resumes after an interactive fetch (#355) and for
prefetch hit/miss + connection reuse (#357), so the whole interactive-vs-background interaction is
traceable from one report.
Follow the existing AppLog tag/level conventions used by the sync/backfill workers.
whichever AppLog util the sync workers already use (match its tags/levels)
Definition of done (per repo DoD)
Unit test asserting a cold open logs a duration breadcrumb and a cached open logs a cache-hit
breadcrumb, with no PII fields present in the logged payload.
Instrumented/E2E coverage that the reader path produces the breadcrumb (and it surfaces in the
diagnostics/Problem Report collector).
Relates to
Enables verification of #355/#356/#357. Maintainer DoD: PII-free AppLog logging. Filed from the
2026-07-05 Pixel 10 Pro XL perf run.
## Summary
The message-open path emits **no `AppLog` breadcrumbs**, so the 35–74 s open stalls this perf
run measured are invisible in field diagnostics — they could only be caught by adb-driving a
physical device. Instrument the reader / `openMessage` path with PII-free latency breadcrumbs so
this class of regression is observable from Problem Reports and so the fixes in #355/#356/#357 can be
verified in the field.
## Evidence (physical-device perf run, 2026-07-05)
- Opening an uncached message took **avg 48.4 s (35–74 s)** on real Gmail over LTE (Pixel 10 Pro
XL, origin/main `6118b6d`) — see #355 for full data and correlation with backfill.
- The perf write-up explicitly notes: "in-app navigation / message-open (`ReaderViewModel`) emits
**no** AppLog breadcrumbs — AppLog is only wired into sync/backfill/prune workers, IMAP IDLE
push, app-lock, DB encryption, and 'Application created'." The only reason this stall is
quantified at all is external adb instrumentation (`am start -W`, uiautomator polling).
- This also aligns with the maintainer DoD rule that no app source change is done without
appropriate PII-free `AppLog` logging.
## Root cause
`ReaderViewModel.init` and `MailRepositoryImpl.openMessage()` do the body fetch with no timing
log around the network round-trip; there is no signal distinguishing a cache hit (instant) from a
cold fetch (seconds–minutes), nor any record of contention with backfill.
## Proposed approach
- Add PII-free `AppLog` breadcrumbs around `openMessage()`:
- open requested / cache-hit vs cold-fetch / body-fetch duration (ms) / attachment-meta count /
result (success|error). **No** subject, sender, addresses, or body — only ids/durations/flags
(e.g. a hashed or account-scoped opaque id, folder name, byte/kb size bucket).
- Emit a breadcrumb when backfill yields to / resumes after an interactive fetch (#355) and for
prefetch hit/miss + connection reuse (#357), so the whole interactive-vs-background interaction is
traceable from one report.
- Follow the existing `AppLog` tag/level conventions used by the sync/backfill workers.
## Affected files
- `app/src/main/kotlin/org/libremail/ui/reader/ReaderViewModel.kt`
- `app/src/main/kotlin/org/libremail/data/repository/MailRepositoryImpl.kt` (`openMessage`, `prefetchMessage`, `inlineImages`)
- whichever `AppLog` util the sync workers already use (match its tags/levels)
## Definition of done (per repo DoD)
- Unit test asserting a cold open logs a duration breadcrumb and a cached open logs a cache-hit
breadcrumb, with **no** PII fields present in the logged payload.
- Instrumented/E2E coverage that the reader path produces the breadcrumb (and it surfaces in the
diagnostics/Problem Report collector).
## Relates to
Enables verification of #355/#356/#357. Maintainer DoD: PII-free `AppLog` logging. Filed from the
2026-07-05 Pixel 10 Pro XL perf run.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
The message-open path emits no
AppLogbreadcrumbs, so the 35–74 s open stalls this perfrun measured are invisible in field diagnostics — they could only be caught by adb-driving a
physical device. Instrument the reader /
openMessagepath with PII-free latency breadcrumbs sothis class of regression is observable from Problem Reports and so the fixes in #355/#356/#357 can be
verified in the field.
Evidence (physical-device perf run, 2026-07-05)
XL, origin/main
6118b6d) — see #355 for full data and correlation with backfill.ReaderViewModel) emitsno AppLog breadcrumbs — AppLog is only wired into sync/backfill/prune workers, IMAP IDLE
push, app-lock, DB encryption, and 'Application created'." The only reason this stall is
quantified at all is external adb instrumentation (
am start -W, uiautomator polling).appropriate PII-free
AppLoglogging.Root cause
ReaderViewModel.initandMailRepositoryImpl.openMessage()do the body fetch with no timinglog around the network round-trip; there is no signal distinguishing a cache hit (instant) from a
cold fetch (seconds–minutes), nor any record of contention with backfill.
Proposed approach
AppLogbreadcrumbs aroundopenMessage():result (success|error). No subject, sender, addresses, or body — only ids/durations/flags
(e.g. a hashed or account-scoped opaque id, folder name, byte/kb size bucket).
prefetch hit/miss + connection reuse (#357), so the whole interactive-vs-background interaction is
traceable from one report.
AppLogtag/level conventions used by the sync/backfill workers.Affected files
app/src/main/kotlin/org/libremail/ui/reader/ReaderViewModel.ktapp/src/main/kotlin/org/libremail/data/repository/MailRepositoryImpl.kt(openMessage,prefetchMessage,inlineImages)AppLogutil the sync workers already use (match its tags/levels)Definition of done (per repo DoD)
breadcrumb, with no PII fields present in the logged payload.
diagnostics/Problem Report collector).
Relates to
Enables verification of #355/#356/#357. Maintainer DoD: PII-free
AppLoglogging. Filed from the2026-07-05 Pixel 10 Pro XL perf run.