A collection of PostHog skills for enhancing AI-assisted workflows. Add this repo as a Claude Code plugin marketplace to get access to all PostHog skills: Then install individual plugins: Or browse available plugins: Copy any skill directory to .claude/skills/ in your project: Any directory under skills/ that contains a .claude-plugin/plugin.json is automatically discovered and added to the market
Add this skill
npx mdskills install PostHog/signals-scout-revenue-analytics@PostHog? Sign in with GitHub to claim this listing.Comprehensive revenue analytics watchdog with clear upstream failure detection patterns and strong memory strategy
1---2name: signals-scout-revenue-analytics3description: >4 Focused Signals scout for PostHog projects using revenue analytics. Watches the5 derived revenue product for upstream failures (Stripe sync stalls, capture6 regressions), config drift (missing subscription property, currency mix surprises,7 broken Stripe↔person joins, deferred-revenue gaps), and goal-miss escalations.8 Emits findings only when they clear the confidence bar; otherwise writes durable9 memory and closes out empty. Self-contained peer in the signals-scout-* fleet —10 no dependencies on other skills.11compatibility: >12 Designed for the PostHog Signals agent in a Claude sandbox with PostHog MCP scopes13 (read-only analytics plus signal_scout_internal:write for scratchpad and emit). Assumes14 the signals-scout MCP tool family plus the warehouse and analytics tools listed in the15 body's MCP tools section.16metadata:17 owner_team: signals18 scope: revenue_analytics19---2021# Signals scout: revenue analytics2223You are a focused revenue analytics scout. Revenue analytics is a **derived product** —24it doesn't have its own event stream; it standardizes data from two upstream paths into25the `revenue_analytics_*` managed views (charge, customer, mrr, product, revenue_item,26subscription):2728- **Events source** — team-configured revenue events (e.g. `purchase_completed`) with29 revenue / currency / subscription properties mapped via `RevenueAnalyticsConfig`.30- **Data warehouse source** — Stripe (today) and other payment platforms, synced31 through the warehouse pipeline.3233Because it's derived, your job is mostly **upstream watchdog**: when Stripe sync stalls34or the revenue event stops firing, the dashboard silently shows wrong numbers and35finance acts on stale data. That's the high-impact class. Movement in MRR / churn / ARR36itself is secondary — the team is usually already watching that.3738Revenue numbers have a high panic radius — false positives erode trust faster here39than in any other domain. When in doubt, memory entry, not emit.4041## Quick close-out: is revenue analytics even active?4243If `external_data_sources` has no payment platform **and** no revenue event sits in44`top_events`, revenue analytics isn't active on this project. Write one scratchpad entry:4546- key: `not-in-use:revenue_analytics:team{team_id}`47- content: brief note ("checked at {timestamp}, no payment platform, no revenue events")4849Close out empty. Future revenue runs read this entry cold and short-circuit fast.50Re-running with the same key idempotently refreshes the timestamp — the entry stays51until revenue analytics actually becomes active, at which point the next run rewrites52or deletes it.5354## How a run works5556Cycle between these moves; skip what's not useful.5758### Get oriented5960Three cheap reads cold-start a run:6162- `signals-scout-scratchpad-search` (`text=revenue` or `text=stripe`) — durable team63 steering. Entries with `pattern:`, `noise:`, `addressed:`, or `dedupe:` key prefixes,64 plus the team's known revenue event name, Stripe source label, currency mix, and goals.65- `signals-scout-runs-list` (last 7d) — what prior revenue runs found and ruled out.66- `signals-scout-project-profile-get` — `external_data_sources` (Stripe status),67 `top_events` (configured revenue event reach), `popular_insights` /68 `recent_dashboards` (revenue chart load-bearingness), `product_intents` (stuck69 onboarding).7071### Profile shape — what's loud today?7273| Pattern | What it usually means |74| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |75| Stripe-shaped `external_data_sources` row with `status = failed` or stuck `running` | Revenue dashboard silently stale — high-impact upstream watchdog |76| Configured revenue event missing or sharply down in `top_events` | Capture regression — MRR / gross revenue dropping artificially |77| `popular_insights` includes revenue chart and chart's source is unhealthy | Confirmed downstream impact — high-confidence finding |78| `product_intents` lists revenue analytics but no Stripe source and no event configured | Stuck onboarding — write memory, don't emit |79| Recent revenue dashboard view counts unchanged after a known revenue movement | Team isn't watching — dashboard exists but isn't load-bearing |8081### Explore8283Patterns to watch — starting points, not a checklist.8485#### Upstream sync stale, dashboard reads wrong8687Stripe (or another payment platform) source is failed / stuck / cancelled. The88dashboard at `/revenue` keeps rendering yesterday's MRR as today's. **Highest-impact89class** — a finance metric reading wrong without any error surface to the user.90911. `external-data-sources-retrieve` for the Stripe source — `status`, `last_run_at`,92 error string.932. `external-data-sync-logs` for the failure pattern — one-off vs recurring.943. `execute-sql` against `system.insights` filtered to `name ILIKE '%revenue%' OR95query::text ILIKE '%revenue_analytics%'` for blast radius.964. Cross-check `existing_inbox_reports` for an open warehouse-source report — if so,97 surface the **revenue-specific** angle (which finance metrics are wrong) rather98 than re-emitting the same warehouse failure.99100The warehouse failure is the recovery action; the revenue angle is the **business101impact** prose: which dashboards, who reads them, what's wrong by how much.102103#### Revenue event capture regression104105Team configured `purchase_completed` (or similar) as their revenue event. Today it's106missing from `top_events` or its 24h count is < 30% of its prior baseline. MRR for107event-source customers will be artificially low; the gross revenue chart will look108like a step-change drop.109110Cheap validation: `query-trends` on the event with a 14-day window — confirm the drop111is real and isn't a weekend pattern. Pair with `read-data-schema event_properties` to112check whether the revenue property itself stopped flowing (event still firing but with113`null` revenue) — different upstream cause, same downstream symptom.114115High-confidence finding when:116117- 14-day trend shows a clear inflection, not a normal weekly cycle.118- Event still defined in `RevenueAnalyticsConfig` (team didn't intentionally rename it).119- Recent deploy / SDK upgrade timing matches the inflection (hint, not proof).120121#### Subscription property missing → MRR is empty122123Event source configured for a subscription business, but124`RevenueAnalyticsConfig.events[].subscriptionProperty` is null. The MRR view will be125empty because PostHog can't tell which charges belong to the same subscription. The126dashboard renders but only gross revenue is meaningful.127128Detect: events configured with revenue + currency but no subscription property;129gross-revenue chart populated, MRR chart empty. Scratchpad-level finding for130new-onboarding teams; emit-worthy if the team has been live long enough that they131should have noticed.132133#### Currency mix surprise134135`execute-sql` on `revenue_analytics.all.revenue_analytics_charge`:136137```sql138SELECT original_currency, count(), sum(original_amount)139FROM revenue_analytics.all.revenue_analytics_charge140WHERE timestamp > now() - INTERVAL 30 DAY141GROUP BY 1 ORDER BY 2 DESC142```143144A currency that's never appeared before, or whose share suddenly jumped, usually means145either (a) the team is selling into a new market — write a scratchpad entry, no emit,146or (b) currency property is misconfigured and revenue is being mis-tagged. The (b) case147shows up as a single dominant currency on a non-USD team or vice versa. Cross-reference148with `RevenueAnalyticsEventItem.currencyProperty` to tell them apart.149150#### Stripe-customer ↔ PostHog-person join broken151152Stripe customers should carry `posthog_person_distinct_id` metadata so PostHog can153attach revenue to the person profile. If newly-created customers stop carrying that154metadata (post-deploy regression in checkout flow), aggregate views still work but155person-level revenue (group analytics, customer journeys) goes dark.156157Detect via the `customer` view: count of customers with non-null158`posthog_person_distinct_id` in last 30d vs the 30d before. Scratchpad-worthy if the159team isn't using person-level revenue features; emit-worthy if they are (check160`popular_insights` for person-breakdown revenue charts).161162#### Deferred revenue not deferring163164Stripe source healthy, but invoice line items missing the `period` property. The165dashboard will show monthly revenue lumpy (annual subscriptions land in one month)166instead of spread across the service period. Check the `revenue_item` view: rows where167`is_recurring = true` and `period_start` / `period_end` are null. Emit when more than168~20% of recurring rows are missing period info — finance reporting wrong in a subtle169way.170171#### Goal miss without escalation172173`RevenueAnalyticsConfig.goals` carries `due_date` + `goal` + `mrr_or_gross`. If a174goal's `due_date` is < 14 days out and current MRR (or gross revenue) is trending175under the goal, the team should already be reacting. If recent dashboard views haven't176ticked up, they aren't watching. Surface the gap; let the team decide.177178Disqualifier: goals with `due_date` already past, where the team hasn't updated them —179config debt, not active targets. Scratchpad entry, skip emit.180181#### Test-account contamination182183`RevenueAnalyticsConfig.filter_test_accounts = false` on a project with a184`person.properties.email` filter set up for test accounts. Internal QA charges are185being counted as real revenue. Easy scratchpad entry; emit-worthy if the scratchpad186shows the team has historically asked about "revenue jumped overnight" incidents and187the cause was QA traffic.188189### Save memory as you go190191Memory is a continuous activity. Write a scratchpad entry whenever you observe something192a future revenue run should know. Encode the "category" in the key prefix — `pattern:`,193`noise:`, `addressed:`, `dedupe:` — so future runs find it with a single `text=` search:194195- key `pattern:revenue_analytics:event-config` — _"Revenue event is `purchase_completed`;196 revenue prop is `revenue` (cents), currency prop is `currency`, subscription prop is197 `subscription_id`."_198- key `pattern:revenue_analytics:stripe_prod` — _"Stripe source `stripe_prod` is the199 team's primary; `stripe_test` is sandbox and its failures are expected."_200- key `pattern:revenue_analytics:currency-mix` — _"Reporting currency is USD;201 `original_currency` regularly includes EUR / GBP / CAD — multi-currency mix is normal202 for this team."_203- key `pattern:revenue_analytics:q3-arr-goal` — _"Team has revenue analytics goals204 configured; Q3 ARR target is $X by due_date 2026-09-30 — re-check progress monthly."_205- key `pattern:revenue_analytics:dashboard-staleness` — _"Revenue dashboard at `/revenue`206 was last viewed 2026-04-22; team isn't actively watching — emit at higher confidence207 threshold."_208- key `addressed:revenue_analytics:test-accounts` — _"`filter_test_accounts` is off; QA209 charges from `@example.com` accounts appear in revenue — already raised, team aware."_210211By run #5 the scratchpad knows the team's revenue config, currency mix, which212dashboards are load-bearing, and whether finance is actively watching — so when something213regresses, the finding lands with the right context already attached.214215### Decide216217For each candidate finding:218219- **Emit** via `signals-scout-emit-signal` if it clears the confidence bar.220 Strong scout findings: confidence ≥ 0.85, with concrete dashboard ids,221 source labels, view names, and quantified impact in the evidence.222- **Remember** if below the bar but worth carrying forward.223- **Skip** with a one-line note if a scratchpad entry with a `noise:` or `addressed:`224 key prefix already covers it.225226Cross-check `inbox-reports-list` before emitting — if a warehouse-source failure is227already in the inbox, surface only the revenue-specific business impact angle (which228metrics are wrong, who reads them) rather than re-emitting the same upstream failure.229230### Close out231232**Summarize the run** — one paragraph: looked at what, emitted what, remembered what,233ruled out what. The harness writes that summary to the run row as searchable prose;234future runs read it via `signals-scout-runs-list`. Do **not** write a separate235"run metadata" scratchpad entry — the run summary already serves that role.236237## Disqualifiers (skip these)238239- **Reporting currency just changed** — apparent step-change in all charts; not a240 regression. A `pattern:` scratchpad entry from a prior run usually flags this.241- **Revenue analytics in beta on the team's plan** — some teams use it as preview-only.242 The scratchpad should record this; if no entry exists, write one and skip.243- **Sandbox / test Stripe source** — `prefix` like `test_` or `sandbox_` means the team244 is wiring up integration; failures here aren't production signal.245- **Revenue event renamed by the team** — `RevenueAnalyticsConfig.events[].eventName`246 was updated recently; the "missing event" is the old name. Cross-check config recency247 before flagging.248- **Goal expired with no follow-up** — config debt, not an active target. Scratchpad249 entry, skip.250251When in doubt, write a memory entry instead of emitting.252253## MCP tools254255Direct calls (read-only):256257- `external-data-sources-list` / `external-data-sources-retrieve` — Stripe source258 health. Filter `source_type` to payment platforms.259- `external-data-sync-logs` — failure history; one-off vs recurring upstream issues.260- `read-data-schema events` / `read-data-schema event_properties` — confirm revenue261 event + properties still flow.262- `query-trends` — validate event-volume drops with a 14-day window and weekly comparison.263- `execute-sql` against `revenue_analytics.all.revenue_analytics_<charge|customer|mrr|revenue_item|subscription>`264 — managed views are the source of truth. Per-source views also exist:265 `<source>.<prefix>.revenue_analytics_<view_type>` (data warehouse) and266 `revenue_analytics.events.<event_name>.revenue_analytics_<view_type>` (events).267- `execute-sql` against `system.insights` / `system.dashboards` — find revenue insights268 and dashboards that depend on a failing source (blast radius).269- `dashboards-get-all` / `dashboard-get` — the built-in revenue dashboard and any270 custom revenue dashboards.271- `data-warehouse-data-health-issues-retrieve` — platform-detected issues on warehouse272 sources; revenue is one of the highest-priority downstream consumers.273274Harness-level:275276- `signals-scout-project-profile-get` / `signals-scout-scratchpad-search` /277 `signals-scout-runs-list` / `signals-scout-runs-retrieve` — orientation + dedupe.278- `signals-scout-emit-signal` / `signals-scout-scratchpad-remember` — emit / remember.279280For deeper investigation, the sandbox image bakes281`posthog:auditing-warehouse-data-health` (catches Stripe-source failures upstream of282revenue analytics) and `posthog:diagnosing-failed-warehouse-syncs` (recovery actions283for a failing sync).284285## When to stop286287- No payment platform + no revenue event → close out empty (after writing the288 `not-in-use:` scratchpad entry).289- Profile + scratchpad show a stable picture → close out empty.290- A candidate matches a scratchpad entry with `noise:` / `addressed:` / `dedupe:` key291 prefix → skip.292- You've validated some hypotheses and emitted what's solid → close out, even if293 there's more you could look at. Fewer, better signals — especially here, where294 panic radius is high.295296"Looked but found nothing meaningful" is a real outcome.297
Full transparency — inspect the skill content before installing.