Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Add telemetry events to track user behavior or system events in the Warp codebase. Use when instrumenting new features, debugging issues, or measuring product metrics.
.claude/skills/warpdotdev-add-telemetry/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | -8% | 0% |
| case-07 | ✗→✓ | ▲ Improved | -18% | 0% |
| case-08 | ✗→✓ | ▲ Improved | -6% | 0% |
| case-09 | ✗→✓ | ▲ Improved | -33% | 0% |
| case-10 | ✗→✓ | ▲ Improved | -32% | 0% |
Warp uses a trait-based telemetry system where feature-specific enums implement the TelemetryEvent trait. This approach keeps telemetry events organized by domain rather than in one giant enum.
Important: Before implementing telemetry, collaborate with the user to:
Adding telemetry code is straightforward, but designing meaningful instrumentation requires careful thought.
Find an existing feature-specific telemetry file (e.g., app/src/antivirus/telemetry.rs) or create a new one for your feature area.
Add a new variant to an enum that implements TelemetryEvent, or create a new enum:
rustuse serde_json::{json, Value}; use strum_macros::{EnumDiscriminants, EnumIter}; use warp_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc}; #[derive(Debug, EnumDiscriminants)] #[strum_discriminants(derive(EnumIter))] pub enum YourFeatureTelemetryEvent { ActionStarted { duration_ms: u64, }, ActionCompleted { success: bool, error: Option<String>, }, }
EnablementState allows you to control when events are sent:
EnablementState::Always - Always send the eventEnablementState::Flag(FeatureFlag::YourFeature) - Only send when the feature flag is enabledEnablementState::Channel(Channel::Dev) - Only send in specific build channelsrustimpl TelemetryEvent for YourFeatureTelemetryEvent { fn name(&self) -> &'static str { YourFeatureTelemetryEventDiscriminants::from(self).name() } fn payload(&self) -> Option<Value> { match self { Self::ActionStarted { duration_ms } => Some(json!({ "duration_ms": duration_ms, })), Self::ActionCompleted { success, error } => Some(json!({ "success": success, "error": error, })), } } fn description(&self) -> &'static str { YourFeatureTelemetryEventDiscriminants::from(self).description() } fn enablement_state(&self) -> EnablementState { YourFeatureTelemetryEventDiscriminants::from(self).enablement_state() } fn contains_ugc(&self) -> bool { match self { Self::ActionStarted { .. } => false, Self::ActionCompleted { .. } => false, } } fn event_descs() -> impl Iterator<Item = Box<dyn TelemetryEventDesc>> { warp_core::telemetry::enum_events::<Self>() } }
rustimpl TelemetryEventDesc for YourFeatureTelemetryEventDiscriminants { fn name(&self) -> &'static str { match self { Self::ActionStarted => "YourFeature.Action.Started", Self::ActionCompleted => "YourFeature.Action.Completed", } } fn description(&self) -> &'static str { match self { Self::ActionStarted => "User started the action", Self::ActionCompleted => "User completed the action", } } fn enablement_state(&self) -> EnablementState { match self { Self::ActionStarted | Self::ActionCompleted => EnablementState::Always, // Or gate behind a feature flag: // EnablementState::Flag(FeatureFlag::YourFeature) } } }
At the end of your telemetry module, register the event:
rustwarp_core::register_telemetry_event!(YourFeatureTelemetryEvent);
Use send_telemetry_from_ctx! in views or models with a ViewContext or ModelContext:
rustuse warp_core::send_telemetry_from_ctx; // In a view update or model method send_telemetry_from_ctx!( YourFeatureTelemetryEvent::ActionStarted { duration_ms: 150, }, ctx );
For code with only AppContext, use send_telemetry_from_app_ctx! instead.
Run Warp with the log_named_telemetry_events feature flag to see telemetry events logged to the console:
bashcargo run --features log_named_telemetry_events
contains_ugc() to true if the payload includes user-generated contentFeature.Action.ResultSee app/src/antivirus/telemetry.rs for a complete example of a feature-specific telemetry implementation.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 18,052 | 10,974 | -39% | 1 | 1 | 0% | 3,845 | 3,623 | -6% | 0 | 0 | — |
case-02 | fail→fail | 27,256 | 15,089 | -45% | 1 | 1 | 0% | 3,622 | 4,268 | +18% | 0 | 0 | — |
case-03 | fail→pass | 18,862 | 9,703 | -49% | 1 | 1 | 0% | 3,452 | 3,173 | -8% | 0 | 0 | — |
case-04 | pass→pass | 17,656 | 12,493 | -29% | 1 | 1 | 0% | 2,883 | 3,306 | +15% | 0 | 0 | — |
case-05 | pass→pass | 15,694 | 12,448 | -21% | 1 | 1 | 0% | 3,045 | 3,397 | +12% | 0 | 0 | — |
case-06 | pass→pass | 23,158 | 10,087 | -56% | 1 | 1 | 0% | 2,436 | 2,781 | +14% | 0 | 0 | — |
case-07 | fail→pass | 12,864 | 3,249 | -75% | 1 | 1 | 0% | 2,095 | 1,718 | -18% | 0 | 0 | — |
case-08 | fail→pass | 13,167 | 5,002 | -62% | 1 | 1 | 0% | 2,166 | 2,031 | -6% | 0 | 0 | — |
case-09 | fail→pass | 15,108 | 2,557 | -83% | 1 | 1 | 0% | 2,343 | 1,566 | -33% | 0 | 0 | — |
case-10 | fail→pass | 15,181 | 2,567 | -83% | 1 | 1 | 0% | 2,205 | 1,495 | -32% | 0 | 0 | — |
case-11 | pass→pass | 7,809 | 3,936 | -50% | 1 | 1 | 0% | 1,237 | 1,698 | +37% | 0 | 0 | — |
case-12 | fail→pass | 12,458 | 5,181 | -58% | 1 | 1 | 0% | 1,937 | 1,861 | -4% | 0 | 0 | — |
case-13 | pass→pass | 8,505 | 3,425 | -60% | 1 | 1 | 0% | 1,292 | 1,653 | +28% | 0 | 0 | — |
case-14 | pass→pass | 6,688 | 3,783 | -43% | 1 | 1 | 0% | 990 | 1,729 | +75% | 0 | 0 | — |
case-15 | fail→pass | 8,058 | 4,375 | -46% | 1 | 1 | 0% | 1,237 | 1,718 | +39% | 0 | 0 | — |
case-16 | fail→pass | 14,416 | 2,557 | -82% | 1 | 1 | 0% | 2,433 | 1,565 | -36% | 0 | 0 | — |
case-17 | pass→pass | 13,122 | 4,844 | -63% | 1 | 1 | 0% | 1,794 | 1,966 | +10% | 0 | 0 | — |
case-18 | pass→pass | 10,097 | 4,227 | -58% | 1 | 1 | 0% | 1,427 | 1,799 | +26% | 0 | 0 | — |
case-19 | pass→pass | 5,097 | 3,716 | -27% | 1 | 1 | 0% | 813 | 1,678 | +106% | 0 | 0 | — |
case-20 | fail→pass | 14,444 | 3,328 | -77% | 1 | 1 | 0% | 2,526 | 1,628 | -36% | 0 | 0 | — |
case-21 | pass→pass | 12,739 | 3,261 | -74% | 1 | 1 | 0% | 1,695 | 1,683 | -1% | 0 | 0 | — |
case-22 | pass→pass | 13,340 | 4,712 | -65% | 1 | 1 | 0% | 1,800 | 1,967 | +9% | 0 | 0 | — |
case-23 | fail→pass | 19,650 | 2,358 | -88% | 1 | 1 | 0% | 2,557 | 1,471 | -42% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +43 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.