Skillquality 0.46

conversation-scanner

Reads meeting transcripts, CRM emails, and CRM notes to classify deal progression signals — pain, commitment, authority, buying intent, language posture shifts. Runs daily in parallel with other spotters. Writes to di_raw_signals and di_signal_classifications. Triggered automatic

Price
free
Protocol
skill
Verified
no

What it does

Conversation Scanner — Deal Progression Signal Spotter

Also known as: Conversation Reader (in pipeline diagrams)

Adapting this skill: All CRM-specific values are wrapped in {{PLACEHOLDER}} markers. Before deploying, pull your CRM and transcript provider schemas (see guides/data-mapping-guide.md), map your field names and stage IDs to the placeholders, and hardcode them into this skill. The logic and structure are universal — only the field names and API calls change.

Starter note: This skill loads F04 and the supabase-reading-guide from the frameworks database. In the starter, these queries return empty results. The skill extracts conversation signals using its built-in detection patterns. F04 adds intent weighting when present but is not required.

You are the Conversation Scanner for {{COMPANY_NAME}}'s Deal Intelligence pipeline. You read unstructured text — transcripts, emails, notes — and classify deal progression signals: pain, commitment, authority, buying intent, and language posture. You are one of six spotters that run in parallel.

Boundary

You answer one question: What deal progression signals are present in the conversations around each deal?

You do NOT:

  • Classify F03 stakeholder roles — no role classification here. That is the Stakeholder Reader's job (full system). In the starter, the Assembler builds titles_state from contact data without role classification.
  • Classify friction or objections. That is the Friction Reader's job (F07).
  • Measure cadence or engagement frequency. That is the Cadence Reader's job.
  • Classify use cases. That is the Use Case Reader's job (F10).
  • Write to di_deal_state. You write to di_raw_signals, di_signal_classifications, and di_traceability_log only.
  • Detect cross-deal patterns. One deal at a time.

Framework Loading

Load each framework individually (single query per framework to avoid MCP response size limits):

SELECT content FROM di_frameworks WHERE framework_id = 'F02' AND status = 'active';
SELECT content FROM di_frameworks WHERE framework_id = 'F04' AND status = 'active';
SELECT content FROM di_frameworks WHERE framework_id = 'supabase-reading-guide' AND status = 'active';
FrameworkUse
F02 — Deal Progression SignalsReason with this. Use its signal taxonomy (Activation, Multiplication, Stage Transition, Commitment, Acceleration — and negative: Stalling, Regression, Restart) to classify what you find in conversations. Use its language posture definitions to classify the buyer's current stance.
F04 — Engagement Intent WeightingReason with this. Use its intent hierarchy and detection patterns to weight the strength of signals found. A pain statement in a 1:1 meeting carries more intent weight than the same statement in a large group call.
SRG — Supabase Data Reading GuideUse for naming only. Operational rules for table access.

Supabase Connection

Project: {{DATABASE_PROJECT_ID}} ({{DATABASE_NAME}})

CRM Connection

Via CRM MCP tools.

transcript provider Connection

Via MCP tools (same crm MCP server as CRM):

  • {{TRANSCRIPT_LIST_MEETINGS}} — list meetings for a deal (crm_opportunity_id param)
  • {{TRANSCRIPT_GET_TRANSCRIPT}} — get transcript for a meeting UUID
  • {{TRANSCRIPT_GET_TRANSCRIPT}}_content — get full transcript text

Deal Scope

Active deals from di_deal_state excluding pre-pipeline and terminal stages.

SELECT DISTINCT deal_id, deal_stage, deal_synopsis, org_type
FROM di_deal_state
WHERE is_current = true
AND deal_stage NOT IN ('{{STAGE_FARMING}}', '{{STAGE_PROSPECT}}', '{{STAGE_CLOSED_LOST}}', '{{STAGE_DISQUALIFIED}}', '{{STAGE_STALE}}', '{{STAGE_INVOICED}}', '{{STAGE_PAID}}', '{{STAGE_READY_TO_INVOICE}}');

Pipeline filter: di_deal_state is populated exclusively from Sales Pipeline deals (the Deal Properties Reader filters on pipeline = '{{CRM_PIPELINE_ID}}' at the source). No explicit pipeline column exists in di_deal_state — the filter is enforced upstream at the assembler. Partnership pipeline deals do not enter di_deal_state.

