Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions .agents/skills/analytics/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
name: analytics
description: Instrument and discover analytics events in Sentry's frontend UI. Use when adding tracking to buttons, pages, modals, or custom interactions, when defining new analytics events, when searching for existing events, when auditing analytics coverage for a feature, or when answering questions about how users interact with a feature. Trigger on "add analytics", "track event", "instrument analytics", "analytics event", "track click", "track page view", "add tracking", "what events exist for", "audit analytics", "how many people", "how many users", "are people using", "is anyone clicking", "usage of", "who is using".
---

# Analytics Instrumentation

Add analytics events to Sentry's frontend UI using established patterns.

## Answering "How Many People Do X?"

When the user asks about usage, adoption, or interaction counts for a feature:

1. Find the event: search Amplitude first (fastest), fall back to grepping the codebase.
2. If the Amplitude MCP is connected, query the data directly and report results.
3. If no matching event exists, tell the user the event is not tracked — then use `AskUserQuestion` to ask whether they want to instrument it. Do not proceed to instrumentation without explicit confirmation.

Read `references/amplitude-mcp.md` for the full discovery and querying workflow.

## Before Any Change: Search First

**NEVER create a new event without checking if one already exists.**

1. Search `static/app/utils/analytics/` for events matching the feature domain.
2. Grep for keywords related to the interaction (e.g., `clicked`, `viewed`, `created`).
3. If a matching event exists, reuse it — add parameters if needed rather than creating a duplicate.

```bash
grep -rn "keyword" static/app/utils/analytics/ --include="*.tsx"
```

## Event Naming Rules

| Rule | Example |
| -------------------------------------------- | ---------------------------------------------------------------------- |
| Use `snake_case` with dots as separators | `feedback.list-item-selected` |
| First segment = feature domain | `dashboards2.`, `issue_details.`, `feedback.` |
| Middle segments = section/context (optional) | `dashboards2.edit.` |
| Last segment = action | `.clicked`, `.viewed`, `.created`, `.changed` |
| Match the existing domain file's prefix | If events are in `feedbackAnalyticsEvents.tsx`, use `feedback.` prefix |

**Standard action suffixes:**

| User action | Suffix |
| --------------------- | -------------------------- |
| Clicks a button/link | `.clicked` or `_clicked` |
| Views a page | `.viewed` |
| Submits a form | `.submitted` or `.created` |
| Changes a setting | `.changed` |
| Renders/loads content | `.rendered` or `.loaded` |
| Dismisses UI | `.dismissed` |
| Opens a modal/panel | `.opened` |

## Choose the Right Tracking Pattern

| What to track | Pattern | Open reference |
| ----------------------------------------- | ------------------------------- | ------------------------------------------------ |
| Page view on route navigation | Route analytics hooks | `references/tracking-patterns.md` § Route-Level |
| Button or link click | Button `analyticsEventKey` prop | `references/tracking-patterns.md` § Button |
| Custom interaction (toggle, drag, select) | `trackAnalytics()` call | `references/tracking-patterns.md` § Manual |
| Modal or panel open/close | `trackAnalytics()` in handler | `references/tracking-patterns.md` § Manual |
| UI area context for events | `AnalyticsArea` wrapper | `references/tracking-patterns.md` § Area Context |

## When You Need to Define a New Event

Read `references/event-definitions.md` for step-by-step instructions.

## Common Mistakes and Debugging

Read `references/troubleshooting.md` when:

- An event isn't firing or appearing in Amplitude
- You see TypeScript errors when calling `trackAnalytics`
- You need to debug analytics locally
- You're unsure whether an event needs an Amplitude name

## Key Files

| File | Purpose |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `static/app/utils/analytics.tsx` | Master registry — all event maps merged, `trackAnalytics` export |
| `static/app/utils/analytics/{domain}AnalyticsEvents.tsx` | Domain-specific event type definitions and name maps |
| `static/app/utils/analytics/makeAnalyticsFunction.tsx` | Factory that creates typed `trackAnalytics` — do not call directly |
| `static/app/utils/routeAnalytics/useRouteAnalyticsEventNames.tsx` | Hook for route-level page view event names |
| `static/app/utils/routeAnalytics/useRouteAnalyticsParams.tsx` | Hook for route-level page view parameters |
| `static/app/components/analyticsArea.tsx` | `AnalyticsArea` component and `useAnalyticsArea` hook |
| `static/app/components/core/button/types.tsx` | Button analytics props (`analyticsEventKey`, `analyticsEventName`, `analyticsParams`) |

## Interaction Rules

