Add a cross-platform, standard-library-only Python package under
scripts/device-testing/ that replicates LibreMail's on-device performance-test
scenarios and logging capture, codifying the methodology run by hand on
2026-07-05 (Pixel 10 Pro XL).
Modules:
- breadcrumbs.py: a pure, unit-tested parser for the ImapPerf / MailReader /
Reader / MailBackfiller breadcrumbs, plus open-correlation that reproduces the
manual timing-tables.md figures exactly.
- adb.py: a safety-guarded adb wrapper -- an allow-list of adb subcommands and a
deny-list + assertions on shell commands. The only sanctioned app-state
mutation is clearing LibreMail's own cache/ (exact-match); no pm clear /
uninstall / data wipe, and no touching databases/ files/ shared_prefs/
datastore/ can be constructed.
- uidump.py: uiautomator XML parser + screen recognition (mailbox rows with the
cached "Available offline" flag, reader, and the keyguard / foreign-app guards).
- scenarios.py: cold-open, message-open (uncached), back-nav, prefetch A/B
(fetch-policy toggle) and cross-provider, each keyguard-guarded and driven
through the guarded wrapper.
- report.py + perf_harness.py: aggregates, a timing-tables.md renderer mirroring
the manual write-up, and the CLI (timestamped run dir with the raw logcat, a
filtered breadcrumb extract, and the tables). --dry-run prints the exact
command plan without touching device state.
Tests (stdlib unittest, 68 cases) validate the parser, guardrails, UI
recognition and report against the manual run's real captures (fixtures include
a verbatim perf-extract slice and the reader/lockscreen/alarm dumps). Track the
*.log fixture past the gitignore *.log rule via a scoped negation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The priority-based runner orchestration ("traffic-control") lived as a large
inline-bash step in ci.yml — a two-pass preemption + hold-back script that was
effectively untestable in YAML. Move it into .github/scripts/traffic_control.py,
structured as a pure decision CORE + a thin gh-I/O SHELL:
* Pure functions (no network/clock/subprocess), unit-testable in isolation:
- effective_priority(pr): lowest-numbered P0-P9, default P5; broken OR draft => 10.
- runs_to_cancel(this_pr, all_prs, self_run_id): PASS 1 — run ids to cancel,
empty unless THIS PR is P0; only strictly-lower running/queued runs; never self
(by number or run id), never equal-or-higher.
- wait_blockers(this_pr, all_prs): PASS 2 — yield to any strictly-higher PR with
an active/queued run, and to same-level peers ordered ahead (running-first,
then oldest createdAt). Empty => proceed.
* Shell (run_live): gathers the snapshot via gh, applies cancels, runs the bounded
hold-back poll loop; always exits 0. --dry-run feeds the core a snapshot JSON and
prints decisions with zero network.
* No jq/bash dependency (cross-platform, per the repo's Python-stdlib convention).
ci.yml's traffic-control job now checks out the repo and runs the module. Job
permissions gain `contents: read` (for checkout) alongside the existing
`actions: write` / `pull-requests: read`; env and downstream `needs:` wiring
unchanged; step stays `continue-on-error`.
33 stdlib unittest cases cover priority resolution, P0-only preemption, the
self/main/equal-or-higher invariants, and the same-level running-first/oldest
ordering.
Behaviour is preserved except the ticket's refinements: (1) drafts now count as
P10 (bottom); (2) an explicit same-level running-first-then-oldest tiebreaker; and
(3) per the ticket's order-of-operations, ONLY P0 preempts — the old bash also let
any higher-priority PR cancel a `broken` target's run, which no longer happens
(a broken/draft run is only cancelled by a P0, via the same strictly-lower rule).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CLAUDE.md: build gotchas (JDK 17-21, AGP built-in Kotlin, KSP-not-KAPT),
test stack, the SPDX header rule, Conventional Commits, and commands.
- .claude/settings.json + hooks/check-spdx.py: PostToolUse hook that warns
(non-blocking) when a .kt/.kts file lacks the SPDX license header.
- .claude/skills/preflight: runs the fast CI gate (assembleDebug +
testDebugUnitTest + lintDebug) locally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Initial scaffold for LibreMail, a free and open-source (GPL-3.0) Android email
client. This increment delivers a buildable, runnable, themed app shell on top
of the full architecture skeleton; account sign-in, IMAP/SMTP sync and sending
arrive in later increments.
- Gradle 9.6 + AGP 9.2 + Kotlin 2.4.0 (AGP built-in Kotlin via the buildscript
classpath; KSP, no KAPT); version catalog; minSdk 33, target/compile SDK 37
- Jetpack Compose + Material 3 with Material You dynamic color, light/dark and
edge-to-edge; adaptive, themed launcher icon
- Navigation across Inbox, Reader, Compose, Settings (with an Advanced Settings
group) and Account Setup
- Hilt DI, Room cache (entities/DAOs/database), domain models, and a
MailRepository as single source of truth with a sample-data fallback
- Unit tests (repository + sample data) and a Compose smoke test
- GPL-3.0 LICENSE, SPDX headers, README with build and Gmail OAuth setup steps
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>