diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c43a8e937..4e2bcef16 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -66,16 +66,17 @@ jobs:
# in qa) run in build-and-test below, which now also covers docs-only PRs.
run: |
./mvnw -B -ntp clean \
- "-Dtest=EnginePdfBoundaryTest,DocumentationCoverageTest,CanonicalSurfaceGuardTest,PackageMapGuardTest,VersionConsistencyGuardTest,CiGuardListGuardTest,CiGateCoverageGuardTest,CodeQlScopeGuardTest,AgentsGuideGuardTest,BenchmarkDependencyInstallGuardTest" \
+ "-Dtest=EnginePdfBoundaryTest,DocumentationCoverageTest,CanonicalSurfaceGuardTest,PackageMapGuardTest,VersionConsistencyGuardTest,CiGuardListGuardTest,CiGateCoverageGuardTest,BinaryCompatibilityGateGuardTest,CodeQlScopeGuardTest,AgentsGuideGuardTest,BenchmarkDependencyInstallGuardTest" \
test -pl :graph-compose-core
changes:
# Path-based change detection for selective CI on pull requests. Emits the
# reverse-dependency flags the heavy jobs gate on: `code` (any build input),
- # `docs` (markdown the guard suites read and compile), `core` (the root
- # graph-compose-core module — drives japicmp), `perf`
- # (modules the smoke benchmark exercises), and `jvm` (published library modules
- # + toolchain — drives the JDK matrix width). Pushes/dispatch bypass these gates.
+ # `docs` (markdown the guard suites read and compile), `core` and `templates`
+ # (the graph-compose-core / graph-compose-templates sources + pom — the two
+ # modules under the japicmp gate), `perf` (modules the smoke benchmark
+ # exercises), and `jvm` (published library modules + toolchain — drives the
+ # JDK matrix width). Pushes/dispatch bypass these gates.
name: Detect changed paths
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
@@ -83,6 +84,7 @@ jobs:
code: ${{ steps.filter.outputs.code }}
docs: ${{ steps.filter.outputs.docs }}
core: ${{ steps.filter.outputs.core }}
+ templates: ${{ steps.filter.outputs.templates }}
perf: ${{ steps.filter.outputs.perf }}
jvm: ${{ steps.filter.outputs.jvm }}
steps:
@@ -133,6 +135,9 @@ jobs:
core:
- 'core/src/**'
- 'core/pom.xml'
+ templates:
+ - 'templates/src/**'
+ - 'templates/pom.xml'
perf:
- 'core/src/**'
- 'render-pdf/**'
@@ -478,9 +483,13 @@ jobs:
binary-compat:
name: Binary Compatibility (japicmp vs pom baseline)
- # japicmp diffs the graph-compose-core public surface, so it only matters
- # when the core module (`core/src/**` or `core/pom.xml`) changed.
- if: github.event_name == 'pull_request' && needs.changes.outputs.core == 'true'
+ # japicmp diffs the graph-compose-core and graph-compose-templates public
+ # surfaces (each module carries a `japicmp` profile), so it only matters when
+ # one of them changed (`core/src/**`, `core/pom.xml`, `templates/src/**` or
+ # `templates/pom.xml`). Both are diffed in one reactor invocation: templates
+ # compiles against the core built in the same run, and a core change can move
+ # the surface templates exposes.
+ if: github.event_name == 'pull_request' && (needs.changes.outputs.core == 'true' || needs.changes.outputs.templates == 'true')
needs: [architecture-and-documentation-guards, changes]
runs-on: ubuntu-latest
env:
@@ -506,21 +515,47 @@ jobs:
- name: Install graph-compose-emoji (resolved at test scope by core)
run: ./mvnw -B -ntp -f emoji/pom.xml -DskipTests install
+ - name: Drop cached GraphCompose artifacts before the gate resolves
+ # The gate must diff against the PUBLISHED baselines. Anything of ours
+ # already in the runner's cached repository could stand in for one, so
+ # the pinned coordinates are fetched from Central for this run. The
+ # modules under test come from the reactor, never from the repository.
+ run: |
+ rm -rf ~/.m2/repository/io/github/demchaav/graph-compose-core
+ rm -rf ~/.m2/repository/io/github/demchaav/graph-compose-templates
+
- name: Compare public API against baseline
- # The `japicmp` profile resolves the baseline release pinned
- # by the `japicmp.baseline` property in core/pom.xml (the
- # published graph-compose-core on Maven Central) and diffs it
- # against the freshly-built artifact. Fails the job on any binary-
- # incompatible modification to the public surface. Source-
- # incompatible changes are reported only (phased policy).
- run: ./mvnw -B -ntp -DskipTests -P japicmp verify -pl :graph-compose-core
+ # Each module's `japicmp` profile resolves the published releases its
+ # pom pins from Maven Central and diffs them against the freshly-built
+ # artifact: graph-compose-core against `japicmp.baseline` (the 2.x
+ # floor), graph-compose-templates against `japicmp.baseline.floor` and
+ # `japicmp.baseline.previous` (the floor and the latest release). Fails
+ # the job on any binary-incompatible modification to either public
+ # surface. Source-incompatible changes are reported only (phased policy).
+ run: ./mvnw -B -ntp -DskipTests -P japicmp verify -pl :graph-compose-core,:graph-compose-templates
+
+ - name: Prove every japicmp execution ran
+ # A japicmp execution that does not run — switched off, unbound from the
+ # lifecycle, or left out of the reactor — writes no report and fails
+ # nothing, so the step above can pass having diffed nothing. Each execution
+ # the gate relies on must have left its XML report;
+ # BinaryCompatibilityGateGuardTest keeps this list in step with the poms.
+ # (A baseline japicmp cannot resolve still leaves a report; the templates
+ # gate fails on it through ignoreMissingOldVersion=false.)
+ run: |
+ ls -l core/target/japicmp templates/target/japicmp
+ test -s core/target/japicmp/japicmp-against-baseline.xml
+ test -s templates/target/japicmp/japicmp-against-major-floor.xml
+ test -s templates/target/japicmp/japicmp-against-previous-release.xml
- name: Upload japicmp report
if: always()
uses: actions/upload-artifact@v7
with:
name: japicmp-report-${{ github.run_id }}
- path: core/target/japicmp/**
+ path: |
+ core/target/japicmp/**
+ templates/target/japicmp/**
if-no-files-found: ignore
perf-smoke:
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index ce418b488..b8a64a547 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -119,6 +119,44 @@ jobs:
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
+ - name: Install graph-compose-fonts (resolved at test scope by core)
+ # The gate below runs before the reactor install and without `-am`, so core's
+ # test-scope companions resolve from Central unless they are built here first,
+ # and a tree may pin a companion version that is not published yet. Neither is
+ # a gated artifact, so seeding them cannot supply a baseline.
+ run: ./mvnw -B -ntp -f fonts/pom.xml -DskipTests install
+
+ - name: Install graph-compose-emoji (resolved at test scope by core)
+ run: ./mvnw -B -ntp -f emoji/pom.xml -DskipTests install
+
+ - name: Drop cached GraphCompose artifacts before the gate resolves
+ # Nothing of ours may stand in for a published baseline, so the pinned
+ # coordinates are fetched from Central for this run. This runs before the
+ # install below for the same reason: the release being published must not
+ # be in the local repository while its own baselines resolve.
+ run: |
+ rm -rf ~/.m2/repository/io/github/demchaav/graph-compose-core
+ rm -rf ~/.m2/repository/io/github/demchaav/graph-compose-templates
+
+ - name: Verify binary compatibility against the published baselines
+ # Defence in depth: run the japicmp gate on the tagged commit before any
+ # deploy, so an accidental binary-incompatible change to the public surface
+ # aborts the publish even when the tag reached here by bypassing branch
+ # protection (the CI japicmp job only gates pull requests). One reactor run
+ # diffs graph-compose-core and graph-compose-templates against the releases
+ # their poms pin, exactly as the pull-request job does; templates resolves
+ # the engine from the reactor rather than from the local repository, which
+ # is what lets this run before the install below.
+ run: ./mvnw -B -ntp -DskipTests -P japicmp verify -pl :graph-compose-core,:graph-compose-templates
+
+ - name: Prove every japicmp execution ran
+ # A japicmp execution that does not run writes no report and fails nothing,
+ # so each one the gate relies on must have left its report.
+ run: |
+ test -s core/target/japicmp/japicmp-against-baseline.xml
+ test -s templates/target/japicmp/japicmp-against-major-floor.xml
+ test -s templates/target/japicmp/japicmp-against-previous-release.xml
+
- name: Build, test and install to local m2 (verify + seed the deploys)
# Re-verify the tagged commit before publishing (defence in depth
# against a tag pushed from a broken branch). `install` — not `verify` —
@@ -127,21 +165,10 @@ jobs:
# profile never publishes. Each per-module `-P release deploy` below
# builds in isolation and resolves its inter-module deps (Maven resolves
# test-scope deps even with -DskipTests) from this local m2, so the
- # unpublished tests-jar no longer fails the deploy.
+ # unpublished tests-jar no longer fails the deploy. It runs after the gate
+ # above, so the artifacts it installs cannot serve as baselines.
run: ./mvnw -B -ntp clean install
- - name: Verify binary compatibility against the published baseline
- # Defence in depth: run the japicmp gate on the tagged commit before any
- # deploy, so an accidental binary-incompatible change to the public
- # surface aborts the publish even when the tag reached here by bypassing
- # branch protection (the CI japicmp job only gates pull requests). Compares
- # the freshly built graph-compose-core against the japicmp.baseline release
- # on Maven Central and fails the job on any Stable-surface break; the
- # Internal packages (engine.**, document.layout.**) are excluded per
- # docs/api-stability.md. Test build is skipped — the install step above
- # already ran the full suite on this commit.
- run: ./mvnw -B -ntp -f core/pom.xml -P japicmp -Dmaven.test.skip=true verify
-
- name: Plan the deploy set (start_at resume)
id: plan
# A partial Central publication CANNOT be blindly re-dispatched: the deploys
diff --git a/.github/workflows/release-script-check.yml b/.github/workflows/release-script-check.yml
index b94c2ff2c..f55445fde 100644
--- a/.github/workflows/release-script-check.yml
+++ b/.github/workflows/release-script-check.yml
@@ -12,10 +12,13 @@ on:
paths:
- 'scripts/cut-release.ps1'
- '.github/workflows/release-script-check.yml'
+ # Carries the japicmp previous-release pin the script moves.
+ - 'templates/pom.xml'
pull_request:
paths:
- 'scripts/cut-release.ps1'
- '.github/workflows/release-script-check.yml'
+ - 'templates/pom.xml'
permissions:
contents: read
@@ -351,3 +354,98 @@ jobs:
if ((Get-Content $roadmap -Raw) -ne $after) { throw "a second promotion changed the file" }
Write-Host "roadmap promotion: 3 refusals, 1 rewrite, idempotent on re-run."
+
+ - name: Unit-check the japicmp previous-release baseline move
+ shell: pwsh
+ run: |
+ # The japicmp previous-release pin has to move onto the version just published
+ # at every cut (docs/api-stability.md, Binary-compatibility enforcement), so the
+ # move is code that has to be exercised, not a step someone remembers. Lifted by
+ # AST like the checks above, so the code under test is the code that ships.
+ $path = (Resolve-Path scripts/cut-release.ps1).Path
+ $ast = [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$null, [ref]$null)
+ $want = @('Update-JapicmpPreviousBaseline', 'Update-JapicmpMajorBaseline')
+ $fn = $ast.FindAll({
+ param($n)
+ $n -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $want -contains $n.Name
+ }, $true)
+ $missing = $want | Where-Object { $_ -notin ($fn | ForEach-Object { $_.Name }) }
+ if ($missing) { throw "cut-release.ps1 no longer defines: $($missing -join ', ')" }
+ Invoke-Expression (($fn | ForEach-Object { $_.Extent.Text }) -join "`n")
+ function Note($m) { Write-Host " $m" }
+ $DryRun = $false
+
+ # A function nothing calls moves no pin. Assert the post-release path still
+ # invokes every one of them: the failure this guards is a helper surviving a
+ # refactor that drops the call, which every content assertion below would still
+ # pass — the pin it moves simply stops moving.
+ $calls = $ast.FindAll({
+ param($n)
+ $n -is [System.Management.Automation.Language.CommandAst] -and
+ $want -contains $n.GetCommandName()
+ }, $true)
+ $called = $calls | ForEach-Object { $_.GetCommandName() } | Select-Object -Unique
+ $uncalled = $want | Where-Object { $_ -notin $called }
+ if ($uncalled) { throw "nothing in cut-release.ps1 calls: $($uncalled -join ', ')" }
+
+ function New-Pom($previous) {
+ $file = Join-Path ([IO.Path]::GetTempPath()) ("pom-" + [guid]::NewGuid().ToString() + ".xml")
+ Set-Content -Path $file -NoNewline -Value (@(
+ '',
+ ' 2.4.1-SNAPSHOT',
+ ' ',
+ ' 2.0.0',
+ " $previous",
+ ' false',
+ ' ',
+ ''
+ ) -join "`n")
+ return $file
+ }
+
+ $pom = New-Pom '2.3.0'
+ if (-not (Update-JapicmpPreviousBaseline $pom '2.4.0')) { throw 'moving the pin reported no change' }
+ $after = Get-Content $pom -Raw
+ if ($after -notmatch '2\.4\.0') {
+ throw "the previous-release pin did not move:`n$after"
+ }
+ if ($after -notmatch '2\.0\.0') {
+ throw "the major floor must not move at a cut:`n$after"
+ }
+ if ($after -notmatch '2\.4\.1-SNAPSHOT') { throw "the project version was rewritten:`n$after" }
+
+ if (Update-JapicmpPreviousBaseline $pom '2.4.0') { throw 'a second move reported a change' }
+
+ $bare = Join-Path ([IO.Path]::GetTempPath()) ("pom-" + [guid]::NewGuid().ToString() + ".xml")
+ Set-Content -Path $bare -NoNewline -Value "`n 2.4.1-SNAPSHOT`n"
+ if (Update-JapicmpPreviousBaseline $bare '2.4.0') { throw 'a pom without the pin reported a change' }
+ if (Update-JapicmpPreviousBaseline (Join-Path ([IO.Path]::GetTempPath()) 'no-such-pom.xml') '2.4.0') {
+ throw 'a missing pom reported a change'
+ }
+
+ # And on the real templates pom, so the pattern is proven against the file it
+ # has to move rather than only against one written to fit it.
+ $real = Join-Path ([IO.Path]::GetTempPath()) ("templates-pom-" + [guid]::NewGuid().ToString() + ".xml")
+ Copy-Item templates/pom.xml $real
+ if (-not (Update-JapicmpPreviousBaseline $real '9.9.9')) { throw 'the real templates pom: the pin did not move' }
+ $expected = [regex]::Replace((Get-Content templates/pom.xml -Raw),
+ '[^<]*',
+ '9.9.9')
+ if ((Get-Content $real -Raw) -ne $expected) { throw 'the real templates pom: the move changed more than the pin' }
+
+ # Opening a major is the one release that moves the floor and turns the break
+ # switch back on; every other release leaves both alone (docs/api-stability.md,
+ # Opening a major). Until that release ships, the pins name the previous major.
+ $bootstrap = New-Pom '2.4.0'
+ if (-not (Update-JapicmpMajorBaseline $bootstrap '3.0.0')) { throw 'the X.0.0 release did not move the floor' }
+ $opened = Get-Content $bootstrap -Raw
+ if ($opened -notmatch '3\.0\.0') {
+ throw "the floor did not move onto the release that opened the major:`n$opened"
+ }
+ if ($opened -notmatch 'true') {
+ throw "the break switch did not return to true:`n$opened"
+ }
+ if (Update-JapicmpMajorBaseline $bootstrap '3.0.0') { throw 'a second major move reported a change' }
+ if (Update-JapicmpMajorBaseline (New-Pom '2.4.0') '3.0.1') { throw 'a patch release must not move the floor' }
+
+ Write-Host 'japicmp baseline: previous moves, floor stays except at X.0.0, idempotent, the real pom moves, and the cut calls both.'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ff59335e0..568900e6a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -560,6 +560,29 @@ follow semantic versioning; release dates are ISO 8601.
Per [`docs/api-stability.md`](docs/api-stability.md) § 3 it is Stable-tier, so it is removed
no earlier than 3.0 and not before a full minor has shipped with the deprecation in place.
+### Build
+
+- **`graph-compose-templates` is under the binary-compatibility gate.** japicmp used to diff
+ `graph-compose-core` alone, so a Stable templates method could be deleted in a minor with
+ every check green. The module now carries its own `japicmp` profile, run by the same CI job,
+ the publish workflow and the release script, and diffs each build against two published
+ releases: the 2.x floor (`2.0.0`), which holds the GA surface, and the latest release
+ (`2.3.0` today), which holds everything added since. `cut-release.ps1 -PostReleaseOnly`
+ moves the second pin after each release. Every `templates.*` package is Stable, so the only
+ exclusion is the per-element `@Internal` marker, and nothing carries it. A baseline the gate
+ cannot resolve fails the build; japicmp's default would skip that diff with a warning and
+ pass. Each path that runs the gate then checks that every execution left its report, since
+ one that does not run — switched off, unbound, or not selected — writes none and fails
+ nothing. A pin may never name the version being built: japicmp resolves such a pin to the
+ artifact the build just produced and reports no differences, so both pins stay strictly
+ older than the working version, every path drops our cached artifacts before it resolves,
+ and the publish workflow runs the gate before the `install` that seeds the repository.
+ While a major has no release of its own, the pins name the previous major's floor and last
+ release and `japicmp.break.binary` is `false` — those diffs are reported, not enforced —
+ until the first post-release bump after `X.0.0` ships. `VersionConsistencyGuardTest` holds
+ the pins and that switch to the CHANGELOG, and `BinaryCompatibilityGateGuardTest` holds the
+ executions, their settings, the trigger, the report checks and the publish ordering in place.
+
### Documentation
- **The timeline recipe describes the finished model.** `LEADING | AXIS | CONTENT`, what the
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2e2dea05a..d5e5fd0ca 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,7 +33,7 @@ When writing new code, avoid Java 21+ APIs and language constructs that don't ex
## Build and test
- The blocking validation gate for repository work is `./mvnw -B -ntp clean verify` at the repository root — the root pom is the reactor aggregator, so this builds and verifies **every module**. For a fast inner loop while iterating on the engine, scope it to the core module: `./mvnw -B -ntp verify -pl :graph-compose-core`.
-- Run the engine-resident guard suite with `./mvnw -B -ntp "-Dtest=EnginePdfBoundaryTest,DocumentationCoverageTest,CanonicalSurfaceGuardTest,PackageMapGuardTest,VersionConsistencyGuardTest,CiGuardListGuardTest" test -pl :graph-compose-core` — the same list CI runs. Every name must live in `graph-compose-core`: Surefire drops a name that matches nothing as long as a sibling matches, so a guard that lives elsewhere would silently not run (`CiGuardListGuardTest` fails the build if one creeps in).
+- Run the engine-resident guard suite with `./mvnw -B -ntp "-Dtest=EnginePdfBoundaryTest,DocumentationCoverageTest,CanonicalSurfaceGuardTest,PackageMapGuardTest,VersionConsistencyGuardTest,CiGuardListGuardTest,CiGateCoverageGuardTest,BinaryCompatibilityGateGuardTest,CodeQlScopeGuardTest,AgentsGuideGuardTest,BenchmarkDependencyInstallGuardTest" test -pl :graph-compose-core` — the same list CI runs. Every name must live in `graph-compose-core`: Surefire drops a name that matches nothing as long as a sibling matches, so a guard that lives elsewhere would silently not run (`CiGuardListGuardTest` fails the build if one creeps in).
- The cross-module documentation guards — `DocumentationExamplesTest` and `DocumentationSnippetCompileTest`, which compiles the literal java fences published in `docs/` — live in `graph-compose-qa`: `./mvnw -B -ntp "-Dtest=DocumentationExamplesTest,DocumentationSnippetCompileTest" test -f qa/pom.xml`. A standalone `-f qa/pom.xml` run resolves its `graph-compose-*` dependencies from `~/.m2`, not from the reactor, so run `./mvnw -B -ntp -DskipTests install` once first — otherwise it quietly tests the artifacts you last installed instead of your working tree.
- Run the local benchmark wrapper when you change performance-sensitive code or benchmark tooling: `powershell -ExecutionPolicy Bypass -File .\scripts\run-benchmarks.ps1` (Windows). To compare two branches fairly, use `scripts/ab-bench.ps1` (Windows) or the cross-platform `scripts/ab-bench.sh` (Linux/macOS/Git Bash). See [docs/operations/benchmarks.md](./docs/operations/benchmarks.md).
@@ -70,11 +70,11 @@ Almost all work targets **`develop`**, the ongoing 2.x line. The `1.x` branch ta
- `Architecture and Documentation Guards` — fast canonical / engine-boundary guard tests, fail-first gate (always runs)
- `Build and run tests (JDK 17)`, `(JDK 21)`, `(JDK 25)` — full `mvnw verify` in parallel matrix across the supported JVMs
- `Examples Generation Smoke Test` — regenerates every runnable example and uploads the PDFs as a CI artifact
- - `Binary Compatibility` — PR-only japicmp diff of the `graph-compose-core` surface
+ - `Binary Compatibility` — PR-only japicmp diff of the `graph-compose-core` and `graph-compose-templates` surfaces
- `Performance Smoke Check` — PR-only coarse benchmark to catch performance regressions
- `CI Gate` — single aggregate status check that is green when every job that ran passed
- **Selective on pull requests:** a `dorny/paths-filter` step skips the heavy jobs when a PR touches nothing that affects the build. Markdown counts as a build input, so a **docs-only PR still runs the reactor** — on the baseline JDK alone, and without example generation — because that is where the guards compiling the published snippets live. `Binary Compatibility` runs only when the core module changed, and the `Performance Smoke Check` only when core / render-pdf / templates changed. Pushes to `develop` / `main` (and manual dispatch) always run the full gate. Point branch protection at **`CI Gate`** + **`Architecture and Documentation Guards`** rather than the individual matrix legs, so a docs-only PR is not left waiting on a skipped check.
+ **Selective on pull requests:** a `dorny/paths-filter` step skips the heavy jobs when a PR touches nothing that affects the build. Markdown counts as a build input, so a **docs-only PR still runs the reactor** — on the baseline JDK alone, and without example generation — because that is where the guards compiling the published snippets live. `Binary Compatibility` runs only when the core or templates module changed, and the `Performance Smoke Check` only when core / render-pdf / templates changed. Pushes to `develop` / `main` (and manual dispatch) always run the full gate. Point branch protection at **`CI Gate`** + **`Architecture and Documentation Guards`** rather than the individual matrix legs, so a docs-only PR is not left waiting on a skipped check.
The PR cannot merge into a protected branch until all required checks are green.
7. **Address review comments**, then squash any fixup commits before merge. The maintainer merges through GitHub once review is complete.
diff --git a/core/pom.xml b/core/pom.xml
index 858f0f218..bd2199685 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -92,12 +92,18 @@
0.26.2
+ Maven Central. The 2.0 major transition is complete, so the gate
+ enforces binary compatibility against 2.0.0 (see the japicmp profile)
+ — proven: 2.0.1-SNAPSHOT + a reduced-access public method fails with
+ METHOD_LESS_ACCESSIBLE. It must stay STRICTLY OLDER than the working
+ version, which VersionConsistencyGuardTest enforces: a baseline equal
+ to the version being built is NOT a no-op — japicmp resolves it to this
+ build's own artifact and reports no differences, so the gate passes
+ having compared the release with itself (measured on a simulated 3.0.0
+ cut). Moving this pin onto a new major therefore waits until that
+ major's first release is on Central; docs/api-stability.md (Opening a
+ major) records how the templates gate handles that cycle, and this one
+ needs the same decision when 3.0 opens. -->
2.0.0
true
+
+
+ 0.26.2
+ 2.0.0
+ 2.3.0
+
+ true
@@ -262,5 +280,90 @@
+
+
+
+ japicmp
+
+
+
+ com.github.siom79.japicmp
+ japicmp-maven-plugin
+ ${japicmp.version}
+
+
+ japicmp-against-major-floor
+ verify
+
+ cmp
+
+
+
+
+ io.github.demchaav
+ graph-compose-templates
+ ${japicmp.baseline.floor}
+
+
+
+
+
+ japicmp-against-previous-release
+ verify
+
+ cmp
+
+
+
+
+ io.github.demchaav
+ graph-compose-templates
+ ${japicmp.baseline.previous}
+
+
+
+
+
+
+
+
+ ${project.build.directory}/${project.build.finalName}.jar
+
+
+
+ true
+ ${japicmp.break.binary}
+ false
+ false
+ true
+ true
+
+ false
+
+ @com.demcha.compose.document.api.Internal
+
+
+
+
+
+
+