android-readiness-agent-wave
Use when asked to make an Android app prod ready by fixing a whole readiness audit at once with parallel agents: R8 + signing, Room migration tests, auto-backup privacy leak, crash journal, monotonic
SKILL.mdskills/managed/android-readiness-agent-wave/SKILL.md
GitHubAndroid readiness agent wave
Companion to android-release-readiness-audit (which finds the gaps). This is
how to close them all concurrently, plus the defects that show up every time.
Wave mechanics (briefs, build rule, acceptance) are fixer-wave-orchestration;
this skill adds the Android slice plan.
1. Slice by exclusive file ownership
One agent per slice; no two agents may touch the same file. Paths are Ironvellum's
(D:/Monarch, package com.ironvellum.app); map them onto the target app.
| Slice | Owns | Slice skill |
|---|---|---|
| BuildRelease | app/build.gradle.kts, gradle/libs.versions.toml, app/proguard-rules.pro |
android-r8-release-verification |
| MigrationTests | data/IronvellumDatabase.kt (the @Database class), app/src/androidTest/**, new app/src/test/**/data/** |
room-migration-data-survival-test, kotlin-test-file-overwrite-guard |
| BackupPrivacy | AndroidManifest.xml, res/xml/** |
android-backup-exclusion-verify |
| CrashJournal | new data/CrashJournal.kt, IronvellumApp.kt, ui/settings/SettingsScreen.kt |
android-crash-journal-and-periodic-work-verify |
| SyncGuard | data/cloud/CloudSync.kt, data/cloud/Dtos.kt |
|
| LauncherIcon | res/mipmap-*/** only |
android-adaptive-icon-from-generated-art |
| PolicyDocs | PRIVACY.md, docs/**, README.md |
play-data-safety-code-audit |
| CI | .github/workflows/**, workflow_dispatch triggers only (never push/PR/schedule) |
github-actions-cost-shutdown |
| SdkScout | read-only research (scout agent) |
Brief every slice per fixer-wave-orchestration: Gradle agents run no gradle, lint
or adb, and the parent validates once. Write these cross-slice contracts into the
shared context before dispatch; they always bite:
- The gradle owner must add the androidTest deps,
testInstrumentationRunner, theroom.schemaLocationKSP arg andsourceSets.getByName("androidTest").assets.srcDir("$projectDir/schemas")— without that last lineMigrationTestHelperfails with "Cannot find schema file". - The
@Databaseowner turns onexportSchema, not the gradle owner. - BuildRelease wires signing to credentials the owner supplies. Never create the release keystore; generating their signing key is not yours to do.
2. Parent work while they run
Take anything touching files nobody owns — typically the lint warning cleanup, which spans many files and would collide with everyone.
3. Integration gate — assume damage
Run the subagent-damage-repair probes on every changed .kt, import diff first
(dropped imports are the #1 agent failure mode). Then eyeball non-import deletions
for deleted functions/loops:
git diff -U0 -- "*.kt" | grep "^-" | grep -v "^---" | grep -v "^-import "
CRLF: D:/Monarch checks out CRLF (core.autocrlf=true); git show HEAD: emits LF.
git-bash grep/sed/awk strip the CR, so the probes work as written. ripgrep and
the built-in Grep tool keep it: $-anchored patterns miss, and a ripgrep-fed import
diff lists all 83 imports of an unchanged Repository.kt as dropped (verified
2026-09-24). Add tr -d '\r' around them — a "missing import" the compiler clearly
resolves is the tool, not the file.
4. Defects this wave produces (seen every run)
- Gradle name shadowing:
val keyAlias = ...thencreate("release") { keyAlias = keyAlias }→'val' cannot be reassigned. Rename locals tokeyAliasValue. - Nullable field into non-null param:
@Volatile var dir: File?passed torecord(dir: File, …). Capture a local non-null first. - Early-return guards that disable the feature: e.g.
Thread.getDefaultUncaughtExceptionHandler() ?: returninsideinstall()skips recording entirely. Install unconditionally, delegate withprevious?.….
5. Verification order (each gates the next)
Ironvellum's tasks are flavoured; python tools/gate.py --no-device --backend
covers steps 1 and 4.
:app:assembleFossDebug :app:testFossDebugUnitTest:app:assembleFossRelease— R8 is the risky change; note the size drop, then run the no-device checks inandroid-r8-release-verification.- Negative-test the fail-fast (reversible procedure:
android-r8-release-verification). Ironvellum's gate isvalidateFossReleaseBackend/validatePlayReleaseBackend(supabase.url,supabase.key; play addsgoogle.webClientId). Signing is deliberately not fail-fast: no credentials, unsigned APK. Blank a key by setting it empty inlocal.properties(a deleted key is refilled from the committedcloud-defaults.properties);IRONVELLUM_*env vars beat the file for signing. :app:lintFossRelease→ triageapp/build/reports/lint-results-fossRelease.sarifwith the SARIF script inandroid-release-readiness-audit, not the HTML.- Device, emulator only (the instrumented suite runs
pm clear, deletes rows and uninstalls, destroying the S25's real training history):python tools/gate.py --serial emulator-5554, or:app:connectedFossDebugAndroidTestwithANDROID_SERIAL=emulator-5554. Then install a debug-signed copy of the release APK to smoke R8 at runtime (serialization/ktor break here, not at compile).
6. Judgement calls worth repeating
- Don't blanket-fix lint. The
UnusedResources,ModifierParameterandNonObservableLocalecalls are inandroid-r8-release-verification; non-composable helpers may keepLocale.getDefault(). - Refuse i18n extraction unless a second locale is actually needed — hundreds of strings, high breakage, no user-visible benefit.