# Voc Analyze Calls

> Turn a folder of call transcripts into a prospect intelligence report. Filters noise, separates prospect calls from customer calls, fans out parallel subagents to read transcripts, and consolidates personas, pain points, objections, competitive mentions, feature requests and verbatim quotes. Trigger on /voc-analyze-calls, "analyze this week's calls", "what are prospects saying", "call intelligence report".

- Skill: `aatirs-vault/voc-analyze-calls` (Agent Skill)
- Install (CLI): `npx skillmds@latest add aatirs-vault/voc-analyze-calls`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aatirs-vault/voc-analyze-calls/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: Aatirs-Vault (https://skillmd.com/u/aatirs-vault)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aatirs-vault/voc-analyze-calls

---


# /voc-analyze-calls — Call Intelligence Report

Reads the transcripts `/voc-source-calls` wrote and produces one report answering: who is
showing up, what hurts, what stops them buying, who else they are looking at, and what they
asked for that you do not have.

The orchestration matters. A single context window cannot hold 60 transcripts at 40-80KB each,
and summarizing them one at a time loses exactly the cross-call patterns you are looking for.
So: classify cheaply in the main thread, fan out to subagents that each read a small batch in
full, and consolidate structured JSON back in the main thread.

## Requires

| What | Value |
|---|---|
| Input | `outputs/calls/index-*.json` and transcripts, from `/voc-source-calls` |
| Config | `config/thresholds.json`, `context/product-context.md` |
| Cost | Subagent tokens only. A 60-call week runs ~8-10 subagents. |

---

## Phase 1 — Select the calls worth reading

### 1.1 Load the index

Glob `outputs/calls/index-*.json`, pick the most recent, parse it. **Parse the JSON, not the
markdown table.** Assert that the number of rows you parsed equals `calls.length` in the file;
if it does not, stop and say so rather than analyzing a partial set.

### 1.2 Duration filter

Already applied by the fetcher using `call_filters.min_duration_seconds`. Report the count that
was dropped so the reader knows the denominator.

### 1.3 First pass — classify by title

Title patterns are free to evaluate and catch most internal and customer-success calls. Classify
as **CUSTOMER** (excluded from this report) when the title contains any of:

`weekly` · `biweekly` · `daily` · `standup` · `catch up` / `catchup` / `catch-up` ·
`check in` / `check-in` · `onboarding` · `kickoff` / `kick-off` · `setup` / `set up` ·
`training` · `QBR` · `dry run` · `rehearsal` · `support` · `escalation` · `renewal` ·
`retro` / `retrospective` · `sync` · `internal`

Add your own patterns to `context/product-context.md` under `customer_call_patterns` — every
company has house naming conventions, and the ones above are only a starting set.

Everything else stays **UNCLASSIFIED**.

**Edge case worth handling:** `follow-up` and `next steps` appear in both sales and delivery
contexts. Leave them unclassified and let the second pass decide.

### 1.4 Second pass — classify by content

For each UNCLASSIFIED call, read **only the Metadata, Participants, Summary and Key Points
sections** of the transcript file. Stop at `## Topics`. That is roughly the first 60-80 lines and
it is enough.

| Verdict | Signal |
|---|---|
| **PROSPECT** | Discovery, demo, pricing discussion, security review, "what does your product do", competitive comparison, procurement, first-time exploration |
| **CUSTOMER** | Existing implementation, configuration, an event or project already contracted, support, expansion into a new team |
| **INTERNAL** | Every participant is internal. Excluded and counted separately. |
| **UNREADABLE** | No summary, no key points, no transcript. Counted, not analyzed, and reported. |

Anything genuinely ambiguous goes to PROSPECT. A customer call misread as a prospect call adds
a little noise; a prospect call thrown away loses the signal you ran this for.

### 1.5 Batch

Split the PROSPECT list into batches of **6-8 transcripts**. That fits a subagent context with
room to reason. Cap total subagents at `cost_guards.max_parallel_subagents`; if the prospect
count would exceed it, raise batch size instead of dropping calls, and **say in the report that
you did** — silent truncation reads as full coverage.

Record the funnel: total in index → filtered short → internal → customer → unreadable → prospect.

---

## Phase 2 — Fan out

**Launch every subagent in a single message.** Sequential launches waste the entire benefit of
this architecture.

Give each subagent this prompt, with the batch's absolute file paths substituted:

````
You are analyzing customer-facing sales call transcripts. Read each of these files in full:

[ABSOLUTE FILE PATHS]

Aggregate across all calls in your batch. Do not summarize calls individually.

CRITICAL RULE: Only extract quotes and pain points from speakers marked [external].
Speakers marked [internal] are the sales team — their words are the pitch, not the
voice of the customer. If a transcript has no speaker labels, extract nothing from
it and list it under `unattributable`.

Return ONLY valid JSON, no prose before or after:

{
  "batch_id": 1,
  "calls_analyzed": ["title", ...],
  "unattributable": ["title of any call with no speaker labels", ...],
  "personas": [
    {"role": "...", "seniority": "IC|manager|director|vp|c-level",
     "org_type": "...", "industry": "...", "count": 1}
  ],
  "use_cases": [{"job_to_be_done": "...", "trigger": "...", "count": 1}],
  "pain_points": [
    {"pain": "...", "count": 1, "severity_signal": "blocker|friction|annoyance",
     "quote": "...", "company": "...", "date": "YYYY-MM-DD"}
  ],
  "objections": [
    {"objection": "...", "category": "price|timing|features|security|procurement|incumbent|trust",
     "count": 1, "how_rep_handled": "...", "landed": true}
  ],
  "competitive_mentions": [
    {"competitor": "...", "context": "incumbent|evaluating|switched_from|dismissed",
     "sentiment": "positive|negative|neutral", "what_was_said": "...", "count": 1}
  ],
  "feature_requests": [
    {"request": "...", "count": 1, "blocking_deal": true, "verbatim": "..."}
  ],
  "pricing_signals": [
    {"signal": "...", "budget_mentioned": "...", "reaction": "sticker_shock|neutral|value_accepted"}
  ],
  "quotes": [
    {"quote": "exact verbatim, external speaker only", "speaker_role": "...",
     "company": "...", "date": "YYYY-MM-DD", "why_notable": "..."}
  ]
}

Extraction guidance:
- pain_points: listen for "we currently", "the problem is", "what happens today is",
  "we've been doing this manually", "our current tool can't". Distinguish a blocker
  (deal dies without it) from friction (annoying, survivable).
- objections: capture whether the rep's answer actually landed. An objection that
  recurs after being handled is a messaging failure, not a sales failure — that
  distinction is the most useful thing in this whole report.
- competitive_mentions: `context` matters more than sentiment. "We're leaving Acme"
  and "We're also looking at Acme" are different intelligence.
- feature_requests: only things asked for that were NOT clearly available. A question
  about an existing feature is a discoverability problem, not a request — note those
  under pain_points instead.
- quotes: 2-4 per batch. Prefer quotes that reveal motivation, decision criteria, or
  a memorable objection over quotes that merely praise.
- If a call is a gatekeeper screen, a reschedule, or the prospect barely speaks,
  include the title in calls_analyzed with a "(low content)" suffix and extract nothing.
````

Wait for all subagents before Phase 3.

---

## Phase 3 — Consolidate

Merge the JSON. Rules that matter:

- **Group before you count.** "Registration is manual" and "no self-serve signup" are one pain.
  Merge on meaning, sum the counts, keep the strongest quote.
- **Never invent a count.** If three subagents each report a pain once, it is 3, not "common".
- **Sort by count descending**, then by severity. A blocker mentioned twice outranks an
  annoyance mentioned five times — note both numbers rather than collapsing them.
- **Recurring handled objections get flagged.** Any objection where `landed: false` appears more
  than once is a messaging gap and belongs at the top of the report, not buried in a list.
- **Deduplicate quotes** by company plus first eight words.

---

## Output

Write to `outputs/calls/analysis/call-intelligence-<YYYY-MM-DD>.md`.

```markdown
# Call Intelligence — week of <range>

## Coverage
| Stage | Count |
|---|---|
| Calls in index | N |
| Filtered: under <N>s | N |
| Excluded: internal only | N |
| Excluded: existing customers | N |
| Unreadable (no transcript or summary) | N |
| **Analyzed as prospect calls** | **N** |
| Subagents used | N |

<If anything was truncated or skipped, state it here in plain language.>

## The five things worth acting on
<Five bullets. Each names the pattern, the count, and what to do. Written for someone
who will read only this section. No preamble.>

## Who showed up
| Role | Seniority | Org type | Calls |
|---|---|---|---|

## What hurts
| # | Pain | Calls | Severity | Representative quote |
|---|---|---|---|---|

## What stopped them
| Objection | Category | Calls | Handled? | Note |
|---|---|---|---|---|

**Recurring after being handled:** <these are messaging gaps, list them explicitly>

## Who else they are looking at
| Competitor | Calls | Context | What was actually said |
|---|---|---|---|

## What they asked for that we do not have
| Request | Calls | Blocking a deal? | Verbatim |
|---|---|---|---|

## Pricing reactions
<Only if there is signal. Do not pad.>

## Quotes
> "verbatim"
> — Role, Company, YYYY-MM-DD · *why this is notable*

## What this report cannot tell you
<Honest limits for this specific run: how many calls had no speaker labels, whether
one large deal dominates the sample, which sources were unavailable.>
```

---

## What this gets wrong

State these in the report when they apply. They are the difference between intelligence and a
confident-sounding average.

- **Sales calls are a biased sample.** You hear from people who took a meeting. The ones who
  bounced off your pricing page are invisible here. Never describe these findings as "what the
  market thinks."
- **Counts are mention counts, not prevalence.** Five mentions of a pain in a week of 40 calls
  is a signal to investigate, not a 12.5% incidence rate.
- **Reps lead witnesses.** If every call surfaces the same pain, check whether the discovery
  script asks about it directly before treating it as organic.
- **Unlabelled speakers poison the well.** If a meaningful share of transcripts came back with
  no participant list, your quotes may include your own team. Report the number.
- **One loud account can dominate.** If a single company accounts for several calls, its
  concerns will look like a trend. Check the company spread before believing a top-ranked pain.

