@@ -81,6 +81,15 @@ public abstract class StabilityCheckTask : DefaultTask() {
8181 @get:Optional
8282 public abstract val stabilityFileSuffix: Property <String >
8383
84+ /* *
85+ * Whether to only report regressive changes
86+ */
87+ @get:Input
88+ public abstract val ignoreNonRegressiveChanges: Property <Boolean >
89+
90+ @get:Input
91+ public abstract val allowMissingBaseline: Property <Boolean >
92+
8493 init {
8594 group = " verification"
8695 description = " Check composable stability against reference file"
@@ -99,7 +108,7 @@ public abstract class StabilityCheckTask : DefaultTask() {
99108 }
100109
101110 val stabilityReferenceFiles = stabilityReferenceFiles.asFileTree.files
102- if (stabilityReferenceFiles.isEmpty()) {
111+ if (! allowMissingBaseline.get() && stabilityReferenceFiles.isEmpty()) {
103112 // Directory doesn't exist - no baseline has been created yet
104113 // This is expected for new modules or before the first stabilityDump
105114 logger.lifecycle(
@@ -120,7 +129,7 @@ public abstract class StabilityCheckTask : DefaultTask() {
120129 val referenceFile = stabilityReferenceFiles.firstOrNull {
121130 it.endsWith(" $stabilityFileName .stability" )
122131 }
123- if (referenceFile?.exists() != true ) {
132+ if (! allowMissingBaseline.get() && referenceFile?.exists() != true ) {
124133 // Directory exists but file doesn't - unusual but handle gracefully
125134 logger.lifecycle(
126135 " ℹ️ No stability baseline found for :$stabilityFileName , skipping stability check" ,
@@ -133,7 +142,8 @@ public abstract class StabilityCheckTask : DefaultTask() {
133142
134143 val currentStability = parseStabilityFromCompiler(inputFile)
135144 val referenceStability = parseStabilityFile(referenceFile)
136- val differences = compareStability(currentStability, referenceStability)
145+ val differences =
146+ compareStability(currentStability, referenceStability, ignoreNonRegressiveChanges.get())
137147
138148 if (differences.isNotEmpty()) {
139149 val message = buildString {
@@ -324,7 +334,11 @@ public abstract class StabilityCheckTask : DefaultTask() {
324334 .replace(" \\ t" , " \t " )
325335 }
326336
327- private fun parseStabilityFile (file : java.io.File ): Map <String , StabilityEntry > {
337+ private fun parseStabilityFile (file : java.io.File ? ): Map <String , StabilityEntry > {
338+ if (file?.exists() != true ) {
339+ return emptyMap()
340+ }
341+
328342 val entries = mutableMapOf<String , StabilityEntry >()
329343
330344 var currentQualifiedName: String? = null
@@ -455,22 +469,34 @@ public abstract class StabilityCheckTask : DefaultTask() {
455469 private fun compareStability (
456470 current : Map <String , StabilityEntry >,
457471 reference : Map <String , StabilityEntry >,
472+ ignoreNonRegressiveChanges : Boolean = false,
473+
458474 ): List <StabilityDifference > {
459475 val differences = mutableListOf<StabilityDifference >()
460476
477+ // Check for new functions
461478 current.keys.subtract(reference.keys).forEach { functionName ->
462- differences.add(StabilityDifference .NewFunction (functionName))
479+ if (! ignoreNonRegressiveChanges || ! current.getValue(functionName).skippable) {
480+ differences.add(StabilityDifference .NewFunction (functionName))
481+ }
463482 }
464483
465- reference.keys.subtract(current.keys).forEach { functionName ->
466- differences.add(StabilityDifference .RemovedFunction (functionName))
484+ // Check for removed functions
485+ if (! ignoreNonRegressiveChanges) {
486+ reference.keys.subtract(current.keys).forEach { functionName ->
487+ differences.add(StabilityDifference .RemovedFunction (functionName))
488+ }
467489 }
468490
491+ // Check for changed stability
469492 current.keys.intersect(reference.keys).forEach { functionName ->
470493 val currentEntry = current[functionName]!!
471494 val referenceEntry = reference[functionName]!!
472495
473- if (currentEntry.skippable != referenceEntry.skippable) {
496+ // Check skippability change
497+ if (currentEntry.skippable != referenceEntry.skippable &&
498+ (! ignoreNonRegressiveChanges || ! currentEntry.skippable)
499+ ) {
474500 differences.add(
475501 StabilityDifference .SkippabilityChanged (
476502 functionName,
@@ -482,17 +508,24 @@ public abstract class StabilityCheckTask : DefaultTask() {
482508
483509 // Check if parameter count changed
484510 if (currentEntry.parameters.size != referenceEntry.parameters.size) {
485- differences.add(
486- StabilityDifference .ParameterCountChanged (
487- functionName,
488- referenceEntry.parameters.size,
489- currentEntry.parameters.size,
490- ),
491- )
511+ if (
512+ ! ignoreNonRegressiveChanges ||
513+ currentEntry.parameters.any { it.stability != " STABLE" }
514+ ) {
515+ differences.add(
516+ StabilityDifference .ParameterCountChanged (
517+ functionName,
518+ referenceEntry.parameters.size,
519+ currentEntry.parameters.size,
520+ ),
521+ )
522+ }
492523 } else {
493524 // Check parameter stability changes (only if count is the same)
494525 currentEntry.parameters.zip(referenceEntry.parameters).forEach { (current, ref) ->
495- if (current.stability != ref.stability) {
526+ if (current.stability != ref.stability &&
527+ (! ignoreNonRegressiveChanges || current.stability != " STABLE" )
528+ ) {
496529 differences.add(
497530 StabilityDifference .ParameterStabilityChanged (
498531 functionName,
0 commit comments