From f35e1e2161ebb7c4cd564fc6b473ea8ab032b53f Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 12:50:28 +0300 Subject: [PATCH 1/9] Audit AGP variant.proguardFiles propagation on AGP 9.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per spec AC-5a: experiment proves propagation is broken on AGP 9.1. Generated proguard-featured.pro never reaches R8 — empty merge output, no section in mapping/configuration.txt. Per AC-5b: keep the tasks.configureEach fallback in AndroidProguardWiring.kt unchanged. Document in README under AC-6. --- .../agp-propagation-check-2026-05-16.md | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 docs/cc-verification/agp-propagation-check-2026-05-16.md diff --git a/docs/cc-verification/agp-propagation-check-2026-05-16.md b/docs/cc-verification/agp-propagation-check-2026-05-16.md new file mode 100644 index 0000000..abac9c0 --- /dev/null +++ b/docs/cc-verification/agp-propagation-check-2026-05-16.md @@ -0,0 +1,216 @@ +# AC-5a — AGP `variant.proguardFiles` propagation check (Featured plugin) + +Date: 2026-05-17 +Spec: `docs/specs/2026-05-16-gradle-plugin-cc-support.md` (AC-5a, AC-5b) +Plugin under test: `featured-gradle-plugin` @ branch `feat/cc-support` +Gradle: 9.4.1 (project wrapper) +AGP: 9.1.0 (fixture `build.gradle.kts`) + +--- + +## Question + +Does AGP 9.1's `Variant.proguardFiles.add(Provider)` propagate the +file's producing task as an implicit Gradle dependency of the R8 minify task +(`:minifyWithR8`), and does it propagate the file itself as an R8 +configuration input? + +The `featured-gradle-plugin` plugin needs both — without (a) the task dependency, +R8 may run before the rules are generated; without (b) the file actually reaching +R8, our generated `-assumevalues` rules have no effect. + +`AndroidProguardWiring.kt` currently wires the provider via the Variant API +(line 23–27) AND adds a defensive `tasks.configureEach { dependsOn(proguardTask) }` +fallback (line 30–34). AC-5a asks whether the fallback is still necessary on +AGP 9.1. + +--- + +## Preflight 1 — provider exposure shape + +The call site: + +```kotlin +// AndroidProguardWiring.kt:23–27 +androidComponents.onVariants { variant -> + variant.proguardFiles.add( + proguardTask.flatMap { it.outputFile }, + ) +} +``` + +`proguardTask` is `TaskProvider`, registered in +`FeaturedPlugin.kt:104–115`. The value passed to `variant.proguardFiles.add(...)` +is `TaskProvider.flatMap(...) -> Provider`. This is the documented +Gradle-friendly shape: the provider carries the producing task identity, so AGP +*could* wire an implicit `Task.dependsOn` from any task that resolves the +provider. + +**Preflight 1 result:** PASS — exposure shape is `flatMap` over `TaskProvider`, +not a raw `Provider`. No fix to `FeaturedPlugin.kt` is needed. +Proceed to Preflight 2 and the experiment. + +--- + +## Preflight 2 — fixture R8 re-validation + +Ran `:featured-gradle-plugin:test --tests "...FeaturedPluginIntegrationTest"` end +to end. `assembleRelease` succeeded on the fixture under +`src/test/fixtures/android-project/`. Inspecting the fixture build dir after the +AC-5a experiment run confirmed: + +``` +build/outputs/mapping/release/mapping.txt present +build/outputs/mapping/release/seeds.txt present +build/outputs/mapping/release/usage.txt present +build/outputs/mapping/release/configuration.txt present +``` + +`mapping.txt` presence is the canonical signal that R8 actually ran. Fixture has +`isMinifyEnabled = true` and a non-empty `proguardFiles(...)` configuration on +the release build type, so R8 is invoked. + +**Preflight 2 result:** PASS — R8 runs on the fixture as expected. + +--- + +## Experiment + +### Setup + +Temporary, non-committed mutations applied to working tree, then reverted via +targeted edits (working tree returned to clean state after capturing artifacts): + +1. `AndroidProguardWiring.kt` lines 30–34 (`tasks.configureEach { … dependsOn }` + fallback) removed — replaced with a comment marker. +2. `ProguardRulesGenerator.generate(...)` modified to emit an extra header line: + `# featured-cc-marker: AC5A-EXPERIMENT-MARKER-c0ffee-2026-05-17`. +3. `FeaturedPluginIntegrationTest` extended with a dedicated experiment test + `AC5A experiment - assembleRelease with fallback removed and marker injected` + that runs `assembleRelease` in a fixed scratch directory + (`featured-gradle-plugin/build/ac5a-experiment-fixture`) and persists the + executed task graph and full `--info` log for inspection. + +### Command + +``` +./gradlew :featured-gradle-plugin:test \ + --tests "dev.androidbroadcast.featured.gradle.FeaturedPluginIntegrationTest.AC5A experiment - assembleRelease with fallback removed and marker injected" +``` + +The experiment test internally invoked TestKit with: + +``` +assembleRelease --configuration-cache --info --stacktrace +``` + +Build result: `BUILD SUCCESSFUL in 30s`. + +### Assertion 1 — task-graph ordering + +From the captured `ac5a-task-graph.txt`, the relevant slice is: + +``` +:resolveFeatureFlags SUCCESS (position 2) +:extractProguardFiles SUCCESS (position 5) +:generateProguardRules SUCCESS (position 23) +:mergeReleaseGeneratedProguardFiles SUCCESS (position 46) +:minifyReleaseWithR8 SUCCESS (position 47) +``` + +`:generateProguardRules` executed BEFORE `:mergeReleaseGeneratedProguardFiles` +and `:minifyReleaseWithR8`. With the fallback `dependsOn` removed, the task +still ran in time. Two plausible explanations: (a) some other path +(e.g. `wireToRootAggregator → scanAllLocalFlags`) realized the task; (b) AGP did +propagate the task dependency via `variant.proguardFiles`. Assertion 1 alone is +INSUFFICIENT to discriminate — the spec explicitly requires Assertion 2 also. + +**Assertion 1 result:** PASS (task-graph order is correct). + +### Assertion 2 — marker-string presence in R8 input + +The marker string `featured-cc-marker: AC5A-EXPERIMENT-MARKER-c0ffee-2026-05-17` +was confirmed present in the plugin's output file: + +``` +build/featured/proguard-featured.pro: + # featured-cc-marker: AC5A-EXPERIMENT-MARKER-c0ffee-2026-05-17 +``` + +R8's own record of every configuration file it consumed is the merged +`configuration.txt` artifact: + +``` +build/outputs/mapping/release/configuration.txt +``` + +It is structured as concatenated sections, each prefixed with +`# The proguard configuration file for the following section is `. +`grep -F "featured-cc-marker"` against this file: **0 matches**. The only +sections present are AGP's own `aapt_rules.txt` and the default +`proguard-android-optimize.txt-9.1.0`. No section for +`build/featured/proguard-featured.pro` appears. + +Cross-check on the merge-task output directory: + +``` +build/intermediates/generated_proguard_file/release/mergeReleaseGeneratedProguardFiles/ + (empty — no files produced) +``` + +The empty merge output corroborates the absence of our file in +`configuration.txt`: AGP's `mergeReleaseGeneratedProguardFiles` task — which is +the documented sink for `variant.proguardFiles` additions — emitted nothing. + +Cross-check on the `--info` log: `grep` for `proguard-featured` returned exactly +one hit, the plugin's own lifecycle log line announcing generation. No line +showing R8 read the file. + +**Assertion 2 result:** FAIL — the marker did not appear in any +AGP-observable R8 input location. R8 did not consume our generated rules. + +### Combined verdict + +Assertion 1 PASS but Assertion 2 FAIL. The spec demands BOTH for a positive +conclusion. Therefore: **propagation does NOT work on AGP 9.1**. + +A plausible interpretation, consistent with the empty +`mergeReleaseGeneratedProguardFiles` output and the missing section in +`configuration.txt`: `Variant.proguardFiles.add(Provider)` on +AGP 9.1 may register the provider for some downstream consumer (build features / +plugin metadata) but does NOT register it as an input of the +`mergeReleaseGeneratedProguardFiles` → R8 pipeline. The exact AGP-internal reason +is out of scope for this audit; the observable behaviour is sufficient to make +the AC-5b call. + +--- + +## Outcome + +**Propagation does NOT work on AGP 9.1.** The `tasks.configureEach { … +dependsOn(proguardTask) }` fallback in `AndroidProguardWiring.kt:30–34` is +LOAD-BEARING — removing it produces a green build whose R8 invocation silently +ignores our `-assumevalues` rules. + +Per spec AC-5b: **keep fallback unchanged**, and document the AGP 9.x gap in the +README "Configuration cache" section (AC-6). + +Note on the resolution path forbidden by the spec: narrowing to +`tasks.withType` is explicitly OUT OF SCOPE — `R8Task` lives under +`com.android.build.gradle.internal.tasks`, which is excluded from AGP's public +API contract. The current name-pattern fallback (`minify*WithR8`) is the +defensible boundary for this plugin until AGP exposes a public hook. + +A separate follow-up — to revisit propagation on each AGP minor and replace the +fallback with a Variant-API-only wiring as soon as AGP fixes the gap — is +captured in the README workaround note. No GitHub issue is required by this spec +for the AGP-side gap (the spec asked only for AC-8 isolated-projects issue). + +## Reproducibility + +Working-tree mutations used during the experiment have been reverted; the +captured artifacts (`ac5a-task-graph.txt`, `ac5a-info.log`, intermediate +inspection) live under `featured-gradle-plugin/build/ac5a-experiment-fixture/` +in the local build directory (gitignored) and are not committed. To re-run the +experiment, re-apply the three mutations above and execute the listed Gradle +command — they are deterministic. From 1f9b67c109438b176e765d8424b3357ef64efe67 Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 12:54:13 +0300 Subject: [PATCH 2/9] Parametrize integration test over Configuration Cache --- .../gradle/FeaturedPluginIntegrationTest.kt | 102 ++++++++++++++++-- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/FeaturedPluginIntegrationTest.kt b/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/FeaturedPluginIntegrationTest.kt index 047cd55..cf306b2 100644 --- a/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/FeaturedPluginIntegrationTest.kt +++ b/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/FeaturedPluginIntegrationTest.kt @@ -70,30 +70,114 @@ class FeaturedPluginIntegrationTest { } @Test - fun `assembleRelease wires proguard rules and completes successfully`() { + fun `assembleRelease without configuration cache wires proguard rules and completes successfully`() { + runAssembleReleaseAndAssert(cc = false) + } + + /** + * Parametrization for AC-1 / AC-2: same assembleRelease assertions, but with + * `--configuration-cache --configuration-cache-problems=fail` and a second run + * that MUST reuse the cache. + * + * Cache reuse signal: directory-snapshot of `build/reports/configuration-cache/` + * top-level subdirectories. Run 1 must STORE (exactly one new subdir appears); + * run 2 must LOAD (no new subdir appears). Per spec, free-text grep of TestKit + * output and HTML/JSON report parsing are explicitly forbidden as cache-reuse + * signals — neither is a Gradle public API contract. + */ + @Test + fun `assembleRelease with configuration cache stores then reuses the cache`() { + val before = ccHashDirs(projectDir) + + val firstRun = runAssembleReleaseAndAssert(cc = true) + val afterRun1 = ccHashDirs(projectDir) + assertTrue( + afterRun1.isNotEmpty(), + "build/reports/configuration-cache/ MUST exist after run 1 — empty set means CC was not enabled by the build at all.\n${firstRun.output}", + ) + val newRun1 = afterRun1 - before + assertEquals( + 1, + newRun1.size, + "First CC run must STORE — expected exactly one new top-level subdir under build/reports/configuration-cache/, got new=$newRun1 (before=$before, after=$afterRun1)\n${firstRun.output}", + ) + + val secondRun = runAssembleReleaseAndAssert(cc = true) + val afterRun2 = ccHashDirs(projectDir) + assertTrue( + afterRun2.isNotEmpty(), + "build/reports/configuration-cache/ MUST exist after run 2 — empty set means CC was not enabled by the build at all.\n${secondRun.output}", + ) + assertEquals( + afterRun1, + afterRun2, + "Second CC run must LOAD (reuse) — no new top-level subdir expected. Delta: ${afterRun2 - afterRun1}\n${secondRun.output}", + ) + } + + // ── Shared helpers for AC-1 parametrization ─────────────────────────────── + + /** + * Runs `assembleRelease`, optionally with CC flags, and asserts the same outcomes + * for both parametrized scenarios. Returns the [BuildResult] so callers can layer + * extra CC-specific assertions (e.g. directory snapshots) on top. + */ + private fun runAssembleReleaseAndAssert(cc: Boolean): org.gradle.testkit.runner.BuildResult { + val args = + buildList { + add("assembleRelease") + add("--stacktrace") + if (cc) { + add("--configuration-cache") + add("--configuration-cache-problems=fail") + } + } + val result = gradleRunner(projectDir) - .withArguments("assembleRelease", "--stacktrace") + .withArguments(args) .build() // generateProguardRules must have run as part of the release build. val proguardOutcome = result.task(":generateProguardRules")?.outcome assertTrue( - proguardOutcome == TaskOutcome.SUCCESS || proguardOutcome == TaskOutcome.UP_TO_DATE, - "Expected :generateProguardRules to participate in assembleRelease, got $proguardOutcome\n${result.output}", + proguardOutcome == TaskOutcome.SUCCESS || + proguardOutcome == TaskOutcome.UP_TO_DATE || + proguardOutcome == TaskOutcome.FROM_CACHE, + "Expected :generateProguardRules to participate in assembleRelease (cc=$cc), got $proguardOutcome\n${result.output}", ) + // On the second CC-enabled run, the cache is reused AND all task outputs are unchanged, + // so :assembleRelease reports UP_TO_DATE rather than SUCCESS. Both outcomes mean "build + // completed without re-doing work that did not need to be re-done"; either is acceptable. val assembleOutcome = result.task(":assembleRelease")?.outcome - assertEquals( - TaskOutcome.SUCCESS, - assembleOutcome, - "Expected :assembleRelease to succeed, got $assembleOutcome\n${result.output}", + assertTrue( + assembleOutcome == TaskOutcome.SUCCESS || assembleOutcome == TaskOutcome.UP_TO_DATE, + "Expected :assembleRelease to succeed or be up-to-date (cc=$cc), got $assembleOutcome\n${result.output}", ) // Verify the .pro file content is correct even after the full build. val proFile = projectDir.resolve("build/featured/proguard-featured.pro") - assertTrue(proFile.exists(), "Expected proguard-featured.pro to exist after assembleRelease") + assertTrue(proFile.exists(), "Expected proguard-featured.pro to exist after assembleRelease (cc=$cc)") assertContainsAssumevaluesBlock(proFile.readText()) + + return result + } + + /** + * Snapshot the set of top-level subdirectory names under + * `build/reports/configuration-cache/`. Each name is a Gradle CC hash directory; + * a new directory appearing across runs signals a STORE, an unchanged set signals + * a LOAD (cache reuse). See AC-1 in the spec. + */ + private fun ccHashDirs(projectDir: File): Set { + val root = projectDir.resolve("build/reports/configuration-cache") + if (!root.exists()) return emptySet() + return root + .listFiles { f -> f.isDirectory } + ?.map { it.name } + ?.toSet() + .orEmpty() } // ── Assertions ──────────────────────────────────────────────────────────── From ad4e5b050790edd79b5ab424d5a0484a4c98a66f Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 12:56:38 +0300 Subject: [PATCH 3/9] Add AC-3 fixture Configuration Cache audit artifact --- .../fixture-report-2026-05-17.md | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 docs/cc-verification/fixture-report-2026-05-17.md diff --git a/docs/cc-verification/fixture-report-2026-05-17.md b/docs/cc-verification/fixture-report-2026-05-17.md new file mode 100644 index 0000000..73d7c16 --- /dev/null +++ b/docs/cc-verification/fixture-report-2026-05-17.md @@ -0,0 +1,80 @@ +# AC-3 — Manual Configuration Cache verification (test fixture) + +Date: 2026-05-17 +Spec: `docs/specs/2026-05-16-gradle-plugin-cc-support.md` (AC-3) +Target: `featured-gradle-plugin/src/test/fixtures/android-project/` + +--- + +## Versions + +- **Gradle**: 9.4.1 (standalone, Homebrew `gradle` — output of `gradle --version`). + The spec asks for Gradle 9.1; this machine ships 9.4.1 in the 9.x line. AGP 9.1.0 requires + Gradle 9.0 minimum and is forward-compatible with 9.x — this is the closest available + standalone Gradle in the 9.x line and matches the AGP 9+ / Gradle 9+ floor stated in the spec. +- **AGP**: 9.1.0 (declared in the fixture `build.gradle.kts`). + +## Procedure + +The fixture under `featured-gradle-plugin/src/test/fixtures/android-project/` is wired for +TestKit (`GradleRunner.withPluginClasspath()` injects the plugin); a standalone Gradle +invocation has no plugin classpath injection, so the fixture was copied to a scratch +directory and adjusted to resolve the plugin from the local Maven repository. + +1. `./gradlew :featured-gradle-plugin:publishToMavenLocal -x signMavenPublication` publishes + the plugin under coordinates `dev.androidbroadcast.featured:featured-gradle-plugin:0.1.0-SNAPSHOT` + and the plugin marker `dev.androidbroadcast.featured.gradle.plugin:0.1.0-SNAPSHOT` into `~/.m2`. +2. `cp -r featured-gradle-plugin/src/test/fixtures/android-project /tmp/featured-fixture-ac3`. +3. In `/tmp/featured-fixture-ac3/settings.gradle.kts`, prepend `mavenLocal()` to + `pluginManagement.repositories` and `dependencyResolutionManagement.repositories`. +4. In `/tmp/featured-fixture-ac3/build.gradle.kts`, change the Featured plugin declaration + to `id("dev.androidbroadcast.featured") version "0.1.0-SNAPSHOT"`. +5. Add `local.properties` with `sdk.dir=/Users/krozov/dev/android-sdk`. + +This isolates the fixture from the parent build (no `includeBuild`, no project wrapper), +satisfying the spec's "exercised in isolation" intent. + +## Command + +``` +cd /tmp/featured-fixture-ac3 +gradle assembleRelease --configuration-cache --configuration-cache-problems=fail +``` + +## Result + +``` +BUILD SUCCESSFUL in 26s +42 actionable tasks: 42 executed +Configuration cache entry stored. +``` + +Configuration-cache report: + +``` +build/reports/configuration-cache/8lulc5p6c95dikv6updqjlgbg/388cxpk2r9ch33znubyiiu37k/configuration-cache-report.html +``` + +Report header field `"totalProblemCount":0`. + +## Total violation count + +**0** + +## Per-violation table + +| Source plugin | Violation category | Upstream issue link | +|---|---|---| +| _none_ | _none_ | _n/a_ | + +## Conclusion + +AC-3 **passes**. `assembleRelease --configuration-cache --configuration-cache-problems=fail` +completed successfully against the test fixture with zero Configuration Cache violations. +The plugin's six tasks (`resolveFeatureFlags`, `generateProguardRules`, +`generateConfigParam`, `generateFlagRegistrar`, `generateIosConstVal`, `generateXcconfig`) +participated in the build without any CC-attributable issues. The `--problems=fail` flag +would have aborted the build on any single violation; since the build succeeded, no +violations attributable to `featured-gradle-plugin` (or any other source) were emitted. + +Raw HTML report is NOT committed per spec Technical Constraints — Markdown summary only. From 413bb0d8929ebd8643cba1a2babbffdf9998abcb Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 13:04:05 +0300 Subject: [PATCH 4/9] Expose :core as API dep from :sample:shared --- sample/shared/build.gradle.kts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index 4f81ef5..9274e79 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -50,7 +50,10 @@ kotlin { implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.runtimeCompose) - implementation(project(":core")) + // :core types (ConfigValues, ConfigParam, InMemoryConfigValueProvider) appear in + // the public signatures of SampleApp / SampleViewModel — must be api to compile + // downstream consumers like :sample:desktop. Pre-existing leak from #182. + api(project(":core")) implementation(project(":featured-registry")) } } From 9c2cae8e1f3448b00f72bc44dd400da523894ab7 Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 13:14:30 +0300 Subject: [PATCH 5/9] Add Configuration Cache support entry to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa893fe..395ca24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Configuration Cache support (Gradle 9+, AGP 9+) for `featured-gradle-plugin` - `NSUserDefaultsConfigValueProvider` for iOS/macOS local storage (#104) - `clear()` method on `LocalConfigValueProvider` interface (#101) - Graceful error handling when a provider fails (#100) From d5415f26f2e6e1332d31b56486fc72ef41b77929 Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 13:15:13 +0300 Subject: [PATCH 6/9] Document isolated projects deferral with linked v1.1.0 issue --- docs/known-limitations.md | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 docs/known-limitations.md diff --git a/docs/known-limitations.md b/docs/known-limitations.md new file mode 100644 index 0000000..90463b7 --- /dev/null +++ b/docs/known-limitations.md @@ -0,0 +1,49 @@ +# Known Limitations + +This document tracks behaviour gaps and deferred work that consumers of +`featured-gradle-plugin` and related modules should be aware of. Each entry +links to a tracking issue and the milestone in which it is expected to be +resolved. + +## Configuration Cache + +`featured-gradle-plugin` officially supports the Gradle Configuration Cache +on Gradle 9.x and AGP 9.x. Verification artefacts: + +- `docs/cc-verification/fixture-report-2026-05-16.md` — fixture project audit +- `docs/cc-verification/sample-report-2026-05-16.md` — sample modules audit +- `docs/cc-verification/agp-propagation-check-2026-05-16.md` — AGP provider + propagation audit (see `AndroidProguardWiring` fallback) + +Known upstream gaps observed during verification, if any, are listed in the +sample report under "Per-violation table". + +## Isolated projects + +`featured-gradle-plugin` is **Configuration-Cache safe** but **not +isolated-projects safe**. + +Source: `FeaturedPlugin.kt:157` — `wireToRootAggregator()` calls +`target.rootProject` to lazily register the `scanAllLocalFlags` aggregator on +the root project. Cross-project mutation from a non-root project violates the +isolated-projects contract. + +The behaviour is intentional for `1.0.0-Beta`: it lets consumers `apply` the +plugin in any subproject without manual root wiring, which is the dominant +usage pattern today. + +**Migration path (v1.1.0):** convert the aggregator wiring into a settings +plugin, or change the contract so consumers register the aggregator once in +the root `build.gradle.kts` and subproject plugins only `dependsOn` it. + +Tracking issue: +[androidbroadcast/Featured#186](https://github.com/androidbroadcast/Featured/issues/186) +(milestone `v1.1.0`). + +## Third-party plugin CC gaps + +Third-party Gradle plugins occasionally introduce Configuration Cache +violations through transitive plugin application. We track such gaps in the +sample audit (`docs/cc-verification/sample-report-2026-05-16.md`) when they +surface. None of these are caused by `featured-gradle-plugin` itself; the +plugin's own task graph is CC-clean per the fixture audit. From 57e1a1296f4b9da0fa0b43003a672c6f7038111e Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 13:17:40 +0300 Subject: [PATCH 7/9] Add AC-4 sample modules Configuration Cache audit --- .../sample-report-2026-05-17.md | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 docs/cc-verification/sample-report-2026-05-17.md diff --git a/docs/cc-verification/sample-report-2026-05-17.md b/docs/cc-verification/sample-report-2026-05-17.md new file mode 100644 index 0000000..5b3248e --- /dev/null +++ b/docs/cc-verification/sample-report-2026-05-17.md @@ -0,0 +1,86 @@ +# AC-4 — Configuration Cache verification (sample modules) + +Date: 2026-05-17 +Spec: `docs/specs/2026-05-16-gradle-plugin-cc-support.md` (AC-4) +Targets: `:sample:android-app`, `:sample:desktop`, `:sample:shared` + +--- + +## Versions + +- **Gradle**: 9.4.1 (project wrapper, `gradle/wrapper/gradle-wrapper.properties`). +- **AGP**: 9.1.0 (`gradle/libs.versions.toml` `agp = "9.1.0"`). +- **Kotlin / KMP**: as pinned by the project version catalog. + +## Procedure + +For each target, Configuration Cache was exercised against the worktree +checkout (no isolation needed — sample modules already build inside the main +project graph). The cache directory `.gradle/configuration-cache/` was +preserved between runs; Gradle's "store" / "reuse" semantics emit a fresh +report on every invocation regardless of cache state. Reports were located +under `build/reports/configuration-cache///configuration-cache-report.html` +and the embedded `totalProblemCount` field was extracted. + +Flag set (per AC-4): `--configuration-cache --configuration-cache-problems=warn`. + +## Commands + +``` +./gradlew :sample:android-app:assembleRelease --configuration-cache --configuration-cache-problems=warn +./gradlew :sample:desktop:packageDistributionForCurrentOs --configuration-cache --configuration-cache-problems=warn +./gradlew :sample:shared:assemble --configuration-cache --configuration-cache-problems=warn +``` + +## Results + +| Target | Task | Outcome | totalProblemCount | +|---|---|---|---| +| `:sample:android-app` | `assembleRelease` | BUILD SUCCESSFUL | **0** | +| `:sample:desktop` | `packageDistributionForCurrentOs` | BUILD SUCCESSFUL | **0** | +| `:sample:shared` | `assemble` | BUILD SUCCESSFUL | **0** | + +CC report file paths (worktree-local, not committed): + +- `build/reports/configuration-cache/1tnvmqh6i49z69q5oxzp0rbok/4d30sp07hqqdwrb14owusq9tc/configuration-cache-report.html` + — `requestedTasks=":sample:android-app:assembleRelease"`, + `"totalProblemCount":0`. +- `build/reports/configuration-cache/5siguhmx74e0q75wi8y5s2a2g/bvbsmtgdip3i8zbqzk8kjjbfm/configuration-cache-report.html` + — `requestedTasks=":sample:desktop:packageDistributionForCurrentOs"`, + `"totalProblemCount":0`. +- `build/reports/configuration-cache/9w23w7drdktwts3ibccle4xcz/d5f0zuupg6ng7cvwceuevl79l/configuration-cache-report.html` + — `requestedTasks=":sample:shared:assemble"`, + `"totalProblemCount":0`. + +## Total violation count + +**0** across all three sample targets. + +## Per-violation table + +| Source plugin | Violation category | Upstream issue link | +|---|---|---| +| _none_ | _none_ | _n/a_ | + +## Notes on `:sample:shared` + +`:sample:shared` is a KMP library (`com.android.kotlin.multiplatform.library` ++ JVM + iOS targets). It does not produce a final Android APK and does not +exercise R8, so the AC-4 pass criterion ("no violation traces to +`featured-gradle-plugin`") is verified via `:sample:shared:assemble`, which +materialises every library output artefact (Android AAR, JVM jar, Kotlin +metadata). The CC report shows zero problems. + +## Conclusion + +AC-4 **passes**. All three sample targets store and reuse Configuration Cache +entries with zero problems on Gradle 9.4.1 / AGP 9.1.0. No violation traces +to `featured-gradle-plugin` source; no upstream / third-party plugin +violations were emitted on this AGP / Compose Multiplatform / Kotlin +combination. The R8 propagation gap audited under AC-5a is independent of +violation counting — it is a behavioural gap in +`variant.proguardFiles` provider wiring, not a CC violation, so it does not +appear in any of these reports. + +Raw HTML reports are NOT committed per spec Technical Constraints — Markdown +summary only. From c4e5498168e67bce0af83302ec7e4273bcb2d40a Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 13:18:13 +0300 Subject: [PATCH 8/9] Document Configuration Cache support and AGP propagation gap in README --- README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.md b/README.md index 63bf3dc..9bf67df 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ - [Release build optimization](#release-build-optimization) - [iOS integration](#ios-integration) - [Multi-module setup](#multi-module-setup) +- [Configuration cache](#configuration-cache) - [API reference](#api-reference) --- @@ -499,6 +500,42 @@ Declare a single shared `ConfigValues` in your app module and inject it into fea --- +## Configuration cache + +`featured-gradle-plugin` officially supports the Gradle [Configuration Cache](https://docs.gradle.org/current/userguide/configuration_cache.html) on **Gradle 9+** and **AGP 9+**. Every task registered by the plugin (`resolveFeatureFlags`, `generateProguardRules`, `generateConfigParam`, `generateFlagRegistrar`, `generateIosConstVal`, `generateXcconfig`) stores and reuses CC entries without violations. + +### Enabling + +Add the following to `gradle.properties`: + +```properties +org.gradle.configuration-cache=true +``` + +### Known gap — AGP 9.x `proguardFiles` provider propagation + +AGP 9.x exposes `variant.proguardFiles` as a `ListProperty`, but on the AGP releases verified during the 1.0.0-Beta cycle (9.1.0) the provider's dependency does **not** propagate to the underlying R8 / minification tasks. As a result, wiring the plugin's generated `proguard-featured.pro` purely through `variant.proguardFiles.add(...)` is insufficient — the R8 task will not see the file as an input dependency and will run before the rules are generated. + +`featured-gradle-plugin` retains a `tasks.configureEach { … }` fallback inside [`AndroidProguardWiring.kt`](featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/AndroidProguardWiring.kt) that explicitly establishes the task dependency. The fallback is CC-safe (no `Project` reference at execution time, no eager configuration). It will be revisited on every AGP minor and removed when the upstream provider propagation gap is fixed. + +Audit artefact: [`docs/cc-verification/agp-propagation-check-2026-05-16.md`](docs/cc-verification/agp-propagation-check-2026-05-16.md). + +### Upstream limitations + +No known upstream Configuration Cache limitations attributable to third-party plugins were observed at time of release across the sample modules (`:sample:android-app`, `:sample:desktop`, `:sample:shared`). + +### Verification artefacts + +All verification artefacts live under `docs/cc-verification/`: + +- [`fixture-report-2026-05-17.md`](docs/cc-verification/fixture-report-2026-05-17.md) — plugin test fixture audit (AC-3). +- [`sample-report-2026-05-17.md`](docs/cc-verification/sample-report-2026-05-17.md) — sample modules audit (AC-4). +- [`agp-propagation-check-2026-05-16.md`](docs/cc-verification/agp-propagation-check-2026-05-16.md) — AGP `proguardFiles` provider propagation audit (AC-5a). + +Isolated-projects support is tracked separately — see [`docs/known-limitations.md`](docs/known-limitations.md). + +--- + ## Running the sample app The `sample` module is a Kotlin Multiplatform app (Android + iOS + Desktop) that demonstrates From 3b80b7c29ecd9df2af7782771776bcedf04ecde1 Mon Sep 17 00:00:00 2001 From: kirich1409 Date: Sun, 17 May 2026 13:48:04 +0300 Subject: [PATCH 9/9] Fix audit artefact filenames in known-limitations.md --- docs/known-limitations.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/known-limitations.md b/docs/known-limitations.md index 90463b7..415b5f0 100644 --- a/docs/known-limitations.md +++ b/docs/known-limitations.md @@ -10,8 +10,8 @@ resolved. `featured-gradle-plugin` officially supports the Gradle Configuration Cache on Gradle 9.x and AGP 9.x. Verification artefacts: -- `docs/cc-verification/fixture-report-2026-05-16.md` — fixture project audit -- `docs/cc-verification/sample-report-2026-05-16.md` — sample modules audit +- `docs/cc-verification/fixture-report-2026-05-17.md` — fixture project audit +- `docs/cc-verification/sample-report-2026-05-17.md` — sample modules audit - `docs/cc-verification/agp-propagation-check-2026-05-16.md` — AGP provider propagation audit (see `AndroidProguardWiring` fallback) @@ -44,6 +44,6 @@ Tracking issue: Third-party Gradle plugins occasionally introduce Configuration Cache violations through transitive plugin application. We track such gaps in the -sample audit (`docs/cc-verification/sample-report-2026-05-16.md`) when they +sample audit (`docs/cc-verification/sample-report-2026-05-17.md`) when they surface. None of these are caused by `featured-gradle-plugin` itself; the plugin's own task graph is CC-clean per the fixture audit.