Skip to content

Decouple Muzzle planning from CI slot selection - #12831

Open
bric3 wants to merge 2 commits into
sarahchen6/skip-unnecessary-planningfrom
bdu/use-task-dependency
Open

bric3 wants to merge 2 commits into
sarahchen6/skip-unnecessary-planningfrom
bdu/use-task-dependency

Conversation

@bric3

@bric3 bric3 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What Does This Do

Builds on #12800 by planning Muzzle checks only for modules whose muzzle task is included in Gradle's execution graph.

Now a Provider<Set<Task>> whose role to provide the computed plan; it is passed to the main muzzle task's finalizedBy. The use of the provider allow to defer the directive checks planning and registration until Gradle resolves its task finalizers during task graph assembly.

Concretely, this remove the need to parse the task path parsing and MuzzleExtension.includeInAggregate. And keeps the CI convention handling runMuzzle and slots.

Motivation

#12800 avoids planning modules outside the selected CI slot. This change lets the task graph determine which modules need planning, without requiring the Muzzle plugin to understand the CI aggregate or its selection rules.

There are other lazy mechanisms available, but they have different roles or properties:

  • TaskProvider references an already registered task. Muzzle needs completed directives and resolved versions to know the check names and count. Registering every check up front would still incur discovery costs for unused modules, and a task configuration action cannot register sibling tasks. See task configuration avoidance.

  • TaskCollection connects registered tasks, but does not discover versions or register missing checks.

  • Task-output providers connect values and infer producer dependencies. In particular, dependency lookup can follow a task-backed map provider's producer without evaluating its mapped value, so registering checks as a mapping side effect is unsuitable. See Provider dependency information.

  • A plain Provider or Callable could defer the same registration work. A live provider can be queried repeatedly, so it would need the same protection to happen only once. TaskDependency gives this operation an explicit task-set resolution contract during graph construction. In this PR, the wrapper caches success and failure to prevent duplicate registration after partial failure.

  • TaskDependency, whose role is precisely to provide concrete tasks at execution graph assembly.

    [!NOTE]
    I first tried a TaskDependency implementation, which worked well. Although
    the interface is not currently deprecated,
    Gradle maintainers have discussed removing it alongside Buildable.
    As a precaution, this PR now uses a regular provider, which is already supported as finalizer dependency notation.

Finally the planned execution order remains the same: muzzle -> directive checks as emitted by planner -> muzzle-end.

Tip

Resolving the finalizers after project evaluation incidentally removes the need for the dreaded afterEvaluate, which also happens to be one of the long term goal to remove legacy APIs.

Additional Notes

  • Version discovery is unchanged.
  • They are still a few existing configuration-cache limitations that need follow-up.
  • Excluding a module's muzzle task with -x is supported and avoids planning it. However excluding individual generated checks by name remains unsupported because they are registered after CLI task selection. Workaround exists, but do not feel useful
exclusion support patch
diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt
--- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt
+++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzlePlugin.kt
@@ -140,6 +140,12 @@ class MuzzlePlugin : Plugin<Project> {
     muzzleTask.configure {
       finalizedBy(validationTasks.tasks)
     }
+
+    project.tasks.addRule("Pattern: muzzle-Assert*") {
+      if (startsWith("muzzle-Assert")) {
+        validationTasks.tasks.get()
+      }
+    }
   }
 
   companion object {
diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleValidationTasks.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleValidationTasks.kt
--- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleValidationTasks.kt
+++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleValidationTasks.kt
@@ -24,8 +24,8 @@ import org.gradle.api.tasks.TaskProvider
  * tasks when assembling the execution graph, exists today but is on the side-line possibly
  * getting deprecated for removal.
  *
- * shortcut: generated-check CLI selectors remain unsupported; revisit if individual checks
- * need to be selected or excluded.
+ * A task rule can resolve this provider during CLI selection. Unqualified generated-check
+ * selectors can trigger planning in projects outside the execution graph.
  *
  * @see <a href="https://docs.gradle.org/current/javadoc/org/gradle/api/provider/ProviderFactory.html#provider(java.util.concurrent.Callable)">ProviderFactory.provider API</a>
  * @see <a href="https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskProvider.html">TaskProvider API</a>
diff --git a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzlePluginPerformanceTest.kt b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzlePluginPerformanceTest.kt
--- a/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzlePluginPerformanceTest.kt
+++ b/buildSrc/src/test/kotlin/datadog/gradle/plugin/muzzle/MuzzlePluginPerformanceTest.kt
@@ -139,10 +139,10 @@ class MuzzlePluginPerformanceTest : MuzzlePluginTestFixture() {
     )
 
     assertThat(result.task(":dd-java-agent:instrumentation:demo:tasks")?.outcome).isEqualTo(SUCCESS)
-    assertThat(result.output).contains("BUILD SUCCESSFUL")
+    assertThat(result.output).contains("BUILD SUCCESSFUL", "Pattern: muzzle-Assert*")
 
     assertThat(result.tasks)
       .withFailMessage("Should not create or execute any muzzle tasks when not requested")
       .noneMatch { it.path.contains("muzzle") }
-    assertThat(result.output).doesNotContain("muzzle-Assert", "muzzle-end")
+    assertThat(result.output).doesNotContain("muzzle-AssertPass", "muzzle-AssertFail", "muzzle-end")
   }

