Skip to content

japicmp API-Compatibility Gate — Policy (v1.1)

Status: Wired in kairo-api/pom.xml as of v1.0.0-RC1 (2026-04-24). Baseline default bumped to 1.0.0 at v1.1.0 cut (2026-04-25). The gate enforces v1.0 GA's @Stable surface; new v1.1 SPIs (sandbox / workspace / tenant / bridge) are pure additions and produce no break diffs on the v1.0 baseline.

Related: ADR-023 (SPI Stability Policy) • spi-census-v1.0.mdspi-annotation-application.md


Purpose

Prevent silent binary/source-incompatible changes to any type, method, or field in io.kairo.api.* that carries the @io.kairo.api.Stable annotation. The gate is a mechanical backstop behind the human review process: once ADR-023 is in effect, changing a @Stable signature requires an explicit decision. japicmp ensures the decision was actually made rather than slipping through a review.

Scope

  • Module: kairo-api only. kairo-core and other runtime modules are not public contract — they may evolve freely.
  • Surface: @Stable-annotated types, methods, and fields. @Experimental / @Internal / unannotated elements pass through silently — this is intentional per ADR-023.
  • Break kinds enforced: binary-incompatible AND source-incompatible modifications. (Both flags set — a source-only change that is still binary-compatible, e.g., renaming a parameter, still breaks the build.)

Mechanics

The profile is defined in kairo-api/pom.xml under <profile id="api-compat">.

ElementValueRationale
Plugincom.github.siom79.japicmp:japicmp-maven-plugin:0.23.1Latest stable at time of wiring
GoalcmpCompare two artifacts
PhaseverifyRuns after package, so the new jar exists on disk
Old version${project.groupId}:${project.artifactId}:${api.baseline.version}Property-driven for promotion
New version${project.build.directory}/${project.artifactId}-${project.version}.jarThe jar being built
includeAnnotations@io.kairo.api.StableOnly Stable surface is policed
breakBuildOnBinaryIncompatibleModificationstrueBinary breaks fail the build
breakBuildOnSourceIncompatibleModificationstrueSource breaks fail the build too
onlyModifiedtrueDon't re-report unchanged APIs
onlyBinaryIncompatibleModificationstrueSkip purely-additive diffs in the report

Activation

bash
# Default build — gate NOT active
mvn -pl kairo-api -am verify

# Gate active — compares against baseline
mvn -pl kairo-api -am verify -Papi-compat -Dapi.baseline.version=1.0.0-RC1

# Gate active but suppressed (RC1 bootstrap, incidental debugging)
mvn -pl kairo-api -am verify -Papi-compat -Djapicmp.skip=true

Baseline Lifecycle

Releaseapi.baseline.version defaultEnforcement state
v1.0.0-RC1emptyNo-op (nothing to compare)
v1.0.0-RC21.0.0-RC1First enforcement — freezes RC1 surface
v1.0.0 GA1.0.0-RC2 (or latest RC)Enforcement active
v1.0.x patcheslatest v1.0.xEnforcement active
v1.1.0 (current)1.0.0Enforcement active — additions only (sandbox / workspace / tenant / bridge)
v1.2.01.1.0Enforcement active
v2.0.0-RC1emptyReset — major version allowed to break

Promotion rule: when cutting a release vX.Y.Z, the next release bumps api.baseline.version default in kairo-api/pom.xml to vX.Y.Z. This is a one-line PR that the release engineer owns; the SOT release checklist calls it out explicitly.

What Constitutes a Break

Per ADR-023 §"Semantics of additive change":

Binary-incompatible (always fails):

  • Remove or rename a @Stable type / method / field
  • Change method signature (return type, parameter types, throws clause)
  • Change field type
  • Narrow visibility (public → package-private)
  • Add a new abstract method to a @Stable interface (use default instead)
  • Add a new enum value NOT at the tail
  • Add a new record component (breaks canonical constructor)

Source-incompatible (always fails):

  • Rename a public method parameter (Javadoc reference break)
  • Change a method's generic bounds in a way that forces call-site updates

Additive (always allowed):

  • Add a new default method on an interface
  • Add a new public method / field on a class
  • Add a new enum value at the tail
  • Add a new static factory on a record

Intentional-Break Workflow

When a deliberate break must ship (e.g., critical security fix, RC → GA tightening):

  1. File ADR documenting the break, rationale, and migration path.
  2. In the commit, add an explicit suppression entry to kairo-api/pom.xml under the japicmp <parameter><ignoreMissingClasses> or <excludes> section, referencing the ADR number as an XML comment.
  3. Bump the SOT row with a BREAKING-CHANGE note.
  4. Update CHANGELOG.md under a ### Breaking changes heading.

Do not silently disable the profile or set breakBuildOn...=false. The gate must always be capable of flagging breaks — suppressions must be targeted and documented.

CI Integration

  • Default CI: runs mvn clean verify without -Papi-compat. Gate does not run.
  • Release CI: release.yml workflow adds -Papi-compat -Dapi.baseline.version=<previous-release> as a required step before publishing.
  • PR CI (planned, post-RC2): add a dedicated "API Compat" job that activates the profile against the last released baseline. Failing job blocks merge. Will be wired in a follow-up PR once RC1 is published.

Failure Triage

When the gate fails:

  1. Inspect the HTML / XML report at kairo-api/target/japicmp/.
  2. For each reported break, ask: was this intentional?
    • Intentional → follow the intentional-break workflow above.
    • Accidental → revert the offending change; ADR-023's rules apply.
  3. If the break is intentional but contained to @Experimental / @Internal: the gate should not have fired. Verify the includeAnnotations scope is not being misread.

Known Limitations

  • japicmp runs only against the kairo-api module. Transitive contract leaks (e.g., a @Stable method returning a kairo-core type that then changes shape) are NOT caught. Policy-level mitigation: kairo-api must not expose non-JDK / non-Reactor types from other kairo modules. A separate static analysis check (TBD) can enforce this physically.
  • Generic-type bound changes occasionally produce false positives when combined with inheritance — handle case-by-case.

Open items

  • [x] First enforcement run at v1.0.0-RC2 cut.
  • [x] Baseline promoted to 1.0.0 at v1.1.0 cut (2026-04-25).
  • [ ] CI workflow integration (planned post-RC1 publish).
  • [ ] Baseline promotion automation (release script should bump the property).

This doc will evolve as the gate accrues real-world findings. Keep it current.