posthog/posthog-investigate-metric
> Diagnose why a product metric changed (dropped, spiked, or plateaued) by orchestrating breakdowns, actors, paths, lifecycle, retention, and annotations queries. Use when the user reports an anomaly, asks "why did X change?", or needs root-cause analysis for a trend, funnel, retention, stickiness, or lifecycle metric.
This is a copy. The original lives at posthog/ai-plugin-investigate-metric.
npx skills add https://github.com/PostHog/posthog --skill investigate-metric
For "why did X change?" questions about a saved insight, dashboard tile, or pasted query.
Don't load this skill for plain "what is X?" questions — only when there's an observed
change to explain.
Targets PostHog MCP v2. Typed query tools accept the query body directly — pass
kind, series, dateRange as top-level fields, do not wrap in InsightVizNode.
| Tool | Purpose |
| -------------------------------- | ------------------------------------------------ |
| posthog:query-trends | Trends (count over time) |
| posthog:query-funnel | Funnels (multi-step conversion) |
| posthog:query-retention | Retention (cohort return rates) |
| posthog:query-stickiness | Stickiness (active days per user) |
| posthog:query-lifecycle | Lifecycle (new/returning/resurrecting/dormant) |
| posthog:query-paths | Paths (navigation flow) |
| posthog:query-trends-actors | Users behind a trend bucket (trends source only) |
| posthog:execute-sql | HogQL — when no typed tool fits |
| posthog:read-data-schema | Discover events, properties, sample values |
| posthog:insight-get / -query | Fetch a saved insight's metadata / data |
Plus the standard PostHog tools the playbooks reference by name (feature-flag-get-all,
experiment-get-all, annotations-list, query-error-tracking-issues-list, query-logs,
query-session-recordings-list, cohorts-list/-create, annotation-create,
insight-create).
compare_to_prior_periods.py — auto-detectsinterval and compares recent values to the natural cycle (day-of-week, hour-of-week,
or sequential). Use to resolve step 2.2 cheaply.
breakdown_attribution.py — ranks breakdownsegments by absolute delta and flags offsetting moves.
python3 scripts/compare_to_prior_periods.py < query_result.json
WINDOW=7 python3 scripts/breakdown_attribution.py < breakdown_result.json
Read query.kind from the source the user pointed at:
short_id): posthog:insight-get → query.kind. Useposthog:insight-query if you also need the numbers.
kind directly.| kind | Playbook |
| ----------------- | ------------------------------------------------------------- |
| TrendsQuery | trend-playbook.md |
| FunnelsQuery | funnel-playbook.md |
| RetentionQuery | retention-playbook.md |
| StickinessQuery | stickiness-playbook.md |
| LifecycleQuery | lifecycle-playbook.md |
| PathsQuery | paths-playbook.md |
| HogQLQuery | route by what the SQL aggregates (see below) |
If kind === "TrendsQuery" and trendsFilter.display === "BoxPlot", use
box-plot-playbook.md — distribution metric, no
breakdowns.
For HogQLQuery insights, classify by the SQL's shape: count over time → trend
playbook, multi-step conversion → funnel playbook, cohort return → retention playbook.
Run the SQL through posthog:execute-sql to get the data, then follow the closest
playbook's steps. See HogQL insights in shared-patterns.md.
If the user's question spans multiple kinds, run the playbooks in sequence.
Run the primary tool. Record baseline, current, delta (absolute and %), and the start
of the anomaly window.
Widen to 3–4× the user's interval (or use compareFilter: {"compare": true} on
TrendsQuery / StickinessQuery; for other kinds run two date ranges).
Pipe the widened result through
compare_to_prior_periods.py — it flags
seasonality, partial right-edge buckets, and real anomalies. If the movement is
normal variance, report that and stop.
In rough order of signal:
posthog:feature-flag-get-all → flags with updated_at near the anomaly start.posthog:experiment-get-all → start_date / end_date near the start.posthog:annotations-list → date_marker near the start.git log for the window if the repo is reachable (highest signal when available).Any match is a hypothesis to confirm in the playbook (usually via breakdown on
$feature/<flag_key>, app_version, or utm_source).
Open the playbook for the kind from Step 1 and follow its numbered steps. Carry the
record from 2.1 and any candidates from 2.3 into it.
Pick a segment the suspected cause should not have affected and rerun there. Stable
in the control = strong hypothesis; moved too = expand the investigation. Skip when
2.2 already explained the movement.
Use the format below. Offer to save key charts via posthog:insight-create. If a
cause is found and no annotation marks it, offer posthog:annotation-create. See
common-causes.md for the cause taxonomy.
# Investigation: <metric>
**Anomaly**: <baseline> → <current> (<delta>) starting <date>
## Likely cause
<one sentence>
**Confidence**: low | medium | high — <one-line reason>
**Evidence**
- <query result>
- <flag / experiment / annotation / commit if applicable>
## Possible causes (ruled out)
- <hypothesis>: <why>
## Affected segment
- <shared properties of affected users/events>
## Data gaps
- <checks skipped and why>
## Suggested follow-ups
- <concrete next action>
- <offer to save chart / create annotation>
Confidence rule of thumb:
delta _and_ a flag/version aligns _and_ an error or annotation matches).
cross-check.
rules things _out_.
Link insights and dashboards inline: Name.
box-plot,
funnel,
retention,
stickiness,
lifecycle,
paths
Take posthog/posthog-investigate-metric from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.