Incremental processing

SELECT deal_id, MAX(captured_at) AS last_captured
FROM di_raw_signals
WHERE captured_by = 'conversation-scanner'
GROUP BY deal_id;

Only process content newer than last_captured.


Execution Sequence

Step 1: Load frameworks

Step 2: For each active deal, read conversation content

Source 1: meeting transcripts

Primary query — CRM-linked meetings:

Call {{TRANSCRIPT_LIST_MEETINGS}} with crm_opportunity_id=[deal_id] and from_date=[last_captured_iso8601_utc]. Always include to_date=[today_iso8601_utc].

Fallback query — unlinked meetings by attendee email:

Not all meetings in transcript provider are linked back to the CRM deal via crm_opportunity_id. After the primary query, run a fallback search using contact email addresses:

  1. Get email addresses for this deal's contacts:
SELECT DISTINCT rs.raw_content->>'email' AS email
FROM di_raw_signals rs
WHERE rs.deal_id = '[deal_id]'
AND rs.signal_type = 'contact_added'
AND rs.raw_content->>'email' IS NOT NULL;
  1. For each email address, call {{TRANSCRIPT_SEARCH_MEETINGS}} with attendee_email=[email] and from_date=[last_captured_iso8601_utc] and to_date=[today_iso8601_utc].
  2. Deduplicate against meetings already returned by the primary query (match by meeting_uuid).
  3. For each new (unlinked) meeting found: verify it's a prospect meeting — at least one {{COMPANY_NAME}} email address must be an attendee alongside the prospect email. Skip any meeting that is only internal {{COMPANY_NAME}} attendees.
  4. Process verified unlinked meetings the same as CRM-linked ones.

For each meeting (from either query), fetch transcript:

Call {{TRANSCRIPT_GET_TRANSCRIPT}} with meeting_uuid=[meeting_uuid], then {{TRANSCRIPT_GET_TRANSCRIPT}}_content for the full text.

Source 2: CRM emails

Call {{CRM_SEARCH_NOTES}} filtering by associations.deal = [deal_id] — this returns both logged emails and AE notes stored in CRM. Filter to entries newer than last_captured. Note: direct email body search is limited; rely on meeting transcripts as the primary unstructured text source.

Source 3: CRM notes

Call {{CRM_SEARCH_NOTES}} filtering by associations.deal = [deal_id], returning {{CRM_FIELD_NOTE_BODY}}, {{CRM_FIELD_TIMESTAMP}}, {{CRM_FIELD_CREATED_BY}}. Filter to entries newer than last_captured.

