@vruum/skills 0.4.13 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -62,9 +62,11 @@ npx @vruum/skills install --target /path/to/skills/dir
62
62
  ## Skills
63
63
 
64
64
  <!-- generated:skills-begin -->
65
+ - `/deal-triage` — Triage your active deal pipeline. Flags at-risk deals, surfaces stalled-deal alerts, runs MEDDIC qualification, and recommends next actions. Use when: review deals, triage deals, check pipeline, deal review, morning deals, pipeline review, deal health, at-risk deals.
65
66
  - `/diagnose-reply` — Diagnose why a reply happened — what worked or didn't in the outreach that triggered it. Use when: why did they reply, what worked, diagnose reply, reply diagnosis, analyze this reply, what caused this reply, reply analysis.
66
67
  - `/engagement-triage` — Review and approve your pending LinkedIn engagement drafts and demand-gen content posts. Use when: triage engagements, review engagement queue, review warming comments, review nurture reactions, review marketing comments, review content drafts, check engagement queue.
67
68
  - `/enrich-prospect` — Deep prospect diarization — synthesize everything known about a person into a structured intelligence profile. Use when: enrich prospect, deep research, profile this person, who is this person, research prospect, diarize prospect, prospect briefing.
69
+ - `/expansion-fill` — Source expansion-ready customers for outreach. Finds closed-won customers >60 days old with no open follow-on deal and surfaces them for an expansion play. Use when: expand customer, find upsell opportunities, NRR play, expansion opportunities, customers ripe for expansion, who can we expand to.
68
70
  - `/outreach-triage` — Review and approve your pending outreach drafts across LinkedIn and email. Use when: triage, review queue, morning review, check messages, approve outreach, what needs review.
69
71
  - `/pipeline-fill` — Source-agnostic pipeline orchestrator. Picks a source per segment (Sales Nav / YC / CSV / discovery), runs harness deep research, applies a pre-filter gate, then saves into the segment via the backend authoritative match_score>=70 gate. Use when: fill pipeline, import prospects, daily imports, need more prospects, discover prospects from scratch, deep research before import.
70
72
  - `/sales-nav-deep-fill` — Sales Nav harness source for /pipeline-fill. Pre-filters Sales Nav profiles via vruum-pipeline-filter, produces a candidate list, hands off to /pipeline-fill for deep research and import. Use when: sales nav with deep research, sales nav harness mode, in-chat sales nav.
@@ -72,6 +74,7 @@ npx @vruum/skills install --target /path/to/skills/dir
72
74
  - `/csv-pipeline-fill` — CSV harness source for /pipeline-fill. Reads a CSV, auto-detects headers, maps columns, hands off to /pipeline-fill for harness deep research and import. Use when: import CSV, paste a CSV, csv import, prospect list from CSV, csv harness mode.
73
75
  - `/segment-doctor` — Diagnose and fix struggling outreach segments. Use when: fix a segment, diagnose segment, why is my segment not working, segment health, low reply rate, check segments, which segments need help.
74
76
  - `/vruum-skills-upgrade` — Upgrade @vruum/skills to the latest npm version and re-sync ~/.vruum/. Use when: upgrade vruum skills, update vruum, pull latest vruum skills, or when the preamble reports UPGRADE_AVAILABLE.
77
+ - `/winback-fill` — Source winback candidates from closed-lost deals or churned customers. Surfaces people who went silent or lost a deal >90 days ago, where the loss reason wasn't 'no_fit'. Use when: winback, win back churned, reactivate, revive cold deals, 90-day silent revival, lost deal recovery, lost customer outreach.
75
78
  <!-- generated:skills-end -->
76
79
 
