feat(development): add the extension authoring skill - #4
Merged
jbachorik merged 1 commit intoJul 28, 2026
Merged
Conversation
Covers designing a new extension against a target library, which nothing in the suite addressed. It sits in btrace-development because the trigger is building something rather than diagnosing a running system, beside the permissions skill it depends on. The guidance is bounded by what @ExternalType can actually express, which is narrower than it first appears and is not stated anywhere an author would look: - The whole method signature must be nameable at compile time. Adapters are emitted with an exact MethodType from erased declared types and handed to findVirtual/findStatic, so any target method taking or returning a target-library type is unreachable. Overloads are rejected outright, and fields, constructors, instanceof and non-public members are unsupported. - Static dispatch resolves the owning class through the thread context class loader while virtual dispatch uses the receiver's defining loader. The defining loader is the documented guidance, and a context loader is whatever the application thread happens to have set. - Class resolution runs on every call. Only the method handle is cached, keyed by the resolved class, so a class that never resolves is never cached and the failure repeats at the probe call site as an undeclared exception. So the skill routes anything richer, and anything version-variant, to ClassLoadingUtil and MethodHandleCache, whose failures are deliberately not cached and therefore work as a capability check that recovers if the target appears later. The @ExternalType gaps are recorded upstream in btraceio/btrace#931. It also states the implementation naming rule as an exact name or a provider declaration, rather than treating the provider file as optional. Skipping it is what left the Spark example unable to resolve its own service.
Closed
8 tasks
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.
Closes btraceio/btrace#907. Stacked on #3, so this diff is the new skill alone.
Summary
Adds
btrace-extension-authoringtobtrace-development: designing a new extension against atarget library, framework, or runtime. #906's skill migrates an existing legacy integration; this
one starts from a target system with no integration yet, which is a build-time trigger, so it sits
beside
btrace-extensions-and-permissionsrather than in the incident plugin.Bounded by what
@ExternalTypecan actually expressThe issue names
@ExternalTypeas the technique for target interaction. Reading the adapteremitter first-hand, its reach is narrower than it looks, and the limits are not stated anywhere an
author would encounter them:
MethodTypefrom erased declared types and handed tofindVirtual/findStatic, so any targetmethod that takes or returns a target-library type is unreachable. Overloaded names are rejected
outright; fields, constructors,
instanceofand non-public members are unsupported; andpublicLookup()has no module read edge.receiver's defining loader. The defining loader is the documented guidance for target
interaction, and a context loader is whatever the application thread happens to have set —
frequently wrong under application servers, OSGi, and plugin loaders.
ClassValuecache holds only the method handle andis keyed by the resolved class, so a class that never resolves is never cached; the failure
repeats and reaches the probe frame as an undeclared
ClassNotFoundException.The skill therefore teaches the boundary and routes anything richer — and anything version-variant
— to
ClassLoadingUtilandMethodHandleCache. That cache never stores failures, by design, whichis what makes a caught lookup failure a usable capability check that recovers if the target appears
later. The gaps are recorded upstream as btraceio/btrace#931.
The implementation naming rule, stated as a rule
An earlier draft of this work argued the issue was wrong to call the
META-INF/servicesproviderdeclaration required. That was incorrect. Resolution is
ServiceLoaderfirst, then the exactname
<serviceInterfaceFqcn>Impl— so anapi/implpackage split, which the issue's own designrequirements encourage, needs the provider declaration.
Skipping it is precisely what left the Spark example unable to resolve its own service
(btraceio/btrace#930).
Not claimed
The issue's ten-step workflow exceeds what a skill in this corpus should carry. Project layout,
permission scanning, and packaging are cross-referenced to the neighbouring skills rather than
restated; fat agents stay with the startup and packaging guidance. What survives here is what
nothing else covers: the analysis-first gate, the
@ExternalTypeboundary and loader split, thehand-written escape hatch, the version strategy, and the implementation naming rule.
Verification
scripts/validate-marketplace.shpasses: 9 eval cases, 2 plugins, 14 skills. Every mechanism theskill asserts was read in the emitter and cache sources directly rather than taken from a summary.