Skip to content

Multi-module sample showcasing Featured aggregator plugin#200

Merged
kirich1409 merged 9 commits into
developfrom
feat/multi-module-sample
May 20, 2026
Merged

Multi-module sample showcasing Featured aggregator plugin#200
kirich1409 merged 9 commits into
developfrom
feat/multi-module-sample

Conversation

@kirich1409
Copy link
Copy Markdown
Contributor

What changed

Restructures the sample app from a single hand-maintained SampleFeatureFlags.kt into a 3-module aggregator demonstration of featured-gradle-plugin's end-to-end workflow.

Sample structure:

  • :sample:feature-checkout — owns CheckoutVariant enum + 2 local flags (new_checkout Boolean, checkout_variant enum).
  • :sample:feature-promotions — 1 remote flag (promo_banner_enabled Boolean).
  • :sample:feature-ui — 2 local UI flags (main_button_red Boolean, new_feature_section_enabled Boolean).
  • :sample:shared — pure aggregator applying dev.androidbroadcast.featured.application, declaring featuredAggregation(project(":sample:feature-*")), consuming GeneratedFeaturedRegistry.all. Houses Compose UI and SampleViewModel. No flag declarations of its own.
  • :sample:android-app / :sample:desktop / iosApp/ — unchanged shells; Android wires DataStoreConfigValueProvider + FeatureFlagsDebugScreen.

Plugin moves:

  • featured-gradle-plugin extracted to a build-logic/ included build wired via pluginManagement.includeBuild("build-logic"). Subprojects apply id("dev.androidbroadcast.featured") without a version coordinate.
  • build-logic/settings.gradle.kts propagates the parent gradle.properties (notably VERSION_NAME) so Vanniktech publishes with the project version.
  • Root publishToMavenCentral / publishToMavenLocal proxy tasks delegate to the included build — single entrypoint preserved.

