refactor(plugins): group skills by trigger, not by topic - #3
Merged
jbachorik merged 3 commits intoJul 28, 2026
Merged
Conversation
btrace-extensions-and-permissions answers "should this be an extension, and what may it do" -- a question asked while building something, not while diagnosing a running system. It sat in btrace-observability, whose coordinator and README address an operator working an incident. Split the plugins by what prompts the reader instead: btrace-observability for "something is wrong right now", btrace-development for "I am building something". Extensions as a topic then spans both, which is correct -- btrace-legacy-libs-migration stays in observability because its trigger really is breakage, someone arriving mid-incident after custom classes stopped resolving on upgrade. Moving it costs one cross-plugin reference: the migration skill pointed at this skill by name, which a reader with only btrace-observability installed could not load. That pointer now names the plugin rather than instructing a load. This is the coordinator's single-plugin routing limit showing through, and it is the argument against splitting further. Done now because neither plugin is published yet, so no installed configuration moves underneath anyone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
jbachorik
merged commit Jul 28, 2026
97a1d73
into
agent/btrace-legacy-libs-migration
2 checks passed
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.
Stacked on #2 — base is that branch, so this diff is the move alone.
Split by trigger, not by topic
btrace-extensions-and-permissionsanswers "should this be an extension, and what may it do".That is asked while building something, not while diagnosing a running system. It sat in
btrace-observability, whose coordinator and README are written for an operator working anincident.
btrace-observability— "something is wrong right now"btrace-development— "I am building something"Extensions as a topic then spans both plugins, which is the right outcome.
btrace-legacy-libs-migrationstays in observability because its trigger genuinely is breakage:someone arrives mid-incident because custom classes stopped resolving after an upgrade.
The cost, stated plainly
The migration skill previously ended by instructing the reader to load
btrace-extensions-and-permissionsby name. Across plugins that is not loadable for someone whoinstalled only observability, so the pointer now names the plugin instead of instructing a load.
That is the coordinator's single-plugin routing limit showing through, and it is the argument
against splitting any further.
Timing
Neither plugin is published yet, so nothing moves underneath an installed configuration. This gets
cheaper to do now and more awkward later.
Verification
scripts/validate-marketplace.shpasses: 13 skills, 2 plugins, 8 eval cases. No eval casereferenced the moved skill by name, and
requiredSkillsresolves against any plugin regardless.