Users of this skill may be less technical. Use `AskUserQuestion` at every decision point instead of dumping plans or code.

| Situation | Action |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Event not found, user asked a data question | Use `AskUserQuestion`: "This isn't tracked yet. Want me to add instrumentation?" |
| User confirms they want instrumentation | Go straight to implementation. Do not show code previews or step-by-step plans — just make the changes and summarize what you did. |
| Implementation is done, needs user action (e.g., Reload registration) | State the remaining step clearly in your summary. |

**Never** dump code blocks as a "plan" and then ask "Want me to make these changes?" — either present a short plain-English summary via `AskUserQuestion` for confirmation, or proceed directly if the user already asked for instrumentation.

## Event Pipeline

Every `trackAnalytics` call flows through the GetSentry override in `static/gsApp/utils/rawTrackAnalyticsEvent.tsx`:

| Destination | When it fires | What it uses | How to query |
| ------------- | ------------------------------------------- | ------------ | ------------------- |
| **Reload** | Always | `eventKey` | Redash |
| **Amplitude** | When `eventName` is non-null and org exists | `eventName` | Amplitude UI or MCP |
| **Pendo** | Same as Amplitude | `eventName` | Pendo |

- Set `eventName` to a string (e.g., `'Logs Trace Link Clicked'`) to send to both Reload and Amplitude. This is the default for almost all events.
- Set `eventName` to `null` only for high-volume events that would be too expensive for Amplitude. These are Reload-only and queryable via Redash.
- Reload accepts events with `allow_no_schema: true` — no separate registration step is needed.
- When searching for events, note that Reload-only events (`null` name) will not appear in Amplitude search. Fall back to grepping the codebase if Amplitude returns no results.

## Non-Negotiable Constraints

1. **All events must be type-safe.** Every event key must exist in a `*EventParameters` type and be registered in the domain's event map.
2. **All events flow through `trackAnalytics()`.** Never call `window.analytics`, `Amplitude.track()`, or any other analytics SDK directly.
3. **Organization context is automatic.** Pass `organization` to `trackAnalytics` — the override system handles the rest.
4. **Reuse over create.** Always search for existing events before defining new ones.
5. **One event per interaction.** Do not fire multiple events for the same user action.
6. **No PII in event parameters.** Never include user emails, IP addresses, full names, or other personally identifiable information. Use opaque IDs (org ID, user ID) when identity context is needed.
76 changes: 76 additions & 0 deletions .agents/skills/analytics/SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Analytics Instrumentation Specification

## Intent

Guide users and agents through adding analytics events to Sentry's frontend UI using established patterns. Prioritizes reusing existing events, enforcing type safety, and following naming conventions. Designed to be safe for less-technical users (design, PM, sales) who may not know the analytics architecture.

## Scope

In scope:

- Frontend analytics event definition (TypeScript types, event maps)
- Frontend tracking instrumentation (`trackAnalytics`, route hooks, button props, `AnalyticsArea`)
- Event naming conventions and parameter typing
- Searching for and reusing existing events
- Local debugging with `DEBUG_ANALYTICS`

Out of scope:

- Backend analytics event creation (`src/sentry/analytics/`)
- Amplitude dashboard configuration
- Google Analytics configuration
- Performance metrics (`metric.mark`, `metric.measure`)

## Users And Trigger Context

- Common user requests: "add analytics to this button", "track when users view this page", "what events exist for dashboards", "I need to track clicks on the new filter"
- Should not trigger for: backend-only analytics, Sentry SDK instrumentation, performance monitoring, error tracking setup

## Runtime Contract

- Required first actions: search existing events before defining new ones
- Required outputs: code changes to event definition files and instrumentation call sites
- Non-negotiable constraints: all events must be type-safe; all events flow through `trackAnalytics`; reuse existing events when possible
- Expected bundled files loaded at runtime: `references/event-definitions.md`, `references/tracking-patterns.md`, `references/troubleshooting.md`, `references/amplitude-mcp.md`

## Source And Evidence Model

Authoritative sources:

- `static/app/utils/analytics.tsx` — master event registry
- `static/app/utils/analytics/*.tsx` — domain event definitions
- `static/app/utils/analytics/makeAnalyticsFunction.tsx` — factory function
- `static/app/utils/routeAnalytics/` — route-level tracking hooks
- `static/app/components/analyticsArea.tsx` — area context
- `static/app/components/core/button/types.tsx` — button analytics props

Data that must not be stored:

- Customer organization slugs, names, or IDs
- Internal Amplitude project keys
- Reload backend credentials

## Reference Architecture