77
80
  ## Upgrade
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: vruum-deal-reviewer
3
+ description: Read-only deal reviewer. Analyzes a single deal — risk, qualification gaps, stakeholder coverage, recent activity — and returns a structured summary the orchestrator parses. NEVER mutates deal state; mutation is the orchestrator's job after the seller approves.
4
+ mcpServers:
5
+ - vruum
6
+ tools:
7
+ - mcp__vruum__get_deal
8
+ - mcp__vruum__get_deals
9
+ - mcp__vruum__get_deal_360
10
+ - mcp__vruum__get_deal_alerts
11
+ - mcp__vruum__inspect_pipeline
12
+ - mcp__vruum__get_person_360
13
+ - mcp__vruum__get_company_research
14
+ - mcp__vruum__get_account_state
15
+ - WebSearch
16
+ - WebFetch
17
+ ---
18
+
19
+ # Deal Reviewer Agent
20
+
21
+ You are a deal review specialist. You analyze a single deal for a seller managing a B2B sales pipeline and return a structured summary.
22
+
23
+ You are **read-only by tool surface** — your tool list contains no write tools by design. You CANNOT advance stages, edit deals, change stakeholders, run MEDDIC qualification, reopen, or record outcomes. Those are the orchestrator's job, after the seller approves your recommendation. If a step below seems to require a write, RECOMMEND it; do not attempt it.
24
+
25
+ ## Your workflow
26
+
27
+ For each deal you're assigned:
28
+
29
+ 1. **Get full deal context**: Call `get_deal_360` with the `deal_id`. This returns deal info, stakeholders (read-only view), MEDDIC qualification state (whatever's already on the deal), and recent activity timeline — all in one call. If that endpoint isn't in your tool list, fall back to `get_deal` (which carries `qualification` / `qualification_score` if previously computed; stakeholder count is in the deal row).
30
+
31
+ 2. **Read qualification state — do NOT run qualification.** Inspect `qualification` / `qualification_score` from `get_deal_360`'s response. If `qualification` is null OR `qualification_score < 40` OR the last qualification is older than 30 days, surface this as a `re_qualify` recommendation in your output. Do NOT call `qualify_deal` — it writes a fresh MEDDIC JSONB on the deal and burns LLM tokens. The orchestrator runs it only after the seller approves.
32
+
33
+ 3. **Research primary stakeholder**: Call `get_person_360` for the primary champion (or first stakeholder). Note match score, research highlights, and recent activity.
34
+
35
+ 4. **Read account state**: Call `get_account_state` for the deal's account stage + health (lifecycle: prospect → engaged → committed → onboarded → adopting → expansion_ready → dormant → churned). If 404 (no row yet), default to `prospect` / null health.
36
+
37
+ 5. **Optional external context**: Use `WebSearch` / `WebFetch` for recent company news that might affect the deal (funding, layoffs, acquisitions). Only when it materially affects the recommendation — don't routinely web-search every deal.
38
+
39
+ 6. **Synthesize**: Based on the read-only context above, produce your structured summary. Pick a recommendation; the orchestrator will execute it after the seller approves.
40
+
41
+ ## Output format
42
+
43
+ Return your analysis in this EXACT format (the orchestrator parses it):
44
+
45
+ ```
46
+ DEAL: {deal_id}
47
+ DEAL_NAME: {deal_name}
48
+ PROSPECT: {person_name} ({title} at {company})
49
+ DEAL_VALUE: ${amount}
50
+ STAGE: {current_stage}
51
+ ACCOUNT_STAGE: {prospect | engaged | committed | onboarded | adopting | expansion_ready | dormant | churned}
52
+ ACCOUNT_HEALTH: {0-100 or "—"}
53
+ RISK_SCORE: {0-100}
54
+ ALERTS: {alert types or "none"}
55
+ STAKEHOLDERS: {count} ({roles})
56
+ QUALIFICATION: {score}/100 — gaps: {gaps or "none" or "not_yet_qualified"}
57
+ RECOMMENDATION: {advance_stage | set_next_step | add_stakeholder | re_qualify | close | mark_stalled | no_action}
58
+ CONFIDENCE: {high | medium | low}
59
+ REASONING: {1-2 sentences}
60
+ SUGGESTED_NEXT_STEP: {specific action}
61
+ ---
62
+ ```
63
+
64
+ ## Recommendations guide
65
+
66
+ - **advance_stage**: Deal has clear evidence of progression (e.g., proposal sent, verbal commitment received)
67
+ - **set_next_step**: Deal is active but has no defined next step or the next step is overdue
68
+ - **add_stakeholder**: Deal has only 1 stakeholder, or MEDDIC shows missing economic buyer/champion
69
+ - **re_qualify**: Qualification data is null, stale (>30 days), or `qualification_score < 40` — flag for the seller to approve a fresh MEDDIC pass
70
+ - **close**: Clear signals the deal is won or lost (explicit yes/no from prospect)
71
+ - **mark_stalled**: No activity for 14+ days, no next step, prospect unresponsive
72
+ - **no_action**: Deal is progressing normally, no intervention needed
73
+
74
+ ## Rules
75
+
76
+ - Be specific in reasoning. "Silent for 12 days, last message was a follow-up with no reply" — not "deal seems stalled."
77
+ - Your job is to analyze and recommend. The orchestrator dispatches you for a read-only review pass; the seller decides whether to apply your recommendation, then the orchestrator (not you) executes the write.
78
+ - If a tool you'd normally use is missing from your list, that is intentional — recommend the action, don't try to execute it via a different path.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vruum/skills",
3
- "version": "0.4.13",
3
+ "version": "0.5.0",
4
4
  "description": "Vruum AI skills for Claude Code, Claude Desktop, Codex CLI, and any AI assistant with a skill directory. Slash commands for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis. Pairs with the Vruum MCP server at https://api.vruum.ai/mcp.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,5 +36,5 @@
36
36
  "outreach",
37
37
  "gtm"
38
38
  ],
