Repository navigation
Conversation
…ay no
A source- or binary-breaking change to the published Java API rode through CI
unflagged - nothing compared the surface a branch produces against the surface
already on Maven Central. docs/refactoring.md already states the policy ("they
are release-gated: do not fold them into a minor/patch") and lists five breaks
waiting on it; nothing enforced any of it.
Adds japicmp behind an opt-in `api-compat` Maven profile, driven by
bin/check-api-breaking.sh, pinned by bin/test-check-api-breaking.sh, and run as
the `api: breaking` job on PRs. Covers the four published modules; the examples
modules are deploy-skipped and have no published surface.
The design is lifted wholesale from bin/check-proto-breaking.sh (#242),
which had already solved the identical problem for the proxy protocol schema:
a self-arming grace branch so the gate does not block PRs before a baseline
exists, a self-test that runs first, and exit 2 for cannot-run as a third
outcome that is not a pass.
DIAGNOSIS - why the script resolves explicit files and compares hashes.
The published baseline carries the SAME Maven version string as the working
tree (both 0.6.0.0-SNAPSHOT), so a coordinate-based <oldVersion> resolves both
sides to the local artifact and japicmp compares the new jar against itself.
Three separate wrong configurations were hit while building this, and every one
exited 0 printing "No changes.":
1. <oldVersion> as a coordinate -> compared the new jar to itself
2. rebuild failed, missing -am -> compared a stale jar
3. rebuild failed, test compile error -> compared a stale jar
(3) is the likely one in practice, since a breaking API change usually breaks
the tests that call it. The script now exits 2 when the two sides are the same
file or the built jar predates its sources; both cases are pinned by the
self-test, alongside a renamed and a removed public method.
BASELINE: master's snapshot, which publish.yml redeploys on every green push,
because it exists today and answers "does this branch change the published
surface?" before any release is cut. Repointing at 0.6.0.0 once released is a
one-property change.
NOT a required status check, deliberately: a required context no run has
produced blocks every PR whose base predates it (docs/ci.md). Arming is a
follow-up once the job is on master, the same state `tooling: package rename`
is in.
REJECTED: revapi - japicmp names the semver bump a change demands
("Semantic versioning suggestion: 1.0.0"), which is the input CHANGELOG.adoc's
=== Breaking section currently takes from memory. The head-to-head comparison
available is japicmp's own, so that choice is worth revisiting independently.
What is deliberately still open - what happens on red, whether .state/.offsets
/.metrics are really public API, and arming as a required check - is recorded
in docs/inflight/ci-api-compatibility-gate.md and the plan doc, not decided here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.OpenSSF Scorecard
Scanned Files
|
✅ Duplicate Code ReportTwo engines run in parallel for cross-validation. Each has its own thresholds tuned to its baseline - the real safety net is the per-engine "max increase vs base" check. ✅ PMD CPD
No new clones introduced by this PR. ✅ jscpd (language-agnostic)
No new clones introduced by this PR. Powered by astubbs/duplicate-code-cross-check |
✅ SpotBugs ReportNo bugs found (new bugs only — baseline from base branch excluded). |
🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its |
astubbs
added a commit
that referenced
this pull request
Aug 20, 2026
Jabel compiles Java 17 source to Java 8 bytecode, so calling a post-Java-8 JDK method compiles, ships, and fails with NoSuchMethodError on a Java 8 consumer. <release.target>8</release.target> is the only guard, and relaxing what that restricts is the whole point of Jabel. animal-sniffer-maven-plugin checks exactly that, and was started here once: origin/animal-sniffer adds it against the java18 signature in March 2022, one commit past START, both bodies empty, never merged. The plugin appears zero times in today's poms and nothing replaced it in that direction. Filed as its own note rather than folded into the API-compatibility work, because the two check OPPOSITE directions and neither substitutes for the other: #315 asks whether our published API breaks our consumers, Animal Sniffer asks whether our own call sites exist on the target platform. The note says so explicitly, since the pair reads as duplicates. Open rather than deferred: the configuration is already written, it depends on no other branch, and the exposure is live on every release. What is undecided is small and named - phase binding, the Mutiny module's Java 17 floor, and whether a dependency baseline joins the JDK one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019infZnCpPvX8c6QWFC1uLC
astubbs
added a commit
that referenced
this pull request
Aug 20, 2026
Both earn a note on evidence rather than on a branch existing, and both are one unreferenced commit away from being lost. Shared collections across the poll/control boundary. Master still has PCMetrics.registeredMeters as a plain ArrayList, appended per partition inside a rebalance callback and walked-then-cleared in close(); RemovedPartitionState still shares a mutable static TreeSet across every instance in the JVM. Filed as a stall, not a lost metric: the ConcurrentModificationException propagates into doClose's finally and skips the state = CLOSED transition on the next line, so the consumer never departs and the group waits out its session timeout. fix/concurrent-collection-sweep fixes six such sites with a reproduction and a regression test and has no PR - and it touches the files #57 declares it owns, so the sequencing is an open call. The Class 2 chaos RED. test-chaos-phase2.md still says "widen it if it ever flakes"; it flaked, and a seed replay on an uncontended box (154.5s to 121.3s, zero violations) shows the RED was runner contention between Performance and Chaos Pain Suite, not a product stall. Tagged misdirection because that is precisely what it cost: a signature that reads as the exact Class 2 occurrence the hunt wants. The owning note gets a one-line pointer rather than a second copy of the finding. Not written, and why: #315, #313 and #303 are open PRs whose diffs carry their own context; docs/router-ownership-pass, docs/mirror-2023-admin-sweep, docs/plan-package-rename-dry-run and docs/v6-release-ideas-old have all merged or been superseded on master; and docs/297-review-route-and-timing-evidence is superseded by better material already there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019infZnCpPvX8c6QWFC1uLC
astubbs
added a commit
that referenced
this pull request
Aug 25, 2026
…per test, qualified refs, retired links Three corpus repairs behind red gate lanes on this branch: - docs/quarantined-tests.md carried two live entries for PCMetricsTest.metricsRegisterBinding - the original mechanism diagnosis and the #265 re-quarantine - which the registry check rightly reads as drift. The re-quarantine entry is the current one; it now carries a pointer to the first diagnosis (which still stands, in bug-pcmetrics-committed-offset-vs-completion-count.md) and the superseded block is gone. History keeps the original. - docs/data/landing-page.yaml still linked docs/inflight/next-landing-page.md, retired by b0a5da3 when the owner dropped the workstream; both mentions are reworded inline, the same repair that commit applied to the six notes it caught. - Six inflight notes carried bare #NN refs on lines this stack adds vs master: each now names its repo (confluentinc#346/#390/#315/confluentinc#606, #51 - each resolved in both repos first), a quoted upstream issue body takes the line-scoped exempt marker, and two ordinals that were never issue refs at all ("problem 1", "hypothesis 6") are written as ordinals. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019HwDcZdfv1Ne4u3g6ThrG9
astubbs
added a commit
that referenced
this pull request
Sep 10, 2026
…lassic API (U1) The first unit of the UX modernisation plan's Milestone A. The package bz.stub.parallelconsumer.fluent exists in core with a package-level javadoc naming it incubating, so every later unit adds classes without touching the build again. Core gains Jackson as an optional dependency for the fluent API's JSON helpers, pinned module-locally at the examples' line and deliberately not in the root dependency management, since a global pin is transitive to WireMock and broke the Vert.x tests when tried. The compatibility gate's exclusion for the package is recorded on the UX inflight note for the gate's branch (#315), which has not merged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpkQjk4NxtZtPERHvyxeQu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
A source- or binary-breaking change to the published Java API currently rides through CI unflagged. Nothing compares the surface a branch produces against the surface already on Maven Central.
docs/refactoring.mdalready states the policy — "they are release-gated: do not fold them into a minor/patch" — and lists five breaks waiting on it. Nothing enforces it. This is that enforcement arm.Draft deliberately: the tooling works, the policy around it is unresolved. See "Open decisions" below.
What this adds
bin/check-api-breaking.sh— resolves the published baseline per module, compares, exits0/1/2bin/test-check-api-breaking.sh— five cases pinning the gate's behaviourapi-compatMaven profile. Nothing runs in a normal or CI build without-Papi-compatapi: breakingjob on PRs, covering the four published modulesThe design is not new — it is copied
bin/check-proto-breaking.sh(#242, unmerged) had already solved the identical problem for the proxy protocol schema. Four decisions taken from it verbatim: the self-arming grace branch, the self-test that runs first, exit 2 for cannot-run as an outcome that is not a pass, and the<area>: <check>job name.Diagnosis — three green checks that checked nothing
This is the part worth reviewing. Whilst building the spike, three separate misconfigurations each exited 0 printing
No changes.:<oldVersion>as a Maven coordinate-am)Root cause of (1) is specific to this repo: master's published baseline carries the same version string as the working tree (both
0.6.0.0-SNAPSHOT), so Maven cannot distinguish "the remote one" from "the local one" and resolves both sides to the same file.(3) is the likely one in practice — a breaking API change usually breaks the tests that call it, so the build fails, the jar goes stale, and the gate waves the break through.
The script therefore resolves both sides to explicit files and refuses to run (exit 2) when they are the same file, or when the built jar predates its own sources. Class:
a-check-that-reports-success-without-having-run.md.Evidence
Self-test, all passing:
Real run against the live Central baseline — all four published modules compatible:
The gate firing, on a renamed
PollContext.value():That last line is why japicmp specifically: it names the version bump a change demands, which is the input
CHANGELOG.adoc's=== Breakingsection currently takes from memory.Baseline choice
The
0.6.0.0-SNAPSHOTthatpublish.ymlredeploys on every green master push — because it exists today and answers "does this branch change the published surface?" before any release is cut. Repointing at0.6.0.0once released is a one-property change; both are the same comparison with a different left-hand side.Not a required status check, deliberately
A required context that no run has produced blocks every PR whose base predates it (
docs/ci.md). Arming is a follow-up once the job is on master — the same statetooling: package renameis in. So this PR cannot demonstrate blocking behaviour, only the report.Open decisions — recorded, not made
In
docs/inflight/ci-api-compatibility-gate.mdand the plan doc:docs/data/module-maturity.yamlpublishes the claim that pre-1.0 APIs may change, so a gate that simply forbids breaks would contradict it..state,.offsetsand.metricsare really public API. The gate currently asserts they are. Nobody decided that — it fell out of**.internal.**being the only boundary the project has.Reviewer attention
bin/check-api-breaking.share the load-bearing part. They are what stops this becoming a permanent green tick.parallel-consumer-corefive times, so the job costs a few minutes. Not measured against the runner budget indocs/inflight/ci-disabled-jobs-and-runner-load.md, and that doc exists because jobs were disabled for exactly this reason.ignoreMissingClassesis on — third-party types on the API surface (Kafka, SLF4J) are not on japicmp's classpath. The cost is that changes reaching the API through those supertypes are invisible.Same-defect sweep
The defect class is "a check that reports success without having run". Searched for other instances:
bin/check-proto-breaking.shandbin/check-docs-data.shboth already carry self-tests, andbin/ci-mutation-test.shis documented inAGENTS.mdas exiting 0 printing "nothing to mutate, skipping" when its package regex goes stale — a known live instance, pre-existing and out of scope here. No new instances found.Checklist
docs/ci.md,.github/workflows/README.md, plus a new plan doc and in-flight notedocs/features/—N/A - CI tooling, no user-facing library featurebin/test-check-api-breaking.sh, five cases, all passing