Skip to content

feat(tracing): collect skill usage events - #6727

Open
joaomdmoura wants to merge 1 commit into
mainfrom
feat/trace-skill-usage-events
Open

feat(tracing): collect skill usage events#6727
joaomdmoura wants to merge 1 commit into
mainfrom
feat/trace-skill-usage-events

Conversation

@joaomdmoura

@joaomdmoura joaomdmoura commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

The problem

#6652 added SkillUsedEvent and deliberately shipped no listener wiring ("No new listener/tracing wiring — the event flows through the existing bus"). Nothing subscribes to it, so it reaches no collector and never lands in a trace.

The trace listener subscribes to the five setup events:

event when
SkillDiscoveryStartedEvent scanning a dir
SkillDiscoveryCompletedEvent scan done
SkillLoadedEvent metadata parsed
SkillActivatedEvent promoted to INSTRUCTIONS
SkillLoadFailedEvent parse/load failed

None of them answers the question skills observability exists for. Activation is the closest proxy and it is idempotent, fired once at setup — an agent using a skill across twenty turns produces exactly one event.

The fix

Subscribe to SkillUsedEvent. It is the only runtime signal and the only one that re-fires per execution, so a trace can attribute usage to an agent and a task.

@event_bus.on(SkillUsedEvent)
def on_skill_used(source: Any, event: SkillUsedEvent) -> None:
    self._handle_action_event("skill_used", source, event)

Tests

tests/tracing/test_skill_used_tracing.py — usage reaches the collector, every use is collected (not collapsed), and the setup events still are. Verified they fail with the subscription removed.

Found while wiring these events through to AMP traces, where the same gap exists on the enterprise listener.

🤖 Generated with Claude Code


Note

Low Risk
Observability-only wiring plus unit tests; no changes to skill execution or auth/data paths.

Overview
Traces can now record per-execution skill usage, not just one-time setup signals like discovery, load, and activation.

TraceCollectionListener subscribes to SkillUsedEvent and forwards each emission as a skill_used action event via _handle_action_event, matching the other skill handlers. That closes a gap where SkillUsedEvent was emitted at runtime but never reached the trace collector, so repeated use of the same skill could not be attributed to tasks.

New tests in test_skill_used_tracing.py assert that skill_used is collected, that multiple uses produce multiple trace events (unlike idempotent activation), and that existing setup events such as skill_activated still work.

Reviewed by Cursor Bugbot for commit c66d958. Bugbot is set up for automated code reviews on this repo. Configure here.

PR #6652 added SkillUsedEvent but deliberately shipped no listener wiring,
so the event reached no collector. The trace listener subscribed to the five
setup events -- discovery, load, activation, failure -- and none of them can
answer the question skills observability is for: activation is idempotent and
fires once at setup, so an agent using a skill across twenty turns produces
exactly one event.

SkillUsedEvent is the only runtime signal and the only one that re-fires per
execution. Subscribing to it lets a trace attribute skill usage to an agent
and a task.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 30, 2026 07:16

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.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Skill usage events are now registered with TraceCollectionListener and emitted as "skill_used" trace events. New tests verify single and repeated skill usage collection while preserving "skill_activated" tracing.

Changes

Skill usage tracing

Layer / File(s) Summary
Register skill usage tracing
lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py
SkillUsedEvent is registered with the event bus and forwarded to _handle_action_event using the "skill_used" type.
Validate emitted trace events
lib/crewai/tests/tracing/test_skill_used_tracing.py
Tests verify single and repeated SkillUsedEvent emissions and confirm SkillActivatedEvent remains collected.

Sequence Diagram(s)

sequenceDiagram
  participant SkillUsedEvent
  participant CrewAIEventsBus
  participant TraceCollectionListener
  SkillUsedEvent->>CrewAIEventsBus: emit skill usage event
  CrewAIEventsBus->>TraceCollectionListener: invoke registered handler
  TraceCollectionListener->>TraceCollectionListener: handle as "skill_used"
Loading

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: tracing now collects skill usage events.
Description check ✅ Passed The description is directly related to the changeset and accurately explains the new tracing wiring and tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/trace-skill-usage-events

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c66d958. Configure here.


with patch.object(TraceCollectionListener, "_handle_action_event") as handled:
listener._register_action_event_handlers(bus)
yield bus, handled

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Test leaks handlers on shared bus

Medium Severity

The fixture claims an isolated bus, but CrewAIEventsBus() always returns the process-wide singleton. _register_action_event_handlers therefore attaches many handlers (including the new skill_used one) with no cleanup via scoped_handlers, so they remain after the patch ends and can fire against a listener created only with __new__ (no batch_manager). That risks flaky failures and handler errors in other tests.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c66d958. Configure here.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
lib/crewai/tests/tracing/test_skill_used_tracing.py (1)

21-33: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the attribution arguments are forwarded.

The single-use test emits source=None and checks only the event type, so a regression that drops or replaces the source or SkillUsedEvent would still pass. Use a representative source and assert ("skill_used", source, event) was forwarded to the action handler.

Suggested assertion
-        bus.emit(
-            None,
-            SkillUsedEvent(
+        source = object()
+        event = SkillUsedEvent(
                 skill_name="pdf-processing",
                 skill_path=Path("/skills/pdf-processing"),
-            ),
         )
+        bus.emit(source, event)
         bus.flush()
 
-        assert "skill_used" in _event_types(handled), (
-            "SkillUsedEvent was emitted but the trace listener ignored it"
-        )
+        assert handled.call_args.args == ("skill_used", source, event)

As per coding guidelines, tests should focus on observable behavior rather than only implementation details.

Also applies to: 40-51

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/tests/tracing/test_skill_used_tracing.py` around lines 21 - 33,
Update the single-use skill tracing test and the related test covering lines
40–51 to emit a representative non-None source and retain the SkillUsedEvent
instance. Assert the mocked _handle_action_event receives ("skill_used", source,
event), verifying both attribution arguments are forwarded unchanged rather than
checking only the event type.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/crewai/tests/tracing/test_skill_used_tracing.py`:
- Around line 21-33: Update the single-use skill tracing test and the related
test covering lines 40–51 to emit a representative non-None source and retain
the SkillUsedEvent instance. Assert the mocked _handle_action_event receives
("skill_used", source, event), verifying both attribution arguments are
forwarded unchanged rather than checking only the event type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 84923888-8f08-4039-85a2-663823e763af

📥 Commits

Reviewing files that changed from the base of the PR and between 112762a and c66d958.

📒 Files selected for processing (2)
  • lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py
  • lib/crewai/tests/tracing/test_skill_used_tracing.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants