RUM Tracking
Guides product-analytics and RUM event tracking for web (React/Next.js)
and mobile (React Native/Expo) apps.
Decides what to capture, what to drop, what's PII, and how to add, audit,
update, and remove tracking code without breaking downstream dashboards.
External dependency. The OTel guidance in rules/otel-conventions.md
builds on the otel-instrumentation and otel-semantic-conventions skills,
which live in the dash0 agent-skills repo,
not this one. That rule invokes them at runtime via Skill() when they're
installed (and skips silently otherwise) — install them alongside this skill
to get their authoritative span/metric/attribute guidance.
This SKILL.md is a thin index.
Detailed rules live in rules/*.md and load on demand.
Worked examples live in references/*.md.
Literal scaffolding lives in templates/*.md.
Mode Detection
Parse $1 as the mode.
State the detected mode in one line before continuing.
| Mode |
Default |
Trigger |
guide |
yes |
"what should I track", "is this worth tracking", default if no mode |
implement |
|
"add tracking", "instrument this", "track this event" |
audit |
|
"audit tracking", "review analytics", "find tracking issues" |
remove |
|
"remove tracking", "delete this event", "deprecate", "/rm-tracking" |
plan |
|
"tracking plan", "design event schema", "what events do we need" |
If $1 is a target file or directory, treat it as the scope for audit,
implement, or remove.
Workflow by Mode
Guide mode (default)
The user is deciding whether and what to track at a specific point.
- Load
rules/what-to-track.md and
rules/what-not-to-track.md.
- Cross-check the proposal against
rules/pii-and-compliance.md.
- Recommend an event name + property set using
rules/event-design.md.
- If the project uses OpenTelemetry RUM (Dash0 SDK Web, OTel browser /
mobile, Embrace), also apply
rules/otel-conventions.md.
- Surface canonical events from
references/event-catalog.md instead
of inventing new ones when one fits.
Implement mode
The user wants tracking code written.
- Confirm the event is in the tracking plan
(
rules/tracking-plan.md).
If not, propose adding it to the plan first and gate the user
before writing instrumentation.
- Pick the platform:
- If using OpenTelemetry, also load
rules/otel-conventions.md.
- All tracking calls must go through the centralized wrapper
(
templates/analytics-wrapper.template.ts).
Never call the vendor SDK directly from a component.
- Run the PII gate from
rules/pii-and-compliance.md on every
property before the diff is final.
Audit mode
The user wants existing tracking reviewed.
- Walk the checklist in
rules/audit-checklist.md.
- For every finding, cite a file path and line number.
- Group findings into: blocking (PII / consent / compliance), important
(drift / ghost events / cardinality), nice-to-have (naming consistency).
- Output a ranked fix list — do not auto-edit unless the user approved a
pre-defined audit scope.
Remove mode
The user wants tracking deprecated or deleted.
- Apply the lifecycle in
rules/update-and-remove.md.
- Find every callsite via the centralized wrapper's typed event names.
- Identify downstream consumers (dashboards, funnels, dbt models,
cohorts) before deletion.
- Mark
deprecated first, set a sunset date, then remove.
- Update the tracking plan and the inventory in the same PR.
Plan mode
The user wants to design, update, or codegen a tracking plan.
- Apply the structure in
rules/tracking-plan.md.
- Start from
templates/tracking-plan.template.yaml.
- Choose a naming school (
rules/event-design.md)
and freeze it for the project.
- Wire codegen (Avo, RudderTyper, Typewriter, or hand-rolled
json-schema-to-typescript) so the wrapper is type-checked.
Required Reading by Mode
Load on demand — do not preload.
| Mode |
Files |
guide |
rules/what-to-track.md, rules/what-not-to-track.md, rules/event-design.md, rules/pii-and-compliance.md, references/event-catalog.md |
implement |
rules/tracking-plan.md, rules/implementation-web.md or rules/implementation-mobile.md, rules/otel-conventions.md (if OTel), rules/pii-and-compliance.md, templates/analytics-wrapper.template.ts |
audit |
rules/audit-checklist.md, rules/pii-and-compliance.md, rules/event-design.md |
remove |
rules/update-and-remove.md, rules/tracking-plan.md |
plan |
rules/tracking-plan.md, rules/event-design.md, templates/tracking-plan.template.yaml |
references/platforms.md is optional — load
when the user asks "which platform should we use" or names a specific
vendor.
Core Principles
- The tracking plan is the source of truth.
Every event must exist in the plan before it exists in code.
- Centralized wrapper, never raw SDK calls.
One module owns every
track() callsite; swapping vendors must be a
single-file change.
- Type-safe events.
Use codegen (Avo, RudderTyper, Typewriter) or a hand-rolled
discriminated union so renames break the build.
- PII never appears in event properties.
Use opaque
user.id, hash for correlation, strip URLs and free-text.
- Low-cardinality event names; rich, bounded properties.
≤ 30 event names in a typical app; high-value context in properties.
- Defer sampling to the pipeline.
SDKs export everything; the Collector or platform decides what to
keep.
- OpenTelemetry semantic conventions when present.
user.id, session.id, browser.*, app.*, error.type come from
the registry — do not invent custom names that overlap.
- Remove tracking the same way you add it.
Plan first, deprecate, find consumers, then delete.
Anti-patterns (one-liners)
- Scattering
posthog.capture() / mixpanel.track() calls across
components instead of one wrapper.
- Tracking every hover, scroll, or render — drowns signal, explodes cost.
- Putting email, full URLs with tokens, raw
req.body, or stack traces
with user input into event properties.
- Using email or username as
distinct_id / user.id — always opaque.
- Naming events inconsistently (
signup and user_registered for the
same concept).
- Deleting an event before checking which dashboards consume it.
- Configuring SDK-side sampling — sample in the Collector instead.
- Treating hashed email as anonymous — it remains personal data under
GDPR.
Definition of Done
Source: mthines/agent-skills — distributed by TomeVault.
1---2name: mthines-agent-skills-rum-tracking3description: RUM Tracking4---56# RUM Tracking78Guides product-analytics and RUM event tracking for web (React/Next.js)9and mobile (React Native/Expo) apps.10Decides what to capture, what to drop, what's PII, and how to add, audit,11update, and remove tracking code without breaking downstream dashboards.1213> **External dependency.** The OTel guidance in [`rules/otel-conventions.md`](./rules/otel-conventions.md)14> builds on the `otel-instrumentation` and `otel-semantic-conventions` skills,15> which live in the [dash0 agent-skills repo](https://github.com/dash0hq/agent-skills),16> not this one. That rule **invokes them at runtime via `Skill()` when they're17> installed** (and skips silently otherwise) — install them alongside this skill18> to get their authoritative span/metric/attribute guidance.1920> **This `SKILL.md` is a thin index.**21> Detailed rules live in `rules/*.md` and load on demand.22> Worked examples live in `references/*.md`.23> Literal scaffolding lives in `templates/*.md`.2425---2627## Mode Detection2829Parse `$1` as the mode.30State the detected mode in one line before continuing.3132| Mode | Default | Trigger |33| ----------- | ------- | -------------------------------------------------------------------- |34| `guide` | **yes** | "what should I track", "is this worth tracking", default if no mode |35| `implement` | | "add tracking", "instrument this", "track this event" |36| `audit` | | "audit tracking", "review analytics", "find tracking issues" |37| `remove` | | "remove tracking", "delete this event", "deprecate", "/rm-tracking" |38| `plan` | | "tracking plan", "design event schema", "what events do we need" |3940If `$1` is a target file or directory, treat it as the scope for `audit`,41`implement`, or `remove`.4243---4445## Workflow by Mode4647### Guide mode (default)4849The user is deciding *whether* and *what* to track at a specific point.50511. Load [`rules/what-to-track.md`](./rules/what-to-track.md) and52 [`rules/what-not-to-track.md`](./rules/what-not-to-track.md).532. Cross-check the proposal against54 [`rules/pii-and-compliance.md`](./rules/pii-and-compliance.md).553. Recommend an event name + property set using56 [`rules/event-design.md`](./rules/event-design.md).574. If the project uses OpenTelemetry RUM (Dash0 SDK Web, OTel browser /58 mobile, Embrace), also apply59 [`rules/otel-conventions.md`](./rules/otel-conventions.md).605. Surface canonical events from61 [`references/event-catalog.md`](./references/event-catalog.md) instead62 of inventing new ones when one fits.6364### Implement mode6566The user wants tracking code written.67681. Confirm the event is in the tracking plan69 ([`rules/tracking-plan.md`](./rules/tracking-plan.md)).70 If not, propose adding it to the plan **first** and gate the user71 before writing instrumentation.722. Pick the platform:73 - Web (React/Next.js) → [`rules/implementation-web.md`](./rules/implementation-web.md).74 - Mobile (React Native/Expo) → [`rules/implementation-mobile.md`](./rules/implementation-mobile.md).753. If using OpenTelemetry, also load76 [`rules/otel-conventions.md`](./rules/otel-conventions.md).774. All tracking calls must go through the centralized wrapper78 ([`templates/analytics-wrapper.template.ts`](./templates/analytics-wrapper.template.ts)).79 Never call the vendor SDK directly from a component.805. Run the PII gate from81 [`rules/pii-and-compliance.md`](./rules/pii-and-compliance.md) on every82 property before the diff is final.8384### Audit mode8586The user wants existing tracking reviewed.87881. Walk the checklist in89 [`rules/audit-checklist.md`](./rules/audit-checklist.md).902. For every finding, cite a file path and line number.913. Group findings into: blocking (PII / consent / compliance), important92 (drift / ghost events / cardinality), nice-to-have (naming consistency).934. Output a ranked fix list — do not auto-edit unless the user approved a94 pre-defined audit scope.9596### Remove mode9798The user wants tracking deprecated or deleted.991001. Apply the lifecycle in101 [`rules/update-and-remove.md`](./rules/update-and-remove.md).1022. Find every callsite via the centralized wrapper's typed event names.1033. Identify downstream consumers (dashboards, funnels, dbt models,104 cohorts) before deletion.1054. Mark `deprecated` first, set a sunset date, then remove.1065. Update the tracking plan and the inventory in the same PR.107108### Plan mode109110The user wants to design, update, or codegen a tracking plan.1111121. Apply the structure in113 [`rules/tracking-plan.md`](./rules/tracking-plan.md).1142. Start from115 [`templates/tracking-plan.template.yaml`](./templates/tracking-plan.template.yaml).1163. Choose a naming school ([`rules/event-design.md`](./rules/event-design.md))117 and freeze it for the project.1184. Wire codegen (Avo, RudderTyper, Typewriter, or hand-rolled119 `json-schema-to-typescript`) so the wrapper is type-checked.120121---122123## Required Reading by Mode124125Load on demand — do not preload.126127| Mode | Files |128| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |129| `guide` | [`rules/what-to-track.md`](./rules/what-to-track.md), [`rules/what-not-to-track.md`](./rules/what-not-to-track.md), [`rules/event-design.md`](./rules/event-design.md), [`rules/pii-and-compliance.md`](./rules/pii-and-compliance.md), [`references/event-catalog.md`](./references/event-catalog.md) |130| `implement` | [`rules/tracking-plan.md`](./rules/tracking-plan.md), [`rules/implementation-web.md`](./rules/implementation-web.md) or [`rules/implementation-mobile.md`](./rules/implementation-mobile.md), [`rules/otel-conventions.md`](./rules/otel-conventions.md) (if OTel), [`rules/pii-and-compliance.md`](./rules/pii-and-compliance.md), [`templates/analytics-wrapper.template.ts`](./templates/analytics-wrapper.template.ts) |131| `audit` | [`rules/audit-checklist.md`](./rules/audit-checklist.md), [`rules/pii-and-compliance.md`](./rules/pii-and-compliance.md), [`rules/event-design.md`](./rules/event-design.md) |132| `remove` | [`rules/update-and-remove.md`](./rules/update-and-remove.md), [`rules/tracking-plan.md`](./rules/tracking-plan.md) |133| `plan` | [`rules/tracking-plan.md`](./rules/tracking-plan.md), [`rules/event-design.md`](./rules/event-design.md), [`templates/tracking-plan.template.yaml`](./templates/tracking-plan.template.yaml) |134135[`references/platforms.md`](./references/platforms.md) is optional — load136when the user asks "which platform should we use" or names a specific137vendor.138139---140141## Core Principles1421431. **The tracking plan is the source of truth.**144 Every event must exist in the plan before it exists in code.1452. **Centralized wrapper, never raw SDK calls.**146 One module owns every `track()` callsite; swapping vendors must be a147 single-file change.1483. **Type-safe events.**149 Use codegen (Avo, RudderTyper, Typewriter) or a hand-rolled150 discriminated union so renames break the build.1514. **PII never appears in event properties.**152 Use opaque `user.id`, hash for correlation, strip URLs and free-text.1535. **Low-cardinality event names; rich, bounded properties.**154 ≤ 30 event names in a typical app; high-value context in properties.1556. **Defer sampling to the pipeline.**156 SDKs export everything; the Collector or platform decides what to157 keep.1587. **OpenTelemetry semantic conventions when present.**159 `user.id`, `session.id`, `browser.*`, `app.*`, `error.type` come from160 the registry — do not invent custom names that overlap.1618. **Remove tracking the same way you add it.**162 Plan first, deprecate, find consumers, then delete.163164## Anti-patterns (one-liners)165166- Scattering `posthog.capture()` / `mixpanel.track()` calls across167 components instead of one wrapper.168- Tracking every hover, scroll, or render — drowns signal, explodes cost.169- Putting email, full URLs with tokens, raw `req.body`, or stack traces170 with user input into event properties.171- Using email or username as `distinct_id` / `user.id` — always opaque.172- Naming events inconsistently (`signup` and `user_registered` for the173 same concept).174- Deleting an event before checking which dashboards consume it.175- Configuring SDK-side sampling — sample in the Collector instead.176- Treating hashed email as anonymous — it remains personal data under177 GDPR.178179## Definition of Done180181- [ ] Mode detected and stated.182- [ ] Required reading for the mode loaded.183- [ ] If `implement` or `remove`: tracking plan updated in the same diff.184- [ ] PII gate run on every new or modified event.185- [ ] Centralized wrapper used; no raw SDK calls in feature code.186- [ ] Downstream consumers identified before removal.187- [ ] Recommendation cites file paths and line numbers, or proposes a188 concrete diff.189190---191> Source: [mthines/agent-skills](https://github.com/mthines/agent-skills) — distributed by [TomeVault](https://tomevault.io).192<!-- tomevault:4.0:skill_md:2026-06-15 -->