Ports the last bash dev-script to cross-platform Python and rewires the local /preflight E2E off Gradle Managed Devices (GMD).
What changed
local_instrumented.sh → local_instrumented.py (the .sh is removed). Stdlib-only Python 3, cross-platform (Windows / Linux / macOS), matching api37_e2e.py's style. Behavior preserved exactly: comma-separated test-class CLI arg, pre-boot orphan-kill, manual cold-boot of the dev36 AVD (no GMD, no snapshot), targeted connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=…, and the EXIT-trap teardown — now try/finally + atexit + SIGINT/SIGTERM handlers (idempotent). Exit codes 0/2/3/4 preserved.
Cross-platform process kill abstracted per-OS: taskkill /F /IM … on Windows, pkill -f qemu-system on *nix; process listing via tasklist / ps ax.
Teardown hardened vs the .sh: it now also reaps the emulator launcher image, not just qemu. The Windows -no-window emulator spawns a sibling emulator.exe that briefly outlives the qemu VM, which the qemu-only sweep left as an orphan on return — caught by the smoke run and fixed (the non-negotiable no-orphan rule).
Preflight rewire (SKILL.md + CLAUDE.md): the local api35DebugAndroidTest / api36DebugAndroidTest GMD steps are replaced with python3 .claude/skills/preflight/local_instrumented.py <classes>. CI's own multi-API matrix (ci.yml, build.gradle.kts) is untouched — this is local preflight only.
Convention documented in CLAUDE.md: prefer Python (stdlib) for cross-platform dev scripts; no new bash-only (.sh) or PowerShell-only helpers.
Validation
python -m py_compile, --help, and no-arg → exit 2 (argparse).
Logic diff vs the .sh confirmed on every branch (gradle command, emulator flags, boot loop, preamble/teardown kills, exit codes, JAVA_HOME/ANDROID_AVD_HOME pinning).
Guarded emulator smoke run PASSED.local_instrumented.py org.libremail.data.local.DatabaseEncryptionTest cold-booted the dev36 AVD, ran green, and tore down clean — no qemu/emulator orphan on return (verified independently after the run).
Closes #281
Ports the last bash dev-script to cross-platform Python and rewires the local `/preflight` E2E off Gradle Managed Devices (GMD).
## What changed
- **`local_instrumented.sh` → `local_instrumented.py`** (the `.sh` is removed). Stdlib-only Python 3, cross-platform (Windows / Linux / macOS), matching `api37_e2e.py`'s style. Behavior preserved exactly: comma-separated test-class CLI arg, pre-boot orphan-kill, manual cold-boot of the `dev36` AVD (no GMD, no snapshot), targeted `connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=…`, and the EXIT-trap teardown — now `try/finally` + `atexit` + `SIGINT`/`SIGTERM` handlers (idempotent). Exit codes **0/2/3/4** preserved.
- **Cross-platform process kill** abstracted per-OS: `taskkill /F /IM …` on Windows, `pkill -f qemu-system` on \*nix; process listing via `tasklist` / `ps ax`.
- **Teardown hardened** vs the `.sh`: it now also reaps the emulator *launcher* image, not just `qemu`. The Windows `-no-window` emulator spawns a sibling `emulator.exe` that briefly outlives the qemu VM, which the qemu-only sweep left as an orphan on return — caught by the smoke run and fixed (the non-negotiable no-orphan rule).
- **Preflight rewire** (`SKILL.md` + `CLAUDE.md`): the local `api35DebugAndroidTest` / `api36DebugAndroidTest` GMD steps are replaced with `python3 .claude/skills/preflight/local_instrumented.py <classes>`. CI's own multi-API matrix (`ci.yml`, `build.gradle.kts`) is **untouched** — this is local preflight only.
- **Convention documented** in `CLAUDE.md`: prefer Python (stdlib) for cross-platform dev scripts; no new bash-only (`.sh`) or PowerShell-only helpers.
## Validation
- `python -m py_compile`, `--help`, and no-arg → exit 2 (argparse).
- Logic diff vs the `.sh` confirmed on every branch (gradle command, emulator flags, boot loop, preamble/teardown kills, exit codes, `JAVA_HOME`/`ANDROID_AVD_HOME` pinning).
- **Guarded emulator smoke run PASSED.** `local_instrumented.py org.libremail.data.local.DatabaseEncryptionTest` cold-booted the `dev36` AVD, ran green, and tore down **clean — no qemu/emulator orphan on return** (verified independently after the run).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.
Closes #281
Ports the last bash dev-script to cross-platform Python and rewires the local
/preflightE2E off Gradle Managed Devices (GMD).What changed
local_instrumented.sh→local_instrumented.py(the.shis removed). Stdlib-only Python 3, cross-platform (Windows / Linux / macOS), matchingapi37_e2e.py's style. Behavior preserved exactly: comma-separated test-class CLI arg, pre-boot orphan-kill, manual cold-boot of thedev36AVD (no GMD, no snapshot), targetedconnectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=…, and the EXIT-trap teardown — nowtry/finally+atexit+SIGINT/SIGTERMhandlers (idempotent). Exit codes 0/2/3/4 preserved.taskkill /F /IM …on Windows,pkill -f qemu-systemon *nix; process listing viatasklist/ps ax..sh: it now also reaps the emulator launcher image, not justqemu. The Windows-no-windowemulator spawns a siblingemulator.exethat briefly outlives the qemu VM, which the qemu-only sweep left as an orphan on return — caught by the smoke run and fixed (the non-negotiable no-orphan rule).SKILL.md+CLAUDE.md): the localapi35DebugAndroidTest/api36DebugAndroidTestGMD steps are replaced withpython3 .claude/skills/preflight/local_instrumented.py <classes>. CI's own multi-API matrix (ci.yml,build.gradle.kts) is untouched — this is local preflight only.CLAUDE.md: prefer Python (stdlib) for cross-platform dev scripts; no new bash-only (.sh) or PowerShell-only helpers.Validation
python -m py_compile,--help, and no-arg → exit 2 (argparse)..shconfirmed on every branch (gradle command, emulator flags, boot loop, preamble/teardown kills, exit codes,JAVA_HOME/ANDROID_AVD_HOMEpinning).local_instrumented.py org.libremail.data.local.DatabaseEncryptionTestcold-booted thedev36AVD, ran green, and tore down clean — no qemu/emulator orphan on return (verified independently after the run).🤖 Generated with Claude Code