What the agent does
The agent clones the repository into its own cloud sandbox and finds every place the Java version is written down, because a version upgrade that misses one of them passes locally and fails somewhere else. In a project like spring-projects/spring-petclinic there are two builds side by side. pom.xml inherits from spring-boot-starter-parent 4.1.0 and sets <java.version>17</java.version>, which also feeds the maven-enforcer-plugin rule requireJavaVersion and the java.source and java.target values written into build-info. build.gradle applies the org.springframework.boot plugin 4.1.0 and sets a toolchain with JavaLanguageVersion.of(17). The wrappers are Maven 3.9.16 in .mvn/wrapper/maven-wrapper.properties and Gradle 9.5.1 in gradle/wrapper/gradle-wrapper.properties, and the agent checks that both run on a JDK 25 before touching anything else.
Next it reads the plugins that care about the bytecode version. JaCoCo is pinned at 0.8.15 and instruments every test class; the GraalVM native build tools, spring-javaformat 0.0.47 and the CycloneDX plugin all run during the build. Checkstyle is held at 12.3.1, with a comment in pom.xml saying it stays on 12 because Checkstyle 13 needs JDK 21. On Java 25 that reason is gone, so the agent says so in the pull request and leaves the bump as a separate decision rather than mixing it into the JDK change. It moves the version in one place per build: java.version to 25 in pom.xml and the toolchain to JavaLanguageVersion.of(25) in build.gradle.
Then it updates what runs the build outside the sandbox. .github/workflows/maven-build.yml and gradle-build.yml both use a java: [ '17' ] matrix with actions/setup-java@v4 and distribution: 'adopt', which setup-java has removed in favour of temurin, so the agent switches the distribution to temurin and the matrix to 25. It notes that gradle-build.yml caches with cache: maven even though it runs ./gradlew build, and changes it to gradle. There is no Dockerfile (the README builds images with ./mvnw spring-boot:build-image), but .devcontainer/Dockerfile defaults to VARIANT=17-bullseye for Gitpod and devcontainer.json asks for Java 21-oracle, so the agent updates both or explains why one stays.
Finally it builds and tests on JDK 25 with both tools: ./mvnw -B verify, which runs the enforcer, the javaformat check and the nohttp Checkstyle rules before the tests, and ./gradlew build. The project has 20 test classes. MySqlIntegrationTests is marked @Testcontainers(disabledWithoutDocker = true) and PostgresIntegrationTests assumes DockerClientFactory.instance().isDockerAvailable(), so the agent reports whether those two ran or were skipped in the sandbox; the workflows on the pull request run them on GitHub's runners. When a compile error, a deprecation that is now an error or a failing test appears, it fixes the cause, runs the suite again, and only then opens the pull request.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Upgrade this Spring Boot project to Java 25. Find every place the Java version is set: java.version in pom.xml, the Gradle toolchain in build.gradle, the maven-enforcer rule, the GitHub Actions workflows (use the temurin distribution, since setup-java removed adopt), the devcontainer and any Dockerfile. Check that the Maven and Gradle wrappers and the build plugins (JaCoCo, GraalVM native build tools, spring-javaformat, Checkstyle) work on JDK 25 and update only the ones that need it. Build and run the tests on JDK 25 in the sandbox with ./mvnw -B verify and ./gradlew build, fix what breaks, and say which tests were skipped and why. Open a pull request listing each file changed, each fix, and anything left for a separate change.",
"resources": [
{
"url": "https://github.com/your-org/your-app"
}
]
}- Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
- Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
- Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.
What comes back
What comes back is a pull request on a new branch. In a project shaped like spring-petclinic it changes pom.xml and build.gradle to Java 25, both workflow files to temurin with a Java 25 matrix (and the Gradle workflow's cache to gradle), and the devcontainer files, plus any source or test fixes the new JDK forced. The description lists each changed file with the reason, every compile or test failure the agent hit with the fix it made, the tests that were skipped in the sandbox and why, and the follow-ups it chose not to mix in, such as moving Checkstyle to 13.
The pull request's own checks are the proof that matters: the Maven and Gradle workflows it edits run on it with JDK 25, including the Testcontainers tests on a runner with Docker. The session's transcript keeps every command the agent ran in its sandbox, from java -version to each ./mvnw -B verify and ./gradlew build, with the output.
Before merging, a reviewer checks that the JDK distribution matches what the team deploys on, that the base image used in production (built by spring-boot:build-image or elsewhere) will also run Java 25, that any --add-opens flag or suppressed warning the agent added has a reason in the description, and that both workflows passed on the pull request rather than only one.
Run it on every new JDK release
Java ships a feature release every March and September. A scheduled job on your server can send this request with the new version in the prompt once your JDK vendor publishes it, one request per repository, each with that repository in resources. Each run gets its own sandbox and opens its own pull request, so a team with many Spring Boot services sees which ones move cleanly and which need work.
Set a webhook URL on the API key and Rush POSTs run.completed or run.failed when each run's turn finishes, with the session_url. A key runs at most max_concurrent sessions at once (4 by default) and answers 429 past that, so a job covering many repositories should queue its requests.
The use case this job follows, with its automation and trigger: Upgrade dependencies weekly.
Why run it on Rush?
A JDK upgrade touches the machine as much as the code: a new JDK, both wrappers downloading their distributions, every dependency resolved again, and a full test run that has to happen on the new version and not on whatever JAVA_HOME points at on a laptop. In a Rush sandbox the agent installs and uses JDK 25 on its own disk, so no one's local toolchain changes and the build is not quietly running on Java 17.
The proof is in two places. The transcript keeps each ./mvnw and ./gradlew run with its output, and the pull request's workflows run the same builds on JDK 25 on GitHub. A reviewer can see that the upgrade was built and tested on the new version rather than taken on trust, and several services can be upgraded at once, each in its own sandbox.
Frequently asked questions
My project has both Maven and Gradle builds. Does it update both?
Yes. The agent finds the Java version in each build file, moves both, and runs both ./mvnw -B verify and ./gradlew build on JDK 25. If one of them fails and the other passes, the pull request says which and why.
Will it upgrade Spring Boot at the same time?
No, unless you ask. The prompt changes the Java version and only the plugins that need it to run on JDK 25. A Spring Boot upgrade is a separate change with its own risks, and keeping them apart makes each pull request easier to review and revert.
What about integration tests that need Docker?
In spring-petclinic the MySQL and Postgres tests skip themselves when Docker is not available. The agent reports in the pull request whether they ran or were skipped in its sandbox, and the workflows on the pull request run them on GitHub's runners, which have Docker.
Why switch the setup-java distribution?
The workflows use distribution: 'adopt', a legacy AdoptOpenJDK distribution setup-java no longer supports, so the setup step fails before the build starts; temurin replaces it. The agent switches to temurin, the Eclipse Adoptium builds; if your team uses another distribution, name it in the prompt.