Skip to content

3.0.0 release readiness: release tooling fixes, ASM ceiling to Java 27, GHCR images, docs - #956

Open
jbachorik wants to merge 27 commits into
developfrom
claude/btrace-3-release-readiness-v5k9id
Open

jbachorik wants to merge 27 commits into
developfrom
claude/btrace-3-release-readiness-v5k9id

Conversation

@jbachorik

@jbachorik jbachorik commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

What does this change do?

Final readiness pass for BTrace 3.0.0. The review itself is in internal/plans/2026-09-19-v3.0.0-final-readiness.md (every claim carries a file:line or a command run with its result). The branch fixes what the review found:

  • Release tooling could not run. scripts/release.sh and three release.yml steps read/wrote project.version in common.gradle, a line removed in c354f12b; the version lives in the root build.gradle. btrace --version printed (dev) on every Gradle 9 build because Project.exec no longer exists (now providers.exec). Every Gradle job in release.yml now installs the JDK 24 toolchain the agent's java24 source set needs and runs Gradle on JDK 21 (three jobs ran Gradle 9 on JDK 11, which it does not support). Both the script and the workflow still handle 2.2.x release branches, which keep project.version in common.gradle.
  • JDK-warning release gate now proves once-per-JVM semantics (probe invokes the warning twice) and gates the -Dbtrace.suppressJavaDeprecationWarning=true property; both negative cases were confirmed to fail.
  • ASM routing ceiling MAX_ASM_MAJOR_VERSION 69 → 71: the bundled ASM 9.10.1 (newest on Maven Central) parses Java 27 class files, so Java 26/27 use the ASM backend and the ClassFile API backend covers Java 28+. ClassFileApiBackendTest uses version-72 fixtures on a JDK 28 EA toolchain (continuous.yml installs 28-ea); selector/backend tests assert the new boundary.
  • Class-header reads above the ASM ceiling (found by this PR's CI on JDK 28 EA): ClassInfo read JDK classes with ASM and threw Unsupported class file major version 72, so isAssignable failed and CALL probes were dropped. ClassHeaderReader now dispatches like instrumentation does: ASM up to the ceiling, the JDK ClassFile API above it (ClassFileApiClassHeaderReader in the java24 source set, loaded reflectively). The lazy lookups in ClassHeaderReader, LinkerInstrumentor and BackendSelector publish their reference before the "attempted" flag, so a concurrent caller can no longer observe "attempted" with a null reference.
  • Agent argument lists were truncated. -javaagent:btrace.jar=grant=NETWORK,THREADS granted only NETWORK: the agent splits the whole argument string on commas before reading key=value pairs, so the second name became a bare key. The same applied to deny=, allowExtensions=, denyExtensions= and probes=, all documented in the comma form. A bare token that directly follows one of those keys now continues that key's list (MainTest covers it); help still works as a bare key.
  • JDK matrix: no lane pins a build any more. Lanes 8/11/17/21/25 and 28-ea are the newest Temurin release of their major from actions/setup-java (check-latest: true); the 27 lane is marked sdkman: true and resolves through scripts/resolve-sdkman-java.sh to the newest 27 GA build SDKMAN lists (27.0.0-oracle until Temurin 27 is published). CI on this PR showed why: SDKMAN retired 27.ea.31-open at 27 GA, rejected 27-open, and its listing (the API and sdk list java alike) carried no Temurin 8/11/21 build at all while the old pins still installed. update-jdk-versions.yml stays and becomes active again as soon as a lane pins a build.
  • Container images publish to ghcr.io/btraceio/btrace from a new publish-container-images release job (amd64+arm64, three variants, GITHUB_TOKEN, Docker actions pinned to commit SHAs); the btrace Docker Hub namespace belongs to an unrelated account. All image references updated.
  • Curated release notes .github/release-notes/v3.0.0.md, wired into the release body via body_path.
  • Docs/tutorials: agent error text and Tutorial Lesson 6 told users to pass --grant to the client (not parsed); download URLs used asset names that never existed; Oracle Contributor Agreement references removed (project is Apache-2.0, no CLA); tutorials 03/08/09 had steps that could not work as written; ClassFile-backend limitations were stale; Lesson 14 claimed embedding a privileged extension "is the grant" (it is not: the fat-agent plugin carries the permissions manifest into the embedded descriptor and the bridge still gates it); Java support statement is now "8–27+" (JDK 27 GA 2026-09-15); SECURITY.md supported-versions table; blog drafts fact-checked.

Related issue

Release plan internal/plans/2026-07-11-v3.0.0-release-plan.md; touches #932 (mitigations only, root cause stays open) and supersedes the pins in #938.

Closes N/A

Scope and compatibility

  • I identified the affected module(s) and kept unrelated changes out of this PR.
  • This preserves the supported Java/runtime compatibility tiers, or the change is documented below.
  • This does not change the masked-JAR layout, class-loader boundary, or wire protocol.
  • If it does, I updated the relevant architecture documentation and verification plan.

Compatibility or migration notes:

Java 26 and 27 class files are now instrumented by the ASM backend instead of the ClassFile API backend (docs/architecture/InstrumentationBackends.md updated). The agent's "permissions not granted" message now points at agent-side grant= / policy instead of the nonexistent client --grant flag, and multi-value grant=/deny=/allowExtensions=/denyExtensions=/probes= lists on the -javaagent line now take effect in full. Container images move from btrace/btrace (never published) to ghcr.io/btraceio/btrace.

Testing

  • Unit tests
  • Integration tests (if applicable)
  • spotlessCheck
  • Documentation/link or sample verification (if applicable)

Commands and results:

# JDK 8/11/17/21 (apt), Oracle JDK 24.0.2 / 26.0.2.1 / 27 GA (27+35) as toolchains
./gradlew spotlessCheck :btrace-dist:build                          BUILD SUCCESSFUL
./gradlew build                                                     BUILD SUCCESSFUL (agent 1019, core 315, client 75, compiler 70, runtime 27 tests, 0 failures)
./gradlew :btrace-dist:releaseSmoke                                 release smoke PASSED (7 sections)
./gradlew -Pintegration :integration-tests:test  (JDK 21 target)   72 tests, 0 failures, 2 skipped
TEST_JAVA_HOME=<jdk27> ... :integration-tests:test  (JDK 27 GA)    72 tests, 0 failures, 2 skipped — with the Java 27 ceiling, i.e. ASM backend on major-71 classes
TEST_JAVA_HOME=<jdk26> ... --tests ClassFileApiTests --tests PreparedModeAuthenticationFunctionalTest   4/4, 6/6
scripts/verify-jdk-warning.sh on JDK 8, 11, 17, 21, 24, 26, 27      all PASSED (8/11 warn once, 17+ silent; suppression pass green)
DRY_RUN=true scripts/release.sh minor HEAD                          Release Version v3.0.0, release/3.0._, next 3.1.0-SNAPSHOT
markdown link check over README/docs/docker/.github                 0 broken
:btrace-agent:test MainTest / ClassHeaderReaderTest / BackendSelectorTest   11/11, 5/5, 4/4 (review-driven fixes)

Not verifiable in the authoring environment (no JDK 28 EA or Docker reachable): classFileApiBackendTest on JDK 28 EA and the GHCR publish job (skipped on dry runs; first exercised by the real release). CI on the final code head db2abfe5 (run 35471638858) is green on every lane: Temurin 8.0.504+1, 11.0.32+101, 17.0.20+101, 21.0.12+101, 25.0.4+101, Oracle JDK 27+35 via SDKMAN 27.0.0-oracle, and Temurin 28-ea; the ClassFile API backend tests ran on JDK 28 EA in the build job. Copilot review on that head reports no findings; all eleven review threads are resolved.

Documentation and release impact

  • User-facing documentation is updated, or no documentation change is needed.
  • Release notes/changelog are updated, or no release-note entry is needed.
  • Samples, distribution contents, or published coordinates are updated if affected.
  • This change is safe to merge independently of a release, or the dependency is explained below.

Release notes / follow-up work:

  • Release 3.0.0 with ./scripts/release.sh minor (develop is already 3.0.0-SNAPSHOT; major would produce 4.0.0). Run one dry_run: true workflow first; the Release workflow has never run.
  • One-time after the first release: make the btrace GHCR package public.
  • Drop the sdkman: true entry for the 27 lane once actions/setup-java can serve Temurin 27.
  • Open follow-ups listed in the review §2.3, §4.3, §5.3: PreparedModeAuthenticationFunctionalTest hangs intermittently on V2/V2, silently consuming the job timeout #932 root cause, docs SHOULD-FIX items, tutorial recordings, DCO decision, B7 verification against btraceio/agent-plugins.

Final checklist

  • I reviewed the complete diff and removed unrelated changes.
  • New or changed behavior has appropriate tests, or the reason for not adding them is explained above.
  • User-facing behavior, APIs, samples, and documentation are consistent with this change.
  • I did not include generated build output, local configuration, credentials, or other accidental files.

🤖 Generated with Claude Code

https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1


This change is Reviewable

jbachorik and others added 12 commits September 19, 2026 13:25
…mon.gradle

c354f12 moved the project version into the allprojects block of the root
build.gradle and removed project.version from common.gradle, but
scripts/release.sh and three steps of the release workflow (dry-run version
update, develop snapshot bump, release-branch snapshot bump) still grepped and
sed-ed `project.version` in common.gradle. Since that line no longer exists,
release.sh aborts with "Cannot find version in common.gradle" and the workflow
bumps would no-op and then fail on an empty commit.

Point both at build.gradle with an anchored `^\s*version = '` pattern so only
the allprojects version line is matched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
…e, fix stale docs

- common.gradle: Gradle 9 removed Project.exec, so getGitCommit() always fell
  back to 'dev' and every 3.0.0 build printed "BTrace v.3.0.0 (dev)" with
  Build-Revision: dev. Use providers.exec.
- scripts/release-gates/JdkWarningProbe.java + verify-jdk-warning.sh: invoke
  the warning twice so "exactly one warning" proves the once-per-JVM guard, and
  add a suppressed run so -Dbtrace.suppressJavaDeprecationWarning=true is gated
  too. Both negative cases were confirmed to fail the gate.
- Agent permission error and Tutorial Lesson 6 told users to pass --grant /
  --grantAll to the btrace client, which does not parse them; point at the
  agent-side grant= args and the permission policy instead. Usage text named
  btrace-agent.jar/java-agent.jar instead of the masked btrace.jar.
- README/GettingStarted download URLs used asset names that never existed
  (assets are btrace-v<version>-bin.tar.gz with bin/ and libs/ at the root).
- Drop the Oracle Contributor Agreement requirement left over from the GPL
  era; contributions follow CONTRIBUTING.md under Apache-2.0.
- InstrumentationBackends.md still said the ClassFile API backend supports only
  ENTRY/RETURN; all 17 probe kinds are supported now (only @duration on
  SYNC_ENTRY/SYNC_EXIT is not).
- Tutorial 08 manifest snippet (Loader is the Premain/Agent class), tutorial 01
  Ctrl+C menu semantics, JDKCompatibility EA id and pre-17 deprecation note,
  releasing.md SDKMAN task and release-type guidance, broken FAQ/README anchors.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
… release job

The GitHub release body was produced only by generate_release_notes, which for
a 393-commit major release yields a flat, uncategorised PR list. Add
.github/release-notes/v3.0.0.md (highlights, breaking changes, deprecations,
behavior notes, install, links) and a release step that uses it as body_path
when a curated file exists for the version, keeping the auto-generated list
appended after it.

Also: SECURITY.md supported-versions table; migration guide rows/sections for
the authenticated prepared endpoint (2.x clients rejected), the removed
@RequestPermission(s)/@RequiresPermission(s) annotations, and client-side
suppression of the Java deprecation notice; README pointer to the notes;
release/tutorial plan status refreshed to the audited state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
…s 03, 08, 09

- 08: embedded privileged extensions are gated like filesystem ones
  (EmbeddedExtensionRepository honors BTrace-Extension-Permissions), so Step 4
  passes allowExtensions=btrace-metrics on the -javaagent line and shows the
  real "! ERROR" / "btrace -le" output otherwise; Step 5 uses the jar the demo
  build actually produces (demo-btrace-agent.jar) and launches DemoApp.java;
  zip-name and task-list explanations corrected from the plugin source.
- 09: no official image contains a fat agent (btrace-dist writes it to
  build/fat-agent and the Docker context depends only on btraceJar), so Step 6
  builds it and copies it from the build context; the btrace-scripts ConfigMap
  the pod mounts is now created (from demo/OrderTiming.java) before apply and
  deleted in cleanup; stale docker/README remark removed; base-image tags
  aligned with the Dockerfiles. Same Dockerfile fix in fat-agent-plugin.md.
- 03: inline listing and migrate-script output replaced with the real file and
  real --dry-run output (9 matches incl. the header comment), repo-root paths,
  and suppression advice for both the target and the client JVM.
- 04/07 expected output shows 3.0.0; README/Lesson 13 describe tutorial 05 as
  the pointer to btraceio/agent-plugins it is; JAVA_HOME prerequisite stated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
… code

- B1: libs/profiles are removed in 3.0.0 (agent logs an error and loads
  nothing), not deprecated; three runtime tiers (8 / 9-10 / 11+), not five;
  adds the aggregation fixes, detach-safe indy dispatch, and the pre-release
  permission annotations that were removed before 3.0.0.
- B2: jbang coordinate is io.btrace:btrace; Ctrl+C opens the client menu
  (1 exit retracts the probe, 6 detach leaves it running); stale catalog
  caveat dropped since the release workflow rewrites the catalog.
- B5: io.btrace:btrace coordinates; the Gradle plugin's service auto-detection
  uses the io.btrace descriptors (the "dead code" gotcha was fixed in #897).
- B7 / v6 script: the MCP server lives in btraceio/agent-plugins
  (btrace-observability, JBang launch); tool-level claims carry VERIFY
  markers until checked against that repository.
- B8: fat-agent manifest uses io.btrace.boot.Loader + BTrace-Agent-Main; Maven
  fat-agent paragraphs removed (module not shipped); embedded extensions honor
  permissions; bundled probes fail loudly when missing.
- B9: the architecture doc already uses the real btrace.comm.* property names;
  the JMH harness (BinaryProtocolBenchmark, :btrace-core:jmh) exists but no
  results are committed.
- B10: all 17 probe kinds supported by the ClassFile API backend; ASM 9.10.1.
- All: fixed ../GettingStarted.md links, 3.0.0-SNAPSHOT sample output,
  TODO markers for per-post Discussions threads and missing recordings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
- @RequestPermission(s)/@RequiresPermission(s) were added on 2025-12-26 and
  removed on 2026-01-07 (f476295); they are absent from the v2.2.6 tree, so
  they are not a 2.x -> 3.0 breaking change. Reworded the release notes, the
  migration guide, and tutorial 03 accordingly.
- The bundled ASM is 9.10.1 (settings.gradle) and defines V26/V27; the
  MAX_ASM_MAJOR_VERSION = 69 ceiling is a routing decision to the ClassFile
  API backend, not the parser's limit. Comments and InstrumentationBackends.md
  said "ASM 9.9.x tops out at 69".
- BTraceTutorial Lesson 15 still described the ClassFile API backend as
  ENTRY/RETURN-only.
- Tutorial 04's expected "Blocked privileged extension" line lists the three
  permissions the built btrace-metrics manifest declares.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
JDK 27 went GA on 2026-09-15 (Oracle build 27+35). Verified locally on that
build: JDK warning gate passes with no warning, full integration suite green
(72 tests, 0 failures), ClassFile API smoke runs. Update the canonical support
sentence everywhere it appears, mark 27 as supported in JDKCompatibility.md
(the CI lane still uses the last EA build until SDKMAN publishes a GA id), and
record the JDK 27 and ASM 9.10.1 findings in the readiness review.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
…tests on JDK 28 EA

ASM 9.10.1 (already the newest release on Maven Central) parses class files
up to major 71 (Java 27), so MAX_ASM_MAJOR_VERSION moves from 69 to 71: Java
26 and 27 class files now take the ASM backend and the ClassFile API backend
covers Java 28+ (early-access builds). Tests follow: ClassFileApiBackendTest
fixtures are version 72 and require JDK 28, the classFileApiBackendTest task
uses a JDK 28 toolchain (continuous.yml installs 28-ea), BackendSelectorTest
and AsmInstrumentationBackendTest assert the new boundary, and the ClassFile
integration smoke assumes a JDK 28+ target.

CI matrix: add a 27-open GA lane; keep the 27.ea entry so the Monday JDK
update automation advances it to 28 EA (the script only rewrites an existing
EA entry).

release.yml: every Gradle job now installs JDK 24 (toolchain for the agent's
java24 source set, which continuous.yml had and release.yml did not) and runs
Gradle on JDK 21; three jobs ran Gradle 9 on JDK 11, which Gradle 9 does not
support. Drop the tool-cache step that skipped setup-java on a cache hit.

Docs: InstrumentationBackends.md, Tutorial Lesson 15, release notes, JDK
compatibility table (27 GA + 28 EA rows), releasing/checklist matrix lists,
draft B10; remove the last Oracle Contributor Agreement mention (FAQ).

Verified: spotless + core/agent/client unit tests green; integration suite
green on a JDK 27 GA target with the new ceiling.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
Docker Hub cannot host the documented btrace/btrace images: that namespace is
an unrelated user account (joined 2020, owns kafka-topic-operator), and an
organization namespace would need a paid Team plan. GitHub Container Registry
is free for public images and needs no account or secret, so:

- new release job publish-container-images (after create-github-release)
  builds the Debian, Alpine, and distroless variants for linux/amd64 and
  linux/arm64 from the candidate distribution, pushes <version>/latest tags
  with GITHUB_TOKEN (packages: write), and verifies manifests and OCI labels;
  summary table row added.
- every image reference (README, docker/README, GettingStarted, QuickReference,
  tutorial 09 + demo Dockerfile/pod, fat-agent-plugin.md, release notes, draft
  B8, Gradle buildDockerImage* tags) now uses ghcr.io/btraceio/btrace.
- docs/releasing.md documents the job and the one-time step of making the
  package public.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
… parse them

PR #956 CI on a JDK 28 EA target failed
ClassFileApiBackendTest.callProbeBeforeAllowsAssignableTargetInstanceAndMethodName:
the ClassFile API backend instrumented the class, but the type-assignability
check (InstrumentUtils.isAssignable -> ClassInfo.loadExternalClass) still read
java/util/ArrayList with ASM, which throws "Unsupported class file major
version 72", so the hierarchy did not resolve and the CALL probe was skipped.

Class-header reads now dispatch the same way instrumentation does:
ClassHeaderReader uses ASM up to MAX_ASM_MAJOR_VERSION and the JDK ClassFile
API (ClassFileApiClassHeaderReader in the java24 source set, loaded
reflectively like ClassFileApiLinkerGuard) above it. On a JVM without the
ClassFile API a newer class file fails with an explicit message and ClassInfo
stays unavailable, never handed to ASM.

Tests: ClassHeaderReaderTest (ASM path, dispatch, fallback without the
ClassFile API) and two ClassFileApiBackendTest cases (forced ClassFile API
read on any JDK 24+, major-72 hierarchy resolution on JDK 28+).

CodeQL: `-x test build` still ran classFileApiBackendTest, which needs a JDK
28 toolchain that workflow does not install; exclude the task and drop the
now-unneeded JDK 26 setup.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
jbachorik and others added 7 commits September 19, 2026 20:05
…Reader

CodeQL's default-setup analysis flagged the two locals as "never read": it
cannot resolve ClassHeader across the java24 source set, so the constructor
call that reads them looked unresolved. Pass the expressions directly; no
behavior change (re-verified on JDK 27 through the forced ClassFile API path).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
PR #956 CI showed both JDK 27 lanes failing in "Prepare JDK": SDKMAN reports
`27-open` as not available, and the pre-existing `27.ea.31-open` download now
fails because that early-access build was retired when JDK 27 went GA (so the
EA lane is red on develop as well). Pinning an identifier requires an SDKMAN
lookup that cannot be done from every environment and goes stale at each GA,
so the two moving lanes now carry a spec instead of a pin:

- `27`    -> newest 27 GA build (Temurin, else java.net, else Oracle)
- `28-ea` -> newest `28.ea.N-open` early-access build

scripts/resolve-sdkman-java.sh turns a spec into a concrete identifier from
`sdk list java` (full identifiers pass through unchanged; unknown specs fail
loudly), and the Prepare JDK steps export TEST_JAVA_HOME from the resolved
identifier. The pinned lanes (8, 11, 17, 21, 25) are unchanged and remain
maintained by scripts/update-jdk-versions.sh, which ignores the two specs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
Every integration lane on the previous head failed with
"sdkman-init.sh: line 65: ZSH_VERSION: unbound variable": the resolver runs
under `set -euo pipefail` and sdkman-init.sh reads unset variables, so the
source aborted and the identifier list was empty even for pinned lanes.
Relax `-u` around the source and the `sdk list java` call. Reproduced and
verified against a stand-in sdkman-init.sh with the same hazard and SDKMAN's
table layout (exact ids pass through, `27` and `28-ea` resolve, unknown specs
still fail loudly).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
…setup-java

`sdk list java` prints a curated subset per vendor, so the resolver rejected
the pinned `11.0.31-tem` lane and found no 28 EA build. SDKMAN also has no
`28.ea.N-open` at all yet (the 2026-09-14 update-jdk-versions run kept
`27.ea.31-open`), so an EA lane cannot come from SDKMAN.

- resolve-sdkman-java.sh: full identifiers pass through unchanged (`sdk
  install` is the check); `<major>` resolves against the complete SDKMAN API
  list that update-jdk-versions.sh already uses, with `sdk list java` only as
  a fallback; `<major>-ea` is rejected with a pointer to setup-java.
- continuous.yml, release.yml: `-ea` lanes are installed by actions/setup-java
  (Temurin EA), the same step the build job uses for classFileApiBackendTest;
  TEST_JAVA_HOME comes from its `path` output. In release.yml the step runs
  before the JDK 24/21 steps so Gradle keeps JAVA_HOME=21.
- docs: JDKCompatibility.md 28 EA row; readiness plan §10 row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
… at run time

Pinned identifiers (8.0.492-tem ... 25.0.3-tem) are only valid until SDKMAN
drops the superseded build: `sdk list java` already lists only 25.0.4-tem
for 25, and 27.ea.31-open was retired at 27 GA. Each pin is also stale
between a Temurin patch release and the Monday bump.

- continuous.yml, release.yml: matrix lanes are bare majors ('8' ... '27')
  resolved by scripts/resolve-sdkman-java.sh (Temurin preferred); the
  Gradle-side `sdk install java 11` line resolves the same way.
- v2-protocol-tests.yml: the `sdk` matrix column is gone; the JDK under test
  resolves from the major.
- docs/JDKCompatibility.md: table describes the run-time resolution; the
  Monday update-jdk-versions job has no pinned entries left to rewrite.
- readiness plan: pin-related rows updated.

Verified locally: the three workflows parse; the resolver maps 8/11/17/21/25/27
to the newest Temurin (or Oracle for 27) build of a canned API list;
update-jdk-versions.sh with a stubbed curl reports "up to date" and exits 1
without error.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
…s a build again

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
…he 27 lane

Run 35467559258 (lanes 8 and 11) showed that SDKMAN's `versions/all`
endpoint returns the same 40 curated identifiers as `sdk list java`: one
build per vendor and major, and on 2026-09-19 no Temurin 8, 11 or 21 build
at all, although the pinned 8.0.492-tem, 11.0.31-tem and 21.0.11-tem still
installed on the previous run. Its java.net entries (21.0.2-open,
25.0.2-open) are frozen GA builds. So SDKMAN cannot name the newest Temurin
build of an LTS major; actions/setup-java can (Adoptium API) and the build
job has used it for 24 and 28-ea all along.

- continuous.yml, release.yml: lanes 8/11/17/21/25 and 28-ea come from
  actions/setup-java (temurin, check-latest); the 27 lane carries
  `sdkman: true` and resolves through scripts/resolve-sdkman-java.sh
  (27.0.0-oracle until Temurin 27 is published). The Gradle-side
  `sdk install java 11` line is gone: toolchains come from the runner image
  and the foojay resolver, as in the build job.
- v2-protocol-tests.yml: JDK under test from setup-java, handed to Gradle via
  org.gradle.java.installations.paths.
- resolve-sdkman-java.sh: vendor order Temurin, Oracle, java.net.
- update-jdk-versions.sh: the build-JDK grep no longer aborts the script
  under pipefail when the line is absent; the job stays in place.
- docs: JDKCompatibility.md, workflows README, readiness plan row.

Verified locally: the three workflows parse; resolver 27 -> 27.0.0-oracle
(27.0.1-tem once listed); the Monday script exits "up to date" on these
workflows and still proposes bumps on the pinned develop workflows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical release-action pinning and moderate CI, JDK-matrix updater, and class-header initialization issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 2 Medium severity · 3 Low severity

Open (6)
What changed in this PR

Prepares BTrace 3.0.0 by improving release automation, Java compatibility, container publishing, and documentation.

Changes:

  • Fixes release tooling, JDK matrices, warning gates, and GHCR publishing.
  • Raises ASM support through Java 27 and adds ClassFile API handling for newer classes.
  • Updates tutorials, architecture docs, release notes, policies, and blog drafts.
File Summary
SECURITY.md Adds supported-version policy.
scripts/​verify-jdk-warning.sh Verifies warning suppression and once-only behavior.
scripts/​update-jdk-versions.sh Updates JDK matrix handling; a moderate quote-normalization issue remains.
scripts/​resolve-sdkman-java.sh Resolves SDKMAN Java lanes.
scripts/​release.sh Reads release versions from the root Gradle configuration.
scripts/​release-gates/​JdkWarningProbe.java Tests repeated warning behavior.
README.md Updates Java, download, Docker, and contribution guidance.
internal/​tutorial-assets/​scripts/​v6-mcp-claude.md Updates MCP tutorial references.
internal/​plans/​2026-09-19-v3.0.0-final-readiness.md Records readiness evidence; nit inconsistencies remain in release guidance.
internal/​plans/​2026-07-11-v3.0.0-tutorials-plan.md Updates tutorial-plan status.
internal/​plans/​2026-07-11-v3.0.0-release-plan.md Updates release-plan status.
internal/​blog-drafts/​B9-under-the-hood-v2-wire-protocol.md Updates protocol and benchmark references.
internal/​blog-drafts/​B8-btrace-everywhere-fat-agents-docker-k8s.md Updates fat-agent and GHCR guidance.
internal/​blog-drafts/​B7-we-gave-claude-a-jvm-debugger.md Updates external MCP guidance.
internal/​blog-drafts/​b6-llm-observability.md Updates release references.
internal/​blog-drafts/​b5-write-your-own-extension.md Corrects extension guidance.
internal/​blog-drafts/​b4-extensions-and-permissions.md Updates permission examples.
internal/​blog-drafts/​b3-flat-dsl.md Clarifies TLS limitations.
internal/​blog-drafts/​B2-debug-a-live-jvm-in-one-line.md Corrects detach and JBang guidance.
internal/​blog-drafts/​B10-how-btrace-stays-ahead-of-the-jdk.md Updates ASM and Java support details.
internal/​blog-drafts/​B1-btrace-3.0-everything-new.md Updates release and migration guidance.
integration-tests/​src/​test/​java/​tests/​ClassFileApiTests.java Moves ClassFile API coverage to JDK 28+.
integration-tests/​src/​test/​java/​resources/​MainJdkApi.java Updates test documentation.
integration-tests/​src/​test/​btrace/​ClassFileApiEntryTest.java Updates fixture documentation.
docs/​tutorials/​README.md Reframes MCP tutorial scope.
docs/​tutorials/​demo/​k8s-sidecar-pod.yaml Uses the GHCR image.
docs/​tutorials/​demo/​Dockerfile.k8s-sidecar-demo Uses the GHCR image.
docs/​tutorials/​09-kubernetes-sidecar.md Fixes Kubernetes and fat-agent instructions.
docs/​tutorials/​08-fat-agent.md Corrects packaging and permission behavior.
docs/​tutorials/​07-llm-observability.md Updates prerequisites and versions.
docs/​tutorials/​04-extensions-and-permissions.md Updates permission output.
docs/​tutorials/​03-upgrade-from-2x.md Makes migration steps executable.
docs/​tutorials/​01-first-trace-in-2-minutes.md Corrects cleanup instructions.
docs/​Troubleshooting.md Updates Java support range.
docs/​releasing.md Documents release and GHCR procedures.
docs/​ReleaseChecklist.md Updates the JDK release matrix.
docs/​README.md Fixes links and support information.
docs/​QuickReference.md Updates Docker and Java references.
docs/​Migration-2.x-to-3.0.md Documents prepared-mode and permission changes.
docs/​JDKCompatibility.md Documents dynamic JDK provisioning.
docs/​GettingStarted.md Updates downloads and packaging guidance; a nit mismatch remains.
docs/​FAQ.md Updates support and contribution information.
docs/​BTraceTutorial.md Corrects permission and backend guidance; a nit contradiction remains.
docs/​architecture/​InstrumentationBackends.md Updates backend boundaries; a nit threshold mismatch remains.
docs/​architecture/​fat-agent-plugin.md Corrects fat-agent packaging guidance.
docker/​README.md Migrates image references to GHCR.
common.gradle Replaces removed Gradle execution APIs.
btrace-dist/​build.gradle Updates Docker image tags.
btrace-core/​src/​main/​resources/​io/​btrace/​core/​messages.properties Corrects JAR names in usage text.
btrace-agent/​src/​test/​java/​io/​btrace/​instr/​ClassHeaderReaderTest.java Tests class-header dispatch.
btrace-agent/​src/​test/​java/​io/​btrace/​instr/​BackendSelectorTest.java Tests backend boundaries.
btrace-agent/​src/​test/​java/​io/​btrace/​instr/​AsmInstrumentationBackendTest.java Tests the Java 27 ASM ceiling.
btrace-agent/​src/​main/​java24/​io/​btrace/​instr/​ClassFileApiLinkerGuard.java Updates backend boundary documentation.
btrace-agent/​src/​main/​java24/​io/​btrace/​instr/​ClassFileApiClassHeaderReader.java Adds ClassFile API header reading.
btrace-agent/​src/​main/​java24/​io/​btrace/​instr/​ClassFileApiBackend.java Updates backend documentation.
btrace-agent/​src/​main/​java/​io/​btrace/​instr/​LinkerInstrumentor.java Updates high-version routing documentation.
btrace-agent/​src/​main/​java/​io/​btrace/​instr/​InstrumentationBackend.java Updates SPI documentation; a nit boundary mismatch remains.
btrace-agent/​src/​main/​java/​io/​btrace/​instr/​ClassInfo.java Uses version-aware header parsing.
btrace-agent/​src/​main/​java/​io/​btrace/​instr/​ClassHeaderReader.java Adds adaptive header dispatch; a moderate publication-order issue remains.
btrace-agent/​src/​main/​java/​io/​btrace/​instr/​ClassHeader.java Adds shared class metadata.
btrace-agent/​src/​main/​java/​io/​btrace/​instr/​BTraceTransformer.java Updates verification-boundary documentation.
btrace-agent/​src/​main/​java/​io/​btrace/​instr/​AsmInstrumentationBackend.java Raises the ASM ceiling to 71.
btrace-agent/​src/​main/​java/​io/​btrace/​agent/​Client.java Corrects permission error guidance.
btrace-agent/​build.gradle Runs backend tests on JDK 28.
.github/​workflows/​v2-protocol-tests.yml Uses dynamic test JDKs; a moderate Gradle-JVM setup issue remains.
.github/​workflows/​release.yml Adds GHCR publishing; a critical mutable-action pinning issue remains.
.github/​workflows/​README.md Updates workflow documentation; nit matrix and JVM descriptions remain.
.github/​workflows/​continuous.yml Adds rolling JDK lanes and JDK 28 EA; a moderate updater parsing issue remains.
.github/​workflows/​codeql-analysis.yml Excludes unavailable JDK 28-only tests.
.github/​release-notes/​v3.0.0.md Adds curated release notes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/release.yml
Comment thread .github/workflows/v2-protocol-tests.yml Outdated
Comment thread scripts/update-jdk-versions.sh
Comment thread .github/workflows/README.md Outdated
Comment thread docs/architecture/InstrumentationBackends.md
…ater quotes, stale ceiling text)

- release.yml: pin docker/setup-qemu-action (v3.7.0), docker/setup-buildx-action
  (v3.12.0) and docker/login-action (v3.7.0) to commit SHAs from the official
  repositories' tags, like every other third-party action in the workflow.
- v2-protocol-tests.yml: the JDK-under-test step ran after the JDK 21 step,
  so setup-java left JAVA_HOME on the matrix JDK and Gradle 9 would start on
  Java 11. The test JDK is installed first and JDK 21 last, unconditionally,
  so JAVA_HOME always hosts the Gradle daemon.
- update-jdk-versions.sh: strip quotes around matrix entries so a quoted
  pinned identifier is recognised again (verified with a stubbed API: quoted
  pins are bumped, the current un-pinned matrix reports "up to date").
- InstrumentationBackend.java javadoc, docs/architecture/InstrumentationBackends.md:
  ASM ceiling text was still 69; now 71 (Java 27), matching MAX_ASM_MAJOR_VERSION.
- .github/workflows/README.md: build job JDKs and the full test matrix.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Two moderate findings remain in concurrent header-reader initialization and legacy release version lookup, plus a selector documentation nit.

Review effort: Lite
Findings: None

Resolved since last review (6)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Publish ClassFile API result before marking initialization complete

btrace-agent/​src/​main/​java/​io/​btrace/​instr/​ClassHeaderReader.java:113

classFileApiReadAttempted is set to true before loadClassFileApiRead() publishes its result. If another class transformation reaches this method during that load, it takes the return null fast path and ClassInfo marks the hierarchy unavailable, so type-constrained/CALL probes can be dropped transiently on the first newer-class read. Publish the method before marking initialization complete (or make readers wait) so concurrent callers observe the same result.

…empted" flag; legacy version lookup in release.sh

Copilot's second pass found that ClassHeaderReader set the volatile
`classFileApiReadAttempted` flag before storing the loaded Method, so a
concurrent caller reading the flag outside the lock could see "attempted"
with a null reference and treat the ClassFile API as unavailable for one
transformation (ClassInfo then marks the hierarchy unavailable and drops
type-constrained/CALL probes). LinkerInstrumentor and BackendSelector carry
the same double-checked pattern with the same ordering; all three now store
the reference first and set the flag last, so a reader that observes the
flag observes the final reference (volatile happens-before).

BackendSelector.select javadoc still said "<= 69 (Java 25)"; it now refers
to MAX_ASM_MAJOR_VERSION (71, Java 27).

scripts/release.sh: the release-branch preview read `version = '...'` from
build.gradle only; release branches cut before c354f12 (2026-04-22, the
2.2.x line) carry `project.version = '...'` in common.gradle and showed an
empty version. The lookup now tries both files and both spellings
(verified: origin/release/2.2.6 -> 2.2.6, HEAD -> 3.0.0-SNAPSHOT).

Verified: :btrace-agent:spotlessCheck; ClassHeaderReaderTest (5) and
BackendSelectorTest (4) pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved legacy release-version handling and multi-permission grant parsing issues block approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)

Comment thread scripts/release.sh Outdated
jbachorik and others added 2 commits September 19, 2026 21:30
…en parsing javaagent args

Main.loadArgs split the whole `-javaagent:btrace.jar=...` string on commas
and then read `key=value` pairs, so the documented `grant=NETWORK,THREADS`
(README, BTraceTutorial.md, messages.properties, the extension example
READMEs and the "permissions not granted" hint in Client) granted only
NETWORK: THREADS became a bare key. The same applied to deny=,
allowExtensions= and denyExtensions=.

parseAgentArgs (extracted from loadArgs, package-private for tests) now
re-attaches a bare token that directly follows one of those list-valued keys
to that key's list; `help` keeps working as a bare key and a bare token
after a scalar key still becomes a key, as before.

MainTest covers grant across a following scalar key, all four list keys,
the bare-token cases and null/empty input.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
…e on 2.2.x branches

The `patch` flow read `version = '...'` from build.gradle at the source ref
only; release branches cut before c354f12 (2026-04-22) keep
`project.version = '...'` in common.gradle, so `release.sh patch
release/2.2._` kept the checkout's version and would compute the wrong
release. A shared version_at_ref() now tries both files and both spellings
(verified: origin/release/2.2.6 -> 2.2.6, origin/release/2.2.4 -> 2.2.4,
HEAD -> 3.0.0-SNAPSHOT).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Critical release-workflow and agent-option parsing issues remain in this broad release-readiness change.

Review effort: Lite
Findings: 2 High severity

Open (2)
Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Low severity Resolve conflicting privileged extension guidance

docs/​tutorials/​08-fat-agent.md:236

This corrected section says embedded privileged extensions remain gated, but the canonical docs/BTraceTutorial.md:2037 still says the opposite: “embedding a privileged extension is the grant.” That conflicting guidance can cause users to deploy a fat agent without allowExtensions/allowPrivileged; update the remaining tutorial claim as part of this correction.

Comment thread .github/workflows/release.yml Outdated
Comment thread btrace-agent/src/main/java/io/btrace/agent/Main.java Outdated
jbachorik and others added 3 commits September 19, 2026 21:51
…agent args

loadBundledProbes splits `probes=` on commas and the docs write
`probes=DriverTracer,ExecutorTracer`, but the argument splitter turned the
second name into a bare key, so only the first bundled probe loaded. PROBES
joins the list-valued keys; MainTest covers it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
…hecked-out ref

All four version edits in release.yml rewrote `version = '...'` in
build.gradle only. 2.2.x release branches keep `project.version = '...'` in
common.gradle, so a patch release from such a branch would change nothing
and the following commit would fail with nothing to commit. Each step now
picks build.gradle when it carries a `version = '...'` line and
common.gradle otherwise, rewrites `(project.)?version = '...'`, and the
commit steps add that file (passed through GITHUB_ENV). Verified the snippet
against HEAD's build.gradle and origin/release/2.2.6's common.gradle.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1
Lesson 14 said embedding a privileged extension "is the grant"; Tutorial 8
and the code say otherwise (the fat-agent plugin copies
BTrace-Extension-Permissions into the embedded descriptor,
EmbeddedExtensionRepository parses it, ExtensionBridgeImpl refuses the
extension unless allowExtensions/allowPrivileged is set). Lesson 14 now
matches.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qpfQbkp5ASygBF8WncoL1

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

One or more issues must be addressed before approval.

Review effort: Lite
Findings: None

Resolved since last review (2)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants