← Command Center

Research Links UX + ICP Library

Version 1.0 Date 2026-03-09 Draft Owner MasteryMade Engineering

1. Problem Statement

Reveal Live's research links flow has friction that slows down the live demo moment:

Input friction:

Users must paste full URLs — but at a networking event you often only know "@username" or "FirstName LastName" not https://linkedin.com/in/john-smith-a1b2c3

Only one URL can be added per social platform — but prospects often have multiple presences (LinkedIn + podcast + company page + Substack)

There's no camera/OCR path — the prospect's profile is literally on the screen in front of you (their phone or yours) but you have to manually type/paste the URL

Data waste:

We do deep research (LinkedIn agent, Company agent, Social agent running in parallel) but the extracted intelligence is used once for page generation and discarded

There's no ICP library — if we later want to email this prospect, build a follow-up page, or reference their profile in a different template, we start from scratch

The psychographic deep-dive (dreams/fears/suspicions, communication style, implied needs) is computed but not persisted as a reusable contact profile

What's broken today:


2. Current State

What Exists

ComponentStatusLocation
Research links inputWorkingReveal Live wizard — URL text fields per platform
LinkedIn agentWorkingExtracts profile data from LinkedIn URL
Company agentWorkingExtracts company data from company URL
Social agentWorkingExtracts social presence from social URLs
Agent swarm (parallel)WorkingAll three agents run concurrently
CRM profilesWorkingcomponents/reveal/CRMProfileEditor.tsx — manual entry
ICP profilesWorkingcomponents/reveal/ICPProfileEditor.tsx — manual entry
Screenshot-to-textWorkingLinkedIn intel extraction from screenshots

What's Missing

ComponentImpact
Username-to-URL resolutionUsers must find and paste full URLs manually
Multi-URL per platformCan't add LinkedIn + LinkedIn company page + podcast
Camera/OCR for profile captureMust manually type what's visible on screen
Persistent contact/ICP libraryResearch intelligence discarded after run
Cross-run profile reuseStart from scratch on every new run for same prospect
Email funnel template injectionCan't pipe saved profiles into future templates

3. Research Links UX Improvements

3.1 Smart Input — URL or Username

The research link input field accepts EITHER format:

Full URL:    https://linkedin.com/in/john-smith-a1b2c3
Username:    @johnsmith
Name only:   John Smith
Handle:      linkedin.com/in/john-smith

Resolution logic (LLM-assisted):

async function resolveResearchInput(
  input: string, platform: string
): Promise<string> {
  // 1. Already a full URL? Return as-is.
  if (input.startsWith('http://') || input.startsWith('https://')) {
    return normalizeUrl(input, platform)
  }

  // 2. Partial URL (e.g., "linkedin.com/in/john")? Prepend https://
  if (input.includes('.com/') || input.includes('.io/')) {
    return 'https://' + input
  }

  // 3. Username or name — LLM resolves to full URL
  // LinkedIn: @johnsmith → https://linkedin.com/in/johnsmith
  // Twitter/X: @johnsmith → https://x.com/johnsmith
  // Instagram: johnsmith → https://instagram.com/johnsmith
  return await llmResolveProfileUrl(input, platform)
}

UI behavior:

3.2 Multi-URL Support

Current: One URL field per platform (LinkedIn, Company, Social).
New: Dynamic list — add as many URLs as needed, tagged by type.

┌── Research Links ───────────────────────────────┐ │ │ │ linkedin.com/in/john-smith [x] │ │ linkedin.com/company/acme-corp [x] │ │ johnsmith.substack.com [x] │ │ x.com/johnsmith [x] │ │ acme-corp.com/about [x] │ │ │ │ [+ Add link] │ │ │ │ Paste URL, @username, or name: │ │ [ ] │ └───────────────────────────────────────────────┘

Auto-tagging: When a URL is added, auto-detect the platform:

Agent routing: The agent swarm receives ALL URLs, grouped by type. LinkedIn agent gets all LinkedIn URLs (personal + company). Social agent gets all social platform URLs. Company agent gets all website/company URLs.

3.3 Camera/OCR Capture

Two capture modes:

