feat/v2.2-adoption-feedback: polish the operator experience surfaced by the service pilot#37
Merged
vedanthvdev merged 1 commit intomasterfrom Apr 24, 2026
Merged
Conversation
e767f48 to
5816f43
Compare
…by the security-service pilot The v2.1 pilot against Modulr's security-service (CAR-5190) surfaced five non-breaking rough edges that an adopter hits in their first hour with the plugin. v2.2 sharpens each one while keeping every DSL knob, default, and resolved behaviour from v2.1 bit-for-bit identical. Bug A: `--explain` no longer forces a full compile. The unconditional `testClasses` dependency turned a 3-second diagnostic run into a multi-minute compile on a real-world repo. The dependency is now wrapped in a `Callable` that re-evaluates after command-line parsing and returns an empty list when `--explain` is set, pruning `testClasses` from the task graph entirely. Dispatch runs keep the dependency as before. Bug B: situation-specific `Hint:` lines in the `--explain` trace. Pre-v2.2 every mapper-touching situation printed the same "outOfScopeTestDirs is configured" hint — correct for a subset of DISCOVERY_SUCCESS runs, actively misleading everywhere else. v2.2 splits the hint into three targeted branches: DISCOVERY_EMPTY names the realistic causes (testSuffixes, testDirs, no coverage yet), DISCOVERY_INCOMPLETE names the actual risk (parse failure → partial selection) plus the `onDiscoveryIncomplete` escalation knob, and DISCOVERY_SUCCESS keeps the v2.1 OOS wording in the one situation it was right for. The DISCOVERY_INCOMPLETE hint is action-aware: only the SELECTED branch claims the selection is partial; the FULL_SUITE branch (CI/STRICT default, or explicit override) drops that wording and the circular "escalate to what we already did" advice. Risk C: LOCAL mode + DISCOVERY_INCOMPLETE now emits a lifecycle WARN when it accepts a partial selection. LOCAL keeps `onDiscoveryIncomplete = SELECTED` on purpose (devs iterating on WIP want fast feedback), but without a visible signal a parse failure silently understates what actually ran. The new `WARN` marker "affectedTest: LOCAL mode accepted a partial selection" is grep-friendly, visible at Gradle's default log level, and gated so it never fires in CI/STRICT modes (which already escalate to FULL_SUITE on DISCOVERY_INCOMPLETE). The gate also short-circuits on `skipped=true` / empty FQN lists so an operator never sees a "0 test classes accepted" WARN immediately before a skipped run. The wording cross-references the engine's own parse-failure WARN rather than restating it. Feature D: `-PaffectedTestsMode=local|ci|strict|auto` runtime override. Mirrors the existing `-PaffectedTestsBaseRef` pattern so adopters can A/B modes without editing `build.gradle`. Implemented as a `convention()` on the mode Property so DSL-declared `mode = '...'` keeps precedence — a repo pinning its CI mode in the build script cannot be silently overridden by a stray `-P`. Polish E: `--explain` now shows the `:module:test` dispatch breakdown on a SELECTED run. Same grouping helper the dispatch path uses, same preview-truncation cap — so an operator comparing `--explain` against a real dispatch sees the exact same shape. Non-SELECTED runs suppress the block entirely rather than print a noisy "0 modules" line. The task-path normalisation is shared between explain and dispatch via a `testTaskPath` helper so the two operator-facing strings cannot drift. Every user-facing behaviour above is pinned by a Cucumber e2e scenario in `06-v2.2-adoption-feedback.feature`, plus unit tests on the Callable prune shape, the mode-precedence contract, the three hint variants (including the action-aware DISCOVERY_INCOMPLETE split), the Risk C WARN four-way gate (mode, situation, action, skipped/empty), and the Modules-block empty/multi-module/root/truncation edges. The two-module Bug A e2e scenario pins that `--explain` prunes `compileJava` on every subproject, not just the root. CHANGELOG bumped with the adoption-feedback narrative plus a follow-up Unreleased section capturing the v2.2 code-review polish; the release-version pin is set to 2.2.0 so the next merge to master tags and publishes v2.2.0.
5816f43 to
758da15
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The v2.1 pilot against Modulr's
security-service(CAR-5190) surfaced five non-breaking rough edges that an adopter hits in their first hour with the plugin. This PR sharpens each one while keeping every DSL knob, default, and resolved behaviour from v2.1 bit-for-bit identical — v2.1 users can bump to v2.2 without touchingbuild.gradle.What (the five fixes)
Bug A —
--explainno longer forces a full compileThe unconditional
testClassesdependency turned a 3-second diagnostic run into a multi-minute compile on the real repo. v2.2 wraps the dependency in aCallablethat re-evaluates after CLI parsing: when--explainis set the Callable returns an empty list andtestClassesis pruned from the task graph; when--explainis absent the dependency behaves exactly as before.Bug B — situation-specific
Hint:lines in the--explaintracePre-v2.2 every mapper-touching situation printed the same "outOfScopeTestDirs is configured" hint — misleading on runs where OOS wasn't the cause. v2.2 splits the hint into three targeted branches:
DISCOVERY_EMPTY→ "discovery mapped 0 test classes" + testSuffixes / testDirs / no-coverage-yet causesDISCOVERY_INCOMPLETE→ names the parse-failure risk + points atonDiscoveryIncomplete = "full_suite"DISCOVERY_SUCCESSwith OOS configured-but-unmatched keeps the v2.1 wordingRisk C — LOCAL + DISCOVERY_INCOMPLETE now emits a loud WARN
LOCAL keeps
onDiscoveryIncomplete = SELECTEDon purpose, but without a visible signal a parse failure silently understates what ran. v2.2 emits a lifecycleWARNwith markeraffectedTest: LOCAL mode accepted a partial selection— grep-friendly, visible at Gradle's default log level, gated so it never fires in CI/STRICT (which already escalate to FULL_SUITE on DISCOVERY_INCOMPLETE).Feature D —
-PaffectedTestsModeruntime overrideMirrors
-PaffectedTestsBaseRef: set-PaffectedTestsMode=local|ci|strict|autoto flip the plugin's mode without editingbuild.gradle. Implemented as aconvention()so DSL-declaredmode = '...'keeps precedence — a repo pinning its CI mode cannot be silently overridden by a stray-P.Polish E —
:module:testdispatch breakdown in--explainA SELECTED
--explainrun now prints the exact dispatch grouping a non-explain run would use:Non-SELECTED runs suppress the block entirely.
Verification
./gradlew test functionalTestgreen locally (73 unit tests + 54 Cucumber scenarios, +2 new unit tests and +10 new e2e scenarios from this PR).06-v2.2-adoption-feedback.featurepins each user-facing behaviour above, so a regression on any of the five surfaces as a named scenario failure rather than silent UX drift.Release plan
CHANGELOG.mdhas a full v2.2.0 entry describing each fix and its scope..release-versionpins2.2.0so the next merge-to-master release workflow tags and publishesv2.2.0, then auto-deletes the pin in a[skip ci]follow-up (matches the v2.1.0 release flow).Test plan
./gradlew test— green./gradlew functionalTest— green (all 6 feature files, including new06-v2.2-adoption-feedback.feature)