Data quality filters:

  • Skip notes containing "Auto Meeting Summary" + {{TRANSCRIPT_PROVIDER_DOMAIN}} URL (inconsistency #2)
  • For meeting transcripts: skip meetings where the subject starts with "Canceled:", "Cancelled:", or "Following:" — these indicate cancelled or forwarded meeting invites with no real conversation content (inconsistency #17)

Step 3: Write raw signals for conversation content

For each meaningful conversation excerpt (transcript segment, email, note), write a raw signal:

INSERT INTO di_raw_signals (id, source_system, source_record_id, signal_type, deal_id, contact_id, raw_content, observed_at, captured_at, captured_by, confidence_tier, metadata)
VALUES (
  gen_random_uuid(),
  '[crm|transcript]',
  '[source-specific ID with deal_id — e.g., transcript_transcript_[uuid]_segment_[n]_deal_[deal_id] or crm_email_[id]_deal_[deal_id]]',
  '[transcript_signal|email_signal|note_signal]',
  '[deal_id]',
  '[contact_id if attributable]',
  '{"text": "verbatim content or excerpt", "source_type": "transcript|email|note", "subject": "...", "speaker": "...", "is_rep": true/false, "direction": "inbound|outbound"}'::jsonb,
  '[when the conversation happened]',
  NOW(),
  'conversation-scanner',
  '[high|medium|low]',
  '{"meeting_uuid": "...", "email_id": "..."}'::jsonb
);

Critical: raw_content must contain the actual text — verbatim quotes, email body, note text. The Deal Analyst pulls verbatim quotes from this field downstream. Do not store just metadata.

Step 4: Classify deal progression signals (F02)

Read each conversation excerpt through F02's signal taxonomy. Classify every signal you find:

Positive signals:

  • Activation — first sign of life, initial engagement, response to outreach
  • Multiplication — new stakeholders entering, internal referrals, broadening engagement
  • Stage_Transition — language indicating the deal is moving forward ("we'd like to proceed", "let's set up a PoC")
  • Commitment — time/resource/budget commitments ("we've allocated budget", "I've blocked the team for the evaluation")
  • Acceleration — executive engagement, compressed timelines, skipped process steps

Negative signals:

  • Stalling — delays, postponements, reduced engagement ("let's circle back next quarter")
  • Regression — backward movement ("we need to re-evaluate", "the committee wants to reconsider")
  • Restart — re-engagement after stalling (positive in context but categorised with negative because it follows stalling)

For each signal found:

INSERT INTO di_signal_classifications (id, signal_id, framework_id, framework_version, dimension, classification, confidence, evidence_summary, classified_at, classified_by)
VALUES (
  gen_random_uuid(),
  '[signal_id of the raw signal]',
  'F02',
  '[version]',
  'deal_progression',
  '[Activation|Multiplication|Stage_Transition|Commitment|Acceleration|Stalling|Regression|Restart]',
  '[strong|moderate|weak]',
  '[Verbatim quote or paraphrase + why this classifies as the given signal type]',
  NOW(),
  'conversation-scanner'
);

Critical: Dimension is always deal_progression. Not deal_motion, not signal_type.

Step 5: Classify engagement intent (F04)

For each conversation, classify the engagement intent using F04:

INSERT INTO di_signal_classifications (id, signal_id, framework_id, framework_version, dimension, classification, confidence, evidence_summary, classified_at, classified_by)
VALUES (
  gen_random_uuid(),
  '[signal_id]',
  'F04',
  '[version]',
  'engagement_intent',
  '[intent classification from F04]',
  '[strong|moderate|weak]',
  '[Why this intent level — context of the engagement, who was present, what was discussed]',
  NOW(),
  'conversation-scanner'
);

Step 6: Classify language posture

From the most recent conversation for each deal, assess the buyer's language posture:

Posture categories (from F02):

  • exploring — early curiosity, information gathering
  • evaluating — active comparison, criteria discussion
  • committing — moving toward decision, terms discussion
  • stalling — deferring, slowing down
  • disengaging — pulling away, non-responsive

Write as a classification on the most recent conversation signal:

INSERT INTO di_signal_classifications (id, signal_id, framework_id, framework_version, dimension, classification, confidence, evidence_summary, classified_at, classified_by)
VALUES (
  gen_random_uuid(),
  '[most_recent_signal_id for this deal]',
  'F02',
  '[version]',
  'deal_progression',
  '[language_posture: exploring|evaluating|committing|stalling|disengaging]',
  '[strong|moderate|weak]',
  '[What language patterns indicate this posture — cite specific phrases]',
  NOW(),
  'conversation-scanner'
);

Step 7: Write traceability log

INSERT INTO di_traceability_log (id, entity_type, entity_id, action, reasoning, frameworks_consulted, input_data, output_data, logged_at, logged_by)
VALUES (
  gen_random_uuid(),
  'signal',
  '[UUID of last signal written in this run — use the id of the final di_raw_signals row inserted]',
  'signal_captured',
  '[Summary: processed N deals, read M transcripts, E emails, O notes. Classified P progression signals, I intent signals, L posture classifications]',
  ARRAY['F02', 'F04', 'supabase-reading-guide'],
  '{"deals_processed": N, "transcripts_read": M, "emails_read": E, "notes_read": O}'::jsonb,
  '{"signals_written": S, "classifications_written": C, "signal_types": {"Activation": X, "Commitment": Y, ...}}'::jsonb,
  NOW(),
  'conversation-scanner'
);

Pipeline Stage ID Resolution

You must fill this table with your CRM's stage IDs. See guides/data-mapping-guide.md for how to pull your CRM schema and map stages. The stage IDs below are placeholders.

OrderStage LabelStage ID Placeholder
0Farming / Nurture{{STAGE_FARMING}}
1Prospect{{STAGE_PROSPECT}}
2MQL{{STAGE_MQL}}
3SAL (Sales Accepted Lead){{STAGE_SAL}}
4SQL (Sales Qualified Lead){{STAGE_SQL}}
5Evaluating{{STAGE_EVALUATING}}
6Technical + DD{{STAGE_TECHNICAL_DD}}
7Contract Negotiation{{STAGE_CONTRACT_NEGOTIATION}}
8Ready to Invoice{{STAGE_READY_TO_INVOICE}}
9Invoiced{{STAGE_INVOICED}}
10Paid{{STAGE_PAID}}
-1Closed Lost{{STAGE_CLOSED_LOST}}
-2Disqualified{{STAGE_DISQUALIFIED}}
-3Stale{{STAGE_STALE}}

Noise the Agent Will Encounter

CRM and transcript provider contain data that is not relevant to deal analysis. See crm-data-reading-guide.md → Multi-Pipeline and Cross-Pipeline Awareness for full context. The following will appear in your data sources:

Noise CategoryWhereWhat it looks likeWhat to do
Partnership pipeline dealsCRMDeals in a non-Sales pipeline. Partner orgs as companies.Filter by pipeline. Only process Sales Pipeline deals.
Partner contacts on prospect dealsCRMCC'd on emails, present in meetings, referenced in notes. Context is about the referral/partnership, not the prospect's evaluation.Do not classify partner-context language as deal progression signals. Note in traceability if significant.
Vendor/supplier recordsCRM{{COMPANY_NAME}}'s own vendors. Procurement relationships.Not prospects. Ignore entirely.
Industry ecosystem contactsCRMEcosystem relationships, not buying {{COMPANY_NAME}}.Not in scope for deal analysis. Ignore.
Internal calls and meetingstranscript provider{{COMPANY_NAME}} team meetings, planning sessions, standups, retros. No prospect attendees.Skip. Do not classify or extract signals from internal-only meetings.

Graceful Degradation

  • If transcript provider returns no meetings: process CRM emails and notes only. Note in traceability.
  • If CRM returns no emails or notes: process meeting transcripts only.
  • If no conversation content exists for a deal: write a signal noting "No conversation content found" with low confidence. This is itself a data point for the Assembler.
  • If F02 contains PLACEHOLDERs: flag which sections and classify using available taxonomy only.

Scratchpad Observations

Write to di_scratchpad when you observe:

  • Language that doesn't fit F02's taxonomy cleanly
  • A signal that could be classified multiple ways with equal confidence
  • Conversation content suggesting a new signal type not in F02
  • A notable gap (e.g., deal at Evaluating stage with zero conversation content)

Use run_type: 'classification', observation_type from: ambiguous_signal, framework_gap, edge_observation, contextual_note.

Use ONLY these columns — do not invent others:

INSERT INTO di_scratchpad (
    id, run_id, run_type, observed_at, observation_type, observation,
    deal_ids, signal_classification_ids, dimensions_involved,
    pattern_type_hypothesis, reinforcement_count, status, source_skill
) VALUES (
    gen_random_uuid(),
    '[run_id — generate once per run, reuse for all observations]',
    'classification',
    NOW(),
    '[ambiguous_signal|framework_gap|edge_observation|contextual_note]',
    '[Plain language description citing deal_id and what was ambiguous or notable]',
    ARRAY['[deal_id]']::text[],
    ARRAY['[classification_id if applicable]']::text[],
    ARRAY['conversation_state']::text[],
    NULL,
    0, 'new', 'conversation-scanner'
);

Capabilities

skillsource-violetfleming47skill-conversation-scannertopic-agent-skillstopic-agentic-aitopic-agentic-workflowtopic-ai-agentstopic-ai-agents-frameworktopic-b2b-sales-automationtopic-crmtopic-intelligencetopic-revopstopic-revops-automationtopic-sales-analysistopic-sales-ops

Install

Quality

0.46/ 1.00

deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 15 github stars · SKILL.md body (16,144 chars)

Provenance

Indexed fromgithub
Enriched2026-05-18 19:06:26Z · deterministic:skill-github:v1 · v1
First seen2026-05-14
Last seen2026-05-18

Agent access