/managing-path-cleaning-rules
Inspects URL paths and proposes, tests, orders, and applies project-level path cleaning rules so dynamic segments (numeric IDs, UUIDs, slugs, dates) collapse into readable aliases. Use when the user says "clean the paths", "normalize URLs", "group similar pages", "too many
$ npx -y skills add posthog/posthog --skill managing-path-cleaning-rules --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/managing-path-cleaning-rules
Context preview
The summary Claude sees to decide when to auto-load this skill.
Inspects URL paths and proposes, tests, orders, and applies project-level path cleaning rules so dynamic segments (numeric IDs, UUIDs, slugs, dates) collapse into readable aliases. Use when the user says "clean the paths", "normalize URLs", "group similar pages", "too many
SKILL.md
managing-path-cleaning-rules.SKILL.mdname: managing-path-cleaning-rules
description: 'Inspects URL paths and proposes, tests, orders, and applies project-level path cleaning rules so dynamic segments (numeric IDs, UUIDs, slugs, dates) collapse into readable aliases. Use when the user says "clean the paths", "normalize URLs", "group similar pages", "too many distinct paths", "/users/123 and /users/456 are the same page", "set up path cleaning", or asks why a Web analytics or Paths breakdown is fragmented across thousands of nearly-identical URLs. Covers regex syntax (re2), alias placeholder convention, rule ordering, the test workflow, and applying rules via the path-cleaning-rules-update MCP tool.'
Managing path cleaning rules
Path cleaning rules normalize `$pathname` and `$entry_pathname` so that pages sharing the same template (`/users/123/profile`, `/users/456/profile`, …) collapse into one row (`/users/<id>/profile`) in Web analytics tiles, Paths insights, and any HogQL query that calls `apply_path_cleaning`. They are the right answer when a breakdown is fragmented across thousands of near-identical URLs.
This skill teaches you how to:
- recognize when path cleaning is the right tool
- inspect real paths to find what needs cleaning
- write `regex` + `alias` rules in re2 syntax with the project's placeholder
convention
- test rules before saving them
- order rules so specific patterns aren't swallowed by generic ones
- apply the rules via MCP
Data model
`Team.path_cleaning_filters` is a JSON list of `PathCleaningFilter` objects:
{
"regex": "/users/\\d+/profile",
"alias": "/users/<id>/profile",
"order": 0
}- **`regex`** — a [re2](https://github.com/google/re2/wiki/Syntax) pattern. No
need to escape `/`. Anchor with `^` / `$` when you mean it.
- **`alias`** — the literal replacement. Use angle-bracket placeholders
(`<id>`, `<slug>`, `<uuid>`, `<date>`) by convention so the cleaned path stays human-readable. The alias is _not_ a regex template — backreferences are not supported.
- **`order`** — integer. Rules apply **sequentially** in `order` ascending,
each rule's output feeds the next.
Application is `replaceRegexpAll(pathname, regex, alias)` per rule, chained.
Workflow
1. Confirm path cleaning is the right move
Ask yourself: is the user complaining about cardinality (too many distinct paths in a chart), or do they want a per-URL drill-down? Path cleaning is for the former. If they want per-URL data, suggest a property filter on `$pathname` instead.
2. Inspect the real paths
Don't guess at patterns — query them. With the `execute-sql` MCP tool:
SELECT properties.$pathname AS path, count() AS views
FROM events
WHERE event = '$pageview'
AND timestamp > now() - INTERVAL 7 DAY
GROUP BY path
ORDER BY views DESC
LIMIT 200
Scan the result for:
- numeric IDs: `/users/123`, `/orders/4242`
- UUIDs: `/sessions/8f3c1a3b-…`
- slugs: `/posts/why-i-love-posthog`
- dates: `/archive/2024-09-12`
- locales: `/en-US/`, `/fr-FR/`
- pagination: `?page=3`, `/page/3/`
3. Draft regex + alias
| Pattern | Example match | `regex` | `alias` | | ------------------- | ---------------------- | ---------------------------- | ---------------------- | | Numeric segment | `/users/123/profile` | `/users/\d+/profile` | `/users/<id>/profile` | | UUID v4 | `/sessions/8f3c1a3b-…` | `/sessions/[0-9a-f-]{36}` | `/sessions/<uuid>` | | Slug | `/posts/why-posthog` | `/posts/[a-z0-9-]+$` | `/posts/<slug>` | | ISO date | `/archive/2024-09-12` | `/archive/\d{4}-\d{2}-\d{2}` | `/archive/<date>` | | Locale prefix | `/en-US/about` | `^/[a-z]{2}-[A-Z]{2}/` | `/<locale>/` | | Trailing query/page | `/blog?page=3` | `\?page=\d+$` | (empty alias drops it) |
Anchoring rules of thumb:
- start the regex with `^` only when the segment must be at the beginning of
the path
- end with `$` to keep a generic rule (e.g. `\d+$`) from matching mid-path
segments
4. Test before saving
Three options, pick one:
- **Settings page tester**: `/settings/project#path_cleaning` has a built-in
"test path" input that replays the full ordered chain.
- **Project HogQL** (via `execute-sql`):
SELECT replaceRegexpAll('/users/42/profile', '/users/\d+/profile', '/users/<id>/profile')Chain `replaceRegexpAll` calls in the same order the rules will run if you want to verify multi-rule interaction.
- **Built-in AI helper**: there is already an `AiRegexHelper` modal accessible
from the rule editor (`Help me with Regex` button) that turns natural language into a regex. Suggest it to the user when they say "I don't know regex" — but always validate the output against real paths via the tester.
5. Order rules from most-specific to most-general
Sequential application means a generic rule placed first will swallow everything that should have hit a specific rule.
order=0 /users/me/profile → /users/me/profile (specific, runs first)
order=1 /users/\d+/profile → /users/<id>/profile
order=2 /users/[a-z0-9-]+ → /users/<slug> (catch-all, runs last)
If `/users/[a-z0-9-]+` ran first it would also match `/users/me/profile` and make the more specific rule unreachable.
6. Apply via MCP
Prefer the `path-cleaning-rules-update` tool. It reads the current rules, applies granular operations (`append`, `insert`, `replace`, `remove`, `reorder`), auto-numbers `order`, and — unless you pass `confirm: true` — returns a **preview** of the resulting rules without saving. Pass `sample_paths` to see how the resulting set rewrites real paths.
First call it without `confirm` to get the preview, surface that to the user, then re-send the same call with `"confirm": true` to save:
{
"operations": [{ "action": "append", "alias": "/users/<id>/profile"Read more
name: managing-path-cleaning-rules description: 'Inspects URL paths and proposes, tests, orders, and applies project-level path cleaning rules so dynamic segments (numeric IDs, UUIDs, slugs, dates) collapse into readable aliases. Use when the user says "clean the paths", "normalize URLs", "group similar pages", "too many distinct paths", "/users/123 and /users/456 are the same page", "set up path cleaning", or asks why a Web analytics or Paths breakdown is fragmented across thousands of nearly-identical URLs. Covers regex syntax (re2), alias placeholder convention, rule ordering, the test workflow, and applying rules via the path-cleaning-rules-update MCP tool.'
Managing path cleaning rules
Path cleaning rules normalize `$pathname` and `$entry_pathname` so that pages sharing the same template (`/users/123/profile`, `/users/456/profile`, …) collapse into one row (`/users/<id>/profile`) in Web analytics tiles, Paths insights, and any HogQL query that calls `apply_path_cleaning`. They are the right answer when a breakdown is fragmented across thousands of near-identical URLs.
This skill teaches you how to:
- recognize when path cleaning is the right tool
- inspect real paths to find what needs cleaning
- write `regex` + `alias` rules in re2 syntax with the project's placeholder
convention
- test rules before saving them
- order rules so specific patterns aren't swallowed by generic ones
- apply the rules via MCP
Data model
`Team.path_cleaning_filters` is a JSON list of `PathCleaningFilter` objects:
{
"regex": "/users/\\d+/profile",
"alias": "/users/<id>/profile",
"order": 0
}- **`regex`** — a [re2](https://github.com/google/re2/wiki/Syntax) pattern. No
need to escape `/`. Anchor with `^` / `$` when you mean it.
- **`alias`** — the literal replacement. Use angle-bracket placeholders
(`<id>`, `<slug>`, `<uuid>`, `<date>`) by convention so the cleaned path stays human-readable. The alias is _not_ a regex template — backreferences are not supported.
- **`order`** — integer. Rules apply **sequentially** in `order` ascending,
each rule's output feeds the next.
Application is `replaceRegexpAll(pathname, regex, alias)` per rule, chained.
Workflow
1. Confirm path cleaning is the right move
Ask yourself: is the user complaining about cardinality (too many distinct paths in a chart), or do they want a per-URL drill-down? Path cleaning is for the former. If they want per-URL data, suggest a property filter on `$pathname` instead.
2. Inspect the real paths
Don't guess at patterns — query them. With the `execute-sql` MCP tool:
SELECT properties.$pathname AS path, count() AS views FROM events WHERE event = '$pageview' AND timestamp > now() - INTERVAL 7 DAY GROUP BY path ORDER BY views DESC LIMIT 200
Scan the result for:
- numeric IDs: `/users/123`, `/orders/4242`
- UUIDs: `/sessions/8f3c1a3b-…`
- slugs: `/posts/why-i-love-posthog`
- dates: `/archive/2024-09-12`
- locales: `/en-US/`, `/fr-FR/`
- pagination: `?page=3`, `/page/3/`
3. Draft regex + alias
| Pattern | Example match | `regex` | `alias` | | ------------------- | ---------------------- | ---------------------------- | ---------------------- | | Numeric segment | `/users/123/profile` | `/users/\d+/profile` | `/users/<id>/profile` | | UUID v4 | `/sessions/8f3c1a3b-…` | `/sessions/[0-9a-f-]{36}` | `/sessions/<uuid>` | | Slug | `/posts/why-posthog` | `/posts/[a-z0-9-]+$` | `/posts/<slug>` | | ISO date | `/archive/2024-09-12` | `/archive/\d{4}-\d{2}-\d{2}` | `/archive/<date>` | | Locale prefix | `/en-US/about` | `^/[a-z]{2}-[A-Z]{2}/` | `/<locale>/` | | Trailing query/page | `/blog?page=3` | `\?page=\d+$` | (empty alias drops it) |
Anchoring rules of thumb:
- start the regex with `^` only when the segment must be at the beginning of
the path
- end with `$` to keep a generic rule (e.g. `\d+$`) from matching mid-path
segments
4. Test before saving
Three options, pick one:
- **Settings page tester**: `/settings/project#path_cleaning` has a built-in
"test path" input that replays the full ordered chain.
- **Project HogQL** (via `execute-sql`):
SELECT replaceRegexpAll('/users/42/profile', '/users/\d+/profile', '/users/<id>/profile')Chain `replaceRegexpAll` calls in the same order the rules will run if you want to verify multi-rule interaction.
- **Built-in AI helper**: there is already an `AiRegexHelper` modal accessible
from the rule editor (`Help me with Regex` button) that turns natural language into a regex. Suggest it to the user when they say "I don't know regex" — but always validate the output against real paths via the tester.
5. Order rules from most-specific to most-general
Sequential application means a generic rule placed first will swallow everything that should have hit a specific rule.
order=0 /users/me/profile → /users/me/profile (specific, runs first) order=1 /users/\d+/profile → /users/<id>/profile order=2 /users/[a-z0-9-]+ → /users/<slug> (catch-all, runs last)
If `/users/[a-z0-9-]+` ran first it would also match `/users/me/profile` and make the more specific rule unreachable.
6. Apply via MCP
Prefer the `path-cleaning-rules-update` tool. It reads the current rules, applies granular operations (`append`, `insert`, `replace`, `remove`, `reorder`), auto-numbers `order`, and — unless you pass `confirm: true` — returns a **preview** of the resulting rules without saving. Pass `sample_paths` to see how the resulting set rewrites real paths.
First call it without `confirm` to get the preview, surface that to the user, then re-send the same call with `"confirm": true` to save:
{
"operations": [{ "action": "append", "alias": "/users/<id>/profile":hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.
Repo: posthog/posthog
Other skills on posthog.
- /analyzing-expensive-users
Analyze the most expensive users in AI observability and explain why they cost so much. Use when the user asks about top spenders, expensive users, per-user LLM cost, user-level cost drivers, or patterns behind high AI observability spend.
Open skill - /creating-online-evaluations
Author continuously-running online evaluations in PostHog AI observability, grounded in real failure modes you've identified. Use when the user wants evaluations that automatically score new generations or whole traces going forward — "create an eval to catch X", "continuously
Open skill - /exploring-ai-failures
Find where an AI/LLM application is failing in production and surface the failure patterns, working from real traces. Use when someone wants to understand what's going wrong with an AI feature, find and categorize failure modes, triage errors, or investigate quality issues
Open skill - /exploring-llm-clusters
Investigate AI observability clusters — understand usage patterns in AI/LLM traffic, compare cluster behavior, compute cost/latency metrics, and drill into individual traces within clusters.
Open skill - /exploring-llm-costs
Investigate LLM spend in PostHog — total cost over time, cost by model, provider, user, trace, or custom dimension, token and cache-hit economics, and cost regressions. Use when the user asks "how much are we spending on LLMs?", "which model / user / feature is most expensive?",
Open skill - /exploring-llm-evaluations
Investigate AI observability evaluations — `hog` (deterministic code-based), `llm_judge` (LLM-prompt-based), and `sentiment` (user-message sentiment). Find existing evaluations, inspect their configuration, run them against specific generations, query individual results, and
Open skill

