NEVER push commits, create remote branches, delete branches, or perform any destructive git operation without explicit manual confirmation from the user. This includes git push, git push -u, git branch -D, git push --delete, and any gh command that modifies remote state (e.g., gh pr create). Local branch creation and local commits are allowed when requested, but nothing leaves the local machine without the user saying so.
FORBIDDEN - The aggregator plugin will reject these:
./gradlew clean build # ❌ WILL FAIL
./gradlew clean publishLocal # ❌ WILL FAIL
./gradlew clean build publishLocal # ❌ WILL FAILALLOWED - Multiple tasks (excluding clean):
./gradlew build publishLocal # ✅ OK - most combinations work
./gradlew test jar # ✅ OKREQUIRED - Clean must run alone:
./gradlew clean
./gradlew build
./gradlew publishLocalThe XVM project uses a custom aggregator plugin that prevents running clean with other lifecycle tasks to avoid:
- Race conditions in composite builds
- Build conflicts between subprojects when cleaning
- Task ordering issues with clean
Most other task combinations work fine - the restriction only applies to clean.
- ALWAYS add a newline at the end of every file
- NEVER use star imports (import foo.*) - always use explicit imports
- NEVER use fully qualified Java package names in the Java code. Always import, so that i.e
org.gradle.api.model.ObjectFactoryis justObjectFactory - ALWAYS use
vardeclarations when the type is clear from the right-hand side (e.g.,var x = new ArrayList<String>()notList<String> x = new ArrayList<>())
Single project tasks:
./gradlew javatools:jar./gradlew xdk:installDist./gradlew javatools:clean
Multiple lifecycle tasks (works for most tasks):
./gradlew build installDist- ✅ Works./gradlew test jar- ✅ Works./gradlew assemble publishLocal- ✅ Works
Clean workflow (clean must run alone):
./gradlew clean(standalone, nothing else)- Wait for completion
- Then run your desired tasks:
./gradlew buildor./gradlew build installDist
Remember: Never combine clean with other lifecycle tasks.
When working with Gradle build files, always follow Gradle Best Practices:
- Configuration Cache Compatibility: Use injected services (
ExecOperations,FileSystemOperations) instead of project-level methods (project.exec,project.javaexec) - Task Dependencies: Declare explicit task dependencies using
dependsOn,mustRunAfter, or input/output relationships - Lazy Configuration: Use Provider APIs and avoid eager evaluation during configuration
- Incremental Builds: Properly declare inputs and outputs for custom tasks
- Build Performance: Minimize configuration time work and prefer build cache compatible patterns
When refactoring build scripts, proactively suggest migrations to follow these best practices, especially for configuration cache compatibility and proper task modeling.
NEVER use old untyped Gradle syntax in build.gradle.kts files. ALWAYS use typed operations:
❌ FORBIDDEN - Never do this:
tasks.register("taskName") {
dependsOn("otherTask") // String-based dependency
}✅ REQUIRED - Always do this:
val taskName by tasks.registering {
dependsOn(tasks.named("otherTask")) // Typed dependency
}or even better:
val otherTask by tasks.existing<SomeTaskType>()
val taskName by tasks.registering {
dependsOn(otherTask) // Typed dependency
}Rules:
- Always use
val taskName by tasks.registeringinstead oftasks.register("taskName") - Always use typed task references with proper Provider API
- This ensures proper build cache support, configuration cache compatibility, and IDE support
- NEVER run without the configuration cache enabled. Everything MUST work with the configuration cache.
The lang/ directory is a Gradle composite build that is disabled by default. Two -P properties are required to enable it when running any ./gradlew :lang:* task from the project root:
./gradlew :lang:<task> -PincludeBuildLang=true -PincludeBuildAttachLang=true-PincludeBuildLang=true: Includes thelang/directory as a composite build, making:lang:*tasks visible to the root project-PincludeBuildAttachLang=true: Wireslang/lifecycle tasks (build, test, etc.) to the root build's lifecycle, so./gradlew buildfrom the root also builds lang
Both properties default to false in the root gradle.properties so that:
- CI and other developers don't need to build
lang/unless they're working on it - The main XDK build stays fast for contributors who aren't touching language tooling
- The composite build inclusion is opt-in to avoid unexpected build interactions
- Always when running any
./gradlew :lang:*task from the project root - Both properties are always needed together -- using only one will fail
- If you forget them, the build will fail with "project ':lang' not found" or similar
Instead of passing -P flags every time, developers can set them in their local gradle.properties:
includeBuildLang=true
includeBuildAttachLang=true- Do what has been asked; nothing more, nothing less.
- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
- NEVER add "Co-Authored-By" lines to commit messages or pull request descriptions.
NEVER WRITE GRADLE CODE THAT IS INCOMPATIBLE WITH THE CONFIGURATION CACHE
- Every time you write Gradle code, you MUST test the build to verify configuration cache compatibility
- Do NOT capture script object references (like
logger,project) in task actions - Use injected services (
@InjectwithExecOperations,Logger, etc.) for configuration cache compatibility - Use Worker API or convention plugins for complex task logic
- Always test with
./gradlew <task> --infoafter making changes