Skip to content

fix(extensions): report loader-visible metadata and refresh API version docs#922

Merged
jbachorik merged 2 commits into
developfrom
agent/issue-919-descriptor-reconciliation
Jul 26, 2026
Merged

fix(extensions): report loader-visible metadata and refresh API version docs#922
jbachorik merged 2 commits into
developfrom
agent/issue-919-descriptor-reconciliation

Conversation

@jbachorik

@jbachorik jbachorik commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

What does this change do?

Makes ExtensionMeta report the metadata the extension loader actually uses, removes a duplicated permission parser, and documents which parts of @ExtensionDescriptor do work.

Please read the severity note before reviewing — this is public-API hygiene and documentation, not a user-facing defect. #919 as originally filed overstated the impact and has been corrected.

The metadata fix

ExtensionMeta derived identity, version, description and permissions solely from a package-level @ExtensionDescriptor. That annotation is optional — four of the seven shipped extensions carry none, so metadata degraded to the implementation class's simple name with an empty version and an empty permission set, while the three that carry one state version = "1.0" for artifacts that are 3.0.0.

It now prefers the JAR manifest — the same source io.btrace.extension.impl.ExtensionMetadata parses when the runtime loads and permission-checks an extension — and consults the annotation only to fill gaps.

Severity: no observable behaviour changes

ExtensionInspector already read the manifest directly for the version and permissions it prints (readVersionFromJar, readPermissionsFromManifestOrProps on both JARs) and uses ExtensionMeta only as an additional permission contributor. ExtensionReport never prints ExtensionMeta's name or version.

Verified rather than assumed: packaged btrace-statsd (has a descriptor stating 1.0) and btrace-contracts (has none) were inspected with and without this change — output is byte-identical in both cases (Version: 3.0.0-SNAPSHOT, Required: [NETWORK] / [THREADS]).

ExtensionMeta.getName(), getVersion(), getDescription() and getMinBTraceVersion() have no callers in the codebase, which is why the wrong values were never noticed. The fix matters for external callers of this public API and for anyone who wires these accessors up in future.

Shared permission parsing

The permission-list parser existed twice, in different packages. It is promoted to PermissionSet.parse(String); the manifest path, the legacy properties path and inspection now share one definition.

@ExtensionDescriptor keeps its real job

The Gradle plugin reads its permissions member and fails the build when the manifest does not cover what the annotation requires (BTraceExtensionPlugin.groovy: "Manifest permissions missing annotated requirements"). That is a genuine assertion and is untouched. Its remaining members are propagated nowhere, so name() and version() gain defaults and the three package descriptors drop the version they were misstating.

There was never a three-way conflict over authority: the manifest is authoritative, the annotation is a build-time permission assertion, and the third "format" was dead metadata.

Docs

BTraceExtensionDevelopmentGuide.md and tutorials/06-write-your-own-extension.md now state which annotation member does work and which do not. The six remaining BTrace-API-Version: 2.3+ examples across ExtensionManifestFormat.md and ExtensionStorageDesign.md are updated to 3.0+, matching what the plugin emits (#918).

Related issue

Closes #918
Addresses #919

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:

@ExtensionDescriptor.name() and version() gain defaults, which is source- and binary-compatible; existing extensions that set them continue to compile. ExtensionMeta.from(Class) is retained and unchanged in signature, delegating to the new from(Class, Attributes) overload. No CLI, probe, protocol or packaging change.

Testing

./gradlew :btrace-core:test :btrace-client:test :btrace-agent:test spotlessCheck
./gradlew :btrace-extensions:btrace-metrics:test :btrace-extensions:btrace-statsd:test \
    :btrace-extensions:btrace-utils:test
./gradlew -p btrace-gradle-plugin test        # covers the permission-alignment check

Plus ext inspect run against packaged btrace-statsd and btrace-contracts archives, before and after, to confirm the output is unchanged.

New tests: ExtensionMetaTest (manifest precedence, id-as-name fallback, permissions for extensions with no package descriptor, blank-value handling, overload equivalence) and PermissionSetParseTest (separators, blank and null input, unknown names skipped rather than failing the list).

Notes for reviewers

  • The lenient handling of unknown permission names is deliberate and now tested: an extension built against a newer BTrace naming an unknown permission still loads with the permissions this runtime understands.
  • Unrelated oddity noticed while verifying, deliberately not touched: ext inspect reports Services: org.slf4j.spi.SLF4JServiceProvider,javax.annotation.processing.Processor for both extensions, which looks like unrelated META-INF/services entries leaking into the report. Worth a separate issue if that is not intended.

🤖 Generated with Claude Code


This change is Reviewable

…on docs

Closes #918. Addresses #919, with a correction to that issue's premise.

ExtensionMeta derived an extension's identity, version, description and
permissions solely from a package-level @ExtensionDescriptor. That annotation
is optional: four of the seven shipped extensions carry none, so the metadata
degraded to the implementation class's simple name with an empty version and an
empty permission set, and the three that do carry one stated version = "1.0"
while their artifacts are 3.0.0. ExtensionMeta now prefers the JAR manifest -
the same source io.btrace.extension.impl.ExtensionMetadata parses when the
runtime loads and permission-checks an extension - and consults the annotation
only to fill gaps.

This is a correctness fix to a public API, not to observable behaviour. The
inspection CLI already read the manifest directly for the version and
permissions it prints, and uses ExtensionMeta only as an additional permission
contributor, so its output is unchanged; verified by inspecting packaged
extensions with and without the change and diffing the result. ExtensionMeta's
name, version, description and minBTraceVersion accessors currently have no
callers at all, which is why the wrong values were never noticed.

The permission-list parser is promoted to PermissionSet.parse so the manifest
path, the legacy properties path and inspection share one definition instead of
restating it.

@ExtensionDescriptor keeps its role. The Gradle plugin reads its permissions
member and fails the build when the manifest does not cover what the annotation
requires, which is a genuine assertion worth keeping; its remaining members are
not propagated anywhere. name() and version() gain defaults so extensions can
omit what the build does not use, and the three package descriptors drop the
version they were misstating.

Docs: the extension development guide and the extension tutorial now say which
member does work and which do not, and the six remaining BTrace-API-Version
examples showing 2.3+ are updated to 3.0+ to match what the plugin emits.

Verified: btrace-core, btrace-client, btrace-agent, the three affected
extensions and the Gradle plugin test suites, spotlessCheck, and `ext inspect`
run against packaged btrace-statsd and btrace-contracts archives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 26, 2026 18:23

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 was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 26, 2026 18:37

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 was unable to review this pull request because the user who requested the review has reached their quota limit.

@jbachorik
jbachorik merged commit c806358 into develop Jul 26, 2026
15 checks passed
@jbachorik
jbachorik deleted the agent/issue-919-descriptor-reconciliation branch July 26, 2026 18:46
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.

[3.0] Refresh ExtensionManifestFormat doc examples from 2.3+ to 3.0+

2 participants