diff --git a/CLAUDE.md b/CLAUDE.md index 4ec7bbf..dff668c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -145,6 +145,17 @@ First build downloads AGP/Compose/Shizuku from Google Maven + Maven Central. Shizuku, **toggle Wireless debugging off/on** — Shizuku keeps running (separate process), a fresh port + mDNS record appear, and the beacon/connector recover. Plan the Shizuku-tier dev loop around this (or USB, if ever available). +- **A poisoned Gradle *build cache* entry can silently drop a whole module from the APK.** + Symptom: the app dies with `ClassNotFoundException` for a class that plainly exists, while the + build is green and `./gradlew :app:dependencies` lists the module on `debugRuntimeClasspath`. + The module's own jar is correct; its code simply never reaches AGP's intermediates. `clean`, + `rm -rf */build` and `--rerun-tasks` all fail to fix it, because **none of them touch the build + cache** — look for `compileKotlin FROM-CACHE` in the log. Fix: rebuild with `--no-build-cache`. + Verify by grepping the APK's dex for a string literal that only that module defines; grepping for + a *class name* proves nothing, because callers carry the name as a reference whether or not the + class is packaged: + `unzip -o -q app-debug.apk "classes*.dex" && grep -a "pin-sha256:" *.dex` + Suspect this whenever a runtime failure contradicts a successful build. - **Empty-jar race with the IDE.** VSCodium's Java/Kotlin extension runs its own Gradle daemon on the same project; when it overlaps a CLI build, a module's `build/libs/*.jar` can end up containing only a manifest, and Gradle then considers `jar` up-to-date. Dependent modules fail diff --git a/echolot-app/app/src/main/kotlin/app/echo_lot/app/RunViewModel.kt b/echolot-app/app/src/main/kotlin/app/echo_lot/app/RunViewModel.kt index e2114b3..2160fcc 100644 --- a/echolot-app/app/src/main/kotlin/app/echo_lot/app/RunViewModel.kt +++ b/echolot-app/app/src/main/kotlin/app/echo_lot/app/RunViewModel.kt @@ -285,7 +285,11 @@ class RunViewModel(app: Application) : AndroidViewModel(app) { state = state.copy( pendingEnroll = PendingEnroll( link = link, - currentServer = settings.serverUrl, + // Compared against the link's URL, which names the server publicly — so this + // has to be the public name too. Using the endpoint made re-enrolling with the + // same server look like a move to a different one, because the endpoint and + // the public name are deliberately different strings. + currentServer = settings.serverPublicUrl, newServer = target, ) )