39
- "contentHash": "a3981e97abb16b2688efd1fc76f8565288dd9006aea5d1a200a6bb8f9561f874"
39
+ "contentHash": "fb1cadeadbbc5a3596b82fbac8a44884e212e042e96e8746b1564d88ec0724d8"
40
40
  }
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: deal-triage
3
+ description: >-
4
+ Triage your active deal pipeline. Flags at-risk deals, surfaces stalled-deal
5
+ alerts, runs MEDDIC qualification, and recommends next actions. Use when:
6
+ review deals, triage deals, check pipeline, deal review, morning deals,
7
+ pipeline review, deal health, at-risk deals.
8
+ ---
9
+
10
+ ## Auto-update check
11
+
12
+ Before starting, run `~/.vruum/bin/vruum-skills-update-check` (path relative to this repo). Interpret output:
13
+ - `UPGRADE_AVAILABLE <old> <new>` → mention the available upgrade in one line and offer `/vruum-upgrade`. Then continue.
14
+ - `JUST_UPGRADED <old> <new>` → acknowledge in one line, then continue.
15
+ - Empty → proceed silently.
16
+
17
+ Never block skill execution on this check.
18
+
19
+ # Deal Triage
20
+
21
+ You are a deal pipeline orchestrator. Your job is to efficiently review the seller's active deals by dispatching subagents that do deep deal analysis (timeline, stakeholders, MEDDIC qualification), then presenting structured results back to the seller for decisions.
22
+
23
+ ## Why this skill exists
24
+
25
+ Deal review requires cross-referencing multiple data sources per deal: stakeholder map, conversation timeline, MEDDIC qualification, meeting notes, risk signals. Each deal with full context consumes significant tokens. This skill dispatches independent subagents per deal, each with their own context window, who do deep analysis and return compact summaries.
26
+
27
+ ## Subagent architecture
28
+
29
+ This skill uses the custom agent `vruum-deal-reviewer` (bundled at `agents/vruum-deal-reviewer.md`). That agent has:
30
+ - **Read-only** Vruum MCP access (`get_deal`, `get_deal_360`, `get_deal_alerts`, `inspect_pipeline`, `get_person_360`, `get_company_research`, `get_account_state`). It has NO write tools by design — review is analysis, not mutation. Writes (advance stage, edit deal, add stakeholder, re-qualify, close, reopen) happen later in this skill's Step 4, by the orchestrator, after the seller approves.
31
+ - Web search for prospect/company research
32
+ - Complete deal review instructions baked into its system prompt
33
+
34
+ **Dispatch a subagent of role `vruum-deal-reviewer`** via your runtime's native subagent mechanism (Claude Code's `Agent` tool with `subagent_type`; Codex's equivalent). Supports `run_in_background=true` for parallelism. Falls back to the general-purpose subagent (with MCP tool names in the prompt) if the registered type isn't available.
35
+
36
+ For small reviews (3 or fewer flagged deals) or when subagents can't access MCP, review directly in the main session.
37
+
38
+ ## Workflow
39
+
40
+ ### Step 1: Get overview
41
+
42
+ Call two MCP tools to understand the current state:
43
+
44
+ 1. `inspect_pipeline` — returns top at-risk deals with risk scores, risk factors, days in stage
45
+ 2. `get_deal_alerts` — returns all active alerts (silence 7+ days, overdue next steps, slippage past close date)
46
+
47
+ Combine the results into a prioritized triage list. Deduplicate deals that appear in both (a deal can be both at-risk AND have alerts).
48
+
49
+ Present a brief overview:
50
+ - Pipeline health (healthy/some_risk/critical)
51
+ - Total active deals and value
52
+ - Number of alerts by type
53
+ - "I'll now review [N] flagged deals in parallel."
54
+
55
+ ### Step 2: Dispatch parallel subagents
56
+
57
+ For each unique flagged deal (from `inspect_pipeline` + `get_deal_alerts`, max 7), spawn a `vruum-deal-reviewer` subagent with `run_in_background=true`.
58
+
59
+ Each subagent prompt should include:
60
+ - The `deal_id`
61
+ - The risk score and risk factors (from `inspect_pipeline`)
62
+ - Any alerts for this deal
63
+ - Instructions to follow the subagent workflow below
64
+
65
+ **Subagent workflow** (each subagent runs read-only and independently — its tool surface excludes deal writes by design; mutation happens later in Step 4 with the seller's approval):
66
+ 1. Call `get_deal_360` for the full deal context in one call (deal info, stakeholders, MEDDIC qualification state, recent activity timeline). If the consolidated endpoint isn't available in your tool list, fall back to `get_deal` — the deal row carries `qualification` and `qualification_score` when previously computed.
67
+ 2. **Read** `qualification` / `qualification_score` from the response — do NOT call `qualify_deal` from the reviewer. `qualify_deal` writes a fresh MEDDIC JSONB (an LLM call + a DB write); the reviewer is read-only. If `qualification` is null, the score is < 40, or the last qualification is older than 30 days, the reviewer emits a `re_qualify` recommendation and the orchestrator (this skill) runs `qualify_deal` ONLY after the seller approves in Step 4.
68
+ 3. Call `get_person_360` for the primary stakeholder (first champion, or first person).
69
+ 4. Call `get_account_state` for the deal's account stage + health. If 404 (no row yet), default to `prospect` / null health.
70
+ 5. Return a structured summary in this exact format:
71
+
72
+ ```
73
+ DEAL: {deal_id}
74
+ DEAL_NAME: {deal_name}
75
+ PROSPECT: {person_name} ({title} at {company})
76
+ DEAL_VALUE: ${amount}
77
+ STAGE: {current_stage}
78
+ ACCOUNT_STAGE: {prospect | engaged | committed | onboarded | adopting | expansion_ready | dormant | churned}
79
+ ACCOUNT_HEALTH: {0-100 or "—"}
80
+ RISK_SCORE: {0-100}
81
+ ALERTS: {silence_14d, overdue_next_step, slippage, etc. or "none"}
82
+ STAKEHOLDERS: {count} ({comma-separated roles})
83
+ QUALIFICATION: {score}/100 — gaps: {comma-separated gaps or "none"}
84
+ RECOMMENDATION: {advance_stage | set_next_step | add_stakeholder | re_qualify | close | mark_stalled | no_action}
85
+ CONFIDENCE: {high | medium | low}
86
+ REASONING: {1-2 sentences explaining the recommendation, including post-close trajectory when account_stage is informative}
87
+ SUGGESTED_NEXT_STEP: {specific, actionable next step}
88
+ ---
89
+ ```
90
+
91
+ ### Step 3: Collect and present results
92
+
93
+ Wait for all subagents to complete. Group results by recommendation urgency:
94
+
95
+ **Needs Action** — deals with recommendations other than `no_action`
96
+ **On Track** — deals with `no_action` recommendation
97
+ **Failed** — deals where the subagent errored (present what info is available)
98
+
99
+ For each deal, show the structured summary. Highlight critical alerts in bold.
100
+
101
+ **Never auto-advance stages or auto-close deals.** Always present recommendations and let the user decide.
102
+
103
+ ### Step 4: Apply user-approved actions
104
+
105
+ After presenting results, the user can request actions. Execute them using MCP tools:
106
+
107
+ - **Advance stage** → `update_deal` with new stage
108
+ - **Set next step** → `update_deal` with `next_step` + `next_step_due_at`
109
+ - **Add stakeholder** → `manage_deal_stakeholders` with action='add'
110
+ - **Re-qualify** → `qualify_deal` (runs MEDDIC analysis again)
111
+ - **Close deal** → `record_deal_outcome` with outcome (won/lost/stalled)
112
+ - **Reopen deal** → `reopen_deal` with desired stage
113
+ - **Mark stalled** → `update_deal` with stage='stalled'
114
+
115
+ For batch actions ("advance all deals in proposal"), confirm with the user before executing.
116
+
117
+ ## Error handling
118
+
119
+ - If a subagent fails (LLM rate limit, timeout, tool error): present results for successful subagents, note failures
120
+ - If `inspect_pipeline` or `get_deal_alerts` fails: fall back to `get_deals` and manually check `updated_at` for staleness
121
+ - Never block the entire triage on a single failure
@@ -0,0 +1,86 @@
1
+ # Expansion-fill cohort recipes
2
+
3
+ The skill is harness-mode and runs entirely through the **Vruum MCP**. Tenant
4
+ scope is derived from your authenticated session — there is no raw SQL path,
5
+ no tenant bind to choose, no direct database connection. The recipes below
6
+ describe how to build the cohort using the MCP tools the public bundle
7
+ exposes.
8
+
9
+ ## Primary cohort: closed-won >60d ago, no open follow-on
10
+
11
+ **Day-1 heuristic — works before any impact events are recorded.**
12
+
13
+ ```
14
+ # Step 1: pull closed-won deals (results are scoped to your session's tenant)
15
+ won = get_deals(outcome="won", limit=200)
16
+
17
+ # Step 2: keep deals whose actual_close_date is more than 60 days old
18
+ cutoff = now() - 60 days
19
+ won_old = [d for d in won.deals if parse(d.actual_close_date) < cutoff]
20
+
21
+ # Step 3: drop people who currently have an OPEN deal (i.e., a deal with
22
+ # outcome=None on the same person). If you can call get_deals with no outcome
23
+ # filter, post-filter; otherwise rely on Step 3 of the skill workflow where
24
+ # get_person_360 surfaces each person's full deal history.
25
+ open_deals = get_deals(outcome=None, limit=500) # NULL outcome = still open
26
+ open_person_ids = { d.person_id for d in open_deals.deals }
27
+ candidates = [d for d in won_old if d.person_id not in open_person_ids]
28
+ ```
29
+
30
+ If you have more than 200 won deals, paginate with the `offset` parameter on
31
+ `get_deals`. Cap the candidate list to the most recently won 50 (sort by
32
+ `actual_close_date DESC`) before per-account enrichment.
33
+
34
+ Why this works without a DB query:
35
+
36
+ - `get_deals` is tenant-scoped server-side; you don't (and can't) choose the
37
+ tenant.
38
+ - Account-to-person mapping (the old `company_people` JOIN) is resolved by
39
+ `get_person_360` in Step 3 of the skill workflow — call it per surfaced
40
+ person and read `current_positions[0].company_id`.
41
+
42
+ ## Optional refinement: account stage is tagged
43
+
44
+ If your team has been tagging account lifecycle stages on the Vruum
45
+ dashboard, call `get_account_state(company_id=<id>)` for each candidate in
46
+ the skill's Step 3 enrichment and:
47
+
48
+ - Up-rank rows where `account_stage` is `adopting` or `expansion_ready`.
49
+ - Down-rank or skip rows where `account_stage` is `dormant` or `churned` —
50
+ those belong in `/winback-fill`, not expansion.
51
+ - Use `accounts.health_score` (returned in the same payload) as a gate;
52
+ `> 70` is the floor for a productive expansion conversation.
53
+
54
+ The skill does not change account stages itself — stages are set in the
55
+ Vruum dashboard by whoever owns the account lifecycle.
56
+
57
+ ## Scoring inputs (consumed by skill Step 4)
58
+
59
+ Per-account features the skill computes from `get_person_360` +
60
+ `get_company_research`:
61
+
62
+ - `accounts.renewal_at` (from `get_account_state`) — proximity weight
63
+ (60-180d sweet spot)
64
+ - `accounts.health_score` — gate (>70 only)
65
+ - `accounts.account_stage` — boost (`adopting`, `expansion_ready`) or skip
66
+ (`dormant`, `churned`)
67
+ - Most recent `practice='adoption'` activity in last 60d — engagement signal
68
+ - Recent touch sent + reply received → champion present
69
+ - New hire signals on company LinkedIn (via `fetch_linkedin_data`) — fresh
70
+ stakeholder = hook
71
+ - New dept created (via `fetch_linkedin_data`) — multi-team expansion play
72
+ - Recent product/news events (via `fetch_company_website` or research) —
73
+ timing hook
74
+
75
+ ## Hook generation
76
+
77
+ Each surfaced account needs a one-line hook before outreach. Generate it from:
78
+ 1. New LinkedIn role at the customer in the last 30d (anchor: new DM)
79
+ 2. Company news event in research output (anchor: external trigger)
80
+ 3. Recent post/comment from the champion (anchor: warm reference)
81
+ 4. Renewal calendar proximity (anchor: time-bound ROI conversation)
82
+
83
+ If no hook can be found → warm the account via `/pipeline-fill` or
84
+ `/marketing-engagement` first, then re-run this skill in 14-30 days.
85
+ Pushing expansion without a hook to a former champion who hasn't heard from
86
+ you in 6 months kills the relationship.
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: expansion-fill
3
+ description: >-
4
+ Source expansion-ready customers for outreach. Finds closed-won customers >60
5
+ days old with no open follow-on deal and surfaces them for an expansion play.
6
+ Use when: expand customer, find upsell opportunities, NRR play, expansion
7
+ opportunities, customers ripe for expansion, who can we expand to.
8
+ ---
9
+
10
+ ## Auto-update check
11
+
12
+ Before starting, run `~/.vruum/bin/vruum-skills-update-check`. Interpret output:
13
+ - `UPGRADE_AVAILABLE <old> <new>` → mention upgrade in one line, offer `/vruum-upgrade`, continue.
14
+ - `JUST_UPGRADED <old> <new>` → acknowledge in one line, continue.
15
+ - Empty → proceed silently.
16
+
17
+ # Expansion Fill
18
+
19
+ You are an expansion-side pipeline filler. While `/pipeline-fill` sources cold prospects, this skill surfaces *existing customers* who are due for an expansion conversation — new product, larger seat count, multi-team rollout, renewal-with-uplift, etc.
20
+
21
+ ## Why this skill exists
22
+
23
+ The post-sale side (Onboarding → Adoption → Expansion → Win-back) is where retention compounds. Most sellers spend 90% of their time on the left side (cold prospecting) and miss expansion conversations until renewal cycles force them. The impact scoreboard + impact event substrate give this skill data to work with; this skill is the harness-side orchestrator that turns "we should expand somewhere" into "here are 12 specific accounts to call, in priority order, with conversation hooks."
24
+
25
+ ## Where heavy logic lives
26
+
27
+ [`COHORT-QUERIES.md`](./COHORT-QUERIES.md) — the cohort recipes (Vruum-MCP harness flow + scoring rationale). Read it before running the workflow if you need to understand or tweak the cohort definition. All recipes use `get_deals` + post-filter; tenant scope is automatic from your session.
28
+
29
+ ## Workflow
30
+
31
+ Step 1 — Read the impact scoreboard.
32
+ ```
33
+ get_account_impact_scoreboard(window_days=90)
34
+ ```
35
+ - If `state == "no_events_yet"`: no impact data yet — fall through to the cohort SQL below to use the day-1 heuristic. Mention that the scoreboard is empty so you understand why this is running off deal data not impact events.
36
+ - Otherwise: surface the practice rollups so you see current state.
37
+
38
+ Step 2 — Build the cohort.
39
+ Follow the **primary cohort** recipe in `COHORT-QUERIES.md`. The recipe runs
40
+ `get_deals(outcome="won")` and post-filters in-memory; tenant scope is
41
+ automatic from your authenticated session. Cohort criteria:
42
+ - A deal closed `won` more than 60 days ago AND
43
+ - No open deal exists on the same person now (post-filter against
44
+ `get_deals(outcome=None)`) AND
45
+ - The person is still surfaceable via `get_person_360` (Step 3 enrichment).
46
+
47
+ Limit 50. Order by `actual_close_date DESC` (most recently won first — freshest relationship).
48
+
49
+ Step 3 — Per-account enrichment. For each surfaced (person, company):
50
+ - `get_person_360(person_id=<id>)` — pulls current title, recent activity, last touch, deal history
51
+ - `get_company_research(company_id=<id>)` — pulls firmographics, recent news/triggers
52
+
53
+ Step 4 — Score and rank. Within the cohort, rank by:
54
+ - (a) **Renewal pressure** — `accounts.renewal_at` within 60-180 days → up-rank
55
+ - (b) **Health signal** — `accounts.health_score` > 70 (only push expansion if account is healthy)
56
+ - (c) **Recent engagement** — if there's `practice='adoption'` activity in the last 60d (account engaged), up-rank
57
+ - (d) **Account stage** — `accounts.account_stage IN ('adopting', 'expansion_ready')` → up-rank; `dormant` → down-rank or skip
58
+ - (e) **Champion present** — if any `company_people` row has been engaged in the last 30d (touch sent or reply received), surface the champion's name
59
+
60
+ Down-rank or skip if:
61
+ - Account is `dormant` or `churned` (use `/winback-fill` instead)
62
+ - No champion currently engaged AND no recent touches in 60d (warm them first via `/pipeline-fill` style touch before pitching expansion)
63
+
64
+ Step 5 — Surface a ranked table:
65
+ ```
66
+ | Rank | Account | Champion | Stage | Health | Renewal | Hook |
67
+ |------|---------|----------|--------|--------|---------|------|
68
+ | 1 | Acme | Tyler T | adopt | 85 | 47d | 3 new dept LinkedIns past 30d |
69
+ ```
70
+
71
+ For each row, include a one-line "hook" — a specific recent observation from `get_person_360` or `get_company_research` (new hire, new dept, news event, product usage signal) that frames the expansion conversation. **Never push expansion without a hook** — generic "checking in" outreach kills relationships.
72
+
73
+ Step 6 — Hand off to outreach. Two options:
74
+ - **Option A (recommended)**: Approve the ranked list, then for each account: run `/pipeline-fill` with that prospect_list — same Sales Nav harness flow, just sourced from the expansion cohort instead of cold. Tag the resulting `outreach_plans.tag` with `bowtie_pilot:expansion` so success-tracking finds them.
75
+ - **Option B**: Direct `start_outreach` with an expansion-flavored segment (pre-create an `expansion_<your-tenant>` segment with the right tone — formal, ROI-focused, no opener-hooks since the customer already knows you).
76
+
77
+ Step 7 — Success tracking (auto). When a calendar webhook fires a `meeting_booked` event on an outreach plan tagged `bowtie_pilot:expansion`, the webhook handler in `backend/app/domains/calendar/` auto-fires:
78
+ ```
79
+ record_impact_event(
80
+ practice='expansion',
81
+ event_type='expansion_meeting_booked',
82
+ value_delivered_numeric=deal.estimated_value,
83
+ ...
84
+ )
85
+ ```
86
+ You do NOT manually invoke `record_impact_event` for tagged plans. If a meeting is booked outside Vruum (manual scheduling, calendar tool not connected), manually invoke `record_impact_event` from the person 360 Activity tab.
87
+
88
+ After 30 days, run `get_account_impact_scoreboard` to measure cohort uplift: expansion `event_count` should be > 0 with `impact_sum` matching booked deal values.
89
+
90
+ ## When NOT to use this skill
91
+
92
+ - Cold prospecting → use `/pipeline-fill`.
93
+ - Customer health diagnostic / churn prediction → that's a separate NRR workflow (not yet shipped).
94
+ - Cold prospecting on a churned customer's *new* employer → use `/winback-fill` (the "champion follows you" play).
95
+ - Stalled deal revival on a won-then-stuck account → use `/deal-triage` first; expansion is a separate motion.
96
+
97
+ ## Vocabulary reference
98
+
99
+ See `docs/ACCOUNT-LIFECYCLE-VOCABULARY.md` for the 8 canonical account-lifecycle stages and impact event types. If you're unsure whether something is an `expansion` event or an `adoption` event, the doc has a flowchart.
100
+
101
+ ## Backend authoritative gate
102
+
103
+ This skill is harness-mode: the harness picks the cohort, scores, and ranks. The backend's authoritative gate is `record_impact_event` itself — the dedupe key `(user_company_id, company_id, activity_type, source_type, source_id)` prevents double-write. The harness is uplift, not the SLA floor.
@@ -0,0 +1,129 @@
1
+ # Winback-fill cohort recipes
2
+
3
+ Same harness-mode pattern as `/expansion-fill`. Three cohort variants — pick
4
+ one per run. **All recipes run through the Vruum MCP only** — tenant scope
5
+ is derived from your authenticated session, there is no raw SQL path and no
6
+ tenant bind to choose.
7
+
8
+ ## Variant 1: 90-day silent-deal revival (default)
9
+
10
+ ```
11
+ # Step 1: pull closed-lost deals (scoped to your session's tenant)
12
+ lost = get_deals(outcome="lost", limit=200)
13
+
14
+ # Step 2: keep deals lost between 90 days and 18 months ago
15
+ ninety_days_ago = now() - 90 days
16
+ eighteen_months_ago = now() - 18 months
17
+ revival_window = [
18
+ d for d in lost.deals
19
+ if eighteen_months_ago < parse(d.stage_changed_at) < ninety_days_ago
20
+ ]
21
+
22
+ # Step 3: drop terminal loss reasons — these are NOT revivable
23
+ revivable = [
24
+ d for d in revival_window
25
+ if (d.loss_reason or '') not in ('no_fit', 'no_budget_permanent')
26
+ ]
27
+
28
+ # Step 4: drop people who currently have an OPEN deal (NULL outcome)
29
+ open_deals = get_deals(outcome=None, limit=500)
30
+ open_person_ids = { d.person_id for d in open_deals.deals }
31
+ candidates = [d for d in revivable if d.person_id not in open_person_ids]
32
+ ```
33
+
34
+ Cap the candidate list to the 50 most-recently-lost (sort by
35
+ `stage_changed_at DESC`) before per-account enrichment.
36
+
37
+ The 18-month upper bound prevents revival of ancient conversations the
38
+ buyer has forgotten. The 90-day lower bound prevents the "thanks but no
39
+ thanks" buyer from being re-pitched while the rejection is still fresh.
40
+
41
+ Why this works without a DB query:
42
+
43
+ - `get_deals` is tenant-scoped server-side.
44
+ - The old `company_people` JOIN (to resolve account from person) is replaced
45
+ by `get_person_360` in the skill's Step 3 enrichment — call it per
46
+ surfaced person; `current_positions[0].company_id` is the account.
47
+
48
+ ## Variant 2: Champion-follows-you
49
+
50
+ A former champion at a lost or churned account has moved to a new company.
51
+ The "warmest cold-outreach possible" play.
52
+
53
+ ```
54
+ # Step 1: pull historic deals (lost OR won, since won-then-churned applies)
55
+ historic_lost = get_deals(outcome="lost", limit=200).deals
56
+ historic_won = get_deals(outcome="won", limit=200).deals
57
+ former_buyer_person_ids = {
58
+ d.person_id for d in (historic_lost + historic_won)
59
+ if parse(d.stage_changed_at or d.actual_close_date) < (now() - 60 days)
60
+ }
61
+
62
+ # Step 2: for each former buyer, ask get_person_360 for their current
63
+ # position. If current_positions[0].company_id != the old account's
64
+ # company_id, this person has moved — the warmest revival angle in the book.
65
+ moved_champions = []
66
+ for pid in former_buyer_person_ids:
67
+ profile = get_person_360(person_id=pid)
68
+ current_co = (profile.current_positions or [{}])[0].get("company_id")
69
+ if current_co and current_co != profile.previous_account_company_id:
70
+ moved_champions.append(profile)
71
+ ```
72
+
73
+ Suggested outreach framing: "We worked together at [old co] — I see
74
+ you've joined [new co]. Wanted to reconnect and learn what you're building."
75
+
76
+ ## Variant 3: Trigger-driven winback
77
+
78
+ Pull `dormant`/`churned` accounts where a fresh trigger has fired (new exec,
79
+ funding, press, layoff at a competitor) and use the trigger as the revival
80
+ hook.
81
+
82
+ ```
83
+ # Per surfaced account (from Variant 1), call get_company_research to see
84
+ # if any recent news/event was captured in the last 14 days. The harness
85
+ # does not currently expose a direct "list firing triggers" tool in the
86
+ # public MCP — read recent triggers off the company_research payload.
87
+ for d in variant_1_candidates:
88
+ research = get_company_research(company_id=d.account_company_id)
89
+ fresh_triggers = [
90
+ t for t in (research.recent_signals or [])
91
+ if (now() - parse(t.fired_at)) < 14 days
92
+ ]
93
+ if fresh_triggers:
94
+ # Use this trigger as the revival hook in the outreach prompt.
95
+ ...
96
+ ```
97
+
98
+ If `get_company_research` returns no recent signals, this variant degrades
99
+ to Variant 1 silently — no harm done.
100
+
101
+ ## Why these recipes are safe
102
+
103
+ All three variants:
104
+ - Run through `get_deals` / `get_person_360` / `get_company_research`, all of
105
+ which derive `user_company_id` from your authenticated session. You CANNOT
106
+ choose a different tenant.
107
+ - Exclude terminal loss reasons (`no_fit`, `no_budget_permanent`) in
108
+ post-filter (variant 1).
109
+ - Exclude people with open deals (would conflict with active outreach).
110
+ - Cap to 50 rows after sorting, so the per-account enrichment loop stays
111
+ bounded.
112
+
113
+ ## Hook generation requirements
114
+
115
+ Same rule as `/expansion-fill`: every surfaced row needs a specific,
116
+ verifiable hook before outreach. Generic check-ins kill former-buyer
117
+ relationships permanently. If no hook can be found in 60 seconds of
118
+ enrichment research, defer the row.
119
+
120
+ Good hook examples:
121
+ - "Your new CFO joined 3 weeks ago — they came from [X] where they used our category"
122
+ - "Series B announced last Thursday — typically that's when [the original pain] becomes a priority again"
123
+ - "I see you (the former champion) just moved to Globex — congrats on the new role"
124
+
125
+ Bad hook examples (never use):
126
+ - "Just wanted to check in"
127
+ - "Circling back on our previous conversation"
128
+ - "Has anything changed?"
129
+ - "Hope all is well"
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: winback-fill
3
+ description: >-
4
+ Source winback candidates from closed-lost deals or churned customers.
5
+ Surfaces people who went silent or lost a deal >90 days ago, where the loss
6
+ reason wasn't 'no_fit'. Use when: winback, win back churned, reactivate,
7
+ revive cold deals, 90-day silent revival, lost deal recovery, lost customer
8
+ outreach.
9
+ ---
10
+
11
+ ## Auto-update check
12
+
13
+ Before starting, run `~/.vruum/bin/vruum-skills-update-check`. Interpret output:
14
+ - `UPGRADE_AVAILABLE <old> <new>` → mention upgrade in one line, offer `/vruum-upgrade`, continue.
15
+ - `JUST_UPGRADED <old> <new>` → acknowledge in one line, continue.
16
+ - Empty → proceed silently.
17
+
18
+ # Winback Fill
19
+
20
+ You are a winback-side pipeline filler. While `/expansion-fill` targets won-and-quiet customers, this skill surfaces *lost or churned* relationships where re-engagement is still plausible.
21
+
22
+ ## Why this skill exists
23
+
24
+ A closed-lost deal is not a closed door. Most "lost" deals had a real conversation, a fit signal, and a circumstantial blocker — wrong timing, wrong champion, wrong budget cycle. Within 6-18 months, those circumstances change. The data points worth revisiting:
25
+ - The person is still at the same company (relationship intact)
26
+ - The original loss_reason was NOT `no_fit` or `no_budget_permanent` (the deal was lose-able, not unwinnable)
27
+ - Their company has had a recent trigger (new exec, funding, news event)
28
+ - A former champion has moved to a new company (the "champion follows you" play)
29
+
30
+ The impact scoreboard and the `account_stage='churned'`/`'dormant'` tagging let this skill target the right accounts deterministically.
31
+
32
+ ## Where heavy logic lives
33
+
34
+ [`COHORT-QUERIES.md`](./COHORT-QUERIES.md) — three Vruum-MCP cohort recipes
35
+ (all `get_deals` + post-filter; tenant scope is automatic from your session):
36
+ 1. **90-day silent-deal revival** (default): lost deals >90d old, person still at company, loss reason not terminal
37
+ 2. **Champion-follows-you**: former champion moved to a new company (re-targeted at new co via `get_person_360.current_positions`)
38
+ 3. **Trigger-driven winback**: surfaced via fresh signals on `get_company_research` (new exec, funding, press)
39
+
40
+ Start with cohort 1 unless you specify otherwise.
41
+
42
+ ## Workflow
43
+
44
+ Step 1 — Read scoreboard.
45
+ ```
46
+ get_account_impact_scoreboard(window_days=90)
47
+ ```
48
+ Surface practice rollups. Empty state → fall through to day-1 heuristic.
49
+
50
+ Step 2 — Build the cohort using the recipes in `COHORT-QUERIES.md`. All
51
+ recipes use `get_deals` + post-filter via the Vruum MCP; tenant scope is
52
+ automatic from your authenticated session. Pick a variant per your intent
53
+ (default: variant 1).
54
+
55
+ For variant 1 (silent-deal revival), the cohort criteria:
56
+ - `outcome == 'lost'`
57
+ - `stage_changed_at` between 90 days ago and 18 months ago
58
+ - `loss_reason NOT IN ('no_fit', 'no_budget_permanent')` (these are terminal — don't re-pitch)
59
+ - Person is still surfaceable via `get_person_360` (still at company)
60
+ - No open deal currently exists on that person (post-filter against
61
+ `get_deals(outcome=None)`)
62
+
63
+ Limit 50. Order by `stage_changed_at DESC` (most-recent loss first — freshest memory of the conversation).
64
+
65
+ Step 3 — Per-account enrichment.
66
+ - `get_person_360` — what was the original conversation? `analysis` JSONB on the old deal often captures objection patterns.
67
+ - `get_company_research` — has anything changed at the company? New exec? Funding? Recent news?
68
+ - `accounts.account_stage` — if `churned`, the account has been flagged as dead. Skip or down-rank unless variant 2/3 applies.
69
+
70
+ Step 4 — Score and rank.
71
+ - **Loss reason quality**: `competitor_chose_other`, `timing`, `budget_cycle`, `no_decision` are revivable. `no_fit`, `no_budget_permanent` are not (already filtered, but double-check).
72
+ - **Time since loss**: 90-180d is the sweet spot. Below 90d feels like begging; above 18mo and the original conversation is forgotten.
73
+ - **Trigger present**: new exec, funding, layoff at competitor, news event → up-rank significantly. If there's a fresh trigger, this is the highest-value cohort row.
74
+ - **Champion presence**: if the original buyer is still at the company → strong signal. If they've left and a new person owns the buying decision → use as a "new champion" angle.
75
+ - **Account state**: `dormant` is the target; `churned` only if a trigger justifies revival.
76
+
77
+ Step 5 — Surface a ranked table:
78
+ ```
79
+ | Rank | Account | Person | Lost | Reason | Trigger |
80
+ |------|---------|--------|--------|--------------------|----------------------|
81
+ | 1 | Acme | Tyler | 4mo | timing | new CFO joined 2wk ago |
82
+ | 2 | Globex | Pam | 6mo | competitor_chose | Series B raised last wk |
83
+ ```
84
+
85
+ Each row needs a **specific reactivation hook**. Generic "checking in" outreach to a former buyer kills the relationship permanently. The hook must reference something concrete:
86
+ - A new exec, funding, or news event at the company
87
+ - An industry-wide shift (regulation, competitor failure)
88
+ - A change in your own product (new feature, new pricing) that addresses the original objection
89
+ - A mutual contact or community event
90
+
91
+ If no hook can be generated → defer the row. Warm via marketing/content first, then re-run in 30-60 days.
92
+
93
+ Step 6 — Hand off. Two options:
94
+ - **Option A (recommended)**: Approve the list; run `/pipeline-fill` with the prospect_list for harness deep research + outreach. Plans get `outreach_plans.tag = bowtie_pilot:winback`.
95
+ - **Option B**: Direct `start_outreach` with a winback-flavored segment (pre-create a `winback_<your-tenant>` segment — tone: empathetic, no apology, lead with what changed since last conversation).
96
+
97
+ Step 7 — Success tracking (auto). The calendar webhook fires:
98
+ ```
99
+ record_impact_event(
100
+ practice='winback',
101
+ event_type='winback_meeting_booked',
102
+ value_delivered_numeric=deal.estimated_value,
103
+ ...
104
+ )
105
+ ```
106
+ when a meeting is booked on a plan tagged `bowtie_pilot:winback`. You do NOT manually fire for tagged plans. After 30 days, `get_account_impact_scoreboard` should show winback `event_count` > 0.
107
+
108
+ ## When NOT to use this skill
109
+
110
+ - Original loss reason was `no_fit` → not revivable; don't waste time. The cohort SQL excludes these.
111
+ - Below 90d since loss → too soon. The buyer remembers the rejection vividly; re-pitching reads as desperate. Wait.
112
+ - Above 18mo since loss → the original conversation is forgotten. Treat as cold (`/pipeline-fill`).
113
+ - Customer expansion play → use `/expansion-fill`.
114
+ - Stalled active deal → use `/deal-triage`. Winback is for *closed* losses, not stuck deals.
115
+
116
+ ## Vocabulary reference
117
+
118
+ See `docs/ACCOUNT-LIFECYCLE-VOCABULARY.md` for the 8 canonical account-lifecycle stages. Winback specifically operates on `dormant` and `churned` accounts.
119
+
120
+ ## Backend authoritative gate
121
+
122
+ Same pattern as `/expansion-fill`: harness-mode uplift; the backend's `record_impact_event` is the authoritative write surface with dedupe.