Contributor Checklist

Resolve validation tasks lazily through `TaskDependency`, so only
selected projects perform planning. Remove task-name parsing and
the CI-specific extension flag.

Cache registration results and failures to prevent repeated lookups
from registering tasks twice. Cover custom aggregates, nested task
paths and CI slot selection.
@bric3 bric3 added tag: no release notes Changes to exclude from release notes type: refactoring comp: tooling Build & Tooling tag: ai generated Largely based on code generated by an AI or LLM labels Oct 12, 2026
@datadog-datadog-us1-prod

This comment has been minimized.

@bric3
bric3 requested a review from sarahchen6 October 12, 2026 15:03
@bric3
bric3 marked this pull request as ready for review October 12, 2026 15:03
@bric3
bric3 requested a review from a team as a code owner October 12, 2026 15:03
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T15:12:19.527837Z 2954da1 Draft marked ready
🔒 Security Review ✅ Completed 2026-10-12T15:08:51.244373Z 2954da1 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@dd-octo-sts

dd-octo-sts Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

🟡 Java Benchmark SLOs — Performance SLO warning (near threshold)

Suite Status
Startup 🟡 warning

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 13.92 s 13.82 s [-0.0%; +1.5%] (no difference)
startup:insecure-bank:tracing:Agent 12.86 s 12.91 s [-1.1%; +0.3%] (no difference)
startup:petclinic:appsec:Agent 17.82 s 17.55 s [+0.7%; +2.4%] (maybe worse)
startup:petclinic:iast:Agent 17.46 s 17.48 s [-0.8%; +0.6%] (no difference)
startup:petclinic:profiling:Agent 17.29 s 17.16 s [-0.3%; +1.8%] (no difference)
startup:petclinic:sca:Agent 17.69 s 17.64 s [-0.7%; +1.3%] (no difference)
startup:petclinic:tracing:Agent 16.54 s 16.73 s [-2.5%; +0.1%] (no difference)

Commit: 17baba07 · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2954da1f19

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/MuzzleValidationTasks.kt Outdated

@datadog-datadog-us1-prod datadog-datadog-us1-prod Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bits Code Review: PASS

More details

Task-graph-driven Muzzle planning preserves the validation finalizer chain and CI slot filtering. Build logic compiled, but targeted execution tests were blocked by dependencies unavailable offline.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Bits Code Review · Commit 2954da1

Supply planned finalizers through a plain `Provider` instead of a
custom `TaskDependency`. Keep task registration deferred and memoise
both successful and failed registration attempts.

Document how this differs from task-backed providers. Verify that
excluding a module avoids resolving its validation directives.

The works like `TaskDependency`, but it appears that `TaskDependency`
could get side-lined, see
gradle/gradle#30534 (comment)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: tooling Build & Tooling tag: ai generated Largely based on code generated by an AI or LLM tag: no release notes Changes to exclude from release notes type: refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant