Android build sync
Skill esaldgut/ai-native-engineering-workspace/global-skills/claude-code-workflow/android-build-sync
One-command Android build-consistency gate, environment-agnostic — format (ktlint via org.jlleitschuh.gradle.ktlint), static analysis (detekt via io.gitlab.arturbosch.detekt, with the Compose ruleset), platform lint (./gradlew lintDebug), then compile/package (./gradlew assembleDebug), with pre-flight checks for JDK (17/21) and the Gradle wrapper. Enforces ./gradlew over system gradle, a detekt baseline for retrofitting, and parallel+caching gradle.properties. Use to set up or run a pre-commit/pre-push/CI build gate for a Kotlin/Compose project. No hardcoded machine paths.From its SKILL.md
npx -y skills add esaldgut/ai-native-engineering-workspace --skill android-build-syncAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.4 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
android-build-sync — one-command Kotlin/Compose build gate
A single consistency gate that runs the consensus 2025-26 pipeline — ktlint → detekt → lintDebug →
assembleDebug — so "it compiles, it's formatted, it passes static analysis, it links" is one command,
not four remembered ones. Environment-agnostic by design: it uses the project-local ./gradlew
wrapper and relative paths only — no absolute machine paths, no external-drive paths, nothing
machine-specific. It works identically on any developer's machine and in CI.
When to invoke
- Setting up or running a build-consistency gate for a Kotlin/Compose project (pre-commit, pre-push, or CI).
- Before opening a PR, to catch formatting/lint/compile breakage locally.
- You see CI running
gradle(system-installed) instead of./gradlew, or formatting failures that pass locally but fail in CI — fix per the rules below.
Announce on invoke: "Using android-build-sync to run the ktlint → detekt → lintDebug →
assembleDebug gate via the project's ./gradlew wrapper."
The pipeline
| Stage | Task | Catches |
|---|---|---|
| 1. Format | ./gradlew ktlintFormat (or ktlintCheck in CI) | style / JetBrains Kotlin formatting |
| 2. Static analysis | ./gradlew detekt | complexity, smells, Compose recomposition pitfalls |
| 3. Platform lint | ./gradlew lintDebug | manifest issues, deprecated APIs, missing translations, Compose lints |
| 4. Compile + package | ./gradlew assembleDebug | "does the whole thing actually compile and link?" |
The rules (load-bearing)
./gradlew, never systemgradle. The wrapper pins the exact Gradle version — the reproducible-build invariant. Running systemgradleproduces non-reproducible builds; CI must always use./gradlew.- JDK 17 minimum for AGP 8.x (21 supported on AGP 8.7+). Building with JDK 11 fails with
Unsupported class file major version 61. The pre-flight checks the active JDK before doing work. lintDebug, notlint, as the gate. Plainlintruns every variant (release + debug + all flavors) and is slow on multi-flavor apps;lintDebugis the pragmatic single-variant gate.- Use a detekt baseline to retrofit onto an existing codebase.
./gradlew detektBaselinewritesdetekt-baseline.xml; subsequent runs fail only on new violations, so you adopt detekt without fixing every legacy finding at once. - Pin the ktlint version. ktlint releases disagree on formatting (import ordering changed in 0.50); an unpinned version causes "passes locally, fails in CI" loops. Pin it in the plugin config.
- Enable parallel builds and caching. Put
org.gradle.parallel=trueandorg.gradle.caching=trueingradle.properties— these dominate CI time for medium apps. - Add the Compose detekt ruleset.
io.nlopez.compose.rules:detektcatches recomposition bugs (lambda allocation in composition, missingkey()initems()) that plain detekt and lint miss.
Canonical example
# scripts/build-sync.sh — environment-agnostic; run from the repo root.
# Uses the project-local ./gradlew only. No absolute or machine-specific paths.
set -euo pipefail
echo "==> Pre-flight: JDK (expect 17 or 21)"
java -version 2>&1 | grep -E 'version "(17|21)' || { echo "JDK 17/21 required"; exit 1; }
echo "==> Pre-flight: Gradle wrapper present + executable"
test -x ./gradlew || { echo "./gradlew missing — run 'gradle wrapper'"; exit 1; }
echo "==> 1/4 ktlint format"; ./gradlew ktlintFormat
echo "==> 2/4 detekt"; ./gradlew detekt
echo "==> 3/4 Android Lint"; ./gradlew lintDebug
echo "==> 4/4 assembleDebug"; ./gradlew assembleDebug
echo "==> All gates passed."
// build.gradle.kts (root) — plugin + detekt baseline wiring (versions are illustrative; pin yours)
plugins {
id("org.jlleitschuh.gradle.ktlint") version "12.1.1" apply false
id("io.gitlab.arturbosch.detekt") version "1.23.7" apply false
}
subprojects {
apply(plugin = "org.jlleitschuh.gradle.ktlint")
apply(plugin = "io.gitlab.arturbosch.detekt")
extensions.configure<io.gitlab.arturbosch.detekt.extensions.DetektExtension> {
baseline = file("$rootDir/detekt-baseline.xml") // relative to repo root — portable
config.setFrom("$rootDir/detekt.yml")
}
}
# gradle.properties — reproducible, fast, portable
org.gradle.parallel=true
org.gradle.caching=true
# Do NOT hardcode org.gradle.java.home to a machine path; let JAVA_HOME / toolchains resolve it.
Portability note: never set
org.gradle.java.hometo an absolute path in a committedgradle.properties— it breaks every other machine and CI. Use Gradle JVM toolchains or the ambientJAVA_HOMEinstead. Likewise keep all script paths relative to the repo root.
Decision aids
- Hook split: run the fast stages (
ktlintFormat+detekt) in apre-commithook for tight feedback, and the slow stages (lintDebug+assembleDebug) inpre-pushor CI. detekt documents the pre-commit pattern directly. - ktlint vs Spotless? Plain
ktlintis simplest; Spotless wraps ktlint and adds non-Kotlin formatting (XML, Markdown) at the cost of more config. Default to ktlint; adopt Spotless only if you need multi-language formatting. - Single aggregate task? You can define a
./gradlew syncCheckthatdependsOnall four — cleaner to invoke but harder to bootstrap on an existing project. The shell script above is the portable starting point.
Related skills
global-skills/android/android-testing-patterns/SKILL.md— the test stage that runs after this gate (this skill stops atassembleDebug; tests are a separate./gradlew teststep).global-skills/claude-code-workflow/capture-lessons-cascade-android/SKILL.md— captures R8 / Compose / Hilt-scoping lessons surfaced by builds gated here.
Sources
- Android Lint · Enable app optimization with R8 / shrink code · AGP releases & JDK compatibility
- detekt · detekt git pre-commit hook · ktlint Gradle plugin · compose-rules (detekt)
Last verified: 2026-06-03 against developer.android.com (Android Lint variant tasks; AGP 8.x JDK 17
minimum), detekt 1.23.x, ktlint-gradle 12.x.
Re-check after: next Claude Code minor release, or by 2026-09-03. Decay risk: medium (Gradle
plugin versions move; the pipeline shape is stable).
Found a drift? Run /skill-pattern-freshness-audit claude-code-workflow.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.