Skip to content

refactor(plugins): group skills by trigger, not by topic - #3

Merged
jbachorik merged 3 commits into
agent/btrace-legacy-libs-migrationfrom
agent/move-authoring-skills
Jul 28, 2026
Merged

refactor(plugins): group skills by trigger, not by topic#3
jbachorik merged 3 commits into
agent/btrace-legacy-libs-migrationfrom
agent/move-authoring-skills

Conversation

@jbachorik

Copy link
Copy Markdown
Contributor

Stacked on #2 — base is that branch, so this diff is the move alone.

Split by trigger, not by topic

btrace-extensions-and-permissions answers "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 an
incident.

  • 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-migration stays 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-permissions by name. Across plugins that is not loadable for someone who
installed 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.sh passes: 13 skills, 2 plugins, 8 eval cases. No eval case
referenced the moved skill by name, and requiredSkills resolves against any plugin regardless.

jbachorik and others added 2 commits July 28, 2026 11:52
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
jbachorik merged commit 97a1d73 into agent/btrace-legacy-libs-migration Jul 28, 2026
2 checks passed
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.

1 participant