A Claude Skill that runs each morning, pulls every account that crossed a churn-risk threshold in the trailing 24 hours, and posts a one-page digest to a Slack channel: account name, ARR at risk, the event that drove the change, the owner, and one specific next action. It replaces the noisy Gainsight email digest most CSMs already filter to a folder, with something tight enough to read between the standup and the first customer call.
The artifact bundle lives at apps/web/public/artifacts/churn-risk-summarizer-claude/ — SKILL.md plus three reference files (1-risk-signal-weights.md, 2-sample-digest.md, 3-escalation-criteria-thresholds.md) that the Skill loads on every run.
When to use
You have Gainsight (or a comparable CS platform) producing risk-score deltas you trust at the direction level, but the existing digest is too long to read or too generic to act on. You have a CSM team that already meets each morning and would benefit from a shared three-minute read on what changed overnight. You want the per-account “what changed and what to do about it” reasoning to be uniform across CSMs instead of each one inventing their own framing.
The skill is a fit when the daily run lands at 5-15 accounts after filtering. Below 5, you don’t need a skill — read the Gainsight view directly. Above 15, the thresholds in references/3-escalation-criteria-thresholds.md need raising before the digest is worth running at all.
When NOT to use
You don’t have risk scores you trust. Garbage in, garbage out. This skill summarizes the scores you give it; it does not compute or correct them. If your Gainsight risk model is broken, fix that first.
You want an automated CSM-action trigger. The skill is read-only signal. Posting a digest to a channel is fine; auto-creating tasks, sending playbook emails, or opening cases is out of scope and will get you in trouble fast (see “alert fatigue” below).
You want customer-facing copy. Nothing the skill produces is cleared to send to the customer. Treat all output as internal.
You want longitudinal churn analysis. The prompt is tuned for the trailing 24-168 hours. For “what happened across Q3” use BI on the Gainsight warehouse.
Your CSM team is two people. The cost of standing this up outweighs the cost of one of you reading the Gainsight view at 7am. Worth it at four CSMs and up, where uniformity of framing starts to compound.
Setup
Define the threshold. Decide what “crossed into churn risk” means concretely. The defaults in references/3-escalation-criteria-thresholds.md use signal_score >= 12 for Red and a health_score drop of -15 for Amber. Edit those numbers against two weeks of historical data before going live.
Install the Skill. Drop the bundle into ~/.claude/skills/churn-risk-summarizer/. Set GAINSIGHT_TOKEN and SLACK_WEBHOOK_CHURN_DIGEST in your env.
Configure scope. Edit references/3-escalation-criteria-thresholds.md with your min_arr floor (most teams use $50k for a daily channel digest) and your segments list (most teams run daily on enterprise + mid-market, separate weekly on SMB).
Tune weights. Edit references/1-risk-signal-weights.md to match your team’s opinion of what matters. The shipped weights are reasonable defaults, not your defaults.
Schedule the run. 7am local time on weekdays via cron, n8n, or a Claude scheduled task. Posts to your CS channel.
Iterate on signal-to-noise. The first two weeks will be noisy. Tune one threshold at a time and watch two days of output before editing again. Do not edit weights and thresholds in the same run — you’ll never know which change moved the needle.
What the skill actually does
The Skill takes a JSON list of accounts plus a JSON list of trailing-window timeline events (the schemas are documented in SKILL.md under “Inputs”). It runs five sequential steps: signal aggregation (sum of severity * weight per account, with a per-event cap of 5 to stop one escalation from dominating); threshold-based bucketing into Red, Amber, and Watch; per-account narrative grounded in the actual event summaries (no paraphrasing — “active seats fell from 142 to 89 over 7 days,” not “engagement is declining”); prioritization by ARR descending then renewal date ascending; and rendering to the literal layout in references/2-sample-digest.md.
The engineering choice worth flagging: weights are explicit and editable rather than letting the model decide what’s important per run. When a CSM lead disagrees with what got surfaced, they can edit one number in references/1-risk-signal-weights.md and see the effect on tomorrow’s digest. A per-run model judgment cannot be edited; it can only be re-prompted, which is harder to audit.
The Action field has a hard guard: if the suggested action contains engage, reach out, touch base, align, or socialize without naming a specific person or artifact, it is replaced with needs human review. Better silence than noise.
Cost reality
A daily digest on 150 enterprise + mid-market accounts with trailing events runs roughly 18-25k input tokens (account JSON + events JSON + the three reference files) and 1-3k output tokens. At Claude Sonnet 4.5 list pricing ($3 / MTok input, $15 / MTok output) that’s about $0.10-0.15 per run, or $2-4 per month running weekdays only. Negligible. The cost that actually matters is the 10-15 minutes of CSM-lead time per week spent tuning weights and thresholds for the first month.
If your account list is in the thousands rather than hundreds, batch by segment and run three or four scoped digests instead of one — the per-token cost is the same but the per-digest scannability stays intact.
Success metric
Watch the percentage of Red-bucket accounts that have a CSM-logged action within 48 hours of the digest. If it sits above 70%, the digest is doing its job: the Red bucket is short enough and trusted enough that owners act on it. If it falls below 50%, either the thresholds are too loose (Red bucket is overflowing and being ignored) or the suggested actions are too generic (Action specificity collapse — see watch-outs). Do not optimize for “more accounts surfaced” — fewer, better-evidenced Red accounts is the goal.
vs alternatives
Gainsight Health Scorecards 2.0 + native digest email. The built-in digest has the data but not the editorial layer — every at-risk account gets the same template, with no per-account driver narrative or specific next action. Works as a system of record; fails as a thing CSMs actually open. Pick this if your team prefers fewer moving parts and you have headcount to read the long version.
Custom BI dashboards on the Gainsight warehouse (Looker, Mode, Hex). Better for cross-cutting “show me retention by segment” questions, worse for “what should the team do today.” A dashboard that requires a click is a dashboard that won’t get clicked at 7am. Run both — the dashboard for monthly review, the skill for daily action.
Manual Monday morning CSM review. What most teams do today. Works at four-CSM scale, breaks down past that because each CSM invents their own framing of “what’s risky.” The skill exists to give the team one shared framing they can argue with by editing the weights file, instead of arguing with each other in standup.
Watch-outs
Alert fatigue. A digest that consistently exceeds 15 accounts gets folder-filtered within a week. Guard: hard-cap at 15 with honest overflow counts in the footer, and if Red exceeds the cap on three consecutive runs prepend a warning that the threshold may be too loose. Implemented in references/3-escalation-criteria-thresholds.md under “Self-tuning trigger.”
False-positive flooding. One miscalibrated event-type weight can produce a Red bucket dominated by, say, every account with a recent support escalation. Guard: the digest footer includes an event-type mix percentage line so the team can spot one signal dominating before trust erodes. Worked example in references/2-sample-digest.md (“Event-type mix this week:”).
Signal weighting drift. The weights file goes stale as the product and customer base evolve. Guard: the skill emits the short SHA of references/1-risk-signal-weights.md in the digest footer. If the hash hasn’t changed in 90 days, the digest prepends a recalibration nudge.
Owner staleness. A digest that pings the wrong CSM is worse than no digest. Guard: accounts whose owner_email cannot be resolved get surfaced under a separate *Ownership broken (n)* bucket with a link to the Gainsight ownership editor — they are not allowed to silently fall into Red or Amber.
---
name: churn-risk-summarizer
description: Produce a daily (or weekly) digest of accounts that crossed a churn-risk threshold in the last N hours. Aggregates Gainsight risk-score deltas, recent timeline events, and ARR exposure into a tightly bucketed Slack-ready summary that names the change driver and one specific next action per account.
---
# Churn-risk summarizer
## When to invoke
Run on a schedule (typically 7am local, daily) or on demand to generate a prioritized list of accounts that crossed a churn-risk threshold in the trailing window. Input is a Gainsight account list with risk signals plus recent timeline activity; output is a bucketed digest sized for a CSM team to read in under three minutes.
Do NOT invoke this skill for:
- Triggering automated CSM actions (creating tasks, sending playbook emails, opening cases). The digest is read-only signal — humans decide the next action.
- Any customer-facing communication. Nothing this skill produces is cleared for sending to the customer; treat all output as internal.
- Backfilling historical churn analysis (the prompt is tuned for the trailing 24-168 hours). For longitudinal review, use BI on the Gainsight warehouse instead.
- Risk scoring itself. The skill summarizes scores it is given; it does not compute or reweight them.
## Inputs
- Required: `accounts` — JSON array of account records with at minimum `id`, `name`, `arr`, `risk_score_current`, `risk_score_prior`, `health_score_current`, `health_score_prior`, `owner_email`, `segment`, `renewal_date`.
- Required: `events` — JSON array of timeline events for the same accounts in the trailing window. Each event needs `account_id`, `type` (one of `usage_drop`, `support_escalation`, `sponsor_change`, `qbr_missed`, `nps_detractor`, `contract_renegotiation`, `exec_disengagement`), `severity` (1-5), `occurred_at`, `summary`.
- Optional: `window_hours` — lookback window. Defaults to 24.
- Optional: `min_arr` — drop accounts below this ARR floor. Defaults to 0 (no floor).
- Optional: `segments` — filter to a list of segments (e.g. `["enterprise", "mid-market"]`). Defaults to all.
- Optional: `cap` — maximum accounts to surface in the digest body. Defaults to 15. Overflow goes into a "+N more" link.
## Reference files
Read these from `references/` before generating the digest. They encode the team's risk-weighting opinion and the digest format. Without them, the output is generic.
- `references/1-risk-signal-weights.md` — per-event-type weighting config the skill applies when ranking accounts within a bucket.
- `references/2-sample-digest.md` — the literal Slack-ready digest format the skill emits, with a worked example.
- `references/3-escalation-criteria-thresholds.md` — the bucket thresholds (red / amber / watch) and the rules for when a single signal is enough to escalate by itself.
## Method
Run these five steps in order. Do not parallelize: bucketing depends on aggregation, narrative depends on bucketing.
### 1. Signal aggregation
For each account, collect the trailing-window events from `events`, sort by `occurred_at` descending, and compute a per-account `signal_score` as the sum of `severity * weight` per event using the weights in `references/1-risk-signal-weights.md`. Cap any single event's contribution at 5 — one escalation should not single-handedly dominate the score unless the weights file says so explicitly.
The reason for explicit weighting (rather than letting the model "decide what's important"): weights are auditable. When a CSM lead disagrees with what got surfaced, they can edit one number in the weights file. A per-run model judgment cannot be edited.
### 2. Threshold-based bucketing
Apply the thresholds in `references/3-escalation-criteria-thresholds.md` to assign each account to exactly one bucket:
- **Red** — risk_score crossed the explicit churn-risk line, OR signal_score >= the red threshold, OR a single event is on the always-escalate list (e.g. `exec_disengagement` at severity 5).
- **Amber** — health_score dropped by more than the amber delta, OR signal_score is between amber and red.
- **Watch** — any other accounts that crossed *into* the band but do not meet amber criteria.
If `min_arr` or `segments` filters drop an account below the floor, exclude it from all buckets — but record the count for the footer.
### 3. Per-account evidence-grounded narrative
For each account in Red and Amber (skip Watch — it gets a count-only line), compose:
- One-line change driver naming the dominant event, never paraphrased away from `events[].summary`. If the dominant event was a usage drop, say "active seats fell from 142 to 89 over 7 days," not "engagement is declining."
- One concrete suggested action, formatted as a verb plus a named artifact (a meeting, a person, a doc). Examples: "Call the CFO before Friday's renewal kickoff." "Open a case with support to triage the open P1." "Forward last week's QBR deck to the new VP of Eng."
- If no concrete action can be supported from the evidence, output `needs human review` rather than padding. Vague actions are the primary failure mode the digest exists to avoid.
### 4. Prioritization
Within each bucket, sort by ARR descending, breaking ties by `renewal_date` ascending (closer renewals first). Apply `cap` to the combined Red + Amber list. If the cap drops accounts, surface them only as a count and a link to the full Gainsight saved view.
### 5. Format and emit
Render to the layout in `references/2-sample-digest.md`. The output is a single Slack-mrkdwn block plus a fallback plaintext copy. Do not include tables, attachments, or threads — the digest must be scannable in the channel without expanding anything.
## Output format
```markdown
*Daily churn-risk digest — {YYYY-MM-DD}*
*Red ({n_red})* — act this week
- *{Account name}* — ${ARR}k ARR · owner @{owner_handle} · renewal {renewal_date}
Driver: {one-line driver from evidence}
Action: {verb + named artifact}
- ...
*Amber ({n_amber})* — review by Friday
- *{Account name}* — ${ARR}k ARR · owner @{owner_handle} · renewal {renewal_date}
Driver: {one-line driver}
Action: {verb + named artifact, or `needs human review`}
- ...
*Watch ({n_watch})* — no action required, tracking only.
{count summary, no per-account detail}
_Filtered out: {n_below_floor} below ${min_arr}k · {n_below_segment} outside segment._
_Capped at {cap} of {n_red + n_amber} qualifying. Full list: {gainsight_saved_view_url}_
```
## Watch-outs
- **Alert fatigue.** If the digest carries more than ~15 accounts day after day, owners stop opening it. Guard: enforce `cap` strictly, and if Red exceeds cap on three consecutive runs, prepend a `_Threshold may be too loose — last 3 runs averaged {n} Red. Consider raising the red threshold in references/3-escalation-criteria-thresholds.md._` warning. Do not silently truncate without flagging.
- **False-positive flooding.** Any single event type producing more than 30% of Red accounts in a week is a signal that its weight is miscalibrated. Guard: at the end of the digest, include a one-line diagnostic — `_Event-type mix this week: usage_drop 18%, support_escalation 22%, ..._` — so the team can spot one signal dominating before it erodes trust in the digest.
- **Signal weighting drift.** Weights in the references file go stale as the product and customer base change. Guard: include the SHA-256 (first 7 chars) of `references/1-risk-signal-weights.md` in the digest footer. If the footer hash hasn't changed in 90 days, the digest prepends `_Weights file last touched 90+ days ago. Time to recalibrate._`
- **Owner staleness.** If `owner_email` is empty or maps to a former employee, the ping goes to the wrong person and the action does not happen. Guard: any account whose owner handle cannot be resolved gets surfaced under `*Ownership broken ({n})*` instead of Red/Amber, with a link to the Gainsight account ownership editor.
- **Action specificity collapse.** Under load, the model defaults to generic "engage stakeholder" suggestions. Guard: post-process the Action field with a literal substring check — if the action contains any of `engage`, `reach out`, `touch base`, `align`, `socialize` without a named person or artifact, replace it with `needs human review`. Better silence than noise.
# Risk signal weights — TEMPLATE
> Replace these weights with values your CSM lead has signed off on.
> The skill multiplies `severity` (1-5) by the weight below to get
> per-event contribution to `signal_score`. Edit one number at a time
> and watch the next two digests before editing again.
## Per-event-type weights
| Event type | Weight | Notes |
|-------------------------|-------:|-----------------------------------------------------------------|
| `exec_disengagement` | 5.0 | Sponsor stops attending QBRs / unread emails for 30+ days |
| `sponsor_change` | 4.0 | Champion left or moved internally; new owner not yet onboarded |
| `contract_renegotiation`| 3.5 | Procurement opened a contract review outside the renewal window |
| `support_escalation` | 3.0 | P1 case open > 5 business days, or 3+ P2s in 14 days |
| `usage_drop` | 3.0 | Active seats / API calls / features-used down >25% over 14 days |
| `nps_detractor` | 2.0 | NPS <= 6 from any buying-committee role in the last 30 days |
| `qbr_missed` | 2.0 | Cancelled or no-show with no reschedule within 14 days |
## Always-escalate single signals
These trigger Red regardless of `signal_score`. The skill should treat them as a hard override, not a soft boost.
- `exec_disengagement` at severity 5
- `sponsor_change` at severity 4-5 when the renewal date is within 90 days
- `contract_renegotiation` at any severity when ARR > 250k
## Per-event contribution cap
A single event contributes at most 5.0 to `signal_score` regardless of `severity * weight`. This prevents one severity-5 sponsor change from single-handedly flooding the digest with one account at the expense of two genuinely declining ones.
## Weight calibration log
Append every change here so the next person editing this file can see why the numbers are what they are. Format: `YYYY-MM-DD — change — reason`.
- {YYYY-MM-DD} — initial weights — placeholder, replace with team-tuned values
## Last edited
{YYYY-MM-DD}
# Sample digest — worked example
> The skill emits this exact format, Slack-mrkdwn flavored. Treat this
> file as the contract: if you change the layout, change it here first
> and the skill follows. Do not let the model improvise structure.
## Worked example output
```
*Daily churn-risk digest — 2025-11-04*
*Red (3)* — act this week
- *Acme Robotics* — $480k ARR · owner @nadia · renewal 2025-12-18
Driver: VP Eng skipped two scheduled syncs and an automation pilot was paused on 10-29.
Action: Get Nadia a 30-min slot with the new VP Eng before Friday's renewal kickoff.
- *Northwind Logistics* — $310k ARR · owner @marcus · renewal 2026-01-09
Driver: Active seats fell from 142 to 89 over the last 7 days; finance opened a contract review.
Action: Open a discount-modeling thread with the deal desk before the procurement call on 11-07.
- *Globex Health* — $265k ARR · owner @priya · renewal 2026-02-22
Driver: P1 outage case open since 10-30 with no first-response SLA met.
Action: Escalate the open P1 to support leadership and brief Priya before her standing customer call.
*Amber (4)* — review by Friday
- *Initech* — $180k ARR · owner @marcus · renewal 2026-03-14
Driver: Sponsor moved to a new role last week; new owner has not been introduced.
Action: Send the QBR deck and offer a 15-min intro call with the new sponsor.
- *Vandelay Imports* — $140k ARR · owner @nadia · renewal 2026-01-30
Driver: Three P2 cases opened in the last 10 days, all on the reporting module.
Action: needs human review
- *Soylent Foods* — $115k ARR · owner @priya · renewal 2026-04-02
Driver: NPS dropped to 4 from the Director of Ops on 10-31.
Action: Forward the survey verbatim to Priya and request a follow-up 1:1 with the Director.
- *Pied Piper* — $95k ARR · owner @marcus · renewal 2026-02-11
Driver: QBR cancelled 10-28, no reschedule.
Action: Propose three slots for next week and copy the original sponsor.
*Watch (6)* — no action required, tracking only.
6 accounts crossed into the watch band; signal not strong enough for action this week.
_Filtered out: 2 below $50k · 0 outside segment._
_Capped at 13 of 13 qualifying. Full list: https://gainsight.example.com/views/churn-risk-trailing-24h_
_Event-type mix this week: usage_drop 24%, support_escalation 28%, sponsor_change 14%, qbr_missed 12%, nps_detractor 10%, exec_disengagement 6%, contract_renegotiation 6%._
_Weights file: 1-risk-signal-weights.md @ a3f9c12_
```
## Notes for the skill
- Account names are bolded with single asterisks (Slack-mrkdwn).
- The `Action:` line is the *only* place where a generic phrase (`engage`, `align`, `socialize`, `reach out`, `touch base`) is rejected and replaced with `needs human review`.
- The Watch bucket never emits per-account lines. Only the count.
- The footer carries three diagnostics — filter counts, event-type mix, and the weights-file hash — that exist to make miscalibration visible early. Do not drop them to save lines.
# Escalation criteria thresholds — TEMPLATE
> Replace these defaults with the values your CSM lead has signed off
> on. Aim for 5-15 total Red+Amber accounts per daily run; if you are
> consistently above or below, the thresholds are wrong, not the team.
## Bucket definitions
An account lands in exactly one bucket per run. The skill evaluates in this order and stops at the first match.
### Red — act this week
ANY of the following:
- `risk_score_current` crossed the explicit churn-risk line set in Gainsight (e.g. moved from "Medium" to "High", or numeric score crossed below 40), AND `risk_score_prior` was on the safe side of that line within the trailing window.
- `signal_score` >= **12.0** (sum of `severity * weight` across trailing-window events, per `1-risk-signal-weights.md`).
- A single event matches the always-escalate list in `1-risk-signal-weights.md` ("Always-escalate single signals").
### Amber — review by Friday
ANY of the following, and NOT already Red:
- `health_score_current - health_score_prior` <= **-15** in the trailing window.
- `signal_score` between **6.0** and **12.0**.
### Watch — count only
Crossed into the band (any negative movement on either score) but does not meet Amber criteria. Surfaced as a count only.
## Filters
Applied before bucketing. Filtered accounts are excluded from all buckets and reported in the footer.
- `min_arr` — drop accounts with `arr` < this value. Default **0**. Most teams that send to a channel set this to **50** (k$) to keep the digest scannable.
- `segments` — restrict to a list of `segment` values. Default: all segments. Most teams running a daily digest restrict to `["enterprise", "mid-market"]` and run a separate weekly digest for SMB to keep the daily list focused on accounts that justify a human-touch action.
## Cap and overflow
`cap` = **15** by default. After Red+Amber are sorted by ARR descending, anything beyond the cap goes into the footer count + a link to the saved Gainsight view. Do not silently drop accounts — the count must always be honest.
## Self-tuning trigger
If Red exceeds `cap` on three consecutive runs, the skill prepends a warning to the digest noting the threshold may be too loose. The skill does NOT auto-edit this file. Threshold edits are a human decision; the skill only surfaces the signal.
## Threshold change log
Append every change so the next person editing this file can see why the numbers are what they are. Format: `YYYY-MM-DD — change — reason`.
- {YYYY-MM-DD} — initial thresholds — placeholder, replace with team-tuned values
## Last edited
{YYYY-MM-DD}