Codegen hardening (fixes surfaced by the first real cross-platform consumption):

  • ExtensionFunctionGenerator now emits suspend extensions — ConfigValues.getValue has always been suspend; the generated callers needed to match.
  • ConfigParamGenerator widens GeneratedLocalFlags / GeneratedRemoteFlags to public and appends a module-derived suffix to both the file name and the object name (e.g. GeneratedLocalFlagsSampleFeatureCheckout). Eliminates JVM class collisions when multiple aggregated modules sit on the same classpath without relying on @file:JvmName (which doesn't resolve in KMP commonMain on iOS native).
  • GenerateConfigParamTask cleans its output directory each run so file-name changes don't leave stale generated sources behind.
  • ConfigParam<T> gains enumConstants: List<T>?; the per-module and aggregator codegen paths both emit kotlin.enumValues<EnumFqn>().toList() for enum-typed params.

Debug UI:

  • FeatureFlagsDebugScreen adds an EnumDropdown (Material3 ExposedDropdownMenuBox) dispatched whenever param.enumConstants != null. Boolean / scalar input paths unchanged.

Why

PR D closes the 4-PR registry-redesign chain (A = manifest producer, B = aggregator codegen, C = UI-agnostic debug screen, D = production-grade demo). The sample now showcases the canonical multi-module workflow the plugin was designed for, including enum flags overridable from the debug screen at runtime — previously demoed only by hand-rolled boilerplate that hid the public-API story.

Artifacts

Per-phase implementation reports + final finalize report live in swarm-report/ (gitignored):

  • pr-d-multi-module-sample-state.md — overall plan + phase tracker.
  • pr-d-multi-module-sample-stage-1.mdstage-7.md — per-phase rationale, verification outcomes, deviations.
  • pr-d-multi-module-sample-debug-extensions.md + pr-d-multi-module-sample-debug-ios.md — root-cause investigations for the codegen / iOS link blockers.
  • pr-d-multi-module-sample-e2e-scenario.md — Phase 6 manual QA scenario (24/24 PASS).
  • pr-d-multi-module-sample-finalize.md — Phase A code review (opus) PASS; 1 NIT fixed in 08a787d.

How to test

Pre-merge CI runs the full Gradle matrix (build / lint / test / publishToMavenLocal). Already-validated locally on:

  • ./gradlew :sample:android-app:assembleDebug — green
  • ./gradlew :sample:desktop:assemble — green
  • ./gradlew :sample:shared:linkDebugFrameworkIosSimulatorArm64 — green
  • ./gradlew -p build-logic :featured-gradle-plugin:check — green
  • ./gradlew spotlessCheck — green
  • ./gradlew publishToMavenLocal — produces featured-gradle-plugin-1.0.0-Beta1 artifacts in ~/.m2

Manual QA (already executed, scenario file in swarm-report/):

  • Android (Pixel 10 emulator — mandatory device) — 13 sub-steps covering default state, debug-screen toggles, DataStore persistence across rotation + cold restart, enum dropdown, source-badge transitions, recomposition stability.
  • Desktop (JVM) — default render, in-memory provider reset on second launch.
  • iOS Simulator (iPhone 16 Pro) — default render, CheckoutVariant accessible from Swift surface.

Result: 24/24 PASS.

Release Notes

Changed

  • Sample restructured into three KMP feature modules (:sample:feature-checkout, :sample:feature-promotions, :sample:feature-ui); :sample:shared becomes a pure aggregator.
  • featured-gradle-plugin extracted to build-logic/; the published Maven coordinates and plugin id are unchanged.
  • Breaking: plugin-generated objects renamed with a module-derived suffix — GeneratedLocalFlags<ModuleSuffix> / GeneratedRemoteFlags<ModuleSuffix> — to avoid duplicate-class JVM collisions across modules. Consumers should prefer module-local extension bridges (*FlagObservers.kt pattern) over direct references to the generated objects.
  • Breaking: the generated is<Name>Enabled() extensions are now suspend. Callers that invoked them from non-suspend contexts will not compile until they switch to either runBlocking (discouraged) or the observe-bridge / suspend-context pattern.

Added

  • EnumDropdown input in FeatureFlagsDebugScreen; ConfigParam<E> carries enumConstants: List<E>? populated by codegen for enum-typed flags.

Fixed

  • iOS framework export(project(...)) no longer triggers an internal ObjCExportCodeGenerator crash — paired with the api(project(...)) linkage so K/N resolves type adapters for generic ConfigParam<E> specializations.

Status

Phase Outcome
Plan (plan-mode) approved
Implementation (7 phases) complete; 7 atomic commits + 1 finalize NIT cleanup
/check (build / lint / tests / publish) PASS
/finalize Phase A (code-reviewer, opus) PASS — 0 critical, 0 major, 1 NIT (fixed)
/finalize Phase C (silent-failure-hunter) partial; findings re-classified (1 invalid by-construction, 3 out-of-scope for PR D)
Phase 6 manual QA 24/24 PASS — Pixel 10 / Desktop / iOS Sim

Follow-ups (not blocking this PR)

  • featured-shrinker-tests and ProguardRulesGenerator still target the legacy non-suspend extension signature; R8 per-function DCE via -assumevalues is silently degraded until reworked. Tracked separately.

🤖 Generated with Claude Code

kirich1409 and others added 7 commits May 19, 2026 20:57
Moves featured-gradle-plugin/ to build-logic/featured-gradle-plugin/ and
wires it via pluginManagement.includeBuild("build-logic") so subprojects
in the main build can apply id("dev.androidbroadcast.featured") without
a version coordinate. Unblocks the multi-module sample restructure that
needs the plugin available to feature modules in the same build.

build-logic has its own settings file with the version catalog reused
from the parent (from(files("../gradle/libs.versions.toml"))) and a
gradle.beforeProject hook that propagates parent gradle.properties
(notably VERSION_NAME) so the plugin still publishes with the project
version instead of "unspecified".

Root publishToMavenCentral / publishToMavenLocal proxy tasks delegate
to the included build so the publish workflow keeps a single entrypoint.
The plugin constraint is dropped from featured-bom: a java-platform BOM
only constrains dependency resolution and is irrelevant to plugins
applied through pluginManagement.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Introduces :sample:feature-checkout, :sample:feature-promotions, and
:sample:feature-ui as canonical demo of the Featured aggregator workflow:
each module applies the plugin, declares its own flags via the
featured { localFlags / remoteFlags } DSL, and exposes a public
observe-bridge file (Flow<T>-shaped) on ConfigValues. The bridges are
the pedagogical surface — GeneratedLocalFlags/GeneratedRemoteFlags
contain ConfigParam<T> instances that consumers compose into Flow via
observe(...).map { it.value }.

Fixes a latent codegen bug surfaced by the first cross-platform use of
GeneratedFlagExtensions: extensions were emitted non-suspend but
ConfigValues.getValue is suspend, so any Kotlin/Native or AndroidMain
compile failed. ExtensionFunctionGenerator now emits suspend extensions,
ConfigParamGenerator widens GeneratedLocalFlags / GeneratedRemoteFlags
to public so they can be referenced from observer bridges in other
modules, and the generated extensions file name now includes a module-
derived suffix (e.g. GeneratedFlagExtensionsSampleFeatureCheckout.kt) so
each module's JVM class name is unique without relying on @file:JvmName
(which doesn't resolve in commonMain on KMP iOS targets).

GenerateConfigParamTask now wipes its output directory on each run so
file-name changes don't leave stale generated sources behind.

Out of scope (tracked as follow-up): ProguardRulesGenerator and
featured-shrinker-tests still target the legacy non-suspend extension
signature; R8 per-function DCE via -assumevalues silently stops
matching the new suspend JVM signature until that subsystem is reworked.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Applies dev.androidbroadcast.featured.application to :sample:shared
and replaces the hand-written SampleFeatureFlags with the plugin-
generated GeneratedFeaturedRegistry.all sourced via featuredAggregation
from :sample:feature-checkout, :sample:feature-promotions, and
:sample:feature-ui.

The three feature modules are also added as api dependencies because
CheckoutVariant and the observe-bridge extensions defined in them
appear in the public surface that downstream sample app modules
(:sample:android-app, :sample:desktop) consume directly. The iOS
framework blocks export the three modules so Swift sees CheckoutVariant
without needing a separate Pod.

generateFeaturedRegistry produces an object with five ConfigParam
entries (sorted by modulePath then key) which the debug screen and
ViewModel now consume in Phase 5.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 5 of PR D rewires the sample ViewModel and screens to consume
flags through the new observe-bridge pattern instead of the deleted
hand-written SampleFeatureFlags. SampleViewModel now exposes five
StateFlow<T> built from the per-module Flow bridges with stateIn
defaults that mirror each feature module's DSL default, plus four
suspend-launching setters routed through ConfigValues.override.
FeaturedSample picks up CheckoutVariant from its new home in
:sample:feature-checkout; MainActivity points FeatureFlagsDebugScreen
at GeneratedFeaturedRegistry.all.

A second collision surfaced during the first Android assembleDebug:
ConfigParamGenerator emitted a fixed-name GeneratedLocalFlags /
GeneratedRemoteFlags object in the same package across every module,
so feature-checkout and feature-ui produced duplicate dex classes
under the same FQN. ConfigParamGenerator now names both the file and
the public object with a module-derived suffix (e.g.
GeneratedLocalFlagsSampleFeatureCheckout), matching the
GeneratedFlagExtensions fix from Phase 3. The three observe-bridge
files reference the suffixed objects accordingly.

The matching iOS link crash in ObjCExportCodeGenerator was the
side-effect of the previous implementation-vs-api wiring: K/N could
not resolve the concrete type adapter for ConfigParam<CheckoutVariant>
because the feature klibs weren't on the link path. Phase 4's
api(project(":sample:feature-*")) switch and Phase 3b's matching
visibility/api widening jointly fix it — a clean
linkDebugFrameworkIosSimulatorArm64 now succeeds without changes here.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ConfigParam<E> now carries enumConstants: List<E>? so the debug UI can
discover the legal values without runtime reflection. Plugin codegen
fills the field for both per-module objects and the aggregated
GeneratedFeaturedRegistry by emitting kotlin.enumValues<EnumFqn>()
.toList() for any flag with enumTypeFqn in the manifest.

FeatureFlagsDebugScreen dispatches to a new EnumDropdown
(ExposedDropdownMenuBox + DropdownMenuItem) whenever
param.enumConstants != null, mirroring the boolean/string/int input
treatment: current value visible in the text field, options in the
dropdown, source badge updates to LOCAL on selection. Boolean and
scalar input paths are unchanged.

Verified on Pixel 10 emulator: the sample's checkout_variant enum can
be flipped to NEW_SINGLE_PAGE / NEW_MULTI_STEP via the debug screen,
the main screen reacts immediately, and DataStore persistence survives
both rotation and cold restart.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Updates the [Unreleased] CHANGELOG with the PR D shape: three sample
feature modules, build-logic-extracted plugin, enum override input in
the debug screen, and the module-suffixed generator filenames. Adds a
sample/CLAUDE.md as the short navigational note for future sessions
working on the sample (module map, observe-bridge convention, how to
add a flag). The GitHub wiki pages Sample-App.md and Multi-Module-
Setup.md are updated in the separate .wiki repo alongside this PR.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The three sample feature modules wrapped iosX64() / iosArm64() /
iosSimulatorArm64() in a listOf(...) whose result was discarded.
The constructors register the targets via side-effect; the wrapper
was leftover from copying :sample:shared (which uses .forEach to
configure framework binaries). In the leaf modules the wrapper
reads as if a .forEach { ... } was deleted. Replace with bare
statements to match the idiom used elsewhere.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@kirich1409 kirich1409 added enhancement New feature or request core Changes to the core module gradle-plugin featured-gradle-plugin work debug-ui featured-debug-ui work sample Sample app labels May 19, 2026
build-logic/settings.gradle.kts read the root gradle.properties via
raw java.util.Properties().load(FileInputStream), which the
configuration cache does not fingerprint. After a VERSION_NAME bump
the included build could publish the cached previous version.
Switch to providers.fileContents(...).asText so Gradle invalidates
the cache when the parent file changes. pluginManagement {} also
moves to the first block as the docs require.

Cover the new ConfigParam.enumConstants field in equals, hashCode,
and toString with focused unit tests so the :core ≥90% line-coverage
gate stays green and the equality semantics — registry dedup depends
on them — are pinned.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@kirich1409 kirich1409 marked this pull request as ready for review May 20, 2026 06:45
Copilot AI review requested due to automatic review settings May 20, 2026 06:45
@qodo-code-review
Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 107 files

Partial review: This PR has more than 50 files, so cubic reviewed the highest-priority files first. During the trial, paid plans get a higher file limit.
You can try an ultrareview to bypass the file limit, comment @cubic-dev-ai ultrareview. Learn more.

Fix all with cubic | Re-trigger cubic

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR turns the repository’s sample into a multi-module, end-to-end demonstration of the Featured aggregation workflow (including enum flags + debug UI overrides), and moves the Gradle plugin into an included build-logic/ build to better mirror real consumer usage.

Changes:

  • Restructure the sample into three feature modules plus a pure aggregator :sample:shared consuming GeneratedFeaturedRegistry.all.
  • Extract featured-gradle-plugin into build-logic/ and add new/updated codegen + manifest/aggregation tasks and tests.
  • Extend debug UI to support enum-typed params via ConfigParam.enumConstants.

Reviewed changes

Copilot reviewed 35 out of 107 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
settings.gradle.kts Includes build-logic and registers new sample feature modules.
sample/shared/src/commonMain/kotlin/dev/androidbroadcast/featured/SampleViewModel.kt Switches sample VM to per-module observer/setter bridges and adds checkout/promotions flags.
sample/shared/src/commonMain/kotlin/dev/androidbroadcast/featured/SampleFeatureFlags.kt Removes legacy hand-maintained sample flag registry.
sample/shared/src/commonMain/kotlin/dev/androidbroadcast/featured/FeaturedSample.kt Updates imports to use feature-checkout CheckoutVariant.
sample/shared/build.gradle.kts Applies aggregator plugin, wires generated registry output, and aggregates feature modules.
sample/feature-ui/src/commonMain/kotlin/dev/androidbroadcast/featured/sample/ui/UiFlagObservers.kt Adds ConfigValues observe/set extensions for UI flags.
sample/feature-ui/src/androidMain/AndroidManifest.xml Adds minimal Android manifest for the new module.
sample/feature-ui/build.gradle.kts New KMP feature module declaring UI local flags via DSL and wiring generated sources.
sample/feature-promotions/src/commonMain/kotlin/dev/androidbroadcast/featured/sample/promotions/PromotionsFlagObservers.kt Adds ConfigValues observe/set extensions for promotions flag.
sample/feature-promotions/src/androidMain/AndroidManifest.xml Adds minimal Android manifest for the new module.
sample/feature-promotions/build.gradle.kts New KMP feature module declaring promotions remote flag via DSL and wiring generated sources.
sample/feature-checkout/src/commonMain/kotlin/dev/androidbroadcast/featured/sample/checkout/CheckoutVariant.kt Moves CheckoutVariant enum into checkout feature module.
sample/feature-checkout/src/commonMain/kotlin/dev/androidbroadcast/featured/sample/checkout/CheckoutFlagObservers.kt Adds ConfigValues observe/set extensions for checkout flags.
sample/feature-checkout/src/androidMain/AndroidManifest.xml Adds minimal Android manifest for the new module.
sample/feature-checkout/build.gradle.kts New KMP feature module declaring checkout flags (incl enum) via DSL and wiring generated sources.
sample/CLAUDE.md Documents the new multi-module sample layout and observer-bridge convention.
sample/android-app/src/main/kotlin/dev/androidbroadcast/featured/sample/MainActivity.kt Switches debug registry source to GeneratedFeaturedRegistry.all.
featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/ExtensionFunctionGenerator.kt Removes legacy generator from old module location.
featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/ConfigParamGenerator.kt Removes legacy generator from old module location.
featured-debug-ui/src/commonMain/kotlin/dev/androidbroadcast/featured/debugui/FeatureFlagsDebugScreen.kt Adds enum dropdown UI path and override wiring.
featured-bom/build.gradle.kts Removes gradle-plugin from BOM dependency list.
core/src/commonTest/kotlin/dev/androidbroadcast/featured/ConfigParamTest.kt Adds tests covering enumConstants behavior in ConfigParam.
core/src/commonMain/kotlin/dev/androidbroadcast/featured/ConfigParam.kt Adds enumConstants to ConfigParam and updates equals/toString.
CHANGELOG.md Documents sample restructure, generator renames/suspend changes, enum dropdown, and build-logic extraction.
build.gradle.kts Adds root proxy publish tasks delegating to included build.
build-logic/settings.gradle.kts Sets up included build repos, version propagation from parent gradle.properties, includes plugin module.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/XcconfigGeneratorTest.kt Adds unit tests for Xcconfig generator output.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/ProguardRulesGeneratorTest.kt Adds unit tests for ProGuard rules generator output.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/manifest/TestFixtureSupport.kt Adds shared fixture-copy + ANDROID_HOME helper utilities.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/manifest/GenerateFeaturedManifestTaskRegistrationTest.kt Verifies manifest task registration and wiring.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/manifest/FeaturedManifestSerializationTest.kt Tests manifest JSON contract and forward-compat behavior.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/manifest/FeaturedManifestIntegrationTest.kt Adds TestKit integration tests for manifest generation.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/manifest/FeaturedManifestEmptyDslTest.kt Tests manifest generation when no DSL block exists.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/manifest/FeaturedManifestConfigurationTest.kt Validates featuredManifest consumable configuration attributes/artifacts.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/manifest/FeaturedKmpPublicationTest.kt Ensures featuredManifest does not leak into published KMP metadata.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/LocalFlagEntryTest.kt Adds unit tests for LocalFlagEntry.isLocal behavior.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/IosConstValGeneratorTest.kt Adds unit tests for iOS const-val generation.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/GenerateXcconfigTaskRegistrationTest.kt Verifies generateXcconfig task registration and wiring.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/GenerateProguardRulesTaskRegistrationTest.kt Verifies ProGuard task registration and wiring.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/GenerateIosConstValTaskRegistrationTest.kt Verifies iOS const-val task registration and wiring.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/FlagEntryUtilsTest.kt Adds tests for modulePathToFileSuffix behavior.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/FlagContainerTest.kt Adds tests for DSL flag container and enum declarations.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/FeaturedPluginTest.kt Smoke tests for plugin application and ID.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/ExtensionFunctionGeneratorTest.kt Updates tests for file naming + suspend extensions + no @JvmName.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/aggregation/GenerateFeaturedRegistryTaskRegistrationTest.kt Verifies aggregator registry task registration and defaults.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/aggregation/GeneratedFeaturedRegistryGeneratorTest.kt Adds enumConstants emission assertions for enum flags.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/aggregation/FeaturedAggregationParseErrorTest.kt Ensures malformed manifests produce path-bearing errors.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/aggregation/FeaturedAggregationIntegrationTest.kt Adds TestKit integration tests for multi-module aggregation.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/aggregation/FeaturedAggregationDuplicateKeyTest.kt Adds tests ensuring duplicate keys are rejected with good messages.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/aggregation/FeaturedAggregationDescriptorIntegrityTest.kt Adds validation tests guarding against Kotlin source injection via manifests.
build-logic/featured-gradle-plugin/src/test/kotlin/dev/androidbroadcast/featured/gradle/aggregation/FeaturedAggregationConfigurationTest.kt Verifies featuredAggregation configurations and attribute setup.
build-logic/featured-gradle-plugin/src/test/fixtures/manifest-publish-project/settings.gradle.kts New fixture project for manifest generation integration tests.
build-logic/featured-gradle-plugin/src/test/fixtures/manifest-publish-project/gradle.properties Fixture properties enabling configuration cache.
build-logic/featured-gradle-plugin/src/test/fixtures/manifest-publish-project/build.gradle.kts Fixture root build file.
build-logic/featured-gradle-plugin/src/test/fixtures/manifest-publish-project/app/src/main/AndroidManifest.xml Fixture manifest for Android library module.
build-logic/featured-gradle-plugin/src/test/fixtures/manifest-publish-project/app/build.gradle.kts Fixture build with flags including enum and remote boolean.
build-logic/featured-gradle-plugin/src/test/fixtures/kmp-publish-project/settings.gradle.kts Fixture settings for KMP publishing smoke test.
build-logic/featured-gradle-plugin/src/test/fixtures/kmp-publish-project/module/src/commonMain/kotlin/.gitkeep Keeps empty fixture source directory.
build-logic/featured-gradle-plugin/src/test/fixtures/kmp-publish-project/module/build.gradle.kts Fixture KMP module applying plugin and maven-publish.
build-logic/featured-gradle-plugin/src/test/fixtures/kmp-publish-project/gradle.properties Fixture configuration cache enablement.
build-logic/featured-gradle-plugin/src/test/fixtures/kmp-publish-project/build.gradle.kts Fixture root build file.
build-logic/featured-gradle-plugin/src/test/fixtures/jvm-empty-featured-project/settings.gradle.kts Fixture settings for “no DSL block” TestKit test.
build-logic/featured-gradle-plugin/src/test/fixtures/jvm-empty-featured-project/build.gradle.kts Fixture JVM module applying plugin with no featured{} block.
build-logic/featured-gradle-plugin/src/test/fixtures/android-project/src/main/kotlin/dev/androidbroadcast/featured/testapp/CheckoutVariant.kt Fixture enum type for Android integration scenarios.
build-logic/featured-gradle-plugin/src/test/fixtures/android-project/src/main/AndroidManifest.xml Fixture Android manifest.
build-logic/featured-gradle-plugin/src/test/fixtures/android-project/settings.gradle.kts Fixture settings for Android integration tests.
build-logic/featured-gradle-plugin/src/test/fixtures/android-project/build.gradle.kts Fixture Android app applying plugin and declaring flags.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/settings.gradle.kts Fixture settings for multi-module aggregation integration tests.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/gradle.properties Fixture configuration cache enablement.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/feature-profile/src/main/AndroidManifest.xml Fixture manifest for feature-profile module.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/feature-profile/build.gradle.kts Fixture feature module with string + remote boolean flags.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/feature-checkout/src/main/AndroidManifest.xml Fixture manifest for feature-checkout module.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/feature-checkout/build.gradle.kts Fixture feature module with boolean + enum flags.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/build.gradle.kts Fixture root build file.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/app/src/main/AndroidManifest.xml Fixture manifest for aggregating app module.
build-logic/featured-gradle-plugin/src/test/fixtures/aggregator-multi-module-project/app/build.gradle.kts Fixture aggregating app using featuredAggregation dependencies.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/XcconfigGenerator.kt Adds Xcode xcconfig generation logic for Swift compilation conditions.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/ScanResultParser.kt Adds backwards-compatible parsing for flags.txt formats into LocalFlagEntry.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/ResolveFlagsTask.kt Adds task to serialize DSL flags into flags.txt.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/ProguardRulesGenerator.kt Adds ProGuard rule generation for local flags.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/manifest/GenerateFeaturedManifestTask.kt Adds per-module manifest JSON generation task.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/manifest/FeaturedManifestContract.kt Defines schema-major attribute and configuration/task constants.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/manifest/FeaturedManifest.kt Introduces manifest JSON model and encoder/decoder settings.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/LocalFlagEntry.kt Adjusts LocalFlagEntry companion constants for new generators.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/IosConstValGenerator.kt Adds iOS expect/actual const-val generation for local flags.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/GenerateXcconfigTask.kt Adds task to write FeatureFlags.generated.xcconfig.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/GenerateProguardRulesTask.kt Adds task to write generated ProGuard rules.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/GenerateIosConstValTask.kt Adds task to write iOS expect/actual constants for flags.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/GenerateConfigParamTask.kt Updates config param generation task to module-suffixed filenames.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/FlagSpec.kt Adds DSL backing model and descriptor serialization.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/FlagEntryUtils.kt Adds modulePathToFileSuffix helper used for unique generated filenames.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/FlagContainer.kt Adds Gradle DSL receiver for flag declarations (incl enum support).
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/FeaturedPlugin.kt New main plugin implementation wiring tasks + manifest configuration.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/FeaturedExtension.kt New DSL extension exposing localFlags/remoteFlags blocks.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/FeaturedApplicationPlugin.kt Adds aggregator plugin creating featuredAggregation configs + registry task.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/ExtensionFunctionGenerator.kt New generator emitting module-suffixed suspend extensions without @JvmName.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/ConfigParamGenerator.kt New generator emitting module-suffixed public Generated{Local,Remote}Flags objects.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/AndroidProguardWiring.kt Wires generated ProGuard rules into AGP variants and minify tasks.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/aggregation/GenerateFeaturedRegistryTask.kt Adds registry aggregation task with validation against duplicates/injection.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/aggregation/GeneratedFeaturedRegistryGenerator.kt Updates registry codegen to emit enumConstants for enum params.
build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/aggregation/AggregationContract.kt Adds constants for aggregation configurations/task/package/object.
build-logic/featured-gradle-plugin/README.md Documents plugin usage and enum caveats.
build-logic/featured-gradle-plugin/CLAUDE.md Adds maintainer notes for plugin DSL/tasks/tests.
build-logic/featured-gradle-plugin/build.gradle.kts Defines plugin publication, plugin IDs, and TestKit classpath wiring.
Comments suppressed due to low confidence (1)

build-logic/featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/GenerateConfigParamTask.kt:57

  • GenerateConfigParamTask deletes the entire outputDir (dir.deleteRecursively()), but this directory is also used by other generation tasks (e.g. GenerateIosConstValTask writes generated/featured/commonMain/FeatureFlagExpect.kt). Running these tasks in the same build can delete each other’s outputs and cause nondeterministic compilation failures. Prefer isolating outputs per task (separate subdirectories) or deleting only the specific files this task owns (the module-suffixed GeneratedLocalFlags*/GeneratedRemoteFlags*/GeneratedFlagExtensions* files).

Comment on lines +99 to +106
private fun LocalFlagEntry.toConfigParamExpression(): String {
val typeArg = type
val namedArgs =
buildList {
add("key = \"$key\"")
add("defaultValue = ${formatDefault()}")
if (description != null) add("description = \"$description\"")
if (category != null) add("category = \"$category\"")
cubic flagged that the codegen emits the deprecated kotlin.enumValues<T>()
helper. Replace with kotlin.enums.enumEntries<T>() (stable since Kotlin
1.9): it returns EnumEntries<T> — a lazy, cached List<T> — so the
trailing .toList() is no longer needed. Generated ConfigParam<E> objects
in per-module GeneratedLocalFlags* and in the aggregated
GeneratedFeaturedRegistry both benefit; ConfigParam.enumConstants still
types as List<T>? unchanged. Tests pin the new emit string.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@kirich1409
Copy link
Copy Markdown
Contributor Author

Addressed in b26ba01 — switched both ConfigParamGenerator and GeneratedFeaturedRegistryGenerator to kotlin.enums.enumEntries<T>() and updated the generator tests. Verified on Pixel 10 (registry still aggregates all 5 flags from 3 modules) and via Android assembleDebug + Desktop assemble + iOS sim link.

@kirich1409 kirich1409 merged commit 9055ad7 into develop May 20, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Changes to the core module debug-ui featured-debug-ui work enhancement New feature or request gradle-plugin featured-gradle-plugin work sample Sample app

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants