From 5346b6a879551523b8bd6ba995c6be925db4911d Mon Sep 17 00:00:00 2001 From: Ole-Morten Duesund Date: Wed, 10 Jun 2026 15:39:25 +0200 Subject: [PATCH] Guardrail: connected tests refuse to run without an emulator ANDROID_SERIAL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post-incident: an unscoped connectedDebugAndroidTest fanned out to every adb device including the personal phone, where the debug test APK's signature mismatch was resolved by UNINSTALLING the release app — wiping its data. The v1->v2 Room migration was initially blamed but is innocent (three emulator replays preserved data; dumpsys firstInstallTime == lastUpdateTime proved the phone got a fresh install, not an upgrade). connected* tasks now fail fast unless ANDROID_SERIAL names an emulator. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 4 ++++ app/build.gradle.kts | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index d2d9323..47cef29 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,5 +90,9 @@ All gradlew calls need `JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64` — system - Verify alarms: `adb shell dumpsys alarm | grep meddetsamme` — `window=0` means exact - The phone (Pixel 7 Pro) has a RELEASE-signed install: `adb install` of debug builds is rejected on signature; use release builds for the phone, emulator for debug. +- **INCIDENT RULE (2026-06-10, data was lost): never run a device-touching command + unscoped.** Every `adb` call gets `-s `; connected tests get + `ANDROID_SERIAL=emulator-`. The build fails connected* tasks without an emulator + serial — that guard stays. The phone is never a test target. See @README.md for architecture detail. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 53508ad..35aeb6a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -71,6 +71,26 @@ android { sourceSets.getByName("androidTest").assets.srcDir("$projectDir/schemas") } +// Guardrail (added after a real incident): an unscoped connected test run fans +// out to EVERY adb device. On 2026-06-10 that meant the personal phone, where +// the debug test APK's signature mismatch got "resolved" by uninstalling the +// release app — destroying its data. Connected tests now refuse to run unless +// ANDROID_SERIAL pins them to a specific (emulator) device. +tasks.matching { it.name.startsWith("connected") }.configureEach { + doFirst { + val serial = System.getenv("ANDROID_SERIAL") + check(!serial.isNullOrBlank()) { + "Refusing to run connected tests without ANDROID_SERIAL — an unscoped run " + + "targets every adb device, including the phone. " + + "Use: ANDROID_SERIAL=emulator-5554 ./gradlew ${this@configureEach.name}" + } + check(serial.startsWith("emulator-")) { + "ANDROID_SERIAL=$serial is not an emulator — connected tests must never " + + "target a physical device." + } + } +} + // Release builds refresh the bundled FEST dataset when it's >30 days old. // The script fast-exits when fresh and keeps the old file on download failure, // so offline/CI release builds never break on this.