Mode A: Screenshot capture (user's phone)
The user takes a screenshot of the prospect's profile on their own phone, then uploads it.

Mode B: Live camera capture (of client's phone/screen)
User points their phone camera at the prospect's phone/laptop showing their profile.

Implementation priority: Mode A first (screenshot upload). Mode B later (live camera is nice-to-have, lower accuracy).
async function extractProfileFromImage(
  imageBase64: string
): Promise<ResearchLink[]> {
  const response = await llm.analyze({
    model: 'claude-sonnet-4-6',
    messages: [{
      role: 'user',
      content: [
        {
          type: 'image',
          source: { type: 'base64', data: imageBase64 }
        },
        {
          type: 'text',
          text: `Extract all visible profile information:
            - Full name
            - Username/handle
            - Profile URL (if visible)
            - Platform (LinkedIn, X, Instagram, etc.)
            - Company name (if visible)
            - Job title (if visible)
            Return as JSON array of
            { url, platform, name, title, company }`
        }
      ]
    }]
  })
  return parseExtractedLinks(response)
}

4. Psychographic Deep-Dive — Confirm Flow

4.1 Current Pipeline (Already Working)

Research Links (URLs)

Agent Swarm (LinkedIn + Company + Social agents in parallel)

Extracted Intelligence (profile data, company data, social presence)

Page Generation (blueprint injects extracted data into template)

Published Page

4.2 What Gets Extracted Today

AgentExtracts
LinkedIn AgentName, title, company, headline, about summary, experience, skills, education, recommendations
Company AgentCompany name, industry, size, description, products/services, recent news, team
Social AgentSocial profiles, posting frequency, content themes, engagement style, audience

4.3 What SHOULD Happen (Psychographic Pass)

After the agent swarm extracts raw data, a psychographic synthesis pass should run:

Extracted Intelligence (raw agent output) → Psychographic Synthesis (LLM pass) →

ICP Contact Profile:

• Implied dreams (what they want but won't say)
• Implied fears (what keeps them up at night)
• Implied suspicions (what they've been burned by before)
• Communication style (formal/casual, data-driven/story-driven)
• Decision-making pattern (consensus/authority, fast/deliberate)
• Trigger phrases (language that resonates with their worldview)
• Anti-patterns (language that will make them tune out)
• DO/DON'T rules for engaging this specific person

This psychographic pass is essentially a per-contact CRM profile generated from research data rather than manually entered.

4.4 Confirm: This Already Partially Exists

The CRM Profile Editor (CRMProfileEditor.tsx) captures dreams/fears/suspicions/DO/DON'T manually. The new flow auto-generates this from research data and saves it persistently.


5. ICP Contact Library

5.1 Schema

New table: contact_profiles

CREATE TABLE IF NOT EXISTS contact_profiles (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES auth.users(id),
  team_id UUID REFERENCES teams(id),

  -- Identity
  full_name TEXT NOT NULL,
  email TEXT,
  company TEXT,
  title TEXT,
  linkedin_url TEXT,
  profile_image_url TEXT,

  -- Research links (all URLs used for research)
  research_links JSONB DEFAULT '[]',
  -- Format: [{ url, platform, added_at, extracted_data_summary }]

  -- Raw agent extraction (full output, for re-processing)
  raw_extraction JSONB,

  -- Psychographic profile (the synthesized intelligence)
  psychographic JSONB,
  -- Format: {
  --   implied_dreams: [],
  --   implied_fears: [],
  --   implied_suspicions: [],
  --   communication_style: "",
  --   decision_pattern: "",
  --   trigger_phrases: [],
  --   anti_patterns: [],
  --   do_rules: [],
  --   dont_rules: [],
  --   summary: ""
  -- }

  -- Usage tracking
  run_ids TEXT[] DEFAULT '{}',
  page_slugs TEXT[] DEFAULT '{}',
  last_researched_at TIMESTAMPTZ,

  -- Metadata
  tags TEXT[] DEFAULT '{}',
  notes TEXT,
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now()
);

-- Indexes for lookup
CREATE INDEX idx_contact_profiles_user
  ON contact_profiles(user_id);
CREATE INDEX idx_contact_profiles_team
  ON contact_profiles(team_id);
CREATE INDEX idx_contact_profiles_name
  ON contact_profiles(full_name);
CREATE INDEX idx_contact_profiles_email
  ON contact_profiles(email);

5.2 When Profiles Are Created/Updated

TriggerAction
Reveal Live run completesAuto-create or update contact profile from agent extraction
User manually creates CRM profileSave as contact profile
User imports from CSV/spreadsheetBatch create contact profiles
Re-run for same contactUpdate existing profile with new research data
Dedup logic: Match on linkedin_url first, then email, then full_name + company fuzzy match. If match found, update rather than create duplicate.

5.3 Contact Library UI

New section in Reveal dashboard (or sidebar):

┌── Contact Library ──────────────────────────────┐ │ Search: [ ] │ │ │ │ ┌─────────────────────────────────────────┐ │ │ │ John Smith · VP Sales · Acme Corp │ │ │ │ LinkedIn | 2 pages generated | Mar 8 │ │ │ │ Tags: prospect, networking-event │ │ │ ├─────────────────────────────────────────┤ │ │ │ Sarah Chen · Founder · TechStart │ │ │ │ LinkedIn | 1 page generated | Mar 7 │ │ │ │ Tags: jv-partner │ │ │ └─────────────────────────────────────────┘ │ │ │ │ [+ Add Contact] │ └───────────────────────────────────────────────┘

5.4 Profile Injection into Templates

When starting a new Reveal run, the user can:

  1. Select existing contact from the library → pre-fills research links + psychographic data
  2. "Use saved profile" checkbox → skips agent swarm (already researched), uses cached extraction
  3. "Re-research" option → runs agents again with saved links, updates profile

This means:

5.5 Cross-Template Reuse

The contact library enables new template types that reference the saved profile:

Template / FeatureUses Profile For
Reveal page (existing)Full psychographic-aware content generation
Avatar steelman review (new)Pre-publish copy review as the prospect — line-by-line reaction, edit suggestions
Email funnel (future)Personalized email sequence using dreams/fears/trigger phrases
Follow-up page (future)Post-meeting summary using saved research + new transcript
Proposal (future)Tailored proposal using company data + communication style
Conference recap (future)Multi-contact summary from event
The library IS the reusable asset. Do the research once, use it everywhere.

6. Avatar Editor — Pre-Publish Steelman Review

6.1 Why This Matters

The page IS the demo. If a single line doesn't land with the prospect, credibility breaks. The psychographic profile gives us everything we need to simulate how the prospect will actually read the page — before it goes live.

6.2 How It Works

After page generation and before publish, the system spins up an avatar editor — an LLM persona constructed from the contact's psychographic profile. This avatar reads the proposed page copy as the prospect would, and provides:

  1. Reaction audit — line-by-line reactions: what resonates, what feels generic, what triggers suspicion
  2. Language alignment — flags where the copy uses trigger phrases (good) vs anti-patterns (bad) from the profile
  3. Credibility check — identifies claims the prospect would challenge based on their known skepticism patterns
  4. Tone match — scores whether the copy matches their communication style (formal/casual, data/story-driven)
  5. Edit suggestions — specific rewrites that would land better with this person

6.3 Avatar Construction

The avatar is built from the saved psychographic profile:

Contact Profile (from ICP Library)
  ├── implied_dreams        → What they want to hear (lean into)
  ├── implied_fears          → What makes them defensive (avoid)
  ├── implied_suspicions     → What they'll fact-check first (substantiate)
  ├── communication_style    → How they process information
  ├── decision_pattern       → How they evaluate proposals
  ├── trigger_phrases        → Language that resonates
  ├── anti_patterns          → Language that repels
  ├── do_rules               → Engagement guidelines
  └── dont_rules             → Known dealbreakers

System prompt for avatar:

You are {full_name}, {title} at {company}.

Your communication style: {communication_style}
Your decision-making pattern: {decision_pattern}

Things you care about deeply: {implied_dreams}
Things that make you skeptical: {implied_suspicions}
Things you've been burned by before: {implied_fears}

Language that resonates with you: {trigger_phrases}
Language that turns you off: {anti_patterns}

Read the following page that was created for you. React honestly.
For each section, note:
- Does this feel like it was written FOR you or AT you?
- What would you challenge?
- What makes you want to keep reading?
- What makes you want to close the tab?

Be specific. Quote the exact lines that work or don't work.

6.4 Review UI Flow

Page Generation Complete

"Review as {Prospect Name}" button

Split View: Page preview (left) | Avatar feedback (right)

Avatar highlights sections with color-coded annotations:
Resonates — "This speaks directly to my situation"
Generic — "I've heard this before, doesn't stand out"
Triggers suspicion — "I'd fact-check this immediately"

Suggested Edits listed below with "Apply" / "Skip" per suggestion

Apply edits → re-render page → optional second avatar pass

Publish

6.5 Where the Contact Library Compounds

Without a saved profile, the avatar is constructed from whatever the agent swarm extracted during this run — good but shallow. With a saved profile that's been enriched across multiple interactions, the avatar is more accurate:

The contact library makes the avatar smarter over time. Each interaction adds signal.

6.6 Connection to Existing Steelman

The audit engine (lib/reveal/audit.ts) already does factual steelman — verifying claims against web sources and scoring confidence. The avatar editor is the psychographic steelman — verifying that the copy will land with this specific person. They're complementary:

LayerWhat It ChecksSource of Truth
Quote verificationAre quotes accurate?Source transcript
Factual auditAre claims true?Web search + LLM verification
Avatar reviewWill this person believe it?Psychographic profile

Three layers of steelman before publish.

Zero hallucination + factually verified + personally calibrated.


7. Implementation Sequence

Phase A: Smart Input + Multi-URL

Immediate UX win

  1. Replace single URL fields with dynamic multi-URL list
  2. Add URL/username/name resolution (LLM-assisted)
  3. Auto-detect platform from URL pattern
  4. Route all URLs to appropriate agents

Files: Research links component in Reveal Live wizard

No schema changes.

Effort: 3-4 hours

Phase B: Screenshot/Camera Capture (Mode A)

  1. Add camera/upload button to research links
  2. Send image to vision LLM for profile extraction
  3. Auto-populate research links from extracted data

Files: Research links component + new extraction utility

No schema changes.

Effort: 2-3 hours

Phase C: Contact Profile Persistence

  1. Create contact_profiles table (migration)
  2. After Reveal run, save extracted data + psychographic synthesis as contact profile
  3. Dedup logic (LinkedIn URL → email → name+company)
  4. Link run_ids and page_slugs for cross-reference

Files: New migration, new lib/contacts.ts, update run/route.ts

Effort: 3-4 hours

Phase D: Contact Library UI

  1. Contact library page/panel in Reveal dashboard
  2. Search, filter by tags, view profile details
  3. "Use for new run" action → pre-fills wizard

Files: New components/reveal/ContactLibrary.tsx, dashboard integration

Effort: 4-5 hours

Phase E: Avatar Editor + Steelman Review

  1. Build avatar system prompt from saved psychographic profile
  2. Split-view review UI: page preview (left) + avatar feedback (right)
  3. Color-coded annotations (resonates / generic / triggers suspicion)
  4. "Apply edit" / "Skip" per suggestion, re-render on apply
  5. Optional second avatar pass after edits

Files: New components/reveal/AvatarReview.tsx, update publish flow

Effort: 4-5 hours

Phase F: Profile Injection + Email Funnel Template

  1. "Select existing contact" in Reveal wizard
  2. Skip/cache agent swarm when using saved profile
  3. Email funnel template that uses psychographic data

Files: Wizard updates, new template

Effort: 4-6 hours (template writing is the bulk)


8. What We're NOT Building (Yet)

ExcludedWhy
CRM integration (HubSpot/Salesforce sync)Build the contact library first, integrate later when API demand exists
Bulk import from LinkedIn Sales NavigatorAPI restrictions make this unreliable
Auto-refresh profiles on scheduleProfiles don't change fast enough — re-research on demand is sufficient

9. Success Criteria