[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-posthog-suggesting-path-cleaning-rules":3,"mdc-xq08po-key":47,"related-repo-posthog-suggesting-path-cleaning-rules":1118,"related-org-posthog-suggesting-path-cleaning-rules":1221},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":22,"repoUrl":23,"updatedAt":24,"license":25,"forks":26,"topics":27,"repo":42,"sourceUrl":45,"mdContent":46},"suggesting-path-cleaning-rules","generate path cleaning rules","Runs and reasons about the automated AI health check that suggests path-cleaning rules for web-analytics teams. Use when asked to generate path-cleaning suggestions for a team or cohort, to run the suggestion check, to review\u002Fapply AI-suggested rules, to inspect path_cleaning_suggestions health issues, or to extend the suggestion pipeline. Covers the suggest_path_cleaning_rules management command, the path_cleaning_suggestions health check, the cohort gating (precompute teams), and how suggestions are validated against real paths before storage. For hand-authoring or applying rules directly, use managing-path-cleaning-rules instead.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"posthog","PostHog","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fposthog.png",[12,16,19],{"name":13,"slug":14,"type":15},"SEO","seo","tag",{"name":17,"slug":18,"type":15},"Data Cleaning","data-cleaning",{"name":20,"slug":21,"type":15},"Analytics","analytics",35568,"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog","2026-08-06T06:09:20.123753",null,2977,[28,29,21,30,31,32,33,34,35,36,37,38,39,40,41],"ab-testing","ai-analytics","cdp","data-warehouse","experiments","feature-flags","javascript","product-analytics","python","react","session-replay","surveys","typescript","web-analytics",{"repoUrl":23,"stars":22,"forks":26,"topics":43,"description":44},[28,29,21,30,31,32,33,34,35,36,37,38,39,40,41],"🦔 PostHog is an all-in-one developer platform for building successful products. We offer product analytics, web analytics, session replay, error tracking, feature flags, experimentation, surveys, data warehouse, a CDP, and an AI product assistant to help debug your code, ship features faster, and keep all your usage and customer data in one stack.","https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog\u002Ftree\u002FHEAD\u002Fproducts\u002Fweb_analytics\u002Fskills\u002Fsuggesting-path-cleaning-rules","---\nname: suggesting-path-cleaning-rules\ndescription: 'Runs and reasons about the automated AI health check that suggests path-cleaning rules for web-analytics teams. Use when asked to generate path-cleaning suggestions for a team or cohort, to run the suggestion check, to review\u002Fapply AI-suggested rules, to inspect path_cleaning_suggestions health issues, or to extend the suggestion pipeline. Covers the suggest_path_cleaning_rules management command, the path_cleaning_suggestions health check, the cohort gating (precompute teams), and how suggestions are validated against real paths before storage. For hand-authoring or applying rules directly, use managing-path-cleaning-rules instead.'\n---\n\n# Suggesting path-cleaning rules\n\nMany teams never configure path cleaning, so their Web analytics breakdowns fragment across\nthousands of near-identical URLs. This feature **proactively suggests** cleaning rules for the\nweb-analytics precompute cohort: weekly, for each team, it samples real paths, asks the LLM for\n`{regex, alias}` rules, validates them against the team's own paths, and stores them for review.\n\nIt **only suggests** — it never auto-applies. Applying rewrites historical numbers in every cleaned\nchart, so that stays a human decision (the existing settings UI, or the `--apply` flag below after\nreview). To hand-author or directly apply rules, use the `managing-path-cleaning-rules` skill.\n\n## Architecture\n\n- **Core**: `products\u002Fweb_analytics\u002Fbackend\u002Fpath_cleaning_suggestions\u002Fservice.py`\n  - `sample_pathnames` \u002F `count_distinct_pathnames` — top `$pathname` by views via HogQL.\n  - `call_llm_for_rules` — one-shot call through the LLM gateway\n    (`get_llm_client(product=\"web_analytics\", team_id=...)`, model\n    `WEB_ANALYTICS_PATH_CLEANING_SUGGESTIONS_MODEL`, default `claude-haiku-4-5`).\n  - `validate_and_annotate_rules` — compiles each regex with **re2** (the engine ClickHouse\n    `replaceRegexpAll` uses) and test-applies it to the sampled paths. Rules that don't compile or\n    match nothing are dropped; survivors get a dense `order`, a `match_count`, and in-memory\n    before\u002Fafter `examples` (printed by the management command, never stored — health-issue\n    payloads are readable with just `health_issue:read` and must not leak real paths). This is\n    the skill's \"test before saving\" step, automated.\n  - `generate_suggestions_for_team` — orchestrates the above with gating (see below); pure\n    generation, no storage.\n  - `apply_suggestions_to_team` — **merges** rules into `path_cleaning_filters`, never overwrites\n    (dedupes by regex, continues `order`).\n- **Storage**: a `path_cleaning_suggestions` **health issue** (`HealthIssue`, severity `info`) — no\n  dedicated model. One active issue per team (`hash_keys=[]`); `payload` carries `rules`, `model`,\n  `sampled_path_count`, `distinct_path_count`. Applying (or hand-configuring rules) resolves the\n  issue on the next check run; dismissal is the health-issue `dismissed` flag.\n- **Schedule**: `PathCleaningSuggestionsCheck`\n  (`products\u002Fweb_analytics\u002Fbackend\u002Ftemporal\u002Fhealth_checks\u002Fpath_cleaning_suggestions.py`), a health\n  check on the shared health-check framework, weekly (Mon 06:23 UTC), small sequential batches\n  because each eligible team costs an LLM call. Teams with an existing active suggestion are\n  re-emitted without a fresh LLM round trip.\n- **Cohort**: `WEB_ANALYTICS_PATH_CLEANING_SUGGESTIONS_TEAM_IDS`, defaulting to the precompute\n  enrollment list `WEB_ANALYTICS_LAZY_PRECOMPUTE_TEAM_IDS`.\n\n## Gating (why a team is skipped)\n\n`generate_suggestions_for_team` returns a status:\n\n- `skipped_inactive` — team sent no `$pageview` within `visited_within_days` (default 30); we only\n  suggest for teams actively using web analytics. Bypass with `--ignore-visit-gate`.\n- `skipped_configured` — team already has path cleaning rules (override with `include_configured`).\n- `skipped_low_cardinality` — fewer distinct paths than `min_distinct_paths` (default 50); cleaning\n  adds no value, so we don't spend tokens.\n- `skipped_no_paths` — no pageviews in the window.\n- `generated` — rules produced (may be an empty list if paths are already clean; empty generations\n  are never stored, so they can't shadow an actionable suggestion).\n- `error` — sampling\u002FLLM failed; captured per-team, never aborts the cohort sweep.\n\n## How users see and apply suggestions\n\n- **Settings banner**: `PathCleaningSuggestionsBanner` on `\u002Fsettings\u002Fproject#path_cleaning` shows the\n  latest `suggested` row as regex → alias previews with match counts; \"Apply all\" (project admins\n  only) merges the rules, the close button dismisses. Driven by `pathCleaningSuggestionsLogic`.\n- **Onboarding step**: `OnboardingWebAnalyticsPathCleaningStep` (stepKey `path_cleaning`) surfaces the\n  same banner during Web analytics onboarding.\n- **API** (`products\u002Fweb_analytics\u002Fbackend\u002Fapi\u002Fweb_analytics_path_cleaning_suggestions.py`):\n  `POST \u002Fapi\u002Fprojects\u002F:id\u002Fweb_analytics_path_cleaning_suggestions\u002Fgenerate\u002F` produces and stores a\n  fresh suggestion on demand; `GET ...\u002F{issue_id}\u002Fpreview\u002F` applies the rules to a fresh sample of\n  the team's top paths and returns before\u002Fafter pairs (read scope, computed on demand, never\n  stored — this backs the banner's \"Preview on your paths\" modal); `POST ...\u002F{issue_id}\u002Fapply\u002F`\n  merges the rules and resolves the issue (project admin only — the same gate the team API puts on\n  `path_cleaning_filters`).\n  Listing and dismissing go through the generic health-issues API\n  (`GET \u002Fapi\u002Fprojects\u002F:id\u002Fhealth_issues\u002F?kind=path_cleaning_suggestions&status=active&dismissed=false`,\n  `PATCH ...\u002Fhealth_issues\u002F{id}\u002F` with `{\"dismissed\": true}`).\n- **Health page**: the check renders on `\u002Fweb\u002Fhealth` alongside the other web-analytics checks, with\n  remediation guidance for humans and agents.\n- **PostHog AI (Max)**: generate\u002Fapply are exposed as MCP tools in\n  `products\u002Fweb_analytics\u002Fmcp\u002Ftools.yaml` (`web-analytics-path-cleaning-suggestions-{generate,apply}`),\n  so a user can ask Max to suggest path-cleaning rules and apply them conversationally. Apply is\n  `destructive` (it changes historical chart numbers), so the MCP confirmation gate applies.\n\n## Running it\n\n```sh\n# Default cohort, print suggestions, store health issues:\npython manage.py suggest_path_cleaning_rules\n\n# Specific teams, dry run (nothing stored):\npython manage.py suggest_path_cleaning_rules --teams 2,19279 --no-store\n\n# Generate AND apply for one reviewed team (merges, never overwrites):\npython manage.py suggest_path_cleaning_rules --teams 2 --apply\n```\n\nUseful flags: `--days` (lookback), `--limit` (top-N paths sampled), `--min-distinct-paths`,\n`--include-configured`, `--no-store`, `--apply`.\n\nThe health check can also be triggered per team from the health-issues `refresh` endpoint or the\nadmin UI, like any other health check.\n\n## Reviewing suggestions\n\nRead a team's active suggestion:\n\n```python\nHealthIssue.objects.filter(team_id=team_id, kind=\"path_cleaning_suggestions\", status=\"active\").first()\n```\n\nEach rule in `payload[\"rules\"]` carries `regex`, `alias`, `order`, `reason`, and `match_count` —\nthat's what to show a human deciding whether to apply. Before\u002Fafter examples on real paths are only\nprinted by the management command at generation time; they are deliberately kept out of the stored\npayload.\n\n## Extending\n\n- Adding a surfacing channel (in-app notification, settings banner, onboarding wizard step): read the\n  team's active `path_cleaning_suggestions` health issue and render its `payload[\"rules\"]`. Keep\n  apply manual.\n- Changing the model: it must be allowlisted for the `web_analytics` product in\n  `services\u002Fllm-gateway\u002Fsrc\u002Fllm_gateway\u002Fproducts\u002Fconfig.py`.\n- The agentic alternative — a `signals-scout-web-analytics-path-cleaning` scout — is sketched in the\n  design notes; prefer the dedicated job for the precompute cohort because it targets that exact\n  cohort and surfaces structured, validated rows rather than Signals-inbox findings.\n",{"data":48,"body":49},{"name":4,"description":6},{"type":50,"children":51},"root",[52,60,83,111,118,451,457,467,574,580,769,775,917,965,978,984,989,1004,1051,1057,1112],{"type":53,"tag":54,"props":55,"children":56},"element","h1",{"id":4},[57],{"type":58,"value":59},"text","Suggesting path-cleaning rules",{"type":53,"tag":61,"props":62,"children":63},"p",{},[64,66,72,74,81],{"type":58,"value":65},"Many teams never configure path cleaning, so their Web analytics breakdowns fragment across\nthousands of near-identical URLs. This feature ",{"type":53,"tag":67,"props":68,"children":69},"strong",{},[70],{"type":58,"value":71},"proactively suggests",{"type":58,"value":73}," cleaning rules for the\nweb-analytics precompute cohort: weekly, for each team, it samples real paths, asks the LLM for\n",{"type":53,"tag":75,"props":76,"children":78},"code",{"className":77},[],[79],{"type":58,"value":80},"{regex, alias}",{"type":58,"value":82}," rules, validates them against the team's own paths, and stores them for review.",{"type":53,"tag":61,"props":84,"children":85},{},[86,88,93,95,101,103,109],{"type":58,"value":87},"It ",{"type":53,"tag":67,"props":89,"children":90},{},[91],{"type":58,"value":92},"only suggests",{"type":58,"value":94}," — it never auto-applies. Applying rewrites historical numbers in every cleaned\nchart, so that stays a human decision (the existing settings UI, or the ",{"type":53,"tag":75,"props":96,"children":98},{"className":97},[],[99],{"type":58,"value":100},"--apply",{"type":58,"value":102}," flag below after\nreview). To hand-author or directly apply rules, use the ",{"type":53,"tag":75,"props":104,"children":106},{"className":105},[],[107],{"type":58,"value":108},"managing-path-cleaning-rules",{"type":58,"value":110}," skill.",{"type":53,"tag":112,"props":113,"children":115},"h2",{"id":114},"architecture",[116],{"type":58,"value":117},"Architecture",{"type":53,"tag":119,"props":120,"children":121},"ul",{},[122,305,401,426],{"type":53,"tag":123,"props":124,"children":125},"li",{},[126,131,133,139],{"type":53,"tag":67,"props":127,"children":128},{},[129],{"type":58,"value":130},"Core",{"type":58,"value":132},": ",{"type":53,"tag":75,"props":134,"children":136},{"className":135},[],[137],{"type":58,"value":138},"products\u002Fweb_analytics\u002Fbackend\u002Fpath_cleaning_suggestions\u002Fservice.py",{"type":53,"tag":119,"props":140,"children":141},{},[142,169,204,262,273],{"type":53,"tag":123,"props":143,"children":144},{},[145,151,153,159,161,167],{"type":53,"tag":75,"props":146,"children":148},{"className":147},[],[149],{"type":58,"value":150},"sample_pathnames",{"type":58,"value":152}," \u002F ",{"type":53,"tag":75,"props":154,"children":156},{"className":155},[],[157],{"type":58,"value":158},"count_distinct_pathnames",{"type":58,"value":160}," — top ",{"type":53,"tag":75,"props":162,"children":164},{"className":163},[],[165],{"type":58,"value":166},"$pathname",{"type":58,"value":168}," by views via HogQL.",{"type":53,"tag":123,"props":170,"children":171},{},[172,178,180,186,188,194,196,202],{"type":53,"tag":75,"props":173,"children":175},{"className":174},[],[176],{"type":58,"value":177},"call_llm_for_rules",{"type":58,"value":179}," — one-shot call through the LLM gateway\n(",{"type":53,"tag":75,"props":181,"children":183},{"className":182},[],[184],{"type":58,"value":185},"get_llm_client(product=\"web_analytics\", team_id=...)",{"type":58,"value":187},", model\n",{"type":53,"tag":75,"props":189,"children":191},{"className":190},[],[192],{"type":58,"value":193},"WEB_ANALYTICS_PATH_CLEANING_SUGGESTIONS_MODEL",{"type":58,"value":195},", default ",{"type":53,"tag":75,"props":197,"children":199},{"className":198},[],[200],{"type":58,"value":201},"claude-haiku-4-5",{"type":58,"value":203},").",{"type":53,"tag":123,"props":205,"children":206},{},[207,213,215,220,222,228,230,236,238,244,246,252,254,260],{"type":53,"tag":75,"props":208,"children":210},{"className":209},[],[211],{"type":58,"value":212},"validate_and_annotate_rules",{"type":58,"value":214}," — compiles each regex with ",{"type":53,"tag":67,"props":216,"children":217},{},[218],{"type":58,"value":219},"re2",{"type":58,"value":221}," (the engine ClickHouse\n",{"type":53,"tag":75,"props":223,"children":225},{"className":224},[],[226],{"type":58,"value":227},"replaceRegexpAll",{"type":58,"value":229}," uses) and test-applies it to the sampled paths. Rules that don't compile or\nmatch nothing are dropped; survivors get a dense ",{"type":53,"tag":75,"props":231,"children":233},{"className":232},[],[234],{"type":58,"value":235},"order",{"type":58,"value":237},", a ",{"type":53,"tag":75,"props":239,"children":241},{"className":240},[],[242],{"type":58,"value":243},"match_count",{"type":58,"value":245},", and in-memory\nbefore\u002Fafter ",{"type":53,"tag":75,"props":247,"children":249},{"className":248},[],[250],{"type":58,"value":251},"examples",{"type":58,"value":253}," (printed by the management command, never stored — health-issue\npayloads are readable with just ",{"type":53,"tag":75,"props":255,"children":257},{"className":256},[],[258],{"type":58,"value":259},"health_issue:read",{"type":58,"value":261}," and must not leak real paths). This is\nthe skill's \"test before saving\" step, automated.",{"type":53,"tag":123,"props":263,"children":264},{},[265,271],{"type":53,"tag":75,"props":266,"children":268},{"className":267},[],[269],{"type":58,"value":270},"generate_suggestions_for_team",{"type":58,"value":272}," — orchestrates the above with gating (see below); pure\ngeneration, no storage.",{"type":53,"tag":123,"props":274,"children":275},{},[276,282,284,289,291,297,299,304],{"type":53,"tag":75,"props":277,"children":279},{"className":278},[],[280],{"type":58,"value":281},"apply_suggestions_to_team",{"type":58,"value":283}," — ",{"type":53,"tag":67,"props":285,"children":286},{},[287],{"type":58,"value":288},"merges",{"type":58,"value":290}," rules into ",{"type":53,"tag":75,"props":292,"children":294},{"className":293},[],[295],{"type":58,"value":296},"path_cleaning_filters",{"type":58,"value":298},", never overwrites\n(dedupes by regex, continues ",{"type":53,"tag":75,"props":300,"children":302},{"className":301},[],[303],{"type":58,"value":235},{"type":58,"value":203},{"type":53,"tag":123,"props":306,"children":307},{},[308,313,315,321,323,328,330,336,338,344,346,352,354,360,362,368,370,376,378,384,385,391,393,399],{"type":53,"tag":67,"props":309,"children":310},{},[311],{"type":58,"value":312},"Storage",{"type":58,"value":314},": a ",{"type":53,"tag":75,"props":316,"children":318},{"className":317},[],[319],{"type":58,"value":320},"path_cleaning_suggestions",{"type":58,"value":322}," ",{"type":53,"tag":67,"props":324,"children":325},{},[326],{"type":58,"value":327},"health issue",{"type":58,"value":329}," (",{"type":53,"tag":75,"props":331,"children":333},{"className":332},[],[334],{"type":58,"value":335},"HealthIssue",{"type":58,"value":337},", severity ",{"type":53,"tag":75,"props":339,"children":341},{"className":340},[],[342],{"type":58,"value":343},"info",{"type":58,"value":345},") — no\ndedicated model. One active issue per team (",{"type":53,"tag":75,"props":347,"children":349},{"className":348},[],[350],{"type":58,"value":351},"hash_keys=[]",{"type":58,"value":353},"); ",{"type":53,"tag":75,"props":355,"children":357},{"className":356},[],[358],{"type":58,"value":359},"payload",{"type":58,"value":361}," carries ",{"type":53,"tag":75,"props":363,"children":365},{"className":364},[],[366],{"type":58,"value":367},"rules",{"type":58,"value":369},", ",{"type":53,"tag":75,"props":371,"children":373},{"className":372},[],[374],{"type":58,"value":375},"model",{"type":58,"value":377},",\n",{"type":53,"tag":75,"props":379,"children":381},{"className":380},[],[382],{"type":58,"value":383},"sampled_path_count",{"type":58,"value":369},{"type":53,"tag":75,"props":386,"children":388},{"className":387},[],[389],{"type":58,"value":390},"distinct_path_count",{"type":58,"value":392},". Applying (or hand-configuring rules) resolves the\nissue on the next check run; dismissal is the health-issue ",{"type":53,"tag":75,"props":394,"children":396},{"className":395},[],[397],{"type":58,"value":398},"dismissed",{"type":58,"value":400}," flag.",{"type":53,"tag":123,"props":402,"children":403},{},[404,409,410,416,418,424],{"type":53,"tag":67,"props":405,"children":406},{},[407],{"type":58,"value":408},"Schedule",{"type":58,"value":132},{"type":53,"tag":75,"props":411,"children":413},{"className":412},[],[414],{"type":58,"value":415},"PathCleaningSuggestionsCheck",{"type":58,"value":417},"\n(",{"type":53,"tag":75,"props":419,"children":421},{"className":420},[],[422],{"type":58,"value":423},"products\u002Fweb_analytics\u002Fbackend\u002Ftemporal\u002Fhealth_checks\u002Fpath_cleaning_suggestions.py",{"type":58,"value":425},"), a health\ncheck on the shared health-check framework, weekly (Mon 06:23 UTC), small sequential batches\nbecause each eligible team costs an LLM call. Teams with an existing active suggestion are\nre-emitted without a fresh LLM round trip.",{"type":53,"tag":123,"props":427,"children":428},{},[429,434,435,441,443,449],{"type":53,"tag":67,"props":430,"children":431},{},[432],{"type":58,"value":433},"Cohort",{"type":58,"value":132},{"type":53,"tag":75,"props":436,"children":438},{"className":437},[],[439],{"type":58,"value":440},"WEB_ANALYTICS_PATH_CLEANING_SUGGESTIONS_TEAM_IDS",{"type":58,"value":442},", defaulting to the precompute\nenrollment list ",{"type":53,"tag":75,"props":444,"children":446},{"className":445},[],[447],{"type":58,"value":448},"WEB_ANALYTICS_LAZY_PRECOMPUTE_TEAM_IDS",{"type":58,"value":450},".",{"type":53,"tag":112,"props":452,"children":454},{"id":453},"gating-why-a-team-is-skipped",[455],{"type":58,"value":456},"Gating (why a team is skipped)",{"type":53,"tag":61,"props":458,"children":459},{},[460,465],{"type":53,"tag":75,"props":461,"children":463},{"className":462},[],[464],{"type":58,"value":270},{"type":58,"value":466}," returns a status:",{"type":53,"tag":119,"props":468,"children":469},{},[470,504,522,541,552,563],{"type":53,"tag":123,"props":471,"children":472},{},[473,479,481,487,489,495,497,503],{"type":53,"tag":75,"props":474,"children":476},{"className":475},[],[477],{"type":58,"value":478},"skipped_inactive",{"type":58,"value":480}," — team sent no ",{"type":53,"tag":75,"props":482,"children":484},{"className":483},[],[485],{"type":58,"value":486},"$pageview",{"type":58,"value":488}," within ",{"type":53,"tag":75,"props":490,"children":492},{"className":491},[],[493],{"type":58,"value":494},"visited_within_days",{"type":58,"value":496}," (default 30); we only\nsuggest for teams actively using web analytics. Bypass with ",{"type":53,"tag":75,"props":498,"children":500},{"className":499},[],[501],{"type":58,"value":502},"--ignore-visit-gate",{"type":58,"value":450},{"type":53,"tag":123,"props":505,"children":506},{},[507,513,515,521],{"type":53,"tag":75,"props":508,"children":510},{"className":509},[],[511],{"type":58,"value":512},"skipped_configured",{"type":58,"value":514}," — team already has path cleaning rules (override with ",{"type":53,"tag":75,"props":516,"children":518},{"className":517},[],[519],{"type":58,"value":520},"include_configured",{"type":58,"value":203},{"type":53,"tag":123,"props":523,"children":524},{},[525,531,533,539],{"type":53,"tag":75,"props":526,"children":528},{"className":527},[],[529],{"type":58,"value":530},"skipped_low_cardinality",{"type":58,"value":532}," — fewer distinct paths than ",{"type":53,"tag":75,"props":534,"children":536},{"className":535},[],[537],{"type":58,"value":538},"min_distinct_paths",{"type":58,"value":540}," (default 50); cleaning\nadds no value, so we don't spend tokens.",{"type":53,"tag":123,"props":542,"children":543},{},[544,550],{"type":53,"tag":75,"props":545,"children":547},{"className":546},[],[548],{"type":58,"value":549},"skipped_no_paths",{"type":58,"value":551}," — no pageviews in the window.",{"type":53,"tag":123,"props":553,"children":554},{},[555,561],{"type":53,"tag":75,"props":556,"children":558},{"className":557},[],[559],{"type":58,"value":560},"generated",{"type":58,"value":562}," — rules produced (may be an empty list if paths are already clean; empty generations\nare never stored, so they can't shadow an actionable suggestion).",{"type":53,"tag":123,"props":564,"children":565},{},[566,572],{"type":53,"tag":75,"props":567,"children":569},{"className":568},[],[570],{"type":58,"value":571},"error",{"type":58,"value":573}," — sampling\u002FLLM failed; captured per-team, never aborts the cohort sweep.",{"type":53,"tag":112,"props":575,"children":577},{"id":576},"how-users-see-and-apply-suggestions",[578],{"type":58,"value":579},"How users see and apply suggestions",{"type":53,"tag":119,"props":581,"children":582},{},[583,623,648,718,736],{"type":53,"tag":123,"props":584,"children":585},{},[586,591,592,598,600,606,608,614,616,622],{"type":53,"tag":67,"props":587,"children":588},{},[589],{"type":58,"value":590},"Settings banner",{"type":58,"value":132},{"type":53,"tag":75,"props":593,"children":595},{"className":594},[],[596],{"type":58,"value":597},"PathCleaningSuggestionsBanner",{"type":58,"value":599}," on ",{"type":53,"tag":75,"props":601,"children":603},{"className":602},[],[604],{"type":58,"value":605},"\u002Fsettings\u002Fproject#path_cleaning",{"type":58,"value":607}," shows the\nlatest ",{"type":53,"tag":75,"props":609,"children":611},{"className":610},[],[612],{"type":58,"value":613},"suggested",{"type":58,"value":615}," row as regex → alias previews with match counts; \"Apply all\" (project admins\nonly) merges the rules, the close button dismisses. Driven by ",{"type":53,"tag":75,"props":617,"children":619},{"className":618},[],[620],{"type":58,"value":621},"pathCleaningSuggestionsLogic",{"type":58,"value":450},{"type":53,"tag":123,"props":624,"children":625},{},[626,631,632,638,640,646],{"type":53,"tag":67,"props":627,"children":628},{},[629],{"type":58,"value":630},"Onboarding step",{"type":58,"value":132},{"type":53,"tag":75,"props":633,"children":635},{"className":634},[],[636],{"type":58,"value":637},"OnboardingWebAnalyticsPathCleaningStep",{"type":58,"value":639}," (stepKey ",{"type":53,"tag":75,"props":641,"children":643},{"className":642},[],[644],{"type":58,"value":645},"path_cleaning",{"type":58,"value":647},") surfaces the\nsame banner during Web analytics onboarding.",{"type":53,"tag":123,"props":649,"children":650},{},[651,656,657,663,665,671,673,679,681,687,689,694,696,702,703,709,711,717],{"type":53,"tag":67,"props":652,"children":653},{},[654],{"type":58,"value":655},"API",{"type":58,"value":329},{"type":53,"tag":75,"props":658,"children":660},{"className":659},[],[661],{"type":58,"value":662},"products\u002Fweb_analytics\u002Fbackend\u002Fapi\u002Fweb_analytics_path_cleaning_suggestions.py",{"type":58,"value":664},"):\n",{"type":53,"tag":75,"props":666,"children":668},{"className":667},[],[669],{"type":58,"value":670},"POST \u002Fapi\u002Fprojects\u002F:id\u002Fweb_analytics_path_cleaning_suggestions\u002Fgenerate\u002F",{"type":58,"value":672}," produces and stores a\nfresh suggestion on demand; ",{"type":53,"tag":75,"props":674,"children":676},{"className":675},[],[677],{"type":58,"value":678},"GET ...\u002F{issue_id}\u002Fpreview\u002F",{"type":58,"value":680}," applies the rules to a fresh sample of\nthe team's top paths and returns before\u002Fafter pairs (read scope, computed on demand, never\nstored — this backs the banner's \"Preview on your paths\" modal); ",{"type":53,"tag":75,"props":682,"children":684},{"className":683},[],[685],{"type":58,"value":686},"POST ...\u002F{issue_id}\u002Fapply\u002F",{"type":58,"value":688},"\nmerges the rules and resolves the issue (project admin only — the same gate the team API puts on\n",{"type":53,"tag":75,"props":690,"children":692},{"className":691},[],[693],{"type":58,"value":296},{"type":58,"value":695},").\nListing and dismissing go through the generic health-issues API\n(",{"type":53,"tag":75,"props":697,"children":699},{"className":698},[],[700],{"type":58,"value":701},"GET \u002Fapi\u002Fprojects\u002F:id\u002Fhealth_issues\u002F?kind=path_cleaning_suggestions&status=active&dismissed=false",{"type":58,"value":377},{"type":53,"tag":75,"props":704,"children":706},{"className":705},[],[707],{"type":58,"value":708},"PATCH ...\u002Fhealth_issues\u002F{id}\u002F",{"type":58,"value":710}," with ",{"type":53,"tag":75,"props":712,"children":714},{"className":713},[],[715],{"type":58,"value":716},"{\"dismissed\": true}",{"type":58,"value":203},{"type":53,"tag":123,"props":719,"children":720},{},[721,726,728,734],{"type":53,"tag":67,"props":722,"children":723},{},[724],{"type":58,"value":725},"Health page",{"type":58,"value":727},": the check renders on ",{"type":53,"tag":75,"props":729,"children":731},{"className":730},[],[732],{"type":58,"value":733},"\u002Fweb\u002Fhealth",{"type":58,"value":735}," alongside the other web-analytics checks, with\nremediation guidance for humans and agents.",{"type":53,"tag":123,"props":737,"children":738},{},[739,744,746,752,753,759,761,767],{"type":53,"tag":67,"props":740,"children":741},{},[742],{"type":58,"value":743},"PostHog AI (Max)",{"type":58,"value":745},": generate\u002Fapply are exposed as MCP tools in\n",{"type":53,"tag":75,"props":747,"children":749},{"className":748},[],[750],{"type":58,"value":751},"products\u002Fweb_analytics\u002Fmcp\u002Ftools.yaml",{"type":58,"value":329},{"type":53,"tag":75,"props":754,"children":756},{"className":755},[],[757],{"type":58,"value":758},"web-analytics-path-cleaning-suggestions-{generate,apply}",{"type":58,"value":760},"),\nso a user can ask Max to suggest path-cleaning rules and apply them conversationally. Apply is\n",{"type":53,"tag":75,"props":762,"children":764},{"className":763},[],[765],{"type":58,"value":766},"destructive",{"type":58,"value":768}," (it changes historical chart numbers), so the MCP confirmation gate applies.",{"type":53,"tag":112,"props":770,"children":772},{"id":771},"running-it",[773],{"type":58,"value":774},"Running it",{"type":53,"tag":776,"props":777,"children":782},"pre",{"className":778,"code":779,"language":780,"meta":781,"style":781},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Default cohort, print suggestions, store health issues:\npython manage.py suggest_path_cleaning_rules\n\n# Specific teams, dry run (nothing stored):\npython manage.py suggest_path_cleaning_rules --teams 2,19279 --no-store\n\n# Generate AND apply for one reviewed team (merges, never overwrites):\npython manage.py suggest_path_cleaning_rules --teams 2 --apply\n","sh","",[783],{"type":53,"tag":75,"props":784,"children":785},{"__ignoreMap":781},[786,798,818,828,837,869,877,886],{"type":53,"tag":787,"props":788,"children":791},"span",{"class":789,"line":790},"line",1,[792],{"type":53,"tag":787,"props":793,"children":795},{"style":794},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[796],{"type":58,"value":797},"# Default cohort, print suggestions, store health issues:\n",{"type":53,"tag":787,"props":799,"children":801},{"class":789,"line":800},2,[802,807,813],{"type":53,"tag":787,"props":803,"children":805},{"style":804},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[806],{"type":58,"value":36},{"type":53,"tag":787,"props":808,"children":810},{"style":809},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[811],{"type":58,"value":812}," manage.py",{"type":53,"tag":787,"props":814,"children":815},{"style":809},[816],{"type":58,"value":817}," suggest_path_cleaning_rules\n",{"type":53,"tag":787,"props":819,"children":821},{"class":789,"line":820},3,[822],{"type":53,"tag":787,"props":823,"children":825},{"emptyLinePlaceholder":824},true,[826],{"type":58,"value":827},"\n",{"type":53,"tag":787,"props":829,"children":831},{"class":789,"line":830},4,[832],{"type":53,"tag":787,"props":833,"children":834},{"style":794},[835],{"type":58,"value":836},"# Specific teams, dry run (nothing stored):\n",{"type":53,"tag":787,"props":838,"children":840},{"class":789,"line":839},5,[841,845,849,854,859,864],{"type":53,"tag":787,"props":842,"children":843},{"style":804},[844],{"type":58,"value":36},{"type":53,"tag":787,"props":846,"children":847},{"style":809},[848],{"type":58,"value":812},{"type":53,"tag":787,"props":850,"children":851},{"style":809},[852],{"type":58,"value":853}," suggest_path_cleaning_rules",{"type":53,"tag":787,"props":855,"children":856},{"style":809},[857],{"type":58,"value":858}," --teams",{"type":53,"tag":787,"props":860,"children":861},{"style":809},[862],{"type":58,"value":863}," 2,19279",{"type":53,"tag":787,"props":865,"children":866},{"style":809},[867],{"type":58,"value":868}," --no-store\n",{"type":53,"tag":787,"props":870,"children":872},{"class":789,"line":871},6,[873],{"type":53,"tag":787,"props":874,"children":875},{"emptyLinePlaceholder":824},[876],{"type":58,"value":827},{"type":53,"tag":787,"props":878,"children":880},{"class":789,"line":879},7,[881],{"type":53,"tag":787,"props":882,"children":883},{"style":794},[884],{"type":58,"value":885},"# Generate AND apply for one reviewed team (merges, never overwrites):\n",{"type":53,"tag":787,"props":887,"children":889},{"class":789,"line":888},8,[890,894,898,902,906,912],{"type":53,"tag":787,"props":891,"children":892},{"style":804},[893],{"type":58,"value":36},{"type":53,"tag":787,"props":895,"children":896},{"style":809},[897],{"type":58,"value":812},{"type":53,"tag":787,"props":899,"children":900},{"style":809},[901],{"type":58,"value":853},{"type":53,"tag":787,"props":903,"children":904},{"style":809},[905],{"type":58,"value":858},{"type":53,"tag":787,"props":907,"children":909},{"style":908},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[910],{"type":58,"value":911}," 2",{"type":53,"tag":787,"props":913,"children":914},{"style":809},[915],{"type":58,"value":916}," --apply\n",{"type":53,"tag":61,"props":918,"children":919},{},[920,922,928,930,936,938,944,945,951,952,958,959,964],{"type":58,"value":921},"Useful flags: ",{"type":53,"tag":75,"props":923,"children":925},{"className":924},[],[926],{"type":58,"value":927},"--days",{"type":58,"value":929}," (lookback), ",{"type":53,"tag":75,"props":931,"children":933},{"className":932},[],[934],{"type":58,"value":935},"--limit",{"type":58,"value":937}," (top-N paths sampled), ",{"type":53,"tag":75,"props":939,"children":941},{"className":940},[],[942],{"type":58,"value":943},"--min-distinct-paths",{"type":58,"value":377},{"type":53,"tag":75,"props":946,"children":948},{"className":947},[],[949],{"type":58,"value":950},"--include-configured",{"type":58,"value":369},{"type":53,"tag":75,"props":953,"children":955},{"className":954},[],[956],{"type":58,"value":957},"--no-store",{"type":58,"value":369},{"type":53,"tag":75,"props":960,"children":962},{"className":961},[],[963],{"type":58,"value":100},{"type":58,"value":450},{"type":53,"tag":61,"props":966,"children":967},{},[968,970,976],{"type":58,"value":969},"The health check can also be triggered per team from the health-issues ",{"type":53,"tag":75,"props":971,"children":973},{"className":972},[],[974],{"type":58,"value":975},"refresh",{"type":58,"value":977}," endpoint or the\nadmin UI, like any other health check.",{"type":53,"tag":112,"props":979,"children":981},{"id":980},"reviewing-suggestions",[982],{"type":58,"value":983},"Reviewing suggestions",{"type":53,"tag":61,"props":985,"children":986},{},[987],{"type":58,"value":988},"Read a team's active suggestion:",{"type":53,"tag":776,"props":990,"children":993},{"className":991,"code":992,"language":36,"meta":781,"style":781},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","HealthIssue.objects.filter(team_id=team_id, kind=\"path_cleaning_suggestions\", status=\"active\").first()\n",[994],{"type":53,"tag":75,"props":995,"children":996},{"__ignoreMap":781},[997],{"type":53,"tag":787,"props":998,"children":999},{"class":789,"line":790},[1000],{"type":53,"tag":787,"props":1001,"children":1002},{},[1003],{"type":58,"value":992},{"type":53,"tag":61,"props":1005,"children":1006},{},[1007,1009,1015,1016,1022,1023,1029,1030,1035,1036,1042,1044,1049],{"type":58,"value":1008},"Each rule in ",{"type":53,"tag":75,"props":1010,"children":1012},{"className":1011},[],[1013],{"type":58,"value":1014},"payload[\"rules\"]",{"type":58,"value":361},{"type":53,"tag":75,"props":1017,"children":1019},{"className":1018},[],[1020],{"type":58,"value":1021},"regex",{"type":58,"value":369},{"type":53,"tag":75,"props":1024,"children":1026},{"className":1025},[],[1027],{"type":58,"value":1028},"alias",{"type":58,"value":369},{"type":53,"tag":75,"props":1031,"children":1033},{"className":1032},[],[1034],{"type":58,"value":235},{"type":58,"value":369},{"type":53,"tag":75,"props":1037,"children":1039},{"className":1038},[],[1040],{"type":58,"value":1041},"reason",{"type":58,"value":1043},", and ",{"type":53,"tag":75,"props":1045,"children":1047},{"className":1046},[],[1048],{"type":58,"value":243},{"type":58,"value":1050}," —\nthat's what to show a human deciding whether to apply. Before\u002Fafter examples on real paths are only\nprinted by the management command at generation time; they are deliberately kept out of the stored\npayload.",{"type":53,"tag":112,"props":1052,"children":1054},{"id":1053},"extending",[1055],{"type":58,"value":1056},"Extending",{"type":53,"tag":119,"props":1058,"children":1059},{},[1060,1079,1099],{"type":53,"tag":123,"props":1061,"children":1062},{},[1063,1065,1070,1072,1077],{"type":58,"value":1064},"Adding a surfacing channel (in-app notification, settings banner, onboarding wizard step): read the\nteam's active ",{"type":53,"tag":75,"props":1066,"children":1068},{"className":1067},[],[1069],{"type":58,"value":320},{"type":58,"value":1071}," health issue and render its ",{"type":53,"tag":75,"props":1073,"children":1075},{"className":1074},[],[1076],{"type":58,"value":1014},{"type":58,"value":1078},". Keep\napply manual.",{"type":53,"tag":123,"props":1080,"children":1081},{},[1082,1084,1090,1092,1098],{"type":58,"value":1083},"Changing the model: it must be allowlisted for the ",{"type":53,"tag":75,"props":1085,"children":1087},{"className":1086},[],[1088],{"type":58,"value":1089},"web_analytics",{"type":58,"value":1091}," product in\n",{"type":53,"tag":75,"props":1093,"children":1095},{"className":1094},[],[1096],{"type":58,"value":1097},"services\u002Fllm-gateway\u002Fsrc\u002Fllm_gateway\u002Fproducts\u002Fconfig.py",{"type":58,"value":450},{"type":53,"tag":123,"props":1100,"children":1101},{},[1102,1104,1110],{"type":58,"value":1103},"The agentic alternative — a ",{"type":53,"tag":75,"props":1105,"children":1107},{"className":1106},[],[1108],{"type":58,"value":1109},"signals-scout-web-analytics-path-cleaning",{"type":58,"value":1111}," scout — is sketched in the\ndesign notes; prefer the dedicated job for the precompute cohort because it targets that exact\ncohort and surfaces structured, validated rows rather than Signals-inbox findings.",{"type":53,"tag":1113,"props":1114,"children":1115},"style",{},[1116],{"type":58,"value":1117},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1119,"total":1220},[1120,1135,1147,1159,1172,1187,1203],{"slug":1121,"name":1121,"fn":1122,"description":1123,"org":1124,"tags":1125,"stars":22,"repoUrl":23,"updatedAt":1134},"analyzing-expensive-users","analyze expensive users in AI observability","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.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1126,1127,1130,1133],{"name":20,"slug":21,"type":15},{"name":1128,"slug":1129,"type":15},"Cost Optimization","cost-optimization",{"name":1131,"slug":1132,"type":15},"Observability","observability",{"name":9,"slug":8,"type":15},"2026-07-28T05:34:11.117757",{"slug":1136,"name":1136,"fn":1137,"description":1138,"org":1139,"tags":1140,"stars":22,"repoUrl":23,"updatedAt":1146},"auditing-endpoints","audit PostHog project endpoints","Audit every endpoint in a PostHog project for staleness, failed materialisations, and unused materialised versions. Use when the user asks \"what endpoints can I clean up?\", \"are any of my endpoints broken?\", \"which materialised versions are still being called?\", or wants a one-shot cleanup pass over the Endpoints product. Produces a prioritised report grouped by issue type, with recommended actions but does not modify anything without explicit confirmation.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1141,1142,1145],{"name":20,"slug":21,"type":15},{"name":1143,"slug":1144,"type":15},"Audit","audit",{"name":9,"slug":8,"type":15},"2026-06-08T08:08:33.693989",{"slug":1148,"name":1148,"fn":1149,"description":1150,"org":1151,"tags":1152,"stars":22,"repoUrl":23,"updatedAt":1158},"auditing-warehouse-source-health","audit PostHog data warehouse source health","Audit the health of a PostHog project's data warehouse sources and syncs — find every broken or degraded source connection, sync schema, and webhook channel. Use when the user asks \"why are my imports failing?\", \"what's broken with my sources?\", \"why is my warehouse data stale?\", or wants a one-shot triage of source\u002Fsync health before deciding where to dig in. Produces a prioritized report grouped by severity, with recommended next steps. For materialized-view health use `auditing-warehouse-view-health`; for a single failing sync use `diagnosing-failed-warehouse-syncs`.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1153,1154,1156,1157],{"name":1143,"slug":1144,"type":15},{"name":1155,"slug":31,"type":15},"Data Warehouse",{"name":1131,"slug":1132,"type":15},{"name":9,"slug":8,"type":15},"2026-06-18T08:22:57.67984",{"slug":1160,"name":1160,"fn":1161,"description":1162,"org":1163,"tags":1164,"stars":22,"repoUrl":23,"updatedAt":1171},"auditing-warehouse-view-health","audit PostHog materialized view health","Audit the health of a PostHog project's materialized views (saved queries) — find every failed materialization and flag unused or stale materialized views that cost storage and compute. Use when the user asks \"which of my views are broken?\", \"why is this materialized view failing?\", \"are any of my views wasting compute?\", or wants a one-shot triage of view health. For source\u002Fsync health use `auditing-warehouse-source-health`.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1165,1166,1167,1170],{"name":1143,"slug":1144,"type":15},{"name":1155,"slug":31,"type":15},{"name":1168,"slug":1169,"type":15},"Performance","performance",{"name":9,"slug":8,"type":15},"2026-06-18T08:25:10.936787",{"slug":1173,"name":1173,"fn":1174,"description":1175,"org":1176,"tags":1177,"stars":22,"repoUrl":23,"updatedAt":1186},"authoring-error-tracking-alerts","author PostHog error tracking alerts","Author error tracking alerts that fire when an issue is created, reopened, or starts spiking. Use when the user asks to set up error notifications, route exceptions to Slack\u002Fwebhook\u002FLinear, or evaluate which error events are worth alerting on. Covers trigger-event selection, integration choice, dedup against existing alerts, and shipping with the canonical message body shape.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1178,1181,1184,1185],{"name":1179,"slug":1180,"type":15},"Alerting","alerting",{"name":1182,"slug":1183,"type":15},"Debugging","debugging",{"name":1131,"slug":1132,"type":15},{"name":9,"slug":8,"type":15},"2026-06-18T08:24:40.318583",{"slug":1188,"name":1188,"fn":1189,"description":1190,"org":1191,"tags":1192,"stars":22,"repoUrl":23,"updatedAt":1202},"authoring-log-alerts","author log alerts in PostHog","Author useful, low-noise log alerts on services in a PostHog project. Use when the user asks to set up alerts for their logs, suggest alerts they should add, or evaluate whether a service is worth monitoring. Covers service triage, baseline characterisation, threshold drafting, back-testing via simulate, and shipping with a notification destination.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1193,1194,1197,1198,1201],{"name":20,"slug":21,"type":15},{"name":1195,"slug":1196,"type":15},"Monitoring","monitoring",{"name":1131,"slug":1132,"type":15},{"name":1199,"slug":1200,"type":15},"Operations","operations",{"name":9,"slug":8,"type":15},"2026-07-18T05:10:54.430898",{"slug":1204,"name":1204,"fn":1205,"description":1206,"org":1207,"tags":1208,"stars":22,"repoUrl":23,"updatedAt":1219},"building-canvases","create and edit PostHog canvases","Create or edit a PostHog canvas — a sandboxed browser application (data board, document, form, small tool, graphics experiment) stored in PostHog and rendered by the desktop\u002Fweb app. Use when a task asks to build, generate, update, or fix a canvas, or when a canvas id is given as the publish target. Covers resolving or creating the target canvas, choosing an implementation approach (React + Quill vs plain HTML\u002Fbrowser APIs), the read → edit → validate → publish → build loop, and which companion canvas skills to load for the details.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1209,1212,1215,1216],{"name":1210,"slug":1211,"type":15},"Automation","automation",{"name":1213,"slug":1214,"type":15},"Design","design",{"name":9,"slug":8,"type":15},{"name":1217,"slug":1218,"type":15},"Prototyping","prototyping","2026-08-06T06:09:29.946969",74,{"items":1222,"total":1348},[1223,1230,1236,1243,1250,1257,1265,1272,1290,1306,1321,1333],{"slug":1121,"name":1121,"fn":1122,"description":1123,"org":1224,"tags":1225,"stars":22,"repoUrl":23,"updatedAt":1134},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1226,1227,1228,1229],{"name":20,"slug":21,"type":15},{"name":1128,"slug":1129,"type":15},{"name":1131,"slug":1132,"type":15},{"name":9,"slug":8,"type":15},{"slug":1136,"name":1136,"fn":1137,"description":1138,"org":1231,"tags":1232,"stars":22,"repoUrl":23,"updatedAt":1146},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1233,1234,1235],{"name":20,"slug":21,"type":15},{"name":1143,"slug":1144,"type":15},{"name":9,"slug":8,"type":15},{"slug":1148,"name":1148,"fn":1149,"description":1150,"org":1237,"tags":1238,"stars":22,"repoUrl":23,"updatedAt":1158},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1239,1240,1241,1242],{"name":1143,"slug":1144,"type":15},{"name":1155,"slug":31,"type":15},{"name":1131,"slug":1132,"type":15},{"name":9,"slug":8,"type":15},{"slug":1160,"name":1160,"fn":1161,"description":1162,"org":1244,"tags":1245,"stars":22,"repoUrl":23,"updatedAt":1171},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1246,1247,1248,1249],{"name":1143,"slug":1144,"type":15},{"name":1155,"slug":31,"type":15},{"name":1168,"slug":1169,"type":15},{"name":9,"slug":8,"type":15},{"slug":1173,"name":1173,"fn":1174,"description":1175,"org":1251,"tags":1252,"stars":22,"repoUrl":23,"updatedAt":1186},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1253,1254,1255,1256],{"name":1179,"slug":1180,"type":15},{"name":1182,"slug":1183,"type":15},{"name":1131,"slug":1132,"type":15},{"name":9,"slug":8,"type":15},{"slug":1188,"name":1188,"fn":1189,"description":1190,"org":1258,"tags":1259,"stars":22,"repoUrl":23,"updatedAt":1202},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1260,1261,1262,1263,1264],{"name":20,"slug":21,"type":15},{"name":1195,"slug":1196,"type":15},{"name":1131,"slug":1132,"type":15},{"name":1199,"slug":1200,"type":15},{"name":9,"slug":8,"type":15},{"slug":1204,"name":1204,"fn":1205,"description":1206,"org":1266,"tags":1267,"stars":22,"repoUrl":23,"updatedAt":1219},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1268,1269,1270,1271],{"name":1210,"slug":1211,"type":15},{"name":1213,"slug":1214,"type":15},{"name":9,"slug":8,"type":15},{"name":1217,"slug":1218,"type":15},{"slug":1273,"name":1273,"fn":1274,"description":1275,"org":1276,"tags":1277,"stars":22,"repoUrl":23,"updatedAt":1289},"building-html-canvases","author HTML and CSS PostHog canvases","Author a PostHog canvas with semantic HTML, CSS, and direct browser APIs — documents, articles, generative graphics, 2D canvas and WebGL experiences, and focused experiments where React components add no useful structure. Use after building-canvases has routed a canvas request to a plain-HTML\u002Fbrowser-API implementation. Covers the thin component wrapper the current runtime requires, styling and theming without Quill, drawing surfaces, and animation\u002Fcleanup patterns.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1278,1281,1282,1285,1288],{"name":1279,"slug":1280,"type":15},"CSS","css",{"name":1213,"slug":1214,"type":15},{"name":1283,"slug":1284,"type":15},"Graphics","graphics",{"name":1286,"slug":1287,"type":15},"HTML","html",{"name":9,"slug":8,"type":15},"2026-08-06T06:09:30.313848",{"slug":1291,"name":1291,"fn":1292,"description":1293,"org":1294,"tags":1295,"stars":22,"repoUrl":23,"updatedAt":1305},"building-react-quill-canvases","build React and Quill canvases","Author the React + Quill implementation of a PostHog canvas: the single-component contract, the allowed imports, Quill (PostHog's design system) component and composition rules, theme-aware design tokens, loading skeletons, and the in-canvas date picker. Use after building-canvases has routed a canvas request to a React implementation — dashboards, data boards, forms, tools, or any canvas that should look native to PostHog.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1296,1297,1300,1302],{"name":1213,"slug":1214,"type":15},{"name":1298,"slug":1299,"type":15},"Frontend","frontend",{"name":1301,"slug":37,"type":15},"React",{"name":1303,"slug":1304,"type":15},"UI Components","ui-components","2026-08-06T06:09:18.633724",{"slug":1307,"name":1307,"fn":1308,"description":1309,"org":1310,"tags":1311,"stars":22,"repoUrl":23,"updatedAt":1320},"building-workflows","build and edit PostHog workflows","Build, edit, test, enable, and monitor PostHog workflows over MCP. Author the action\u002Fedge graph so it runs and opens cleanly in the visual editor, then change drafts surgically with patch operations. Use when asked to build, set up, automate, change, fix, or debug a workflow, campaign, broadcast, drip sequence, or event-triggered automation in the workflows product.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1312,1313,1316,1317],{"name":1210,"slug":1211,"type":15},{"name":1314,"slug":1315,"type":15},"MCP","mcp",{"name":9,"slug":8,"type":15},{"name":1318,"slug":1319,"type":15},"Workflow Automation","workflow-automation","2026-08-06T05:36:26.504811",{"slug":1322,"name":1322,"fn":1323,"description":1324,"org":1325,"tags":1326,"stars":22,"repoUrl":23,"updatedAt":1332},"check-posthog-loading","inspect PostHog SDK loading across URLs","Inspect how the PostHog JavaScript SDK is loaded across a list of URLs. Use to confirm consistent installation across pages, find pages missing the snippet, detect mismatched API keys or hosts between pages, and verify the load method (head snippet vs deferred vs array.js).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1327,1328,1329,1330,1331],{"name":20,"slug":21,"type":15},{"name":1182,"slug":1183,"type":15},{"name":1298,"slug":1299,"type":15},{"name":1131,"slug":1132,"type":15},{"name":9,"slug":8,"type":15},"2026-05-07T05:56:19.828048",{"slug":1334,"name":1334,"fn":1335,"description":1336,"org":1337,"tags":1338,"stars":22,"repoUrl":23,"updatedAt":1347},"consuming-endpoints-from-client-code","integrate PostHog endpoints into client applications","Wire a PostHog endpoint into a client app or SDK. Covers fetching the OpenAPI spec, generating a typed client with openapi-generator or @hey-api\u002Fopenapi-ts, sending the right auth header, shaping the variables payload (HogQL code_name vs insight breakdown property), handling rate-limit and materialised-endpoint error responses. Use when the user says \"how do I call my endpoint\", \"generate a client for this\", or \"what auth header do I use\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1339,1342,1343,1344],{"name":1340,"slug":1341,"type":15},"API Development","api-development",{"name":1298,"slug":1299,"type":15},{"name":9,"slug":8,"type":15},{"name":1345,"slug":1346,"type":15},"SDK","sdk","2026-06-08T08:08:34.929454",243]