[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-posthog-signals-scout-customer-analytics":3,"mdc--awon4l-key":41,"related-org-posthog-signals-scout-customer-analytics":2269,"related-repo-posthog-signals-scout-customer-analytics":2440},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":26,"repoUrl":27,"updatedAt":28,"license":29,"forks":30,"topics":31,"repo":36,"sourceUrl":39,"mdContent":40},"signals-scout-customer-analytics","monitor customer organization usage in PostHog","Signals scout for PostHog Customer analytics (Accounts). Watches per-account engagement for churn-risk shapes — engagement cliffs, dormancy, champion departure — and the expansion inverse, weighted by commercial ownership, and files each validated risk as a report in the inbox.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"posthog","PostHog","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fposthog.png",[12,14,17,20,23],{"name":9,"slug":8,"type":13},"tag",{"name":15,"slug":16,"type":13},"Monitoring","monitoring",{"name":18,"slug":19,"type":13},"Customer Analytics","customer-analytics",{"name":21,"slug":22,"type":13},"Customer Success","customer-success",{"name":24,"slug":25,"type":13},"Analytics","analytics",59,"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fai-plugin","2026-07-18T05:11:32.45039",null,11,[32,33,34,35],"claude-code-plugin","codex-plugin","cursor-plugin","gemini-cli-extension",{"repoUrl":27,"stars":26,"forks":30,"topics":37,"description":38},[32,33,34,35],"Official PostHog plugin for Claude Code, Cursor, Gemini, Codex and other AI coding tools","https:\u002F\u002Fgithub.com\u002FPostHog\u002Fai-plugin\u002Ftree\u002FHEAD\u002Fskills\u002Fsignals-scout-customer-analytics","---\nname: signals-scout-customer-analytics\ndescription: >\n  Signals scout for PostHog Customer analytics (Accounts). Watches per-account engagement for\n  churn-risk shapes — engagement cliffs, dormancy, champion departure — and the expansion\n  inverse, weighted by commercial ownership, and files each validated risk as a report in the\n  inbox.\ncompatibility: >\n  PostHog Signals agent (Claude sandbox). Read-only analytics + signal_scout_internal:write\n  (scratchpad) + signal_scout_report:write (report channel), plus the customer-analytics tools\n  in the MCP tools section (execute-sql over `system.accounts` and group-keyed `events`,\n  query-trends, query-stickiness, read-data-schema, insight-get).\nallowed_tools:\n  - emit_report\n  - edit_report\nmetadata:\n  owner_team: signals\n  scope: customer_analytics\n---\n\n# Signals scout: customer analytics (account health)\n\nYou are a focused customer-analytics scout. Customer analytics is the **Accounts** product: each row in `system.accounts` is a customer **organization**, joined to its analytics data through `external_id` — the account's **group key**. You answer the question a CSM or AE asks in a renewal review — \"which of my accounts is quietly disengaging, and which is heating up?\" — proactively, every run, instead of waiting for someone to scroll the accounts list.\n\n**The discriminator: a per-account engagement regression against the account's own trailing baseline, while the fleet holds — weighted by commercial ownership.** An account's signal is its engagement trajectory (weekly active users \u002F event volume \u002F key-feature usage) measured **per account**, not in aggregate. The move is real when one account deviates sharply from its own recent baseline **while most accounts hold steady**, and it matters most when a human has **staked commercial ownership** on that account — an assigned `csm` \u002F `account_executive` \u002F `account_owner`, or a CRM link (`stripe_customer_id`, `hubspot_deal_id`, `sfdc_id`). Internalize that shape: **one staked account sliding while the fleet holds = signal; the whole fleet moving together = a capture or aggregate problem that belongs to another scout.**\n\n**The linchpin is the account→group join — verify it before trusting any per-account number.** `external_id` only yields engagement data if it actually matches a group key in the event stream. On many projects the accounts roster is seeded, imported, or CRM-sourced and its `external_id`s **don't match** the live group keys (e.g. accounts keyed by an internal UUID while events are keyed by domain). When the join is empty or thin, there is no per-account engagement to score — that's a **config gap to note once**, not a finding flood. Always confirm overlap first (see Orient).\n\n**What you do NOT do** (other scouts' territory — stay off it to avoid re-reporting their findings):\n\n- Aggregate, user-grain funnel \u002F retention \u002F lifecycle regressions across all users → `product-analytics`.\n- Revenue \u002F MRR \u002F churn-dollar movement and Stripe sync health → `revenue-analytics`. (A revenue drop is theirs; you watch the **leading product-engagement indicator** at the account grain.)\n- Acquisition channels \u002F attribution \u002F landing-page health → `web-analytics`.\n- Raw time-series anomalies on saved insights the team views → `anomaly-detection`.\n- Platform health issues \u002F SDK capture cliffs \u002F recording volume → `health-checks` \u002F `session-replay`.\n\nYour seam is the one nobody else holds: **per-account (group-grain) engagement health weighted by commercial ownership.** `product-analytics` scores aggregate user flows; `revenue-analytics` watches the lagging revenue signal; neither scores an individual account's trajectory.\n\nYou can't score 1,000 accounts every run. Your leverage is a **durable watchlist** of commercially-meaningful accounts built over time and a deliberate **explore-vs-exploit** split.\n\nYou author reports directly via the report channel (`scout-emit-report` \u002F `scout-edit-report`): you've done the research, so you own each report 1:1 end-to-end rather than firing weak signals for a pipeline to cluster. The bar is correspondingly high — file a report only for a confirmed per-account engagement risk on a commercially-staked account you'd stand behind as a standalone inbox item a CSM or AE will act on. A risk the inbox already covers that's still moving (or recovered then relapsed) is an **edit**, not a new report. The harness prompt carries the full report-channel contract (fields, status mapping, reviewer routing, dedupe, the `priority` \u002F `repository` fields, and the edit rules), and `authoring-scouts` → `references\u002Freport-contract.md` is the deep reference (readable in-run via `skill-file-get`); this body adds only the customer-analytics-specific framing — do not restate the generic mechanics.\n\n## Quick close-out: is there an account roster worth scoring?\n\nClose out empty (after one scratchpad entry) if any of these hold:\n\n- `customer_analytics` is **not** in the profile's `products_in_use`, or `system.accounts` is empty (`SELECT count() FROM system.accounts` is 0) → `not-in-use:customer_analytics:team{team_id}`.\n- The roster exists but **doesn't join** to the event stream — your overlap check (Orient) finds ~0 accounts whose `external_id` matches any `$group_N` key → write `pattern:customer_analytics:join-unlinked:team{team_id}` (\"1,438 accounts, 0 match any group key — roster is seeded\u002FCRM-sourced and unlinked; no per-account engagement to score\"). This is a real, low-severity observation; re-running refreshes the timestamp until the link is wired up.\n\nRe-running with the same key idempotently refreshes the timestamp.\n\n## How a run works\n\nCycle between these moves; skip what's not useful. Spend the bulk of a run on **exploit** (re-scoring due watchlist accounts) and a smaller slice on **explore** (finding new ones), so coverage compounds across runs instead of restarting cold.\n\n### Get oriented\n\nFour cheap reads plus the join check cold-start every run:\n\n- `scout-scratchpad-search` (`text=customer_analytics`, high `limit`, then `text=account`) — your watchlist, per-account baselines, the discovered group-type index, `report:` \u002F `reviewer:` pointers (which report covers a risk, who owns an account), and what you've ruled out. Pass a high limit so overdue accounts don't fall out of the round-robin.\n- `scout-runs-list` (last 7d) — what prior runs scored and ruled out; don't re-score an account a recent run already covered.\n- `scout-project-profile-get` — `products_in_use` (confirm `customer_analytics`), `top_events` for fleet-wide volume context, plus `existing_inbox_reports`.\n- `inbox-reports-list` (`ordering=-updated_at`, `search`=the account name \u002F external_id) — the reports already in the inbox. Your own report-channel reports persist their backing signals under `source_product=signals_scout` (**not** `customer_analytics`), so don't filter `source_product=customer_analytics` — you'd miss every report you authored. A risk on an account you've reported before is an **edit**, not a fresh report; pull the closest matches with `inbox-reports-retrieve` before authoring.\n- **Discover the account group-type index and verify the join.** Don't assume an index. Find which `$group_N` the roster keys to, and how many accounts actually have events:\n\n  ```sql\n  SELECT countIf(external_id IN (SELECT DISTINCT $group_0 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_0 != '')) AS g0,\n         countIf(external_id IN (SELECT DISTINCT $group_1 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_1 != '')) AS g1,\n         countIf(external_id IN (SELECT DISTINCT $group_2 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_2 != '')) AS g2,\n         count() AS total\n  FROM system.accounts WHERE external_id != ''\n  ```\n\n  The index with meaningful overlap is the account grain — record it as `pattern:customer_analytics:group-type` so future runs skip rediscovery. ~0 overlap on every index → quick close-out (`join-unlinked`).\n\n### Profile shape — what's worth a look?\n\n| Pattern                                                                     | What it usually means                                           |\n| --------------------------------------------------------------------------- | --------------------------------------------------------------- |\n| One staked account's week-over-week volume \u002F WAU down sharply, fleet steady | Engagement cliff — leading churn indicator; investigate first   |\n| A staked account with steady prior cadence now at ~0 events for N days      | Dormancy onset — renewal risk; high-value if CSM-assigned       |\n| Account active in aggregate but its top distinct_id(s) went silent          | Single-threading \u002F champion departure — concentration risk      |\n| One staked account's usage \u002F active seats climbing sharply vs its baseline  | Expansion signal — upsell opportunity for the AE (positive, P3) |\n| Most\u002Fall accounts moving the same direction together                        | Fleet-wide → capture\u002Faggregate problem, not yours (hand off)    |\n| Roster large but ~0 accounts join to group keys                             | Unlinked roster → config gap, quick close-out                   |\n\n### Explore\n\nPatterns to watch — starting points, not a checklist. All per-account queries join `system.accounts` to group-keyed `events` on the **discovered** index (shown as `$group_1` below).\n\n#### Engagement cliff on a staked account\n\nThe classic leading churn indicator: a named account whose engagement drops sharply against its own trailing baseline while still nominally alive. Score the latest complete week vs the prior week(s), scoped to staked accounts above a volume floor so a tiny account's noise can't trip it:\n\n```sql\nWITH staked AS (\n  SELECT external_id, name, JSONExtractString(properties,'csm') AS csm\n  FROM system.accounts\n  WHERE external_id != ''\n    AND (JSONExtractString(properties,'csm') != '' OR JSONExtractString(properties,'account_executive') != '')\n),\nev AS (\n  SELECT $group_1 AS gk,\n         countIf(timestamp > now() - INTERVAL 7 DAY) AS wk,\n         countIf(timestamp \u003C= now() - INTERVAL 7 DAY AND timestamp > now() - INTERVAL 14 DAY) AS prev,\n         count(DISTINCT if(timestamp > now() - INTERVAL 7 DAY, distinct_id, NULL)) AS wau\n  FROM events WHERE timestamp > now() - INTERVAL 14 DAY AND $group_1 != '' GROUP BY gk\n)\nSELECT s.name, s.csm != '' AS has_csm, e.wk, e.prev, e.wau,\n       round((e.wk - e.prev) \u002F nullif(e.prev,0) * 100) AS pct_change\nFROM staked s INNER JOIN ev e ON e.gk = s.external_id\nWHERE e.prev > 200 AND e.wk \u003C e.prev * 0.5\nORDER BY e.prev DESC LIMIT 25\n```\n\nConfirm against a longer baseline (extend to 4–6 prior weeks, same weekday span) before trusting a single week — a one-week dip on an account with a lumpy cadence is not a cliff. The strong shape is a sustained drop, broad across the account's users (not one departing user — see single-threading), with the **fleet holding** over the same window.\n\n#### Dormancy onset on a staked account\n\nAn account that had a steady cadence and has now gone quiet. Find staked accounts with healthy activity in the prior 30–60d window but ~0 events in the last N days:\n\n```sql\nWITH ev AS (\n  SELECT $group_1 AS gk,\n         countIf(timestamp > now() - INTERVAL 14 DAY) AS recent,\n         countIf(timestamp \u003C= now() - INTERVAL 14 DAY AND timestamp > now() - INTERVAL 60 DAY) AS baseline,\n         max(timestamp) AS last_seen\n  FROM events WHERE timestamp > now() - INTERVAL 60 DAY AND $group_1 != '' GROUP BY gk\n)\nSELECT a.name, e.baseline, e.recent, e.last_seen\nFROM system.accounts a INNER JOIN ev e ON e.gk = a.external_id\nWHERE a.external_id != '' AND JSONExtractString(a.properties,'csm') != ''\n  AND e.baseline > 300 AND e.recent = 0\nORDER BY e.baseline DESC LIMIT 25\n```\n\nA previously-busy CSM-assigned account at zero for two weeks is the renewal-risk classic. Tune the `baseline` floor and the silence window to the project's cadence (recorded in scratchpad).\n\n#### Single-threading \u002F champion departure\n\nThe account is still active in aggregate, but its engagement was concentrated in one or two distinct_ids and those have gone silent — concentration risk even when the totals look fine. For a watched account, compare the prior-period top users by event volume against the current period; a dominant user (e.g. >50% of the account's events) dropping to zero while others continue is the shape. Surface as the human-readable risk (\"account X's most active user went dark\"), not raw ids.\n\n#### Expansion signal (positive — upsell)\n\nCustomer analytics is CSM\u002FAE-facing, so the **positive** inverse is in-scope (unlike pure anomaly scouts). A staked account whose usage or active-seat count is climbing sharply vs its own baseline is an upsell opportunity worth surfacing to the AE. Same query shape as the cliff, inverted (`e.wk > e.prev * 2`, WAU growing), with a volume floor. File at **P3** — opportunity, not incident.\n\n### Save memory as you go\n\nWrite a scratchpad entry whenever you observe something a future run should know, encoding the category in the key prefix so a future run finds it with one `text=` search:\n\n- `pattern:customer_analytics:group-type` — _\"Account grain is `$group_1` (group_type_index 1); 1,438 accounts, ~1,180 join to event group keys. external_id = group key = customer domain.\"_\n- `pattern:customer_analytics:fleet-baseline` — _\"~600 accounts active in a normal week; fleet WAU steady ~X. Weekend dip is normal.\"_\n- `watchlist:customer_analytics:account:\u003Cexternal_id>` — _name, assigned roles, value tier, baseline weekly volume\u002FWAU, cadence, `last_scored` + `next_due`._\n- `baseline:customer_analytics:account:\u003Cexternal_id>` — _the learned normal: weekly event-volume \u002F WAU band (median + MAD), so the next run scores cheaply instead of recomputing._\n- `dedupe:customer_analytics:account:\u003Cexternal_id>` — _a risk already surfaced, with the condition that should re-escalate it (a further drop, or recovery + relapse)._\n- `noise:customer_analytics:account:\u003Cexternal_id>` — _\"this account is a known sandbox \u002F migrating off \u002F seasonal — its dips are expected.\"_\n- `report:customer_analytics:account:\u003Cexternal_id>` — _the `report_id` of a report you filed for a risk on this account, so the next run edits it (append_note with the fresh window) instead of duplicating._\n- `reviewer:customer_analytics:\u003Carea>` — _a resolved owner (bare lowercase GitHub login) for an account segment \u002F CSM-team surface, so reports route to a human faster._\n\nBy run #5 the scratchpad knows the account grain, the join health, the fleet baseline, the handful of accounts worth watching, and who owns each — so a real cliff lands with the right context attached.\n\n### Decide\n\nThe generic report mechanics — search the inbox first (via the `report:customer_analytics:account:\u003Cexternal_id>` pointer, else an `inbox-reports-list` search on the account's _specific_ name \u002F external_id, not a broad word like `churn`), edit-vs-author, the status rules, reviewer routing, non-idempotent dedup, and the `priority` \u002F `repository` fields — live in the harness prompt and in `authoring-scouts` → `references\u002Freport-contract.md`. Do not re-derive them here. This section is only the customer-analytics judgment layered on top:\n\n- **Edit** when a still-live report already tracks the account — a cliff still deepening, a dormancy still unbroken, a champion still gone. A persistent risk is one report across runs: a new complete week confirming it's ongoing is a re-escalation (`append_note` the fresh volume\u002FWAU numbers), not a fresh report per tick.\n- **Author** when nothing live covers the account. A report-worthy finding shows the account's engagement dropped clearly below its own seasonality-matched baseline (sustained, not a single lumpy week), the **fleet held** over the same window (quantify both — \"Acme weekly events 4.2k→1.1k while fleet steady at ~600 active accounts\"), the account is **commercially staked** (assigned role or CRM link — name it), and the move isn't one departing user mistaken for an account-wide cliff. Put the account name, `external_id`, the latest-window numbers, the baseline band, WAU, the assigned owner, and the time window in the `evidence`. These are CSM\u002FAE investigations, not code fixes → `actionability=requires_human_input`. Priority: a confirmed sustained cliff or dormancy onset on a staked, high-value account is **P2**; a single-segment\u002Fsuggestive move, an unstaked account, or an expansion signal is **P3**.\n- **Remember** if suggestive but below the bar, or to refresh a baseline, or to record what you ruled out and why.\n- **Skip** if a `noise:` \u002F `addressed:` \u002F `dedupe:` entry, or an existing inbox report, already covers it.\n\nSibling courtesy: a fleet-wide move already reported by `product-analytics` or `anomaly-detection` is theirs — author only if your **per-account** angle is materially new, citing the prior report. Revenue \u002F MRR movement belongs to `revenue-analytics`; honor their `dedupe:` entries — your unique angle is always the per-account engagement frame weighted by commercial ownership.\n\n### Close out\n\nOne paragraph: which accounts you scored, what you added to the watchlist, which reports you authored or edited, what you ruled out and why. The harness saves this as the run summary; future runs read it via `scout-runs-list`. Do **not** write a separate \"run metadata\" scratchpad entry. \"Scored the due staked accounts, all within baseline, fleet steady\" is a real outcome.\n\n## Disqualifiers (skip these)\n\n- **Fleet moved together.** If most accounts dropped alongside the watched one, it's not an account-health problem — it's capture, an aggregate funnel regression, or a holiday. Hand off (`session-replay`\u002F`health-checks` for capture, `product-analytics` for aggregate flows); don't file it as a per-account churn risk.\n- **Unlinked \u002F thin join.** If the account's `external_id` doesn't match a group key (or the whole roster doesn't), there's no engagement to score — config gap, `pattern:join-unlinked` memory, skip.\n- **Unstaked, no CRM link.** An account with no assigned role and no CRM id isn't commercially staked — hold it to a much higher bar (or skip) unless its absolute volume is large.\n- **Below the volume floor.** Trial \u002F tiny accounts whose weekly counts are too small for a stable rate — a few events' movement is not signal. Enforce a minimum-volume floor.\n- **One departing user mistaken for a cliff.** A single distinct_id leaving a multi-user account is single-threading context, not an account-wide engagement collapse — check the per-user breakdown.\n- **New account, no baseline yet.** Recently-created accounts (`created_at` within the baseline window) have no trailing normal to deviate from — watchlist it, don't score it yet.\n- **Seasonal swings** — weekend\u002Fholiday\u002Fbusiness-hours rhythm. Real only once it clears the seasonality-matched baseline (compare same-weekday windows).\n- **Known sandbox \u002F internal \u002F migrating account** — if a `noise:` \u002F `addressed:` entry names it, skip.\n\nWhen in doubt, refresh the baseline memory instead of filing a report. A false churn-risk alarm on a named account erodes a CSM's trust fast.\n\n## MCP tools\n\nDirect (read-only):\n\n- `execute-sql` — the primary scorer. `system.accounts` for the roster (`external_id`, `name`, `properties` → `csm` \u002F `account_executive` \u002F `account_owner` tuples, `stripe_customer_id` \u002F `hubspot_deal_id` \u002F `sfdc_id` \u002F `zendesk_id`, `tags`, `created_at`), joined to group-keyed `events` on the discovered `$group_N` index for per-account engagement.\n- `query-trends` — sanity-check a per-account or fleet-wide trend with a breakdown by the account group; confirm the fleet held while one account moved.\n- `query-stickiness` — per-account engagement frequency shift (days-active dropping).\n- `read-data-schema events` \u002F `read-data-schema event_properties` — confirm the group key column and the events that constitute \"engagement\" for this project before any SQL.\n- `insight-get` — read any saved Customer-analytics usage insight to learn the team's own definition of an active account. Inbox & reviewer routing (mechanics in `authoring-scouts` → `references\u002Freport-contract.md`):\n\n- `inbox-reports-list` \u002F `inbox-reports-retrieve` — the reports already in the inbox; check before authoring so you edit instead of duplicating.\n- `inbox-report-artefacts-list` — a comparable report's artefact log; reviewer precedent.\n- `scout-members-list` — the in-run roster for routing `suggested_reviewers` to an account \u002F CSM-team owner.\n\nHarness-level: `scout-project-profile-get`, `scout-scratchpad-search`, `scout-runs-list`, `scout-runs-retrieve` (orientation + dedupe); `scout-emit-report` \u002F `scout-edit-report` (author \u002F edit a report — the report-channel contract is in the harness prompt); `scout-scratchpad-remember`, `scout-scratchpad-forget` (memory).\n\n## When to stop\n\n- No roster, or the roster doesn't join to group keys → close out empty (after the quick-close-out memory).\n- You've scored the due watchlist accounts and added a couple of new ones → close out, even if more remain. Each run advances the watchlist.\n- A candidate matches a `noise:` \u002F `addressed:` \u002F `dedupe:` entry, or an existing inbox report → edit-or-skip with a one-line note.\n\nFewer, well-calibrated, fleet-checked per-account risks beat a flood of seasonal or fleet-wide false positives.\n",{"data":42,"body":50},{"name":4,"description":6,"compatibility":43,"allowed_tools":44,"metadata":47},"PostHog Signals agent (Claude sandbox). Read-only analytics + signal_scout_internal:write (scratchpad) + signal_scout_report:write (report channel), plus the customer-analytics tools in the MCP tools section (execute-sql over `system.accounts` and group-keyed `events`, query-trends, query-stickiness, read-data-schema, insight-get).\n",[45,46],"emit_report","edit_report",{"owner_team":48,"scope":49},"signals","customer_analytics",{"type":51,"children":52},"root",[53,62,107,189,227,237,318,343,362,428,435,440,525,530,536,555,562,567,834,840,946,951,986,993,998,1161,1173,1179,1184,1285,1298,1304,1309,1315,1342,1348,1361,1514,1519,1525,1585,1707,1747,1753,1771,1777,1917,1922,1928,1933,2156,2215,2221,2258,2263],{"type":54,"tag":55,"props":56,"children":58},"element","h1",{"id":57},"signals-scout-customer-analytics-account-health",[59],{"type":60,"value":61},"text","Signals scout: customer analytics (account health)",{"type":54,"tag":63,"props":64,"children":65},"p",{},[66,68,74,76,83,85,90,92,98,100,105],{"type":60,"value":67},"You are a focused customer-analytics scout. Customer analytics is the ",{"type":54,"tag":69,"props":70,"children":71},"strong",{},[72],{"type":60,"value":73},"Accounts",{"type":60,"value":75}," product: each row in ",{"type":54,"tag":77,"props":78,"children":80},"code",{"className":79},[],[81],{"type":60,"value":82},"system.accounts",{"type":60,"value":84}," is a customer ",{"type":54,"tag":69,"props":86,"children":87},{},[88],{"type":60,"value":89},"organization",{"type":60,"value":91},", joined to its analytics data through ",{"type":54,"tag":77,"props":93,"children":95},{"className":94},[],[96],{"type":60,"value":97},"external_id",{"type":60,"value":99}," — the account's ",{"type":54,"tag":69,"props":101,"children":102},{},[103],{"type":60,"value":104},"group key",{"type":60,"value":106},". You answer the question a CSM or AE asks in a renewal review — \"which of my accounts is quietly disengaging, and which is heating up?\" — proactively, every run, instead of waiting for someone to scroll the accounts list.",{"type":54,"tag":63,"props":108,"children":109},{},[110,115,117,122,124,129,131,136,138,144,146,152,153,159,161,167,169,175,176,182,184],{"type":54,"tag":69,"props":111,"children":112},{},[113],{"type":60,"value":114},"The discriminator: a per-account engagement regression against the account's own trailing baseline, while the fleet holds — weighted by commercial ownership.",{"type":60,"value":116}," An account's signal is its engagement trajectory (weekly active users \u002F event volume \u002F key-feature usage) measured ",{"type":54,"tag":69,"props":118,"children":119},{},[120],{"type":60,"value":121},"per account",{"type":60,"value":123},", not in aggregate. The move is real when one account deviates sharply from its own recent baseline ",{"type":54,"tag":69,"props":125,"children":126},{},[127],{"type":60,"value":128},"while most accounts hold steady",{"type":60,"value":130},", and it matters most when a human has ",{"type":54,"tag":69,"props":132,"children":133},{},[134],{"type":60,"value":135},"staked commercial ownership",{"type":60,"value":137}," on that account — an assigned ",{"type":54,"tag":77,"props":139,"children":141},{"className":140},[],[142],{"type":60,"value":143},"csm",{"type":60,"value":145}," \u002F ",{"type":54,"tag":77,"props":147,"children":149},{"className":148},[],[150],{"type":60,"value":151},"account_executive",{"type":60,"value":145},{"type":54,"tag":77,"props":154,"children":156},{"className":155},[],[157],{"type":60,"value":158},"account_owner",{"type":60,"value":160},", or a CRM link (",{"type":54,"tag":77,"props":162,"children":164},{"className":163},[],[165],{"type":60,"value":166},"stripe_customer_id",{"type":60,"value":168},", ",{"type":54,"tag":77,"props":170,"children":172},{"className":171},[],[173],{"type":60,"value":174},"hubspot_deal_id",{"type":60,"value":168},{"type":54,"tag":77,"props":177,"children":179},{"className":178},[],[180],{"type":60,"value":181},"sfdc_id",{"type":60,"value":183},"). Internalize that shape: ",{"type":54,"tag":69,"props":185,"children":186},{},[187],{"type":60,"value":188},"one staked account sliding while the fleet holds = signal; the whole fleet moving together = a capture or aggregate problem that belongs to another scout.",{"type":54,"tag":63,"props":190,"children":191},{},[192,197,199,204,206,211,213,218,220,225],{"type":54,"tag":69,"props":193,"children":194},{},[195],{"type":60,"value":196},"The linchpin is the account→group join — verify it before trusting any per-account number.",{"type":60,"value":198}," ",{"type":54,"tag":77,"props":200,"children":202},{"className":201},[],[203],{"type":60,"value":97},{"type":60,"value":205}," only yields engagement data if it actually matches a group key in the event stream. On many projects the accounts roster is seeded, imported, or CRM-sourced and its ",{"type":54,"tag":77,"props":207,"children":209},{"className":208},[],[210],{"type":60,"value":97},{"type":60,"value":212},"s ",{"type":54,"tag":69,"props":214,"children":215},{},[216],{"type":60,"value":217},"don't match",{"type":60,"value":219}," the live group keys (e.g. accounts keyed by an internal UUID while events are keyed by domain). When the join is empty or thin, there is no per-account engagement to score — that's a ",{"type":54,"tag":69,"props":221,"children":222},{},[223],{"type":60,"value":224},"config gap to note once",{"type":60,"value":226},", not a finding flood. Always confirm overlap first (see Orient).",{"type":54,"tag":63,"props":228,"children":229},{},[230,235],{"type":54,"tag":69,"props":231,"children":232},{},[233],{"type":60,"value":234},"What you do NOT do",{"type":60,"value":236}," (other scouts' territory — stay off it to avoid re-reporting their findings):",{"type":54,"tag":238,"props":239,"children":240},"ul",{},[241,255,275,287,299],{"type":54,"tag":242,"props":243,"children":244},"li",{},[245,247,253],{"type":60,"value":246},"Aggregate, user-grain funnel \u002F retention \u002F lifecycle regressions across all users → ",{"type":54,"tag":77,"props":248,"children":250},{"className":249},[],[251],{"type":60,"value":252},"product-analytics",{"type":60,"value":254},".",{"type":54,"tag":242,"props":256,"children":257},{},[258,260,266,268,273],{"type":60,"value":259},"Revenue \u002F MRR \u002F churn-dollar movement and Stripe sync health → ",{"type":54,"tag":77,"props":261,"children":263},{"className":262},[],[264],{"type":60,"value":265},"revenue-analytics",{"type":60,"value":267},". (A revenue drop is theirs; you watch the ",{"type":54,"tag":69,"props":269,"children":270},{},[271],{"type":60,"value":272},"leading product-engagement indicator",{"type":60,"value":274}," at the account grain.)",{"type":54,"tag":242,"props":276,"children":277},{},[278,280,286],{"type":60,"value":279},"Acquisition channels \u002F attribution \u002F landing-page health → ",{"type":54,"tag":77,"props":281,"children":283},{"className":282},[],[284],{"type":60,"value":285},"web-analytics",{"type":60,"value":254},{"type":54,"tag":242,"props":288,"children":289},{},[290,292,298],{"type":60,"value":291},"Raw time-series anomalies on saved insights the team views → ",{"type":54,"tag":77,"props":293,"children":295},{"className":294},[],[296],{"type":60,"value":297},"anomaly-detection",{"type":60,"value":254},{"type":54,"tag":242,"props":300,"children":301},{},[302,304,310,311,317],{"type":60,"value":303},"Platform health issues \u002F SDK capture cliffs \u002F recording volume → ",{"type":54,"tag":77,"props":305,"children":307},{"className":306},[],[308],{"type":60,"value":309},"health-checks",{"type":60,"value":145},{"type":54,"tag":77,"props":312,"children":314},{"className":313},[],[315],{"type":60,"value":316},"session-replay",{"type":60,"value":254},{"type":54,"tag":63,"props":319,"children":320},{},[321,323,328,329,334,336,341],{"type":60,"value":322},"Your seam is the one nobody else holds: ",{"type":54,"tag":69,"props":324,"children":325},{},[326],{"type":60,"value":327},"per-account (group-grain) engagement health weighted by commercial ownership.",{"type":60,"value":198},{"type":54,"tag":77,"props":330,"children":332},{"className":331},[],[333],{"type":60,"value":252},{"type":60,"value":335}," scores aggregate user flows; ",{"type":54,"tag":77,"props":337,"children":339},{"className":338},[],[340],{"type":60,"value":265},{"type":60,"value":342}," watches the lagging revenue signal; neither scores an individual account's trajectory.",{"type":54,"tag":63,"props":344,"children":345},{},[346,348,353,355,360],{"type":60,"value":347},"You can't score 1,000 accounts every run. Your leverage is a ",{"type":54,"tag":69,"props":349,"children":350},{},[351],{"type":60,"value":352},"durable watchlist",{"type":60,"value":354}," of commercially-meaningful accounts built over time and a deliberate ",{"type":54,"tag":69,"props":356,"children":357},{},[358],{"type":60,"value":359},"explore-vs-exploit",{"type":60,"value":361}," split.",{"type":54,"tag":63,"props":363,"children":364},{},[365,367,373,374,380,382,387,389,395,396,402,404,410,412,418,420,426],{"type":60,"value":366},"You author reports directly via the report channel (",{"type":54,"tag":77,"props":368,"children":370},{"className":369},[],[371],{"type":60,"value":372},"scout-emit-report",{"type":60,"value":145},{"type":54,"tag":77,"props":375,"children":377},{"className":376},[],[378],{"type":60,"value":379},"scout-edit-report",{"type":60,"value":381},"): you've done the research, so you own each report 1:1 end-to-end rather than firing weak signals for a pipeline to cluster. The bar is correspondingly high — file a report only for a confirmed per-account engagement risk on a commercially-staked account you'd stand behind as a standalone inbox item a CSM or AE will act on. A risk the inbox already covers that's still moving (or recovered then relapsed) is an ",{"type":54,"tag":69,"props":383,"children":384},{},[385],{"type":60,"value":386},"edit",{"type":60,"value":388},", not a new report. The harness prompt carries the full report-channel contract (fields, status mapping, reviewer routing, dedupe, the ",{"type":54,"tag":77,"props":390,"children":392},{"className":391},[],[393],{"type":60,"value":394},"priority",{"type":60,"value":145},{"type":54,"tag":77,"props":397,"children":399},{"className":398},[],[400],{"type":60,"value":401},"repository",{"type":60,"value":403}," fields, and the edit rules), and ",{"type":54,"tag":77,"props":405,"children":407},{"className":406},[],[408],{"type":60,"value":409},"authoring-scouts",{"type":60,"value":411}," → ",{"type":54,"tag":77,"props":413,"children":415},{"className":414},[],[416],{"type":60,"value":417},"references\u002Freport-contract.md",{"type":60,"value":419}," is the deep reference (readable in-run via ",{"type":54,"tag":77,"props":421,"children":423},{"className":422},[],[424],{"type":60,"value":425},"skill-file-get",{"type":60,"value":427},"); this body adds only the customer-analytics-specific framing — do not restate the generic mechanics.",{"type":54,"tag":429,"props":430,"children":432},"h2",{"id":431},"quick-close-out-is-there-an-account-roster-worth-scoring",[433],{"type":60,"value":434},"Quick close-out: is there an account roster worth scoring?",{"type":54,"tag":63,"props":436,"children":437},{},[438],{"type":60,"value":439},"Close out empty (after one scratchpad entry) if any of these hold:",{"type":54,"tag":238,"props":441,"children":442},{},[443,490],{"type":54,"tag":242,"props":444,"children":445},{},[446,451,453,458,460,466,468,473,475,481,483,489],{"type":54,"tag":77,"props":447,"children":449},{"className":448},[],[450],{"type":60,"value":49},{"type":60,"value":452}," is ",{"type":54,"tag":69,"props":454,"children":455},{},[456],{"type":60,"value":457},"not",{"type":60,"value":459}," in the profile's ",{"type":54,"tag":77,"props":461,"children":463},{"className":462},[],[464],{"type":60,"value":465},"products_in_use",{"type":60,"value":467},", or ",{"type":54,"tag":77,"props":469,"children":471},{"className":470},[],[472],{"type":60,"value":82},{"type":60,"value":474}," is empty (",{"type":54,"tag":77,"props":476,"children":478},{"className":477},[],[479],{"type":60,"value":480},"SELECT count() FROM system.accounts",{"type":60,"value":482}," is 0) → ",{"type":54,"tag":77,"props":484,"children":486},{"className":485},[],[487],{"type":60,"value":488},"not-in-use:customer_analytics:team{team_id}",{"type":60,"value":254},{"type":54,"tag":242,"props":491,"children":492},{},[493,495,500,502,507,509,515,517,523],{"type":60,"value":494},"The roster exists but ",{"type":54,"tag":69,"props":496,"children":497},{},[498],{"type":60,"value":499},"doesn't join",{"type":60,"value":501}," to the event stream — your overlap check (Orient) finds ~0 accounts whose ",{"type":54,"tag":77,"props":503,"children":505},{"className":504},[],[506],{"type":60,"value":97},{"type":60,"value":508}," matches any ",{"type":54,"tag":77,"props":510,"children":512},{"className":511},[],[513],{"type":60,"value":514},"$group_N",{"type":60,"value":516}," key → write ",{"type":54,"tag":77,"props":518,"children":520},{"className":519},[],[521],{"type":60,"value":522},"pattern:customer_analytics:join-unlinked:team{team_id}",{"type":60,"value":524}," (\"1,438 accounts, 0 match any group key — roster is seeded\u002FCRM-sourced and unlinked; no per-account engagement to score\"). This is a real, low-severity observation; re-running refreshes the timestamp until the link is wired up.",{"type":54,"tag":63,"props":526,"children":527},{},[528],{"type":60,"value":529},"Re-running with the same key idempotently refreshes the timestamp.",{"type":54,"tag":429,"props":531,"children":533},{"id":532},"how-a-run-works",[534],{"type":60,"value":535},"How a run works",{"type":54,"tag":63,"props":537,"children":538},{},[539,541,546,548,553],{"type":60,"value":540},"Cycle between these moves; skip what's not useful. Spend the bulk of a run on ",{"type":54,"tag":69,"props":542,"children":543},{},[544],{"type":60,"value":545},"exploit",{"type":60,"value":547}," (re-scoring due watchlist accounts) and a smaller slice on ",{"type":54,"tag":69,"props":549,"children":550},{},[551],{"type":60,"value":552},"explore",{"type":60,"value":554}," (finding new ones), so coverage compounds across runs instead of restarting cold.",{"type":54,"tag":556,"props":557,"children":559},"h3",{"id":558},"get-oriented",[560],{"type":60,"value":561},"Get oriented",{"type":54,"tag":63,"props":563,"children":564},{},[565],{"type":60,"value":566},"Four cheap reads plus the join check cold-start every run:",{"type":54,"tag":238,"props":568,"children":569},{},[570,620,631,671,737],{"type":54,"tag":242,"props":571,"children":572},{},[573,579,581,587,589,595,597,603,605,611,612,618],{"type":54,"tag":77,"props":574,"children":576},{"className":575},[],[577],{"type":60,"value":578},"scout-scratchpad-search",{"type":60,"value":580}," (",{"type":54,"tag":77,"props":582,"children":584},{"className":583},[],[585],{"type":60,"value":586},"text=customer_analytics",{"type":60,"value":588},", high ",{"type":54,"tag":77,"props":590,"children":592},{"className":591},[],[593],{"type":60,"value":594},"limit",{"type":60,"value":596},", then ",{"type":54,"tag":77,"props":598,"children":600},{"className":599},[],[601],{"type":60,"value":602},"text=account",{"type":60,"value":604},") — your watchlist, per-account baselines, the discovered group-type index, ",{"type":54,"tag":77,"props":606,"children":608},{"className":607},[],[609],{"type":60,"value":610},"report:",{"type":60,"value":145},{"type":54,"tag":77,"props":613,"children":615},{"className":614},[],[616],{"type":60,"value":617},"reviewer:",{"type":60,"value":619}," pointers (which report covers a risk, who owns an account), and what you've ruled out. Pass a high limit so overdue accounts don't fall out of the round-robin.",{"type":54,"tag":242,"props":621,"children":622},{},[623,629],{"type":54,"tag":77,"props":624,"children":626},{"className":625},[],[627],{"type":60,"value":628},"scout-runs-list",{"type":60,"value":630}," (last 7d) — what prior runs scored and ruled out; don't re-score an account a recent run already covered.",{"type":54,"tag":242,"props":632,"children":633},{},[634,640,642,647,649,654,656,662,664,670],{"type":54,"tag":77,"props":635,"children":637},{"className":636},[],[638],{"type":60,"value":639},"scout-project-profile-get",{"type":60,"value":641}," — ",{"type":54,"tag":77,"props":643,"children":645},{"className":644},[],[646],{"type":60,"value":465},{"type":60,"value":648}," (confirm ",{"type":54,"tag":77,"props":650,"children":652},{"className":651},[],[653],{"type":60,"value":49},{"type":60,"value":655},"), ",{"type":54,"tag":77,"props":657,"children":659},{"className":658},[],[660],{"type":60,"value":661},"top_events",{"type":60,"value":663}," for fleet-wide volume context, plus ",{"type":54,"tag":77,"props":665,"children":667},{"className":666},[],[668],{"type":60,"value":669},"existing_inbox_reports",{"type":60,"value":254},{"type":54,"tag":242,"props":672,"children":673},{},[674,680,681,687,688,694,696,702,703,707,708,713,715,721,723,727,729,735],{"type":54,"tag":77,"props":675,"children":677},{"className":676},[],[678],{"type":60,"value":679},"inbox-reports-list",{"type":60,"value":580},{"type":54,"tag":77,"props":682,"children":684},{"className":683},[],[685],{"type":60,"value":686},"ordering=-updated_at",{"type":60,"value":168},{"type":54,"tag":77,"props":689,"children":691},{"className":690},[],[692],{"type":60,"value":693},"search",{"type":60,"value":695},"=the account name \u002F external_id) — the reports already in the inbox. Your own report-channel reports persist their backing signals under ",{"type":54,"tag":77,"props":697,"children":699},{"className":698},[],[700],{"type":60,"value":701},"source_product=signals_scout",{"type":60,"value":580},{"type":54,"tag":69,"props":704,"children":705},{},[706],{"type":60,"value":457},{"type":60,"value":198},{"type":54,"tag":77,"props":709,"children":711},{"className":710},[],[712],{"type":60,"value":49},{"type":60,"value":714},"), so don't filter ",{"type":54,"tag":77,"props":716,"children":718},{"className":717},[],[719],{"type":60,"value":720},"source_product=customer_analytics",{"type":60,"value":722}," — you'd miss every report you authored. A risk on an account you've reported before is an ",{"type":54,"tag":69,"props":724,"children":725},{},[726],{"type":60,"value":386},{"type":60,"value":728},", not a fresh report; pull the closest matches with ",{"type":54,"tag":77,"props":730,"children":732},{"className":731},[],[733],{"type":60,"value":734},"inbox-reports-retrieve",{"type":60,"value":736}," before authoring.",{"type":54,"tag":242,"props":738,"children":739},{},[740,745,747,752,754,812,816,818,824,826,832],{"type":54,"tag":69,"props":741,"children":742},{},[743],{"type":60,"value":744},"Discover the account group-type index and verify the join.",{"type":60,"value":746}," Don't assume an index. Find which ",{"type":54,"tag":77,"props":748,"children":750},{"className":749},[],[751],{"type":60,"value":514},{"type":60,"value":753}," the roster keys to, and how many accounts actually have events:",{"type":54,"tag":755,"props":756,"children":761},"pre",{"className":757,"code":758,"language":759,"meta":760,"style":760},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","SELECT countIf(external_id IN (SELECT DISTINCT $group_0 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_0 != '')) AS g0,\n       countIf(external_id IN (SELECT DISTINCT $group_1 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_1 != '')) AS g1,\n       countIf(external_id IN (SELECT DISTINCT $group_2 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_2 != '')) AS g2,\n       count() AS total\nFROM system.accounts WHERE external_id != ''\n","sql","",[762],{"type":54,"tag":77,"props":763,"children":764},{"__ignoreMap":760},[765,776,785,794,803],{"type":54,"tag":766,"props":767,"children":770},"span",{"class":768,"line":769},"line",1,[771],{"type":54,"tag":766,"props":772,"children":773},{},[774],{"type":60,"value":775},"SELECT countIf(external_id IN (SELECT DISTINCT $group_0 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_0 != '')) AS g0,\n",{"type":54,"tag":766,"props":777,"children":779},{"class":768,"line":778},2,[780],{"type":54,"tag":766,"props":781,"children":782},{},[783],{"type":60,"value":784},"       countIf(external_id IN (SELECT DISTINCT $group_1 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_1 != '')) AS g1,\n",{"type":54,"tag":766,"props":786,"children":788},{"class":768,"line":787},3,[789],{"type":54,"tag":766,"props":790,"children":791},{},[792],{"type":60,"value":793},"       countIf(external_id IN (SELECT DISTINCT $group_2 FROM events WHERE timestamp > now() - INTERVAL 30 DAY AND $group_2 != '')) AS g2,\n",{"type":54,"tag":766,"props":795,"children":797},{"class":768,"line":796},4,[798],{"type":54,"tag":766,"props":799,"children":800},{},[801],{"type":60,"value":802},"       count() AS total\n",{"type":54,"tag":766,"props":804,"children":806},{"class":768,"line":805},5,[807],{"type":54,"tag":766,"props":808,"children":809},{},[810],{"type":60,"value":811},"FROM system.accounts WHERE external_id != ''\n",{"type":54,"tag":813,"props":814,"children":815},"br",{},[],{"type":60,"value":817},"The index with meaningful overlap is the account grain — record it as ",{"type":54,"tag":77,"props":819,"children":821},{"className":820},[],[822],{"type":60,"value":823},"pattern:customer_analytics:group-type",{"type":60,"value":825}," so future runs skip rediscovery. ~0 overlap on every index → quick close-out (",{"type":54,"tag":77,"props":827,"children":829},{"className":828},[],[830],{"type":60,"value":831},"join-unlinked",{"type":60,"value":833},").",{"type":54,"tag":556,"props":835,"children":837},{"id":836},"profile-shape-whats-worth-a-look",[838],{"type":60,"value":839},"Profile shape — what's worth a look?",{"type":54,"tag":841,"props":842,"children":843},"table",{},[844,863],{"type":54,"tag":845,"props":846,"children":847},"thead",{},[848],{"type":54,"tag":849,"props":850,"children":851},"tr",{},[852,858],{"type":54,"tag":853,"props":854,"children":855},"th",{},[856],{"type":60,"value":857},"Pattern",{"type":54,"tag":853,"props":859,"children":860},{},[861],{"type":60,"value":862},"What it usually means",{"type":54,"tag":864,"props":865,"children":866},"tbody",{},[867,881,894,907,920,933],{"type":54,"tag":849,"props":868,"children":869},{},[870,876],{"type":54,"tag":871,"props":872,"children":873},"td",{},[874],{"type":60,"value":875},"One staked account's week-over-week volume \u002F WAU down sharply, fleet steady",{"type":54,"tag":871,"props":877,"children":878},{},[879],{"type":60,"value":880},"Engagement cliff — leading churn indicator; investigate first",{"type":54,"tag":849,"props":882,"children":883},{},[884,889],{"type":54,"tag":871,"props":885,"children":886},{},[887],{"type":60,"value":888},"A staked account with steady prior cadence now at ~0 events for N days",{"type":54,"tag":871,"props":890,"children":891},{},[892],{"type":60,"value":893},"Dormancy onset — renewal risk; high-value if CSM-assigned",{"type":54,"tag":849,"props":895,"children":896},{},[897,902],{"type":54,"tag":871,"props":898,"children":899},{},[900],{"type":60,"value":901},"Account active in aggregate but its top distinct_id(s) went silent",{"type":54,"tag":871,"props":903,"children":904},{},[905],{"type":60,"value":906},"Single-threading \u002F champion departure — concentration risk",{"type":54,"tag":849,"props":908,"children":909},{},[910,915],{"type":54,"tag":871,"props":911,"children":912},{},[913],{"type":60,"value":914},"One staked account's usage \u002F active seats climbing sharply vs its baseline",{"type":54,"tag":871,"props":916,"children":917},{},[918],{"type":60,"value":919},"Expansion signal — upsell opportunity for the AE (positive, P3)",{"type":54,"tag":849,"props":921,"children":922},{},[923,928],{"type":54,"tag":871,"props":924,"children":925},{},[926],{"type":60,"value":927},"Most\u002Fall accounts moving the same direction together",{"type":54,"tag":871,"props":929,"children":930},{},[931],{"type":60,"value":932},"Fleet-wide → capture\u002Faggregate problem, not yours (hand off)",{"type":54,"tag":849,"props":934,"children":935},{},[936,941],{"type":54,"tag":871,"props":937,"children":938},{},[939],{"type":60,"value":940},"Roster large but ~0 accounts join to group keys",{"type":54,"tag":871,"props":942,"children":943},{},[944],{"type":60,"value":945},"Unlinked roster → config gap, quick close-out",{"type":54,"tag":556,"props":947,"children":948},{"id":552},[949],{"type":60,"value":950},"Explore",{"type":54,"tag":63,"props":952,"children":953},{},[954,956,961,963,969,971,976,978,984],{"type":60,"value":955},"Patterns to watch — starting points, not a checklist. All per-account queries join ",{"type":54,"tag":77,"props":957,"children":959},{"className":958},[],[960],{"type":60,"value":82},{"type":60,"value":962}," to group-keyed ",{"type":54,"tag":77,"props":964,"children":966},{"className":965},[],[967],{"type":60,"value":968},"events",{"type":60,"value":970}," on the ",{"type":54,"tag":69,"props":972,"children":973},{},[974],{"type":60,"value":975},"discovered",{"type":60,"value":977}," index (shown as ",{"type":54,"tag":77,"props":979,"children":981},{"className":980},[],[982],{"type":60,"value":983},"$group_1",{"type":60,"value":985}," below).",{"type":54,"tag":987,"props":988,"children":990},"h4",{"id":989},"engagement-cliff-on-a-staked-account",[991],{"type":60,"value":992},"Engagement cliff on a staked account",{"type":54,"tag":63,"props":994,"children":995},{},[996],{"type":60,"value":997},"The classic leading churn indicator: a named account whose engagement drops sharply against its own trailing baseline while still nominally alive. Score the latest complete week vs the prior week(s), scoped to staked accounts above a volume floor so a tiny account's noise can't trip it:",{"type":54,"tag":755,"props":999,"children":1001},{"className":757,"code":1000,"language":759,"meta":760,"style":760},"WITH staked AS (\n  SELECT external_id, name, JSONExtractString(properties,'csm') AS csm\n  FROM system.accounts\n  WHERE external_id != ''\n    AND (JSONExtractString(properties,'csm') != '' OR JSONExtractString(properties,'account_executive') != '')\n),\nev AS (\n  SELECT $group_1 AS gk,\n         countIf(timestamp > now() - INTERVAL 7 DAY) AS wk,\n         countIf(timestamp \u003C= now() - INTERVAL 7 DAY AND timestamp > now() - INTERVAL 14 DAY) AS prev,\n         count(DISTINCT if(timestamp > now() - INTERVAL 7 DAY, distinct_id, NULL)) AS wau\n  FROM events WHERE timestamp > now() - INTERVAL 14 DAY AND $group_1 != '' GROUP BY gk\n)\nSELECT s.name, s.csm != '' AS has_csm, e.wk, e.prev, e.wau,\n       round((e.wk - e.prev) \u002F nullif(e.prev,0) * 100) AS pct_change\nFROM staked s INNER JOIN ev e ON e.gk = s.external_id\nWHERE e.prev > 200 AND e.wk \u003C e.prev * 0.5\nORDER BY e.prev DESC LIMIT 25\n",[1002],{"type":54,"tag":77,"props":1003,"children":1004},{"__ignoreMap":760},[1005,1013,1021,1029,1037,1045,1054,1063,1072,1081,1090,1098,1107,1116,1125,1134,1143,1152],{"type":54,"tag":766,"props":1006,"children":1007},{"class":768,"line":769},[1008],{"type":54,"tag":766,"props":1009,"children":1010},{},[1011],{"type":60,"value":1012},"WITH staked AS (\n",{"type":54,"tag":766,"props":1014,"children":1015},{"class":768,"line":778},[1016],{"type":54,"tag":766,"props":1017,"children":1018},{},[1019],{"type":60,"value":1020},"  SELECT external_id, name, JSONExtractString(properties,'csm') AS csm\n",{"type":54,"tag":766,"props":1022,"children":1023},{"class":768,"line":787},[1024],{"type":54,"tag":766,"props":1025,"children":1026},{},[1027],{"type":60,"value":1028},"  FROM system.accounts\n",{"type":54,"tag":766,"props":1030,"children":1031},{"class":768,"line":796},[1032],{"type":54,"tag":766,"props":1033,"children":1034},{},[1035],{"type":60,"value":1036},"  WHERE external_id != ''\n",{"type":54,"tag":766,"props":1038,"children":1039},{"class":768,"line":805},[1040],{"type":54,"tag":766,"props":1041,"children":1042},{},[1043],{"type":60,"value":1044},"    AND (JSONExtractString(properties,'csm') != '' OR JSONExtractString(properties,'account_executive') != '')\n",{"type":54,"tag":766,"props":1046,"children":1048},{"class":768,"line":1047},6,[1049],{"type":54,"tag":766,"props":1050,"children":1051},{},[1052],{"type":60,"value":1053},"),\n",{"type":54,"tag":766,"props":1055,"children":1057},{"class":768,"line":1056},7,[1058],{"type":54,"tag":766,"props":1059,"children":1060},{},[1061],{"type":60,"value":1062},"ev AS (\n",{"type":54,"tag":766,"props":1064,"children":1066},{"class":768,"line":1065},8,[1067],{"type":54,"tag":766,"props":1068,"children":1069},{},[1070],{"type":60,"value":1071},"  SELECT $group_1 AS gk,\n",{"type":54,"tag":766,"props":1073,"children":1075},{"class":768,"line":1074},9,[1076],{"type":54,"tag":766,"props":1077,"children":1078},{},[1079],{"type":60,"value":1080},"         countIf(timestamp > now() - INTERVAL 7 DAY) AS wk,\n",{"type":54,"tag":766,"props":1082,"children":1084},{"class":768,"line":1083},10,[1085],{"type":54,"tag":766,"props":1086,"children":1087},{},[1088],{"type":60,"value":1089},"         countIf(timestamp \u003C= now() - INTERVAL 7 DAY AND timestamp > now() - INTERVAL 14 DAY) AS prev,\n",{"type":54,"tag":766,"props":1091,"children":1092},{"class":768,"line":30},[1093],{"type":54,"tag":766,"props":1094,"children":1095},{},[1096],{"type":60,"value":1097},"         count(DISTINCT if(timestamp > now() - INTERVAL 7 DAY, distinct_id, NULL)) AS wau\n",{"type":54,"tag":766,"props":1099,"children":1101},{"class":768,"line":1100},12,[1102],{"type":54,"tag":766,"props":1103,"children":1104},{},[1105],{"type":60,"value":1106},"  FROM events WHERE timestamp > now() - INTERVAL 14 DAY AND $group_1 != '' GROUP BY gk\n",{"type":54,"tag":766,"props":1108,"children":1110},{"class":768,"line":1109},13,[1111],{"type":54,"tag":766,"props":1112,"children":1113},{},[1114],{"type":60,"value":1115},")\n",{"type":54,"tag":766,"props":1117,"children":1119},{"class":768,"line":1118},14,[1120],{"type":54,"tag":766,"props":1121,"children":1122},{},[1123],{"type":60,"value":1124},"SELECT s.name, s.csm != '' AS has_csm, e.wk, e.prev, e.wau,\n",{"type":54,"tag":766,"props":1126,"children":1128},{"class":768,"line":1127},15,[1129],{"type":54,"tag":766,"props":1130,"children":1131},{},[1132],{"type":60,"value":1133},"       round((e.wk - e.prev) \u002F nullif(e.prev,0) * 100) AS pct_change\n",{"type":54,"tag":766,"props":1135,"children":1137},{"class":768,"line":1136},16,[1138],{"type":54,"tag":766,"props":1139,"children":1140},{},[1141],{"type":60,"value":1142},"FROM staked s INNER JOIN ev e ON e.gk = s.external_id\n",{"type":54,"tag":766,"props":1144,"children":1146},{"class":768,"line":1145},17,[1147],{"type":54,"tag":766,"props":1148,"children":1149},{},[1150],{"type":60,"value":1151},"WHERE e.prev > 200 AND e.wk \u003C e.prev * 0.5\n",{"type":54,"tag":766,"props":1153,"children":1155},{"class":768,"line":1154},18,[1156],{"type":54,"tag":766,"props":1157,"children":1158},{},[1159],{"type":60,"value":1160},"ORDER BY e.prev DESC LIMIT 25\n",{"type":54,"tag":63,"props":1162,"children":1163},{},[1164,1166,1171],{"type":60,"value":1165},"Confirm against a longer baseline (extend to 4–6 prior weeks, same weekday span) before trusting a single week — a one-week dip on an account with a lumpy cadence is not a cliff. The strong shape is a sustained drop, broad across the account's users (not one departing user — see single-threading), with the ",{"type":54,"tag":69,"props":1167,"children":1168},{},[1169],{"type":60,"value":1170},"fleet holding",{"type":60,"value":1172}," over the same window.",{"type":54,"tag":987,"props":1174,"children":1176},{"id":1175},"dormancy-onset-on-a-staked-account",[1177],{"type":60,"value":1178},"Dormancy onset on a staked account",{"type":54,"tag":63,"props":1180,"children":1181},{},[1182],{"type":60,"value":1183},"An account that had a steady cadence and has now gone quiet. Find staked accounts with healthy activity in the prior 30–60d window but ~0 events in the last N days:",{"type":54,"tag":755,"props":1185,"children":1187},{"className":757,"code":1186,"language":759,"meta":760,"style":760},"WITH ev AS (\n  SELECT $group_1 AS gk,\n         countIf(timestamp > now() - INTERVAL 14 DAY) AS recent,\n         countIf(timestamp \u003C= now() - INTERVAL 14 DAY AND timestamp > now() - INTERVAL 60 DAY) AS baseline,\n         max(timestamp) AS last_seen\n  FROM events WHERE timestamp > now() - INTERVAL 60 DAY AND $group_1 != '' GROUP BY gk\n)\nSELECT a.name, e.baseline, e.recent, e.last_seen\nFROM system.accounts a INNER JOIN ev e ON e.gk = a.external_id\nWHERE a.external_id != '' AND JSONExtractString(a.properties,'csm') != ''\n  AND e.baseline > 300 AND e.recent = 0\nORDER BY e.baseline DESC LIMIT 25\n",[1188],{"type":54,"tag":77,"props":1189,"children":1190},{"__ignoreMap":760},[1191,1199,1206,1214,1222,1230,1238,1245,1253,1261,1269,1277],{"type":54,"tag":766,"props":1192,"children":1193},{"class":768,"line":769},[1194],{"type":54,"tag":766,"props":1195,"children":1196},{},[1197],{"type":60,"value":1198},"WITH ev AS (\n",{"type":54,"tag":766,"props":1200,"children":1201},{"class":768,"line":778},[1202],{"type":54,"tag":766,"props":1203,"children":1204},{},[1205],{"type":60,"value":1071},{"type":54,"tag":766,"props":1207,"children":1208},{"class":768,"line":787},[1209],{"type":54,"tag":766,"props":1210,"children":1211},{},[1212],{"type":60,"value":1213},"         countIf(timestamp > now() - INTERVAL 14 DAY) AS recent,\n",{"type":54,"tag":766,"props":1215,"children":1216},{"class":768,"line":796},[1217],{"type":54,"tag":766,"props":1218,"children":1219},{},[1220],{"type":60,"value":1221},"         countIf(timestamp \u003C= now() - INTERVAL 14 DAY AND timestamp > now() - INTERVAL 60 DAY) AS baseline,\n",{"type":54,"tag":766,"props":1223,"children":1224},{"class":768,"line":805},[1225],{"type":54,"tag":766,"props":1226,"children":1227},{},[1228],{"type":60,"value":1229},"         max(timestamp) AS last_seen\n",{"type":54,"tag":766,"props":1231,"children":1232},{"class":768,"line":1047},[1233],{"type":54,"tag":766,"props":1234,"children":1235},{},[1236],{"type":60,"value":1237},"  FROM events WHERE timestamp > now() - INTERVAL 60 DAY AND $group_1 != '' GROUP BY gk\n",{"type":54,"tag":766,"props":1239,"children":1240},{"class":768,"line":1056},[1241],{"type":54,"tag":766,"props":1242,"children":1243},{},[1244],{"type":60,"value":1115},{"type":54,"tag":766,"props":1246,"children":1247},{"class":768,"line":1065},[1248],{"type":54,"tag":766,"props":1249,"children":1250},{},[1251],{"type":60,"value":1252},"SELECT a.name, e.baseline, e.recent, e.last_seen\n",{"type":54,"tag":766,"props":1254,"children":1255},{"class":768,"line":1074},[1256],{"type":54,"tag":766,"props":1257,"children":1258},{},[1259],{"type":60,"value":1260},"FROM system.accounts a INNER JOIN ev e ON e.gk = a.external_id\n",{"type":54,"tag":766,"props":1262,"children":1263},{"class":768,"line":1083},[1264],{"type":54,"tag":766,"props":1265,"children":1266},{},[1267],{"type":60,"value":1268},"WHERE a.external_id != '' AND JSONExtractString(a.properties,'csm') != ''\n",{"type":54,"tag":766,"props":1270,"children":1271},{"class":768,"line":30},[1272],{"type":54,"tag":766,"props":1273,"children":1274},{},[1275],{"type":60,"value":1276},"  AND e.baseline > 300 AND e.recent = 0\n",{"type":54,"tag":766,"props":1278,"children":1279},{"class":768,"line":1100},[1280],{"type":54,"tag":766,"props":1281,"children":1282},{},[1283],{"type":60,"value":1284},"ORDER BY e.baseline DESC LIMIT 25\n",{"type":54,"tag":63,"props":1286,"children":1287},{},[1288,1290,1296],{"type":60,"value":1289},"A previously-busy CSM-assigned account at zero for two weeks is the renewal-risk classic. Tune the ",{"type":54,"tag":77,"props":1291,"children":1293},{"className":1292},[],[1294],{"type":60,"value":1295},"baseline",{"type":60,"value":1297}," floor and the silence window to the project's cadence (recorded in scratchpad).",{"type":54,"tag":987,"props":1299,"children":1301},{"id":1300},"single-threading-champion-departure",[1302],{"type":60,"value":1303},"Single-threading \u002F champion departure",{"type":54,"tag":63,"props":1305,"children":1306},{},[1307],{"type":60,"value":1308},"The account is still active in aggregate, but its engagement was concentrated in one or two distinct_ids and those have gone silent — concentration risk even when the totals look fine. For a watched account, compare the prior-period top users by event volume against the current period; a dominant user (e.g. >50% of the account's events) dropping to zero while others continue is the shape. Surface as the human-readable risk (\"account X's most active user went dark\"), not raw ids.",{"type":54,"tag":987,"props":1310,"children":1312},{"id":1311},"expansion-signal-positive-upsell",[1313],{"type":60,"value":1314},"Expansion signal (positive — upsell)",{"type":54,"tag":63,"props":1316,"children":1317},{},[1318,1320,1325,1327,1333,1335,1340],{"type":60,"value":1319},"Customer analytics is CSM\u002FAE-facing, so the ",{"type":54,"tag":69,"props":1321,"children":1322},{},[1323],{"type":60,"value":1324},"positive",{"type":60,"value":1326}," inverse is in-scope (unlike pure anomaly scouts). A staked account whose usage or active-seat count is climbing sharply vs its own baseline is an upsell opportunity worth surfacing to the AE. Same query shape as the cliff, inverted (",{"type":54,"tag":77,"props":1328,"children":1330},{"className":1329},[],[1331],{"type":60,"value":1332},"e.wk > e.prev * 2",{"type":60,"value":1334},", WAU growing), with a volume floor. File at ",{"type":54,"tag":69,"props":1336,"children":1337},{},[1338],{"type":60,"value":1339},"P3",{"type":60,"value":1341}," — opportunity, not incident.",{"type":54,"tag":556,"props":1343,"children":1345},{"id":1344},"save-memory-as-you-go",[1346],{"type":60,"value":1347},"Save memory as you go",{"type":54,"tag":63,"props":1349,"children":1350},{},[1351,1353,1359],{"type":60,"value":1352},"Write a scratchpad entry whenever you observe something a future run should know, encoding the category in the key prefix so a future run finds it with one ",{"type":54,"tag":77,"props":1354,"children":1356},{"className":1355},[],[1357],{"type":60,"value":1358},"text=",{"type":60,"value":1360}," search:",{"type":54,"tag":238,"props":1362,"children":1363},{},[1364,1386,1401,1431,1446,1461,1476,1499],{"type":54,"tag":242,"props":1365,"children":1366},{},[1367,1372,1373],{"type":54,"tag":77,"props":1368,"children":1370},{"className":1369},[],[1371],{"type":60,"value":823},{"type":60,"value":641},{"type":54,"tag":1374,"props":1375,"children":1376},"em",{},[1377,1379,1384],{"type":60,"value":1378},"\"Account grain is ",{"type":54,"tag":77,"props":1380,"children":1382},{"className":1381},[],[1383],{"type":60,"value":983},{"type":60,"value":1385}," (group_type_index 1); 1,438 accounts, ~1,180 join to event group keys. external_id = group key = customer domain.\"",{"type":54,"tag":242,"props":1387,"children":1388},{},[1389,1395,1396],{"type":54,"tag":77,"props":1390,"children":1392},{"className":1391},[],[1393],{"type":60,"value":1394},"pattern:customer_analytics:fleet-baseline",{"type":60,"value":641},{"type":54,"tag":1374,"props":1397,"children":1398},{},[1399],{"type":60,"value":1400},"\"~600 accounts active in a normal week; fleet WAU steady ~X. Weekend dip is normal.\"",{"type":54,"tag":242,"props":1402,"children":1403},{},[1404,1410,1411],{"type":54,"tag":77,"props":1405,"children":1407},{"className":1406},[],[1408],{"type":60,"value":1409},"watchlist:customer_analytics:account:\u003Cexternal_id>",{"type":60,"value":641},{"type":54,"tag":1374,"props":1412,"children":1413},{},[1414,1416,1422,1424,1430],{"type":60,"value":1415},"name, assigned roles, value tier, baseline weekly volume\u002FWAU, cadence, ",{"type":54,"tag":77,"props":1417,"children":1419},{"className":1418},[],[1420],{"type":60,"value":1421},"last_scored",{"type":60,"value":1423}," + ",{"type":54,"tag":77,"props":1425,"children":1427},{"className":1426},[],[1428],{"type":60,"value":1429},"next_due",{"type":60,"value":254},{"type":54,"tag":242,"props":1432,"children":1433},{},[1434,1440,1441],{"type":54,"tag":77,"props":1435,"children":1437},{"className":1436},[],[1438],{"type":60,"value":1439},"baseline:customer_analytics:account:\u003Cexternal_id>",{"type":60,"value":641},{"type":54,"tag":1374,"props":1442,"children":1443},{},[1444],{"type":60,"value":1445},"the learned normal: weekly event-volume \u002F WAU band (median + MAD), so the next run scores cheaply instead of recomputing.",{"type":54,"tag":242,"props":1447,"children":1448},{},[1449,1455,1456],{"type":54,"tag":77,"props":1450,"children":1452},{"className":1451},[],[1453],{"type":60,"value":1454},"dedupe:customer_analytics:account:\u003Cexternal_id>",{"type":60,"value":641},{"type":54,"tag":1374,"props":1457,"children":1458},{},[1459],{"type":60,"value":1460},"a risk already surfaced, with the condition that should re-escalate it (a further drop, or recovery + relapse).",{"type":54,"tag":242,"props":1462,"children":1463},{},[1464,1470,1471],{"type":54,"tag":77,"props":1465,"children":1467},{"className":1466},[],[1468],{"type":60,"value":1469},"noise:customer_analytics:account:\u003Cexternal_id>",{"type":60,"value":641},{"type":54,"tag":1374,"props":1472,"children":1473},{},[1474],{"type":60,"value":1475},"\"this account is a known sandbox \u002F migrating off \u002F seasonal — its dips are expected.\"",{"type":54,"tag":242,"props":1477,"children":1478},{},[1479,1485,1486],{"type":54,"tag":77,"props":1480,"children":1482},{"className":1481},[],[1483],{"type":60,"value":1484},"report:customer_analytics:account:\u003Cexternal_id>",{"type":60,"value":641},{"type":54,"tag":1374,"props":1487,"children":1488},{},[1489,1491,1497],{"type":60,"value":1490},"the ",{"type":54,"tag":77,"props":1492,"children":1494},{"className":1493},[],[1495],{"type":60,"value":1496},"report_id",{"type":60,"value":1498}," of a report you filed for a risk on this account, so the next run edits it (append_note with the fresh window) instead of duplicating.",{"type":54,"tag":242,"props":1500,"children":1501},{},[1502,1508,1509],{"type":54,"tag":77,"props":1503,"children":1505},{"className":1504},[],[1506],{"type":60,"value":1507},"reviewer:customer_analytics:\u003Carea>",{"type":60,"value":641},{"type":54,"tag":1374,"props":1510,"children":1511},{},[1512],{"type":60,"value":1513},"a resolved owner (bare lowercase GitHub login) for an account segment \u002F CSM-team surface, so reports route to a human faster.",{"type":54,"tag":63,"props":1515,"children":1516},{},[1517],{"type":60,"value":1518},"By run #5 the scratchpad knows the account grain, the join health, the fleet baseline, the handful of accounts worth watching, and who owns each — so a real cliff lands with the right context attached.",{"type":54,"tag":556,"props":1520,"children":1522},{"id":1521},"decide",[1523],{"type":60,"value":1524},"Decide",{"type":54,"tag":63,"props":1526,"children":1527},{},[1528,1530,1535,1537,1542,1544,1549,1551,1557,1559,1564,1565,1570,1572,1577,1578,1583],{"type":60,"value":1529},"The generic report mechanics — search the inbox first (via the ",{"type":54,"tag":77,"props":1531,"children":1533},{"className":1532},[],[1534],{"type":60,"value":1484},{"type":60,"value":1536}," pointer, else an ",{"type":54,"tag":77,"props":1538,"children":1540},{"className":1539},[],[1541],{"type":60,"value":679},{"type":60,"value":1543}," search on the account's ",{"type":54,"tag":1374,"props":1545,"children":1546},{},[1547],{"type":60,"value":1548},"specific",{"type":60,"value":1550}," name \u002F external_id, not a broad word like ",{"type":54,"tag":77,"props":1552,"children":1554},{"className":1553},[],[1555],{"type":60,"value":1556},"churn",{"type":60,"value":1558},"), edit-vs-author, the status rules, reviewer routing, non-idempotent dedup, and the ",{"type":54,"tag":77,"props":1560,"children":1562},{"className":1561},[],[1563],{"type":60,"value":394},{"type":60,"value":145},{"type":54,"tag":77,"props":1566,"children":1568},{"className":1567},[],[1569],{"type":60,"value":401},{"type":60,"value":1571}," fields — live in the harness prompt and in ",{"type":54,"tag":77,"props":1573,"children":1575},{"className":1574},[],[1576],{"type":60,"value":409},{"type":60,"value":411},{"type":54,"tag":77,"props":1579,"children":1581},{"className":1580},[],[1582],{"type":60,"value":417},{"type":60,"value":1584},". Do not re-derive them here. This section is only the customer-analytics judgment layered on top:",{"type":54,"tag":238,"props":1586,"children":1587},{},[1588,1606,1665,1675],{"type":54,"tag":242,"props":1589,"children":1590},{},[1591,1596,1598,1604],{"type":54,"tag":69,"props":1592,"children":1593},{},[1594],{"type":60,"value":1595},"Edit",{"type":60,"value":1597}," when a still-live report already tracks the account — a cliff still deepening, a dormancy still unbroken, a champion still gone. A persistent risk is one report across runs: a new complete week confirming it's ongoing is a re-escalation (",{"type":54,"tag":77,"props":1599,"children":1601},{"className":1600},[],[1602],{"type":60,"value":1603},"append_note",{"type":60,"value":1605}," the fresh volume\u002FWAU numbers), not a fresh report per tick.",{"type":54,"tag":242,"props":1607,"children":1608},{},[1609,1614,1616,1621,1623,1628,1630,1635,1637,1643,1645,1651,1653,1658,1660,1664],{"type":54,"tag":69,"props":1610,"children":1611},{},[1612],{"type":60,"value":1613},"Author",{"type":60,"value":1615}," when nothing live covers the account. A report-worthy finding shows the account's engagement dropped clearly below its own seasonality-matched baseline (sustained, not a single lumpy week), the ",{"type":54,"tag":69,"props":1617,"children":1618},{},[1619],{"type":60,"value":1620},"fleet held",{"type":60,"value":1622}," over the same window (quantify both — \"Acme weekly events 4.2k→1.1k while fleet steady at ~600 active accounts\"), the account is ",{"type":54,"tag":69,"props":1624,"children":1625},{},[1626],{"type":60,"value":1627},"commercially staked",{"type":60,"value":1629}," (assigned role or CRM link — name it), and the move isn't one departing user mistaken for an account-wide cliff. Put the account name, ",{"type":54,"tag":77,"props":1631,"children":1633},{"className":1632},[],[1634],{"type":60,"value":97},{"type":60,"value":1636},", the latest-window numbers, the baseline band, WAU, the assigned owner, and the time window in the ",{"type":54,"tag":77,"props":1638,"children":1640},{"className":1639},[],[1641],{"type":60,"value":1642},"evidence",{"type":60,"value":1644},". These are CSM\u002FAE investigations, not code fixes → ",{"type":54,"tag":77,"props":1646,"children":1648},{"className":1647},[],[1649],{"type":60,"value":1650},"actionability=requires_human_input",{"type":60,"value":1652},". Priority: a confirmed sustained cliff or dormancy onset on a staked, high-value account is ",{"type":54,"tag":69,"props":1654,"children":1655},{},[1656],{"type":60,"value":1657},"P2",{"type":60,"value":1659},"; a single-segment\u002Fsuggestive move, an unstaked account, or an expansion signal is ",{"type":54,"tag":69,"props":1661,"children":1662},{},[1663],{"type":60,"value":1339},{"type":60,"value":254},{"type":54,"tag":242,"props":1666,"children":1667},{},[1668,1673],{"type":54,"tag":69,"props":1669,"children":1670},{},[1671],{"type":60,"value":1672},"Remember",{"type":60,"value":1674}," if suggestive but below the bar, or to refresh a baseline, or to record what you ruled out and why.",{"type":54,"tag":242,"props":1676,"children":1677},{},[1678,1683,1685,1691,1692,1698,1699,1705],{"type":54,"tag":69,"props":1679,"children":1680},{},[1681],{"type":60,"value":1682},"Skip",{"type":60,"value":1684}," if a ",{"type":54,"tag":77,"props":1686,"children":1688},{"className":1687},[],[1689],{"type":60,"value":1690},"noise:",{"type":60,"value":145},{"type":54,"tag":77,"props":1693,"children":1695},{"className":1694},[],[1696],{"type":60,"value":1697},"addressed:",{"type":60,"value":145},{"type":54,"tag":77,"props":1700,"children":1702},{"className":1701},[],[1703],{"type":60,"value":1704},"dedupe:",{"type":60,"value":1706}," entry, or an existing inbox report, already covers it.",{"type":54,"tag":63,"props":1708,"children":1709},{},[1710,1712,1717,1719,1724,1726,1731,1733,1738,1740,1745],{"type":60,"value":1711},"Sibling courtesy: a fleet-wide move already reported by ",{"type":54,"tag":77,"props":1713,"children":1715},{"className":1714},[],[1716],{"type":60,"value":252},{"type":60,"value":1718}," or ",{"type":54,"tag":77,"props":1720,"children":1722},{"className":1721},[],[1723],{"type":60,"value":297},{"type":60,"value":1725}," is theirs — author only if your ",{"type":54,"tag":69,"props":1727,"children":1728},{},[1729],{"type":60,"value":1730},"per-account",{"type":60,"value":1732}," angle is materially new, citing the prior report. Revenue \u002F MRR movement belongs to ",{"type":54,"tag":77,"props":1734,"children":1736},{"className":1735},[],[1737],{"type":60,"value":265},{"type":60,"value":1739},"; honor their ",{"type":54,"tag":77,"props":1741,"children":1743},{"className":1742},[],[1744],{"type":60,"value":1704},{"type":60,"value":1746}," entries — your unique angle is always the per-account engagement frame weighted by commercial ownership.",{"type":54,"tag":556,"props":1748,"children":1750},{"id":1749},"close-out",[1751],{"type":60,"value":1752},"Close out",{"type":54,"tag":63,"props":1754,"children":1755},{},[1756,1758,1763,1765,1769],{"type":60,"value":1757},"One paragraph: which accounts you scored, what you added to the watchlist, which reports you authored or edited, what you ruled out and why. The harness saves this as the run summary; future runs read it via ",{"type":54,"tag":77,"props":1759,"children":1761},{"className":1760},[],[1762],{"type":60,"value":628},{"type":60,"value":1764},". Do ",{"type":54,"tag":69,"props":1766,"children":1767},{},[1768],{"type":60,"value":457},{"type":60,"value":1770}," write a separate \"run metadata\" scratchpad entry. \"Scored the due staked accounts, all within baseline, fleet steady\" is a real outcome.",{"type":54,"tag":429,"props":1772,"children":1774},{"id":1773},"disqualifiers-skip-these",[1775],{"type":60,"value":1776},"Disqualifiers (skip these)",{"type":54,"tag":238,"props":1778,"children":1779},{},[1780,1811,1836,1846,1856,1866,1884,1894],{"type":54,"tag":242,"props":1781,"children":1782},{},[1783,1788,1790,1795,1797,1802,1804,1809],{"type":54,"tag":69,"props":1784,"children":1785},{},[1786],{"type":60,"value":1787},"Fleet moved together.",{"type":60,"value":1789}," If most accounts dropped alongside the watched one, it's not an account-health problem — it's capture, an aggregate funnel regression, or a holiday. Hand off (",{"type":54,"tag":77,"props":1791,"children":1793},{"className":1792},[],[1794],{"type":60,"value":316},{"type":60,"value":1796},"\u002F",{"type":54,"tag":77,"props":1798,"children":1800},{"className":1799},[],[1801],{"type":60,"value":309},{"type":60,"value":1803}," for capture, ",{"type":54,"tag":77,"props":1805,"children":1807},{"className":1806},[],[1808],{"type":60,"value":252},{"type":60,"value":1810}," for aggregate flows); don't file it as a per-account churn risk.",{"type":54,"tag":242,"props":1812,"children":1813},{},[1814,1819,1821,1826,1828,1834],{"type":54,"tag":69,"props":1815,"children":1816},{},[1817],{"type":60,"value":1818},"Unlinked \u002F thin join.",{"type":60,"value":1820}," If the account's ",{"type":54,"tag":77,"props":1822,"children":1824},{"className":1823},[],[1825],{"type":60,"value":97},{"type":60,"value":1827}," doesn't match a group key (or the whole roster doesn't), there's no engagement to score — config gap, ",{"type":54,"tag":77,"props":1829,"children":1831},{"className":1830},[],[1832],{"type":60,"value":1833},"pattern:join-unlinked",{"type":60,"value":1835}," memory, skip.",{"type":54,"tag":242,"props":1837,"children":1838},{},[1839,1844],{"type":54,"tag":69,"props":1840,"children":1841},{},[1842],{"type":60,"value":1843},"Unstaked, no CRM link.",{"type":60,"value":1845}," An account with no assigned role and no CRM id isn't commercially staked — hold it to a much higher bar (or skip) unless its absolute volume is large.",{"type":54,"tag":242,"props":1847,"children":1848},{},[1849,1854],{"type":54,"tag":69,"props":1850,"children":1851},{},[1852],{"type":60,"value":1853},"Below the volume floor.",{"type":60,"value":1855}," Trial \u002F tiny accounts whose weekly counts are too small for a stable rate — a few events' movement is not signal. Enforce a minimum-volume floor.",{"type":54,"tag":242,"props":1857,"children":1858},{},[1859,1864],{"type":54,"tag":69,"props":1860,"children":1861},{},[1862],{"type":60,"value":1863},"One departing user mistaken for a cliff.",{"type":60,"value":1865}," A single distinct_id leaving a multi-user account is single-threading context, not an account-wide engagement collapse — check the per-user breakdown.",{"type":54,"tag":242,"props":1867,"children":1868},{},[1869,1874,1876,1882],{"type":54,"tag":69,"props":1870,"children":1871},{},[1872],{"type":60,"value":1873},"New account, no baseline yet.",{"type":60,"value":1875}," Recently-created accounts (",{"type":54,"tag":77,"props":1877,"children":1879},{"className":1878},[],[1880],{"type":60,"value":1881},"created_at",{"type":60,"value":1883}," within the baseline window) have no trailing normal to deviate from — watchlist it, don't score it yet.",{"type":54,"tag":242,"props":1885,"children":1886},{},[1887,1892],{"type":54,"tag":69,"props":1888,"children":1889},{},[1890],{"type":60,"value":1891},"Seasonal swings",{"type":60,"value":1893}," — weekend\u002Fholiday\u002Fbusiness-hours rhythm. Real only once it clears the seasonality-matched baseline (compare same-weekday windows).",{"type":54,"tag":242,"props":1895,"children":1896},{},[1897,1902,1904,1909,1910,1915],{"type":54,"tag":69,"props":1898,"children":1899},{},[1900],{"type":60,"value":1901},"Known sandbox \u002F internal \u002F migrating account",{"type":60,"value":1903}," — if a ",{"type":54,"tag":77,"props":1905,"children":1907},{"className":1906},[],[1908],{"type":60,"value":1690},{"type":60,"value":145},{"type":54,"tag":77,"props":1911,"children":1913},{"className":1912},[],[1914],{"type":60,"value":1697},{"type":60,"value":1916}," entry names it, skip.",{"type":54,"tag":63,"props":1918,"children":1919},{},[1920],{"type":60,"value":1921},"When in doubt, refresh the baseline memory instead of filing a report. A false churn-risk alarm on a named account erodes a CSM's trust fast.",{"type":54,"tag":429,"props":1923,"children":1925},{"id":1924},"mcp-tools",[1926],{"type":60,"value":1927},"MCP tools",{"type":54,"tag":63,"props":1929,"children":1930},{},[1931],{"type":60,"value":1932},"Direct (read-only):",{"type":54,"tag":238,"props":1934,"children":1935},{},[1936,2046,2057,2068,2086,2110,2126,2137],{"type":54,"tag":242,"props":1937,"children":1938},{},[1939,1945,1947,1952,1954,1959,1960,1966,1967,1973,1974,1979,1980,1985,1986,1991,1993,1998,1999,2004,2005,2010,2011,2017,2018,2024,2025,2030,2032,2037,2039,2044],{"type":54,"tag":77,"props":1940,"children":1942},{"className":1941},[],[1943],{"type":60,"value":1944},"execute-sql",{"type":60,"value":1946}," — the primary scorer. ",{"type":54,"tag":77,"props":1948,"children":1950},{"className":1949},[],[1951],{"type":60,"value":82},{"type":60,"value":1953}," for the roster (",{"type":54,"tag":77,"props":1955,"children":1957},{"className":1956},[],[1958],{"type":60,"value":97},{"type":60,"value":168},{"type":54,"tag":77,"props":1961,"children":1963},{"className":1962},[],[1964],{"type":60,"value":1965},"name",{"type":60,"value":168},{"type":54,"tag":77,"props":1968,"children":1970},{"className":1969},[],[1971],{"type":60,"value":1972},"properties",{"type":60,"value":411},{"type":54,"tag":77,"props":1975,"children":1977},{"className":1976},[],[1978],{"type":60,"value":143},{"type":60,"value":145},{"type":54,"tag":77,"props":1981,"children":1983},{"className":1982},[],[1984],{"type":60,"value":151},{"type":60,"value":145},{"type":54,"tag":77,"props":1987,"children":1989},{"className":1988},[],[1990],{"type":60,"value":158},{"type":60,"value":1992}," tuples, ",{"type":54,"tag":77,"props":1994,"children":1996},{"className":1995},[],[1997],{"type":60,"value":166},{"type":60,"value":145},{"type":54,"tag":77,"props":2000,"children":2002},{"className":2001},[],[2003],{"type":60,"value":174},{"type":60,"value":145},{"type":54,"tag":77,"props":2006,"children":2008},{"className":2007},[],[2009],{"type":60,"value":181},{"type":60,"value":145},{"type":54,"tag":77,"props":2012,"children":2014},{"className":2013},[],[2015],{"type":60,"value":2016},"zendesk_id",{"type":60,"value":168},{"type":54,"tag":77,"props":2019,"children":2021},{"className":2020},[],[2022],{"type":60,"value":2023},"tags",{"type":60,"value":168},{"type":54,"tag":77,"props":2026,"children":2028},{"className":2027},[],[2029],{"type":60,"value":1881},{"type":60,"value":2031},"), joined to group-keyed ",{"type":54,"tag":77,"props":2033,"children":2035},{"className":2034},[],[2036],{"type":60,"value":968},{"type":60,"value":2038}," on the discovered ",{"type":54,"tag":77,"props":2040,"children":2042},{"className":2041},[],[2043],{"type":60,"value":514},{"type":60,"value":2045}," index for per-account engagement.",{"type":54,"tag":242,"props":2047,"children":2048},{},[2049,2055],{"type":54,"tag":77,"props":2050,"children":2052},{"className":2051},[],[2053],{"type":60,"value":2054},"query-trends",{"type":60,"value":2056}," — sanity-check a per-account or fleet-wide trend with a breakdown by the account group; confirm the fleet held while one account moved.",{"type":54,"tag":242,"props":2058,"children":2059},{},[2060,2066],{"type":54,"tag":77,"props":2061,"children":2063},{"className":2062},[],[2064],{"type":60,"value":2065},"query-stickiness",{"type":60,"value":2067}," — per-account engagement frequency shift (days-active dropping).",{"type":54,"tag":242,"props":2069,"children":2070},{},[2071,2077,2078,2084],{"type":54,"tag":77,"props":2072,"children":2074},{"className":2073},[],[2075],{"type":60,"value":2076},"read-data-schema events",{"type":60,"value":145},{"type":54,"tag":77,"props":2079,"children":2081},{"className":2080},[],[2082],{"type":60,"value":2083},"read-data-schema event_properties",{"type":60,"value":2085}," — confirm the group key column and the events that constitute \"engagement\" for this project before any SQL.",{"type":54,"tag":242,"props":2087,"children":2088},{},[2089,2095,2097,2102,2103,2108],{"type":54,"tag":77,"props":2090,"children":2092},{"className":2091},[],[2093],{"type":60,"value":2094},"insight-get",{"type":60,"value":2096}," — read any saved Customer-analytics usage insight to learn the team's own definition of an active account. Inbox & reviewer routing (mechanics in ",{"type":54,"tag":77,"props":2098,"children":2100},{"className":2099},[],[2101],{"type":60,"value":409},{"type":60,"value":411},{"type":54,"tag":77,"props":2104,"children":2106},{"className":2105},[],[2107],{"type":60,"value":417},{"type":60,"value":2109},"):",{"type":54,"tag":242,"props":2111,"children":2112},{},[2113,2118,2119,2124],{"type":54,"tag":77,"props":2114,"children":2116},{"className":2115},[],[2117],{"type":60,"value":679},{"type":60,"value":145},{"type":54,"tag":77,"props":2120,"children":2122},{"className":2121},[],[2123],{"type":60,"value":734},{"type":60,"value":2125}," — the reports already in the inbox; check before authoring so you edit instead of duplicating.",{"type":54,"tag":242,"props":2127,"children":2128},{},[2129,2135],{"type":54,"tag":77,"props":2130,"children":2132},{"className":2131},[],[2133],{"type":60,"value":2134},"inbox-report-artefacts-list",{"type":60,"value":2136}," — a comparable report's artefact log; reviewer precedent.",{"type":54,"tag":242,"props":2138,"children":2139},{},[2140,2146,2148,2154],{"type":54,"tag":77,"props":2141,"children":2143},{"className":2142},[],[2144],{"type":60,"value":2145},"scout-members-list",{"type":60,"value":2147}," — the in-run roster for routing ",{"type":54,"tag":77,"props":2149,"children":2151},{"className":2150},[],[2152],{"type":60,"value":2153},"suggested_reviewers",{"type":60,"value":2155}," to an account \u002F CSM-team owner.",{"type":54,"tag":63,"props":2157,"children":2158},{},[2159,2161,2166,2167,2172,2173,2178,2179,2185,2187,2192,2193,2198,2200,2206,2207,2213],{"type":60,"value":2160},"Harness-level: ",{"type":54,"tag":77,"props":2162,"children":2164},{"className":2163},[],[2165],{"type":60,"value":639},{"type":60,"value":168},{"type":54,"tag":77,"props":2168,"children":2170},{"className":2169},[],[2171],{"type":60,"value":578},{"type":60,"value":168},{"type":54,"tag":77,"props":2174,"children":2176},{"className":2175},[],[2177],{"type":60,"value":628},{"type":60,"value":168},{"type":54,"tag":77,"props":2180,"children":2182},{"className":2181},[],[2183],{"type":60,"value":2184},"scout-runs-retrieve",{"type":60,"value":2186}," (orientation + dedupe); ",{"type":54,"tag":77,"props":2188,"children":2190},{"className":2189},[],[2191],{"type":60,"value":372},{"type":60,"value":145},{"type":54,"tag":77,"props":2194,"children":2196},{"className":2195},[],[2197],{"type":60,"value":379},{"type":60,"value":2199}," (author \u002F edit a report — the report-channel contract is in the harness prompt); ",{"type":54,"tag":77,"props":2201,"children":2203},{"className":2202},[],[2204],{"type":60,"value":2205},"scout-scratchpad-remember",{"type":60,"value":168},{"type":54,"tag":77,"props":2208,"children":2210},{"className":2209},[],[2211],{"type":60,"value":2212},"scout-scratchpad-forget",{"type":60,"value":2214}," (memory).",{"type":54,"tag":429,"props":2216,"children":2218},{"id":2217},"when-to-stop",[2219],{"type":60,"value":2220},"When to stop",{"type":54,"tag":238,"props":2222,"children":2223},{},[2224,2229,2234],{"type":54,"tag":242,"props":2225,"children":2226},{},[2227],{"type":60,"value":2228},"No roster, or the roster doesn't join to group keys → close out empty (after the quick-close-out memory).",{"type":54,"tag":242,"props":2230,"children":2231},{},[2232],{"type":60,"value":2233},"You've scored the due watchlist accounts and added a couple of new ones → close out, even if more remain. Each run advances the watchlist.",{"type":54,"tag":242,"props":2235,"children":2236},{},[2237,2239,2244,2245,2250,2251,2256],{"type":60,"value":2238},"A candidate matches a ",{"type":54,"tag":77,"props":2240,"children":2242},{"className":2241},[],[2243],{"type":60,"value":1690},{"type":60,"value":145},{"type":54,"tag":77,"props":2246,"children":2248},{"className":2247},[],[2249],{"type":60,"value":1697},{"type":60,"value":145},{"type":54,"tag":77,"props":2252,"children":2254},{"className":2253},[],[2255],{"type":60,"value":1704},{"type":60,"value":2257}," entry, or an existing inbox report → edit-or-skip with a one-line note.",{"type":54,"tag":63,"props":2259,"children":2260},{},[2261],{"type":60,"value":2262},"Fewer, well-calibrated, fleet-checked per-account risks beat a flood of seasonal or fleet-wide false positives.",{"type":54,"tag":2264,"props":2265,"children":2266},"style",{},[2267],{"type":60,"value":2268},"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":2270,"total":2439},[2271,2288,2300,2313,2326,2341,2355,2372,2386,2401,2411,2429],{"slug":2272,"name":2272,"fn":2273,"description":2274,"org":2275,"tags":2276,"stars":2285,"repoUrl":2286,"updatedAt":2287},"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},[2277,2278,2281,2284],{"name":24,"slug":25,"type":13},{"name":2279,"slug":2280,"type":13},"Cost Optimization","cost-optimization",{"name":2282,"slug":2283,"type":13},"Observability","observability",{"name":9,"slug":8,"type":13},35568,"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog","2026-07-28T05:34:11.117757",{"slug":2289,"name":2289,"fn":2290,"description":2291,"org":2292,"tags":2293,"stars":2285,"repoUrl":2286,"updatedAt":2299},"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},[2294,2295,2298],{"name":24,"slug":25,"type":13},{"name":2296,"slug":2297,"type":13},"Audit","audit",{"name":9,"slug":8,"type":13},"2026-06-08T08:08:33.693989",{"slug":2301,"name":2301,"fn":2302,"description":2303,"org":2304,"tags":2305,"stars":2285,"repoUrl":2286,"updatedAt":2312},"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},[2306,2307,2310,2311],{"name":2296,"slug":2297,"type":13},{"name":2308,"slug":2309,"type":13},"Data Warehouse","data-warehouse",{"name":2282,"slug":2283,"type":13},{"name":9,"slug":8,"type":13},"2026-06-18T08:22:57.67984",{"slug":2314,"name":2314,"fn":2315,"description":2316,"org":2317,"tags":2318,"stars":2285,"repoUrl":2286,"updatedAt":2325},"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},[2319,2320,2321,2324],{"name":2296,"slug":2297,"type":13},{"name":2308,"slug":2309,"type":13},{"name":2322,"slug":2323,"type":13},"Performance","performance",{"name":9,"slug":8,"type":13},"2026-06-18T08:25:10.936787",{"slug":2327,"name":2327,"fn":2328,"description":2329,"org":2330,"tags":2331,"stars":2285,"repoUrl":2286,"updatedAt":2340},"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},[2332,2335,2338,2339],{"name":2333,"slug":2334,"type":13},"Alerting","alerting",{"name":2336,"slug":2337,"type":13},"Debugging","debugging",{"name":2282,"slug":2283,"type":13},{"name":9,"slug":8,"type":13},"2026-06-18T08:24:40.318583",{"slug":2342,"name":2342,"fn":2343,"description":2344,"org":2345,"tags":2346,"stars":2285,"repoUrl":2286,"updatedAt":2354},"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},[2347,2348,2349,2350,2353],{"name":24,"slug":25,"type":13},{"name":15,"slug":16,"type":13},{"name":2282,"slug":2283,"type":13},{"name":2351,"slug":2352,"type":13},"Operations","operations",{"name":9,"slug":8,"type":13},"2026-07-18T05:10:54.430898",{"slug":2356,"name":2356,"fn":2357,"description":2358,"org":2359,"tags":2360,"stars":2285,"repoUrl":2286,"updatedAt":2371},"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},[2361,2364,2367,2368],{"name":2362,"slug":2363,"type":13},"Automation","automation",{"name":2365,"slug":2366,"type":13},"MCP","mcp",{"name":9,"slug":8,"type":13},{"name":2369,"slug":2370,"type":13},"Workflow Automation","workflow-automation","2026-07-28T05:34:12.167015",{"slug":2373,"name":2373,"fn":2374,"description":2375,"org":2376,"tags":2377,"stars":2285,"repoUrl":2286,"updatedAt":2385},"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},[2378,2379,2380,2383,2384],{"name":24,"slug":25,"type":13},{"name":2336,"slug":2337,"type":13},{"name":2381,"slug":2382,"type":13},"Frontend","frontend",{"name":2282,"slug":2283,"type":13},{"name":9,"slug":8,"type":13},"2026-05-07T05:56:19.828048",{"slug":2387,"name":2387,"fn":2388,"description":2389,"org":2390,"tags":2391,"stars":2285,"repoUrl":2286,"updatedAt":2400},"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},[2392,2395,2396,2397],{"name":2393,"slug":2394,"type":13},"API Development","api-development",{"name":2381,"slug":2382,"type":13},{"name":9,"slug":8,"type":13},{"name":2398,"slug":2399,"type":13},"SDK","sdk","2026-06-08T08:08:34.929454",{"slug":2402,"name":2402,"fn":2403,"description":2404,"org":2405,"tags":2406,"stars":2285,"repoUrl":2286,"updatedAt":2410},"copying-endpoints-across-projects","copy PostHog endpoints across projects","Copy a PostHog endpoint (a saved HogQL\u002Finsight query exposed as an API route) to another project in the same organization, or duplicate it under a new name in the same project. Use when the user wants to duplicate an endpoint, promote an endpoint from staging to production, replicate an endpoint's query\u002Fvariables\u002Ffreshness config in another workspace, or clone an endpoint to iterate on it. Unlike feature flags and experiments, endpoints have NO native cross-project copy tool — this skill covers the read-then-recreate flow (endpoint-get then endpoint-create), the active-project switching it requires, name-collision checks, and the safe defaults (land unmaterialised in the target, verify with endpoint-run). Does not cover editing endpoint versions (see managing-endpoint-versions) or authoring a brand-new endpoint from scratch (see creating-an-endpoint).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2407,2408,2409],{"name":2393,"slug":2394,"type":13},{"name":2351,"slug":2352,"type":13},{"name":9,"slug":8,"type":13},"2026-07-15T05:29:58.442727",{"slug":2412,"name":2412,"fn":2413,"description":2414,"org":2415,"tags":2416,"stars":2285,"repoUrl":2286,"updatedAt":2428},"creating-ai-subscription","schedule recurring AI-generated PostHog reports","Create a recurring AI-generated PostHog report — schedule a free-text prompt to run on a cron, with the LLM-synthesized markdown delivered to email or Slack on each tick. Use when the user wants a recurring AI summary of X on any cadence (daily, weekly, monthly, yearly) rather than a one-off report. (To attach an AI summary to an existing insight\u002Fdashboard subscription instead of a free-text prompt, see `managing-subscriptions` and its `summary_enabled` option.)\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2417,2418,2421,2422,2425],{"name":2362,"slug":2363,"type":13},{"name":2419,"slug":2420,"type":13},"Email","email",{"name":9,"slug":8,"type":13},{"name":2423,"slug":2424,"type":13},"Reporting","reporting",{"name":2426,"slug":2427,"type":13},"Slack","slack","2026-06-09T07:32:27.935712",{"slug":2430,"name":2430,"fn":2431,"description":2432,"org":2433,"tags":2434,"stars":2285,"repoUrl":2286,"updatedAt":2438},"creating-an-endpoint","create PostHog API endpoints","Create a PostHog endpoint with the right shape on the first try — covers query kind choice, name conventions, what to expose as variables (HogQL code_name vs insight breakdown), data_freshness_seconds, and whether to materialise on day one. Use when the user says \"create an endpoint\", \"expose this query as an API\", \"turn this insight into an endpoint\", or asks for help structuring a new endpoint. Steers away from common mistakes: materialising a query with cohort breakdowns or compare mode, inline-only variables on a materialised endpoint, unbounded date ranges, ambiguous names.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2435,2436,2437],{"name":24,"slug":25,"type":13},{"name":2393,"slug":2394,"type":13},{"name":9,"slug":8,"type":13},"2026-06-08T08:08:29.624498",231,{"items":2441,"total":2546},[2442,2457,2473,2488,2500,2512,2530],{"slug":2443,"name":2443,"fn":2444,"description":2445,"org":2446,"tags":2447,"stars":26,"repoUrl":27,"updatedAt":2456},"analyzing-experiment-session-replays","analyze session replays for PostHog experiments","Analyze session replay patterns across experiment variants to understand user behavior differences. Use when the user wants to see how users interact with different experiment variants, identify usability issues, compare behavior patterns between control and test groups, or get qualitative insights to complement quantitative experiment results.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2448,2449,2452,2453],{"name":24,"slug":25,"type":13},{"name":2450,"slug":2451,"type":13},"Design","design",{"name":9,"slug":8,"type":13},{"name":2454,"slug":2455,"type":13},"User Research","user-research","2026-04-06T18:44:38.291781",{"slug":2458,"name":2458,"fn":2459,"description":2460,"org":2461,"tags":2462,"stars":26,"repoUrl":27,"updatedAt":2472},"assessing-heatmaps","analyze page heatmaps and suggest improvements","Assesses what a page's heatmap is telling you and recommends concrete changes. Pulls click \u002F rageclick \u002F scroll-depth data for a URL, names the hot elements by cross-referencing autocapture events on the same page, and can create a saved heatmap the user opens in PostHog, then summarizes the behavior and proposes improvements.\nTRIGGER when: user asks what a heatmap shows, why people aren't clicking something, where users rage-click, how far they scroll, what to change on a page based on heatmap\u002Fclick data, or to 'analyze\u002Fassess\u002Freview the heatmap' for a URL.\nDO NOT TRIGGER when: the user only wants to create a saved heatmap screenshot with no analysis (use heatmaps-saved-create directly), or is asking about session replay in general (use investigating-replay).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2463,2464,2465,2466,2469],{"name":24,"slug":25,"type":13},{"name":2381,"slug":2382,"type":13},{"name":9,"slug":8,"type":13},{"name":2467,"slug":2468,"type":13},"Product Management","product-management",{"name":2470,"slug":2471,"type":13},"UX Design","ux-design","2026-06-05T07:40:43.37798",{"slug":2474,"name":2474,"fn":2475,"description":2476,"org":2477,"tags":2478,"stars":26,"repoUrl":27,"updatedAt":2487},"auditing-experiments-flags","audit PostHog experiments and feature flags","Audit PostHog experiments and feature flags for configuration issues, staleness, and best-practice violations. Read when the user asks to audit, health-check, or review experiments or feature flags, check flag hygiene, or verify experiment setup.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2479,2480,2483,2484],{"name":2296,"slug":2297,"type":13},{"name":2481,"slug":2482,"type":13},"Feature Flags","feature-flags",{"name":9,"slug":8,"type":13},{"name":2485,"slug":2486,"type":13},"QA","qa","2026-04-06T18:44:30.657553",{"slug":409,"name":409,"fn":2489,"description":2490,"org":2491,"tags":2492,"stars":26,"repoUrl":27,"updatedAt":2499},"author and edit PostHog Signals scouts","How to author, edit, and adapt PostHog Signals scouts — the scheduled agents that scan a project and write reports into the Signals inbox. Use when a user wants to customize a canonical scout for their own setup (narrow its scope, retune its thresholds, add disqualifiers), tweak a scout's schedule or dry-run posture, or write a brand-new scout from scratch for a specific use case (a custom event, a product surface no canonical scout covers), or steer a scout without editing it at all by leaving it a note. Covers the scout SKILL.md anatomy, the report contract, the dedupe + scratchpad-memory conventions, the scout-notes steering channel, the per-team skills-store path vs the canonical in-repo path, and the write-and-inspect test loop (with dry-run as an optional safety net). Trigger on \"write\u002Fedit\u002Fcustomize a signals scout\", \"new scout for X\", \"tune my scout schedule\", \"make a scout that watches \u003Cevent>\", \"leave a note for \u002F give feedback to a scout\", \"tell the scouts about X\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2493,2496,2497,2498],{"name":2494,"slug":2495,"type":13},"Agents","agents",{"name":2362,"slug":2363,"type":13},{"name":2282,"slug":2283,"type":13},{"name":9,"slug":8,"type":13},"2026-07-28T05:33:45.509154",{"slug":2501,"name":2501,"fn":2502,"description":2503,"org":2504,"tags":2505,"stars":26,"repoUrl":27,"updatedAt":2511},"building-a-dashboard","build and update PostHog dashboards","Build a new dashboard, or update an existing one, from a set of insights — the same job the in-app assistant does with its upsert-dashboard tool, but over MCP. Use when a user asks to create a dashboard, put several metrics\u002Fcharts together on one page, assemble a dashboard for a topic (product analytics, retention, revenue, activation, etc.), or add\u002Fremove\u002Freplace insights on a dashboard they already have. Covers deciding create vs update, reusing existing insights vs creating new ones, and using PostHog's vetted dashboard templates as reference for what a strong dashboard on a topic looks like.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2506,2507,2510],{"name":24,"slug":25,"type":13},{"name":2508,"slug":2509,"type":13},"Dashboards","dashboards",{"name":2365,"slug":2366,"type":13},"2026-07-21T06:07:38.060598",{"slug":2513,"name":2513,"fn":2514,"description":2515,"org":2516,"tags":2517,"stars":26,"repoUrl":27,"updatedAt":2529},"checking-deploy-timing","correlate PostHog deployments with GitHub commits","Determine when a PostHog code change reached a given environment by reading the hidden GIT deploy annotations in the project and correlating them with the merge commit on GitHub. Use when PostHog staff ask \"when was X deployed\", \"is my change live in the US\u002FEU yet\", \"has my PR shipped\", \"did the fix roll out to prod-us\", or otherwise want to know whether\u002Fwhen a commit, PR, or feature went out to a region. Do not answer deploy-timing questions from event\u002Fdata volume alone — that only shows when data changed, not when code shipped.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2518,2521,2524,2527,2528],{"name":2519,"slug":2520,"type":13},"Deployment","deployment",{"name":2522,"slug":2523,"type":13},"Git","git",{"name":2525,"slug":2526,"type":13},"GitHub","github",{"name":2282,"slug":2283,"type":13},{"name":9,"slug":8,"type":13},"2026-06-28T07:46:59.53536",{"slug":2531,"name":2531,"fn":2532,"description":2533,"org":2534,"tags":2535,"stars":26,"repoUrl":27,"updatedAt":2545},"choosing-trend-or-slope-view","visualize trends and growth over time","Clarify how to visualize change over a time range before building a trend. Use whenever the user asks how much something changed, grew, dropped, improved, or regressed between two points or periods — \"how much did X change from A to B\", \"before vs after\", \"start vs end\", \"week over week\", \"compare this month to last\", \"change over time\" — or mentions a \"slope chart\" \u002F \"slopegraph\". Two readings of \"change\" need different charts: the whole trend (a line, every interval) versus just the two endpoints (a slope, start vs end). Ask which they want, then render it. Not for choosing a saved insight ChartDisplayType in the insight editor.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2536,2537,2540,2543,2544],{"name":24,"slug":25,"type":13},{"name":2538,"slug":2539,"type":13},"Charts","charts",{"name":2541,"slug":2542,"type":13},"Data Visualization","data-visualization",{"name":9,"slug":8,"type":13},{"name":2423,"slug":2424,"type":13},"2026-06-18T08:18:57.960157",56]