Skip to content

fix(extensions): report the manifest's service list and drop the unread descriptor reader#923

Open
jbachorik wants to merge 1 commit into
developfrom
agent/issue-919-metadata-consistency-test
Open

fix(extensions): report the manifest's service list and drop the unread descriptor reader#923
jbachorik wants to merge 1 commit into
developfrom
agent/issue-919-metadata-consistency-test

Conversation

@jbachorik

@jbachorik jbachorik commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

The bug

btracex inspect reported the wrong service list for every one of the seven bundled
extensions
:

$ btracex inspect btrace-statsd
Extension: btrace-statsd
Version  : 3.0.0-SNAPSHOT
Privileged: true
Required : [NETWORK]
Services : org.slf4j.spi.SLF4JServiceProvider,javax.annotation.processing.Processor   # <-- wrong

ExtensionInspector.readServices listed the filenames under META-INF/services/ in the
impl JAR. Those files come from shaded dependencies, not from the extension. The service list
now comes from the API JAR's BTrace-Extension-Services attribute — the same source
ExtensionMetadata reads when the runtime loads and permission-checks an extension.

This reaches users through btracex inspect <path>, btracex install, and the repo browser.

docs/tutorials/04 and 07 already documented the correct output, which is what confirms this
was a regression rather than intent. docs/tutorials/06 documented the defect as expected
behaviour
and is rewritten.

Removing the third descriptor reader

ExtensionMeta was a third reader of extension identity that nothing reached. Its only caller
discovered extension classes through a META-INF/services/io.btrace.core.extensions.Extension
entry that no packaged extension contains, so it always returned an empty list; four of its six
getters had no callers at all. It cannot be revived as-is either: BTrace-Extension-Services
names the API interface, while the Extension subclass lives only in the impl JAR, so nothing
yields the Class<? extends Extension> its factory requires.

This is what #919 asked for — "decide the single source of truth". Two formats are live and stay:
the manifest, which is authoritative, and package-level @ExtensionDescriptor, whose
permissions the Gradle plugin still asserts against the manifest at build time. The third is
gone.

ExtensionMeta is public and ships unmasked in io.btrace:btrace, so it would be compilable
API — but no 3.0.0 has been released (latest tag v2.2.6, and the io.btrace.* namespace is
itself new in 3.0; 2.x used org.openjdk.btrace). Removing it now is free; after 3.0.0 it would
not be. There is no CHANGELOG in this repo, so this note is the record of the removal.

The TUI carried a verbatim copy of the same reader feeding a field nothing displayed — deleted.
Its hand-maintained list of privileged permission names now defers to Permission.isPrivileged().

Evidence

Before/after, driving the real CLI over the staged distribution. Only the Services line moved
Version, Privileged and Required are byte-identical for all seven:

Extension Services : after
btrace-contracts io.btrace.contracts.ContractService
btrace-gpu-bridge io.btrace.gpu.GpuBridgeService
btrace-llm-trace io.btrace.llm.LlmTraceService
btrace-metrics io.btrace.metrics.MetricsService
btrace-rag-quality io.btrace.rag.RagQualityService
btrace-statsd io.btrace.statsd.Statsd
btrace-utils io.btrace.utils.PrinterService

Verified the new tests fail against develop's sources and pass with the fix: 5 of 11
ExtensionInspectorTest cases and BTraceJarPackagingTest.inspectionReportsWhatTheManifestDeclares
go red before, green after.

:btrace-dist:build, :btrace-core:test, :btrace-client:test and spotlessCheck all pass.

Tests

  • BTraceJarPackagingTest.inspectionReportsWhatTheManifestDeclares drives the real btracex inspect entry point and asserts reported id, version, services and permissions match the
    manifest, for every allow-listed extension. This is [3.0] Reconcile the three coexisting extension descriptor formats #919's last checklist item.
  • BTraceJarPackagingTest.extensionManifestsAreSelfConsistent pins the manifest invariants
    (-Id = directory name, -Version = distribution version, -Impl resolves, declared service
    classes present in the API JAR).
  • ExtensionInspectorTest gains the services-from-manifest cases, plus coverage for the
    @ServiceDescriptor permission merge — which this change leaves as the only annotation-based
    contributor to the reported permission set, and which had no test at all.
  • inspectExtensionWithPermissions was previously vacuous: its fixture wrote
    META-INF/btrace/permissions.properties while the reader looks for
    META-INF/btrace-extension.properties, so the privileged flag never reached the inspector.
    The fixture now emits the manifest attribute, making privileged detection genuinely tested.

Deliberately not fixed

  • ExtensionMetadata.scanServicesDirectory reads services-file contents (implementation
    FQCNs) rather than names, so the runtime's service list for examples/btrace-hadoop includes an
    impl class. Pre-existing, already recorded at
    internal/analysis/2026-07-04-core-runtime-analysis.md:383, and out of scope here.
  • Out-of-contract behaviour change: anyone who hand-authors
    META-INF/services/io.btrace.core.extensions.Extension in an impl module loses the
    @ExtensionDescriptor permission merge in inspection output. No supported build path emits that
    file — the Gradle plugin never writes it.

Incidental

Three documents carried a stale Required : [THREADS] for btrace-metrics, which reports
[CLASSLOADER,REFLECTION,THREADS] (ExtensionReport.toString sorts). Wrong on develop today,
independent of this change; corrected while editing the same lines.

Fixes #919


This change is Reviewable

…ad descriptor reader

`btracex inspect` reported every shipped extension's services as
`org.slf4j.spi.SLF4JServiceProvider,javax.annotation.processing.Processor`.
`ExtensionInspector.readServices` listed the *filenames* under `META-INF/services/`
in the impl JAR, which are the SPI files that shading leaves behind from
dependencies, not the extension's own services. All seven bundled extensions were
affected. The service list now comes from the API JAR's `BTrace-Extension-Services`
manifest attribute -- the same source `ExtensionMetadata` reads when the runtime
loads and permission-checks an extension, so inspection cannot disagree with what
the runtime enforces.

`ExtensionMeta` was a third reader of extension identity that nothing reached: its
only caller discovered extension classes through a
`META-INF/services/io.btrace.core.extensions.Extension` entry that no packaged
extension contains, so it always returned an empty list. Its four identity getters
had no callers at all. Removing it leaves two live descriptor sources: the manifest,
which is authoritative, and package-level `@ExtensionDescriptor`, whose `permissions`
the Gradle plugin still asserts against the manifest at build time.

The TUI carried a verbatim copy of the same service reader feeding a field nothing
displayed; it is gone. Its hand-maintained list of privileged permission names now
defers to `Permission.isPrivileged()`, and uppercases with `Locale.ROOT` so a Turkish
locale cannot turn `reflection` into a name `valueOf` rejects.

Inspection output is otherwise unchanged: `Version`, `Privileged` and `Required` are
byte-identical across all seven extensions before and after.

Tutorials 04 and 07 already documented the correct service list; tutorial 06
explained the defect as intended behaviour and is rewritten. Three documents also
carried a stale `Required : [THREADS]` for btrace-metrics, which reports
`[CLASSLOADER,REFLECTION,THREADS]`.

Fixes #919
Copilot AI review requested due to automatic review settings July 26, 2026 20:01

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.

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] Reconcile the three coexisting extension descriptor formats

2 participants