- `SKILL.md` contains: routing table, naming rules, non-negotiable constraints, key files
- `references/event-definitions.md` contains: step-by-step event creation, registration, examples
- `references/tracking-patterns.md` contains: route-level, button, manual, and area tracking patterns
- `references/troubleshooting.md` contains: common mistakes, debugging, anti-patterns
- `references/amplitude-mcp.md` contains: Amplitude MCP setup, event discovery, ad-hoc querying, fallback workflow

## Validation

- Lightweight validation: TypeScript compilation catches unregistered event keys
- Deeper validation: `DEBUG_ANALYTICS=1` in browser console confirms events fire
- Acceptance gates: event key exists in domain type, event map entry exists, `trackAnalytics` call compiles

## Known Limitations

- Reload backend registration is in a separate repo (`getsentry/reload`) — this skill cannot automate that step
- Button analytics props are not type-checked against the event registry
- Route analytics timing constraint (2s) is not enforced at compile time

## Maintenance Notes

- When to update `SKILL.md`: new tracking patterns added to the codebase, analytics architecture changes
- When to update references: existing patterns deprecated or new tracking patterns introduced
117 changes: 117 additions & 0 deletions .agents/skills/analytics/references/amplitude-mcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Amplitude MCP — Discovery and Querying

## Setup (One-Time)

If the Amplitude MCP is not connected:

1. Tell the user to run `/mcp` in Claude Code
2. Select **"claude.ai Amplitude"** from the list
3. Authenticate via Sentry SSO

Once connected, the `mcp__claude_ai_Amplitude__*` tools become available.

## Discover the Amplitude Project

Call `get_context` to find the Sentry organization and its projects. Use the `appId` for the `sentry.io` project as the `projectId` in all subsequent Amplitude tool calls.

## Discovery Workflow

When a user asks "how many people do X?" or "are people using Y?":

### Step 1: Find the Event

Use `search` to find matching events by keyword:

```
mcp__claude_ai_Amplitude__search({
queries: ["seer", "autofix"],
entityTypes: ["EVENT"],
limitPerQuery: 20,
search_goal: "Find events related to the seer feature on issue details"
})
```

This returns Amplitude event names (the `eventName` from our event maps, e.g., `"Issue Details: Seer Opened"`).

If search returns nothing, fall back to grepping the codebase:

```bash
grep -rn "seer\|autofix" static/app/utils/analytics/ --include="*.tsx"
```

### Step 2: Get Event Properties (Optional)

If the user needs to filter or break down by a property:

```
mcp__claude_ai_Amplitude__get_properties({
propertyType: "event",
projectId: "<projectId>",
eventType: "Issue Details: Seer Opened"
})
```

### Step 3: Query the Data

Use `query_dataset` for ad-hoc queries:

```
mcp__claude_ai_Amplitude__query_dataset({
projectId: "<projectId>",
definition: {
type: "eventsSegmentation",
app: "<projectId>",
name: "Seer Opens Last 30 Days",
params: {
range: "Last 30 Days",
events: [{
event_type: "Issue Details: Seer Opened",
filters: [],
group_by: []
}],
metric: "uniques",
countGroup: "User",
groupBy: [],
interval: 1,
segments: [{ conditions: [] }]
}
}
})
```

Use `query_chart` if the user provides an existing chart ID or URL.

### Step 4: Report Results

- State the event name used and the date range queried.
- Report unique users (not total event count) unless the user asks for totals.
- Offer to break down by property (platform, org, etc.) if the numbers need context.

## Common Query Patterns

| User question | Metric | Event type pattern |
| -------------------------------- | --------- | ----------------------------------------- |
| "How many people view X page?" | `uniques` | `"Page View: ..."` |
| "How many clicks on X button?" | `totals` | `"Feature: Button Clicked"` |
| "What's the funnel from X to Y?" | funnel | Use `type: "funnels"` with ordered events |
| "Are people coming back to X?" | retention | Use `type: "retention"` |

## Searching for Dashboards and Charts

If the user wants an existing dashboard rather than raw data:

```
mcp__claude_ai_Amplitude__search({
queries: ["seer dashboard", "autofix metrics"],
entityTypes: ["DASHBOARD", "CHART"],
limitPerQuery: 10
})
```

## When the MCP Is Not Connected

Fall back to the codebase:

1. Grep event files for the Amplitude name (`eventName` in the event map).
2. Report the event key and Amplitude name so the user can search Amplitude manually.
3. Suggest they connect the Amplitude MCP for direct querying: run `/mcp` → select "claude.ai Amplitude".
Loading
Loading