Win/Loss Analysis at Scale with Claude Code, Step by Step
Date: July 6, 2026 • Author: Second Axis
Ask your CRM why you lose deals and it will tell you "price." Ask the call recordings and you'll hear something else: the champion went quiet after the security review, the buyer name-dropped a competitor's feature twice, the rep never reached the economic buyer. The dropdown says price because price is the polite answer, the one that closes the record without an argument. Meanwhile your roadmap gets defended in quarterly reviews using exactly that dropdown, and sales coaching runs on reps' own summaries of their own losses.
The evidence to fix this already exists. Every closed deal has CRM notes and, if your team records calls, two to four transcripts. Nobody rereads them, because rereading a quarter of closed deals is a week of work that produces an unrepeatable one-off. Can you build a system with Claude Code that does it continuously? Yes. The shared machinery, the map pass, agents on summaries, committed state, judge loop, is documented in the conversation intelligence playbook; this post won't re-explain it. What's specific here: joining two sources per deal, stated versus evidenced reasons, dollar weighted themes, and two cuts of one dataset.
What you're building
A rolling win/loss system over every closed deal in the CRM:
- Per deal, one structured record extracted from CRM notes and call transcripts together: the reason the rep stated, the reason the evidence supports, competitors named, price sensitivity, missing features, champion strength, verbatim quotes with speaker attribution.
- Themes, not anecdotes: reasons clustered across deals, each theme quantified by pipeline dollars, not deal counts. "Missing SSO cost us $840K across 6 deals" argues for roadmap in a way "6 deals mentioned SSO" never will.
- The stated versus evidenced delta, surfaced explicitly. Where the transcript contradicts the dropdown, the report says so, with the quote. This is the system's core value: analyzing your losses instead of your reps' stories about their losses.
- Representative deals linked from every theme, so a skeptical VP can click through to the actual deal and quote.
- A rolling report plus quarterly rollups, for two audiences: PMs defending roadmap decisions, and sales ops coaching against real failure patterns.
The math
Say you close 80 deals a quarter, each with around 3 recorded calls averaging 45 minutes. A 45 minute call transcribes to roughly 7,000-9,000 words, call it 10-12K tokens. Add CRM notes and logged emails, and one deal is a bundle of about 35-40K tokens. The quarter is therefore around 3 million tokens of source material, 15x a 200K context window, growing every quarter, forever.
But notice what the arithmetic also says: one deal fits comfortably in one context. That's the architectural gift here. Churn analysis needed SQL because no model should read 5M events; conversation intelligence needed a cheap model firehose for 27M weekly tokens. Win/loss sits in between: the unit of work is a deal, and 80 deals a quarter is a modest batch job. The architecture:
- A per deal map pass. Each closed deal's CRM record and transcripts are joined into one bundle and read once, by one model call, producing a small structured record.
- Clustering and dollar math on the records. Embeddings group the evidenced reasons into themes; a script sums deal amounts per theme.
- Agents read only theme summaries plus a handful of exemplar deal records, never the transcript pile.
Phase 0: Prerequisites
The technical list is short. The organizational one is the real gate, so it goes first.
- CRM read access, granted by whoever owns the CRM. Say it plainly: sales owns this data, and a PM cannot self-serve it. Someone, usually a sales ops admin, must grant read scopes on deals, notes, and activity history. This is a meeting, not a config file, and the system does not exist until it happens. The same conversation should set ground rules for stated versus evidenced reporting, because you're about to document when reps' loss reasons don't match the tape. Decide together, up front, whether per rep views exist at all.
- Transcript access. If calls live in Gong or Fireflies, you need API access or an admin-granted integration. Note the ecosystem reality: the CRM side has official MCP servers (HubSpot's hosted server or Salesforce's official CLI MCP with its
run_soql_querytool), but the call recording side currently runs on community servers (kenazk/gong-mcp, Props-Labs/fireflies-mcp). Community servers mean reading the code before trusting them with a sales-call token, or skipping MCP for bulk work and having Claude Code write export scripts against the vendors' documented APIs. For the pipeline, export scripts are right anyway; MCP earns its keep for interactive drill-downs. - Claude Code installed and authenticated, Pro/Max/Team/Enterprise for the scheduling and publishing steps, plus an API key with billing for the extraction pass (metered spend, same as the conversation intelligence build).
- Python 3.11+ with
duckdb,pandas,scikit-learn, and an embeddings option. - A dedicated git repo, because committed state is how a scheduled run knows what the last run concluded.
- A privacy decision. Transcripts contain customer names and commercial terms. The extraction pass sends them to the API; get the same sign-off you'd need for any vendor processing call data.
Phase 1: The repo and its state
win-loss/
├── CLAUDE.md
├── .claude/
│ ├── agents/
│ │ ├── theme-analyst.md # digs into one win/loss theme
│ │ ├── synthesizer.md # drafts the rolling report
│ │ └── critic.md # judge loop before publishing
│ └── skills/
│ └── win-loss/SKILL.md # the orchestration recipe
├── scripts/
│ ├── export_deals.py # closed deals since watermark → data/
│ ├── export_transcripts.py # matching calls → data/transcripts/
│ ├── extract.py # per deal Batch API pass → records
│ └── cluster_themes.py # embed, cluster, dollar-weight
├── state/ # committed
│ ├── watermark.json # last processed close date
│ └── themes.json # stable theme IDs, names, birth dates
├── data/ # gitignored: transcripts, raw exports
└── runs/ # committed: reports, extraction records
└── 2026-07-06/
Two design points carry the recurring behavior. state/watermark.json holds the latest close date already processed, so every run asks the CRM only for deals closed since then; processing is incremental by construction, whether the run fires monthly or hours after a deal closes. state/themes.json gives themes stable identity across runs, same discipline as the anchor playbook's cluster registry, and for the same reason: a trend line is worthless if "weak champion" is theme 4 in July and theme 9 in October.
The per deal extraction records live in runs/, committed: they're small, they hold the quotes the critic verifies, and every future rollup recomputes from them. Raw transcripts stay in data/, gitignored, entering context only during extraction and quote verification.
Phase 2: Per deal extraction (the two source join)
This is what makes win/loss different from other text pipelines: each unit of work is a join. export_deals.py pulls the closed deal's fields (amount, stage history, the rep's dropdown reason, notes). export_transcripts.py matches calls to the deal by CRM association or, failing that, by participant emails and date range; log every deal where matching fails, because silent transcript gaps skew everything downstream. extract.py concatenates the bundle and sends it through the Batch API with a schema like:
{
"deal_id": "0065f00000ABC",
"amount": 140000,
"outcome": "lost",
"stated_reason": "price",
"evidenced_reason": "missing SSO blocked security review; champion disengaged after",
"stated_vs_evidenced_match": false,
"competitor_named": "Vendor X",
"price_sensitivity": "mentioned_once_not_blocking",
"missing_features": ["SSO/SAML", "audit logs"],
"champion_strength": "weak_no_exec_access",
"quotes": [
{"speaker": "buyer_security_lead", "call": "2026-05-14",
"text": "until there's SAML this can't go past pilot",
"verbatim": true}
],
"evidence_confidence": "high",
"schema_version": 2
}
The pair at the top is the point: stated_reason comes from the CRM dropdown, evidenced_reason from the transcripts, and the extraction prompt must treat them as independent questions. Do not let the model harmonize them; the mismatches are the product. Force speaker attribution on quotes ("the buyer said" and "our rep said" are different evidence) and a verbatim flag so the critic can grep. evidence_confidence matters too: a deal with one short call and thin notes should say so rather than manufacture a confident reason.
One model note: volume is low and stakes per record are high, roughly 80 extractions a quarter that get quoted in roadmap fights. You can afford a mid tier model and still spend almost nothing. Hand-grade a sample against raw bundles before trusting any tier.
Phase 3: Clustering and dollar weighting
cluster_themes.py embeds each record's evidenced reason, missing features, and competitor mentions, assigns records to the stable themes in state/themes.json by distance, and holds out records that fit nothing until they form a candidate theme (same mechanics as the anchor playbook, smaller scale).
Then the step that makes the report land: weight every theme by pipeline dollars. Sum the amount of each theme's deals, split by outcome, rank by dollars, not count. Counts flatter frequent small deal patterns; dollars surface the pattern that killed the two enterprise deals. Each theme summary carries the total, deal count, trend versus prior period, mismatch rate, and links to 3-5 representative deal records. Those summaries, a few KB each, are what agents read.
Also compute one table no clustering produces: the contradiction matrix. Rows are stated reasons, columns are evidenced reasons, cells are dollars. "Deals stated as 'price': 62% show a different evidenced reason, $1.1M of it 'weak champion'" is the single most persuasive artifact this system generates.
Phase 4: The agents
Three roles, same pattern as the sibling playbooks.
theme-analyst.md takes one theme summary plus its representative deal records (extraction records, not transcripts) and answers: what actually happens in these deals, at what stage does the deal die, is this a product gap, a sales motion gap, or a segment we shouldn't chase? For competitor losses, it separates "named in passing" from "we lost a bake-off."
synthesizer.md drafts the rolling report: dollar ranked themes, the contradiction matrix, movement since last run, candidate themes flagged as provisional, and separate summary sections for the two audiences in Phase 6.
critic.md is the judge loop, and its checklist has two items specific to this workflow on top of the usual trend and freshness checks:
1. QUOTE VERIFICATION: every quote in the draft, grep it verbatim
against the transcript files in data/. Not found → FAIL.
Check speaker attribution matches the transcript's speaker labels.
2. STATED VS EVIDENCED CONSISTENCY: for every claimed mismatch,
confirm the extraction record actually has stated_vs_evidenced_match
= false AND evidence_confidence high enough to assert it. A wrongly
claimed contradiction accuses a rep of misreporting. Zero tolerance.
3. Recompute three theme dollar totals from the records.
4. Every theme links to deal records that exist in runs/.
Return PASS or FAIL with fixes.
Item 2 is not bureaucracy. This report tells a sales org its self-reported loss reasons are unreliable. It survives that message only if its own claims are bulletproof.
Phase 5: Scheduling and the rolling cadence
Two viable cadences, both wired the same way:
- Monthly (start here): a cloud routine via
/schedule, "first Monday, run /win-loss." The routine clones the repo, inheritsstate/and.claude/, processes deals closed since the watermark, and pushes results on aclaude/branch. Configure CRM and transcript API keys in the routine's environment variables; the cloud session has no access to your laptop's.env. - On close (later, if freshness earns it): routines can also be triggered by an API endpoint with a bearer token, so your CRM's workflow automation can ping the routine when a deal hits closed won/lost. The watermark makes this safe: however triggered, a run processes exactly the unprocessed deals. Keep the quarterly rollup as a separate skill invocation that reads a quarter's committed records; rolling views answer "what's happening," rollups answer "what did the quarter teach us," and they're different documents.
One explicit sentence on continuity, the part people get wrong: a scheduled run doesn't automatically carry forward what last month's run concluded. Continuity is engineered by committing state/ and runs/ and making "read the prior state" step one of the skill.
Self-hosted alternative: system cron running headless mode (claude -p "/win-loss" with --allowedTools scoped to the pipeline), or a GitHub Actions cron with the official claude-code-action. You control the environment; your call data also flows through whatever runs the job, which for many sales orgs decides the question.
Phase 6: Sharing with the team
One dataset, two deliberately different cuts:
- The PM cut: dollar ranked product gap themes, competitor loss detail, the aggregate contradiction matrix, representative quotes. Roadmap defense ammunition: "the top evidenced loss reason this half was X, worth $Y, here are five deals."
- The sales ops cut: champion strength patterns, stage-of-death analysis, coaching themes like "deals stall when we never reach the economic buyer," and whatever per rep visibility was agreed in Phase 0. Do not improvise that boundary in a published report.
Mechanics: Claude Code can publish each cut as a hosted artifact page with a stable URL, org scoped visibility on Team/Enterprise plans. The series-wide caveat applies: artifact publishing works from the CLI and desktop app, not from the Agent SDK or GitHub Actions, so a fully automated setup needs a human touch at publish time or a different channel. Slack delivery of top movers, or filing product gap themes into your tracker, covers the automated path.
Phase 7: Maintenance (what you own now)
- CRM schema changes. Sales ops renames a stage, adds a loss reason value, or restructures the pipeline, and
export_deals.pysilently exports garbage. Add a validation step: expected fields present, stage names in a known set, refuse to run otherwise. - Transcript coverage gaps. Reps skip recording, and your evidence base thins without notice. Track "deals with at least one matched transcript" as a first class metric in every report; when it dips, that's a management conversation, not a data bug.
- Theme taxonomy drift. Products and competitors change, and last year's themes stop carving reality at the joints. Candidate theme review is the weekly ten minutes; a deliberate re-cluster with ID remapping is the quarterly hour.
- Quarterly calibration against hand-read deals. Read 10 closed deals yourself, write down your own stated/evidenced/theme judgments, compare against the system's records. This is your extraction eval and the only defense against the prompt silently aging as your deal mix shifts. Disagreement above tolerance means retuning before the next rollup, not after.
- The access relationship. Tokens rotate, admins change, and the read scopes from Phase 0 are one reorg away from revocation. The system's real dependency is a working relationship with sales ops; maintain it like one.
What this costs
- Extraction is the driver, and it's modest here. Roughly 3M tokens a quarter through the Batch API (discounted about 50%) is an order of magnitude below the conversation intelligence workflow's weekly volume; even on a mid tier model this is small metered spend, single digit to low double digit dollars a quarter depending on tier. State your own assumptions: deals per quarter, calls per deal, transcript length. Volume scales with deals closed, which grows slowly.
- The analysis session reads theme summaries and a few exemplar records, comfortably inside subscription usage.
- Plan tier: routines and org scoped artifact sharing need the same Pro-and-up / Team-and-up tiers as the rest of this series.
- The real invoice: the Phase 0 negotiation, the schema you now steward, the quarterly hand-read calibration, and the standing duty of publishing a report that tells sales their dropdowns are wrong, accurately, every month. Cheap in tokens. Not cheap in ownership.
The four practical challenges
- Setup at scale: a per deal map pass over two joined sources, because a quarter of deals is 3M tokens but one deal fits in one context, plus stable theme identity and dollar weighting so the numbers survive quarter over quarter.
- Sharing with the team: two cuts from one dataset with a pre-negotiated visibility boundary, artifact publish from CLI/desktop only, and a contradiction matrix that must be right before it is loud.
- Maintenance: CRM schema validation, transcript coverage tracking, taxonomy drift, and a quarterly hand-read calibration that you, personally, will be tempted to skip.
- Cost: the cheapest workflow in this series to run and the most expensive to own, because its hardest dependency is organizational: read access to data your team doesn't own, and the standing credibility to report what it says.
The managed alternative
Win/loss has a true specialist: Clozd runs win/loss programs as a service, including buyer interviews your own transcripts can never give you (the buyer who ghosted isn't in your Gong). Call-intelligence platforms like Gong surface deal-risk and competitor mentions from the calls they record. The trades: a specialist program is a real budget line and a cadence of quarters, not weeks; call platforms only see the calls, so the CRM story and the stated-versus-evidenced gap this playbook centers on stays unexamined; and neither hands you a dollar-weighted theme table your PM team can pull into roadmap debates on demand.
Doing this with Second Axis
Everything above is the do-it-yourself path. On Second Axis, win/loss is a workflow in the marketplace: connect your CRM and call recorder, choose the workflow, and set the goal ("rolling dollar-weighted win/loss themes, with stated-versus-evidenced deltas, refreshed as deals close"). It runs incrementally with the two-source join, clustering, and dollar weighting built in, and it monitors itself: missing transcripts, revoked CRM scopes, or failed runs get found and alerted instead of quietly thinning the evidence.