Below the floor? Raise the line first.
/arch-adopt refuses a project on a Spring Boot line below 4.0. /platform-upgrade takes the build to the line the Initializr serves today, leaves the build green and the diff uncommitted, and ends by printing /arch-adopt.
What the export says today
export reads the Spring Boot line from the project's build and compares it with doctor.platform.min_line, today 4.0. Below it, it exits 2 before writing a byte, and so does /arch-adopt, which writes through it. Until /platform-upgrade, the refusal named the problem and stopped.
# example: /work/legacy-app, a project on Spring Boot 3.5 (the path and the line vary) ❌ /work/legacy-app builds on Spring Boot 3.5 — this .claude/ is written for 4.0 and later. Its norms, exemplars and architecture-test allowlist use APIs and packages that do not exist on 3.5, so what they generate would not build or would misbehave there. Nothing was written. Migrate the project to Spring Boot 4.0 or later first, then run this again. The `platform-upgrade` skill does that migration; it is delivered with `arch-adopt`. The floor is `doctor.platform.min_line` in .claude/schemas/extensions.json.
The text is ArchHook.java's own, exportPlatformGate, verbatim. The path and the 3.5 line are an example.
A recipe first, a guided pass for the rest
What is mechanical is a recipe, written from data, so the same input gives the same recipe. What is not is left to the model, one compile error at a time.
An OpenRewrite recipe
ArchHook.jar upgrade-recipe writes it from doctor.platform.upgrade and from the property renames Spring's own jars publish. The Apache-2.0 OpenRewrite core runs it through the project's own Maven or Gradle wrapper; the skill never assumes one on the PATH and never adds one. Two passes: the bump, with the starters, BOMs and types, then the configuration properties, which need the classpath the first pass leaves.
One compile error at a time
What no rename covers: a test starter, Jackson 3 code, an API that changed shape. The model reads the first errors, fixes the cause against the norms and the vendor's migration guide, and compiles again. A failing test is reported, never deleted, skipped or loosened, and the same error three times in a row stops the loop and asks.
Tests run on the untouched tree first and again at the end. ArchHook.jar test-diff sorts every case: a regression, a test that vanished and a failure the baseline never saw each exit 2; a failure that was already there, or a fix, does not. A red build that was red before is reported as that.
What the skill does, in order
Everything the procedure downloads or writes outside the project goes in one directory, $WORK. It needs a clean worktree, a JDK the build accepts, a wrapper at the project root and network access to the Initializr and the engine's repositories.
- 1Refuses a dirty worktree; finds the build tool and its wrapper. No wrapper: it stops.
- 2Fetches the source into
$SRC, asarch-adoptdoes: a project below the floor has noArchHook.jarof this shape. - 3Resolves the target from a throwaway project the Initializr generates, and downloads its
dependenciesmetadata. A version is never written from memory. - 4Resolves the engine's version from the repository that serves it.
- 4bThe baseline: runs the tests on the untouched tree, then
test-diff snapshot. - 5Pass 1: the version, BOMs, coordinates, starters and types.
- 5bGradle only: fixes a wrapper the new Boot plugin refuses.
- 6Lists the new classpath.
- 7Pass 2, without compiling: the property renames the new jars declare.
- 8The guided residue: compile, read, fix the cause, compile again.
- 8bThe context loads: the
@SpringBootTestclasses run first and on their own. - 9Verifies:
clean verifyorclean build, thentest-diff compare. - 10Hands the diff over: no commit, and it prints
/arch-adopt.
› /platform-upgrade [--ref <tag|sha|main>] [--source <local path>] ✅ upgraded — <from line> → <target>, <Maven | Gradle> Source ....... <host>, ref <ref> Engine ....... <engine artifact> <version> Baseline ..... <n> test cases, <n> failing before | none — the tree ran no tests Pass 1 ....... <n> files changed (<n> sources, <n> build) BOMs ......... <n> moved by the recipe, <n> by hand | not moved — no Initializr metadata Wrapper ...... <unchanged | Gradle <from> → <to>> (Gradle only) Pass 2 ....... <n> property renames applied | none declared by the jars Residue ...... <n> edits by hand — <one line each> Context ...... <n> @SpringBootTest classes green | Not verified: context load Verify ....... <green | no worse than the baseline: <n> pre-existing | the regressions, verbatim first line> Not verified . <what no step above ran — one line each | none> Review: git diff · Undo: git checkout -- . Next: /arch-adopt
The report is the format in nerviz's docs; the angle brackets are what each run fills in.
One run, start to finish
A private Gradle project on Spring Boot 3, upgraded with nerviz v0.41.4 on 2026-10-20. The user and project names are blurred. The run took 8m17s and changed 12 files.
- Pre-checks: a clean worktree, Gradle 8.10.2 with its wrapper, JDK 21.
- Target, from the Initializr: Spring Boot
4.1.1. Engine:org.openrewrite:plugin7.39.0 withrewrite-bom8.89.0. - Baseline: 41 test cases, 0 failing.
- Pass 1, in
build.gradle: Boot, Spring Cloud and springdoc moved, 3 starters renamed,spring-boot-starter-cacheadded. The Gradle wrapper went from 8.10.2 to 8.14.6. - The guided pass:
DefaultAuthorizationCodeTokenResponseClient, removed in Spring Security 7, becameRestClientAuthorizationCodeTokenResponseClient; a test starter becamespring-boot-starter-jdbc-test; Spring Cloud AWS went from 3.1.0 to 4.2.0; two beans moved to Jackson 3'sObjectMapper. - Context: 5
@SpringBootTestclasses green. Verify: green, andtest-difffound 0 regressions, 0 missing, 0 new failures.
It did not commit. Under Not verified it named the OAuth2 login, which no test covers, and it left three decisions to the user: the version pins, the OAuth2 converter still on Jackson 2, and where the cache starter sits in build.gradle. Then it printed /arch-adopt.
Limits, said plainly
It is manual only: it rewrites a person's build and sources, so it runs when you ask for it by name. Its write territory is the root build file, the wrapper files and src/**, at the project root and under any module. .claude/** is outside it.
- Commit. The diff is yours to review.
- Write
.claude/. That is/arch-adopt's, which it hands over to. - Delete, skip or loosen a test that fails.
- Rename Jackson 3 code. It is a decision per site, not a rename, so it stays in the guided pass.
- Reach a rename that none of nerviz's own exemplars uses; a moved package none of them touches is the guided pass's.
- Edit around a build that hides its version. A Boot version in a parent POM that is not the project's, or in a version catalog, is not read: it names the file and asks you to change that line.
- Run on a project already at or above the floor. It says so and stops.
Three jobs, and what they leave open
| Job | What it proves |
|---|---|
templates › platform-upgrade | With network, on Maven and on Gradle: a fixture on the newest GA line below the floor goes through the skill's procedure and builds green on the Initializr's line. The context loads with the cache and Flyway starters, a Spring Cloud BOM moves, a pinned starter loses its pin, an old Gradle wrapper is fixed, and no test is worse than before. |
validate › hooks-cross-platform › upgrade-recipe … | The bump carries the target and one step per rename of the production data; each refusal exits 2 with nothing written. |
validate › hooks-cross-platform › test-diff … | A regression, a vanished test and a new failure each exit 2 alone; a failure already there, a fix and a skip exit 0. |
Not proven, on purpose: a scheduled run of the engine job; the Gradle Kotlin DSL and multi-module Gradle builds; and how often a rename is missing from the data.
- Next step
Review the diff, then adopt
Once you have reviewed the diff, type
The /arch-adopt guide →/arch-adopt. The plugin delivers/platform-upgradenext to it, and an adopted project keeps it for the next floor. - The skill
Its contract, its write territory and what the guard does with it.
platform-upgrade · the skill's page → - The full doc
In the nerviz repository.
15-platform-upgrade.md →