Skip to content

Add RFC-0009: Skill Tracing - #37

Draft
jwm4 wants to merge 6 commits into
mlflow:mainfrom
jwm4:add-skill-tracing-rfc
Draft

Add RFC-0009: Skill Tracing#37
jwm4 wants to merge 6 commits into
mlflow:mainfrom
jwm4:add-skill-tracing-rfc

Conversation

@jwm4

@jwm4 jwm4 commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

RFC-0009 proposes Skill Tracing: a SKILL span that links MLflow traces to the registered skill versions that were active when the trace was produced, so that adoption, deprecation impact, cost attribution, and regressions can be evaluated per skill.

Tracing was originally drafted as part of RFC-0008 (MVP Skill Registry, PR #26) and deferred out of that RFC. It now gets its own RFC rather than a follow-on PR.

The RFC covers three instrumentation paths: explicit (skill_context(), with a plain OpenTelemetry equivalent), automatic in agent frameworks (LangGraph, ADK, and others), and automatic in harnesses (Claude Code and others) via an install-time record. Harness installation commands are in scope to support that third path. User journeys span instrumentation, adoption measurement, deprecation impact, token cost attribution, evaluation lineage, and regression detection.

Numbering note

Extended Skill Bundles, previously RFC-0009 in PR #27, has been renumbered to RFC-0010. Skill tracing is the more urgent piece of work, so it takes the 0009 slot. The forward references in the merged RFC-0008 that point to "RFC-0009: Extended Skill Bundles" still link to PR #27 and will be corrected in a separate PR.

Posted by Bill Murdock with assistance from Claude Code.

jwm4 and others added 2 commits August 23, 2026 10:34
Placeholder for the skill tracing RFC. Tracing was drafted as part of
RFC-0008 (MVP Skill Registry) and deferred out of that RFC to keep the
MVP focused on the registry. This reserves the 0009 slot and carries
forward the open questions raised during RFC-0008 review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fill in the Summary, Motivation, and Open questions sections of the
skill tracing RFC, restoring and reworking the tracing content deferred
from RFC-0008. Eight user journeys cover the three instrumentation
paths (explicit, agent framework, harness) and the analysis workflows
they enable. Harness installation is now in scope for this RFC.
Detailed design and remaining sections are TBD.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jwm4

jwm4 commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Status as of 2026-08-23: per reviewer guidance on RFC-0008, this draft currently contains the summary, problem statement, and user journeys so the workflows can be agreed on before technical detail. Detailed design and the remaining sections are marked TBD, and they will not be worked on until all the key stakeholders have agreed on the summary, problem statement, and user journeys.

Remaining work:

  • Fill in summary, motivation, and user journeys
  • Stakeholder agreement on the above
  • Fill in detailed design and remaining sections
  • Resolve open questions (framework activation hooks first)
  • Mark ready for review

Posted by Bill Murdock with assistance from Claude Code.

jwm4 and others added 2 commits August 23, 2026 17:25
Resolve three open questions based on research into current MLflow
internals and framework documentation:

- Skill trace queries extend search_traces filter syntax rather than
  adding a skill-specific function; exact matching on span attributes
  becomes a store-level requirement.
- Automatic context capture follows the pattern MLflow already uses
  for other registry entities: resolving a skill records its identity,
  and autologgers pick it up when spans are created.
- Harnesses split into two tiers: those with MLflow-provided tracing
  integrations get full SKILL spans; those that export OpenTelemetry
  natively get their emitted spans annotated when the skill invocation
  is identifiable, with no spans created on their behalf.

ADK is recategorized accordingly: MLflow traces it by receiving its
native OpenTelemetry output, so it is handled like the equivalent
harnesses rather than via in-process instrumentation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
State both decisions in the journeys where they apply: the skill
context manager manages its span internally and does not expose it to
the caller, and the install-time record is per project or per user
with project entries taking precedence. Remove both from Open
questions, leaving OTel alignment and digest-based linking.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jwm4

jwm4 commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Status update: five of the seven open questions are now resolved in the draft, based on research into current MLflow internals and framework documentation.

  • Query API: skill trace queries extend the existing search_traces filter syntax rather than adding a skill-specific function. Exact matching on span attribute values becomes a store-level requirement of this RFC.
  • Automatic context capture: resolving a skill through MLflow records its identity, and autologgers pick it up when spans are created. This follows the pattern MLflow already uses for other registry entities.
  • Harnesses without in-process MLflow tracing: harnesses whose tracing integration is provided by MLflow get full SKILL spans. Harnesses that trace themselves through native OpenTelemetry export get the spans they already emit annotated with skill coordinates when the skill invocation is identifiable; MLflow does not create spans on their behalf. ADK is handled the same way, since MLflow traces it by receiving its OpenTelemetry output.
  • Span exposure: the skill context manager manages its span internally and does not expose it to the caller, so recorded coordinates cannot be altered after creation.
  • Install record location: project-scoped installs record in the project, user-scoped installs record in the user's MLflow configuration, and project entries take precedence.

Two questions remain open for discussion: OTel alignment (whether the mlflow.skill.* span attribute names are part of the public contract for non-MLflow instrumentation) and digest-based linking (best-effort digest recording and whether digest grouping should work across skill names).

Posted by Bill Murdock with assistance from Claude Code.

Bringing harness installation into this RFC is a significant
undertaking. Add an open question weighing full installation against
a narrower tracing-activation command that records the coordinates of
a skill the user installed themselves, and soften the summary's scope
statement to point at that discussion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
```python
from mlflow import MlflowClient

version = MlflowClient().get_skill_version_by_alias(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we skip the HTTP call and just put alias in the mlflow.genai.skill_context() call? I don't think you actually need to resolve the skill for the trace to link it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. link_skill() now accepts alias directly, so the caller writes no resolution call. I kept one property from the original design: the alias is resolved when the link is recorded and the trace stores the concrete version, which follows RFC-0008's convention that aliases are accepted as input everywhere but never stored in place of versions (the same rule its agent plugin member rows and alias URIs follow). A trace that stored @production would change meaning whenever the alias is repointed, which would undermine the deprecation and comparison journeys. So the lookup still happens under the hood for alias callers, but only for them, and only at link time.

Posted by Bill Murdock with assistance from Claude Code.

the trace tree annotated with their registry coordinates, and the
skill name links to the skill's registry detail page. When the
recorded coordinates do not resolve (the version was deleted, or the
trace came from a different workspace), the UI shows a "not found in

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A "Skills" tab on the trace like we have for "Links" would be nice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. The first journey's UI path now shows a "Skills" tab on the trace view alongside the existing linked-entity tabs, listing the linked skill versions with each linking to its registry detail page. It fits naturally now that the design records skills as trace-level links, the same shape the Links tab already renders.

Posted by Bill Murdock with assistance from Claude Code.

they do not capture is which governed, versioned skill was active during
any part of the run.

This RFC adds a `SKILL` span that carries registry coordinates

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not convinced SKILL should be a new span type. The RFC describes a skill as ambient context, while a span conventionally represents an operation with meaningful start/end, status, input, and output. I imagine that in many harnesses we can observe loading of a skill but cannot reliably determine when the skill stops influencing later calls, so parenting LLM/tool spans beneath it may assert stronger causality than the instrumentation can honestly record.

Could this follow the existing prompt pattern instead where it links the concrete skill version to the trace for governance and related-trace queries? We could also annotate the currently active or activation span when that is detected. For example, if a tool call loads the skill, we can add a linked attribute on the tool call span.

This better aligns with OTel's guidance on what a span should be:
https://opentelemetry.io/docs/specs/semconv/how-to-write-conventions/#defining-spans

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll admit I resisted this one. My mental model of skills is that they're closer to subroutines than to ambient context: a defined invocation with a beginning and an end. Some harnesses partly support that model: Claude Code delimits skill-driven turns and its forked skills are genuinely bounded operations, and Cursor scopes a manually invoked skill to a single message. But I looked at how activation is described across the rest of the ecosystem, and it leans your way: the Agent Skills spec defines activation as a one-way load with no counterpart, and for most harnesses I found an observable activation but nothing that marks the end of a skill's influence. I wouldn't claim that's conclusive (much of it is reading documentation, and silence isn't proof), but it's enough that the RFC shouldn't build its core model on a boundary most harnesses may not have.

So the latest revision adopts your approach: a trace-level link to the skill version following the prompt-linking pattern, plus an annotation on the activation span where one is identifiable. No new span type. The rejected span design is documented under Alternatives.

Posted by Bill Murdock with assistance from Claude Code.

```bash
mlflow autolog claude
```
3. Run the agent. When the harness loads an installed skill during a

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Claude MLflow hook will have that level of detail. Is the plan to parse tool call outputs for any indication that a tool read a skill file (e.g. cat <skill path>) from MLflow?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, essentially in the way you suggest. The MLflow Claude Code plugin already works by parsing the session transcript from its Stop hook, and skill activations are visible there today: a model-invoked skill appears as a Skill tool call whose input carries the skill name, and the tool result carries the resolved command name (the parser already keys on that field to filter skill content injection out of prompt detection). A user-invoked /skill-name takes a different shape (a command expansion with no Skill tool call), so the parser needs handling for that path too, which the detailed design will cover. And a skill run with context: fork already surfaces as a tool span with the subagent's spans nested under it. The revision states the detection approach in the harness journey: harness-native signals where they exist, and otherwise matching tool calls that read a skill's SKILL.md from its installed location, which is your cat <skill path> case.

Posted by Bill Murdock with assistance from Claude Code.

trace came from a different workspace), the UI shows a "not found in
registry" indicator rather than failing to render the span.

#### Trace skills loaded by an agent framework

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice if we can detect tool calls that use a script bundled in a skill and annotate the tool call span as having used the skill. This should be possible by matching the paths in the tool call to where MLflow installed skills.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adopted. The harness journey now specifies exactly this: tool calls that use a skill's bundled files, such as a script under the skill's directory, are annotated as skill usage by matching paths against the locations recorded at install time. The same location matching also serves as the activation-detection fallback (a tool call reading a skill's SKILL.md), so both ride one mechanism, and it has the property that it never fabricates usage: a path match is positive evidence the skill's files were touched.

Posted by Bill Murdock with assistance from Claude Code.

for spans it directly dominates) or inclusive is a design decision, not
a user choice.

#### Trace skill lineage to evaluation results

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The experiment UI shows "prompts" associated with the experiment. Should we consider adding a skills tab too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. The benchmark evaluation journey's UI path now includes a "Skills" tab on the experiment page, listing the skill versions linked from the experiment's traces, following the existing prompts tab.

Posted by Bill Murdock with assistance from Claude Code.

for spans it directly dominates) or inclusive is a design decision, not
a user choice.

#### Trace skill lineage to evaluation results

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the UI show the diff of skills used when comparing to evaluation runs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, added. The run-comparison view shows a diff of the linked skill versions alongside the metric deltas, so a change in outcomes is read against the change in skill configuration. It appears in both comparison journeys: the benchmark one, and a new journey comparing skill versions on production traffic, where one evaluation run is produced per skill version from the linked production traces and the diff makes the comparison legible.

Posted by Bill Murdock with assistance from Claude Code.

APIs. Filtering evaluation results directly by skill version, without
the intermediate trace lookup, is not part of this RFC.

#### Detect a regression after a skill update

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest consolidating this user journey with the one above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The lineage and regression journeys are merged into a single "Evaluate and compare skill versions on a benchmark" journey, which carries the lineage steps (evaluation result to linked skill versions and back) inside the comparison flow.

Posted by Bill Murdock with assistance from Claude Code.

@@ -0,0 +1,518 @@
# RFC 0009: Skill Tracing

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please make the document more concise? Have Claude determine the lowest value sections to cut. The user journeys section may be too exhaustive.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The revision trims throughout: the token-cost journey is dropped, lineage and regression are merged, the span-model justification in the summary is reduced to a statement of the design, and the nesting material is gone. One journey was added (comparing skill versions on production traffic, which I think earns its place as the production counterpart of the benchmark journey), and the document is still net shorter than before. If it still reads as too long, pointers to whichever sections feel lowest-value would help me cut further.

Posted by Bill Murdock with assistance from Claude Code.


1. Install the skill into the harness through MLflow:
```bash
mlflow skills install --skill-uri skills:/code-review@production \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should could probably use a bit more details. It's a bit overlooked in the current RFC.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expanded. The harness journey's run step now spells out what the autologger does: it identifies skill activations in the recorded conversation using the harness's own signals where they exist (a dedicated skill tool call, a slash-command invocation), falls back to matching tool calls that read a skill's SKILL.md from its installed location, annotates tool calls that use a skill's bundled files by the same location matching, and links using the install record with no registry call during the run.

Posted by Bill Murdock with assistance from Claude Code.

Replace the SKILL span model with trace-level links plus
activation-span annotation, following the prompt-linking pattern, per
review discussion on the span-vs-link question. skill_context()
becomes link_skill(), accepting version or alias with aliases
resolved at link time per RFC-0008 convention. Skill queries use a
skill filter over linked entities, mirroring the prompt filter, with
name-only and organization-qualified forms as proposed extensions.
The rejected span design is documented under Alternatives.

Journeys are consolidated from eight to seven: the token-cost journey
is dropped, lineage and regression merge into a benchmark evaluation
journey, the deprecation journey generalizes to security exposure,
and a new journey compares skill versions on production traffic.
Harness detection is specified as a ladder: native skill signals,
SKILL.md read matching, and bundled-file matching for usage
annotation. UI paths add a trace-view Skills tab, an experiment
Skills tab, and a linked-skill diff in run comparison.

Additional review fixes: organization on link_skill and filters, the
OTel receiver path requires coordinates in emitted spans, exposure
queries name their locations, and digest queries resolve through the
registry index. New open question on locally modified installs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants