---
name: research
description: Fast parallel research protocol for prospect + sender research.
visibility: internal
---

# Research Protocol (Fast + Parallel)

This is the shared research flow for **all** active MCP workflows (generate-messages, enrich-prospects, create-campaign).
The orchestrator remains the main model (e.g., Opus). Use host-aware parallel execution:

- Claude host: `Task` subagents on Sonnet-4.5
- Codex host: `multi_tool_use.parallel` for independent tool calls

## Override Rules

1. If the user provides specific research instructions, treat them as a hard override.
2. Only use `mcp/sellable/skills/research/override.md` if it explicitly includes the marker `OVERRIDE: true`.
   - If the file is missing or only contains the template text, ignore it.
3. If `mcp/sellable/skills/research/config.json` exists, load it and override defaults below.

## Inputs

Provide as many as you have. Use `"Unknown"` for missing fields.
Do not assume workers can read history; explicitly interpolate these inputs into each parallel work unit.

- `name`
- `title`
- `companyName`
- `companyLinkedinUrl` (optional)
- `linkedinUrl`
- `companyDomain`
- `experienceSummary`
- `headline` (sender only)
- `positioningMode` (boolean)
- `researchMode` (optional: "sender" | "prospect", default: "prospect")
- `debugMode` (optional, default: true)
- `progressMode` (optional, default: true)
- `parallelMode` (optional, default: "wide")

## Execution Backend Routing (Host Capability)

Determine execution backend once per run:

1. If `Task` tool is available, use `task-subagents` backend.
2. Else if `multi_tool_use.parallel` is available, use `parallel-tools` backend.
3. Else use `sequential-tools` backend.

Backend rules:

- `task-subagents`: use the Task templates below as written.
- `parallel-tools`: map the same research units to direct tool calls and dispatch independent calls together with `multi_tool_use.parallel`.
- `sequential-tools`: run the same units one by one while preserving output schema.

Never claim Task subagents were launched when using tool-parallel or sequential execution.

## Optional Config (config.json)

If `mcp/sellable/skills/research/config.json` exists, read it before execution.
Supported keys (defaults in parentheses):

- `parallelMode` ("wide")
- `agentCount` (6)
- `maxToolCallsPerAgent` (2)
- `progressMode` (true)
- `debugMode` (true)

If `agentCount` < 6, drop agents in this order (first dropped first):

1. Reviews + Traffic
2. Company LinkedIn Snapshot
3. Jobs + Funding
4. Role Context
5. Homepage Snapshot
6. Post Analyzer

## Global Performance Rules

- For `task-subagents`: spawn **all 6 Sonnet agents in a SINGLE message** for `researchMode="prospect"` using `Task` with `model: "sonnet"`, `subagent_type: "general-purpose"`, and **NO `run_in_background`** (regular Task calls in one message run concurrently; background mode causes notification spam).
- For `researchMode="sender"`, delegate to the `research-sender` subskill (0-3 agents).
- For `parallel-tools`: run the same 6 research units (prospect) by batching independent tool calls with `multi_tool_use.parallel`. For sender, delegate to `research-sender`.
- For `sequential-tools`: run the same units sequentially.
- **CRITICAL: Launch ALL agents in one message.** Do not launch agents sequentially or wait for one before starting the next. The whole point is parallel execution.
- Tool budget: **max {maxToolCallsPerAgent} tool calls per agent** (default 2). If you hit the budget, stop and write `None found`.
- Prefer MCP tools over WebSearch/WebFetch when possible.
- Avoid duplicate WebFetch across agents (only the Homepage Snapshot agent should fetch the homepage).
- If inputs are missing or a tool fails, return `Unknown` / `None found` (do NOT add more tool calls).

## Mode Switching (sender vs prospect)

- `researchMode="sender"`: Delegate to `research-sender` subskill (enrichment-first, 0-3 agents). Do NOT use prospect agent templates.
- `researchMode="prospect"` (default): Use **Prospect Flow** (hooks + role context).

### Progress UX (no-dead-air)

If `progressMode=true`:

1. Print a one-liner before launching:

`Launching 6 research agents in parallel (posts, homepage, LinkedIn, role, growth, reviews)...`

2. **In the SAME message**, dispatch all 6 Task calls (no `run_in_background`). Multiple Task calls in one message run concurrently — all 6 launch simultaneously and results return together.

3. After all complete, print the synthesis.

If `researchMode="sender"`, delegate to `research-sender` subskill (it handles its own progress UX).

### Debugging Output

Each subagent must include a final line:

`DEBUG: tool_calls=[...]; skipped=[...]`

If `debugMode=false`, omit the DEBUG line.

## Prospect Flow (parallelMode="wide")

**CRITICAL: Launch ALL 6 agents in a SINGLE message (no `run_in_background`).** Multiple Task calls in one message run concurrently. All results return together when the last agent finishes. Do NOT use `run_in_background: true` — it causes notification spam.

```
Single message with 6 Task calls (all run concurrently):
├── Task Agent 1: Post Analyzer
├── Task Agent 2: Homepage Snapshot
├── Task Agent 3: Company LinkedIn
├── Task Agent 4: Role Context
├── Task Agent 5: Jobs + Funding
└── Task Agent 6: Reviews + Traffic

All 6 results return together → synthesize.
```

### Agent 1: Post Analyzer

```json
{
  "subagent_type": "general-purpose",
  "model": "sonnet",
  "run_in_background": false,
  "description": "Analyze posts for hooks",
  "prompt": "Analyze LinkedIn posts for {name}.\\n\\nIf linkedinUrl is missing or 'Unknown', do NOT call tools and return (omit DEBUG line if debugMode=false):\\n---\\nTOP THEMES: None found\\nRECENT FOCUS: None found\\nBEST HOOKS: None found\\nENGAGEMENT: None found\\nDEBUG: tool_calls=[]; skipped=[linkedinUrl missing]\\n---\\n\\nOtherwise, call mcp__sellable__fetch_linkedin_posts with:\\n- linkedinUrl: \\\"{linkedinUrl}\\\"\\n- limit: 6\\n\\nThen return EXACTLY (omit DEBUG line if debugMode=false):\\n---\\nTOP THEMES: [3 topics they post about most]\\nRECENT FOCUS: [What's top of mind from last 3 posts]\\nBEST HOOKS: [2 short topics or 1 short quote + 1 topic]\\nENGAGEMENT: [What type of content gets most reactions]\\nDEBUG: tool_calls=[fetch_linkedin_posts]; skipped=[]\\n---\\n\\nKeep under 120 words. Use at most one quote (<=12 words)."
}
```

### Agent 2: Homepage Snapshot

```json
{
  "subagent_type": "general-purpose",
  "model": "sonnet",
  "run_in_background": false,
  "description": "Homepage snapshot",
  "prompt": "Capture homepage claims and CTA for {companyName}.\\n\\nIf companyDomain exists, call WebFetch on https://{companyDomain}. Otherwise, do NOT search.\\n\\nReturn EXACTLY (omit DEBUG line if debugMode=false):\\n---\\nWEBSITE: [Key homepage claims + CTA or 'None found']\\nPOSITIONING_HINTS: [Product/category/offer if obvious, else 'Unknown']\\nPROOF: [Logos, metrics, testimonials or 'Unknown']\\nDEBUG: tool_calls=[WebFetch]; skipped=[]  (if WebFetch called)\\nDEBUG: tool_calls=[]; skipped=[companyDomain missing]  (if not called)\\n---\\n\\nKeep under 90 words."
}
```

### Agent 3: Company LinkedIn Snapshot

```json
{
  "subagent_type": "general-purpose",
  "model": "sonnet",
  "run_in_background": false,
  "description": "LinkedIn company snapshot",
  "prompt": "Get LinkedIn company info for {companyName} ({companyDomain}).\\n\\nStep 1: Determine the company LinkedIn URL.\\n- If companyLinkedinUrl is provided and not 'Unknown', use it directly.\\n- Otherwise, do ONE WebSearch query: \\\"site:linkedin.com/company {companyDomain}\\\"\\n- Extract the linkedin.com/company/... URL from search results. If not found, return 'None found' for all fields.\\n\\nStep 2: Call mcp__sellable__fetch_company with the LinkedIn URL.\\n\\nReturn EXACTLY (omit DEBUG line if debugMode=false):\\n---\\nLINKEDIN: [Notable company page info or 'None found']\\nHEADCOUNT_HINTS: [Any size/growth signals or 'Unknown']\\nDEBUG: tool_calls=[WebSearch, fetch_company]; skipped=[]  (if both called)\\nDEBUG: tool_calls=[fetch_company]; skipped=[]  (if URL was provided)\\nDEBUG: tool_calls=[WebSearch]; skipped=[no LinkedIn URL found]  (if search found nothing)\\n---\\n\\nKeep under 90 words."
}
```

### Agent 4: Role Context (no tools)

```json
{
  "subagent_type": "general-purpose",
  "model": "sonnet",
  "run_in_background": false,
  "description": "Analyze role context",
  "prompt": "Analyze context for this person with NO tools.\\n\\nName: {name}\\nTitle: {title}\\nCompany: {companyName}\\nExperience: {experienceSummary}\\nHeadline: {headline}\\n\\nReturn EXACTLY (omit DEBUG line if debugMode=false):\\n---\\nROLE\\nPRIORITIES: [Likely TODOs this week]\\nPAIN POINTS: [Common challenges for this role]\\nDECISION POWER: [What they can buy/approve]\\nCONVERSATION STARTERS: [2-3 relevant topics]\\nDEBUG: tool_calls=[]; skipped=[no tools allowed]\\n---\\n\\nKeep under 110 words."
}
```

### Agent 5: Jobs + Funding Signals (single search)

```json
{
  "subagent_type": "general-purpose",
  "model": "sonnet",
  "run_in_background": false,
  "description": "Jobs and funding signals",
  "prompt": "Gather growth signals for {companyName} ({companyDomain}).\\n\\nDo ONE WebSearch query: \\\"{companyName} jobs careers hiring funding series press release\\\".\\n\\nReturn EXACTLY (omit DEBUG line if debugMode=false):\\n---\\nHIRING: [Notable job titles/teams or 'None found']\\nFUNDING: [Round, amount, date or 'None found']\\nPRESS: [Recent announcement or 'None found']\\nDEBUG: tool_calls=[WebSearch]; skipped=[]\\n---\\n\\nKeep under 100 words."
}
```

### Agent 6: Reviews + Traffic (single search)

```json
{
  "subagent_type": "general-purpose",
  "model": "sonnet",
  "run_in_background": false,
  "description": "Reviews and traffic signals",
  "prompt": "Find reviews/traffic for {companyName}.\\n\\nDo ONE WebSearch query: \\\"{companyName} G2 reviews Capterra TrustRadius Similarweb traffic\\\".\\n\\nReturn EXACTLY (omit DEBUG line if debugMode=false):\\n---\\nREVIEWS: [G2/other review signal or 'None found']\\nTRAFFIC: [Traffic estimate source or 'None found']\\nDEBUG: tool_calls=[WebSearch]; skipped=[]\\n---\\n\\nKeep under 80 words."
}
```

## Sender Flow (parallelMode="wide")

**Sender research uses the dedicated `research-sender` subskill.**

When `researchMode="sender"`:

1. Load via `get_subskill_prompt({ subskillName: "research-sender" })`
2. Follow its instructions — it handles enrichment-first logic and conditional agent spawning
3. Do NOT use the prospect agent templates below for sender research

## Synthesize Results (Orchestrator)

After all agents return, combine into a short Research Notes block. **Strip DEBUG lines before final output.**

**If researchMode="sender":** Handled by `research-sender` subskill — do not synthesize here.

**If researchMode="prospect":**

If the caller passed cross-skill insights (from `~/.sellable/insights/cross-skill.md`), use them to prioritize hooks — e.g., if engage sessions show "AI compute time" angles get high engagement, rank those hooks higher.

```markdown
## {Name} - {Title} at {Company}

**Top of Mind:** [From post analysis]
**Company Context:** [From homepage + LinkedIn + reviews/traffic]
**Pain Points:** [From role analysis]

**Hooks to Use:**

- [Specific post quote or topic]
- [Recent company news angle]
- [Role-specific conversation starter]
- [Cross-skill insight angle, if relevant]

**Growth Signals:**

- [Hiring signal]
- [Funding signal]
- [Press signal]
```

If `positioningMode=true` and WEBSITE is not "None found":

- For **prospect mode**, append the Positioning Notes block below.
- For **sender mode**, populate the **Positioning Notes** line inside the sender summary using these fields.

```markdown
**Positioning Notes:**

- OFFER: ...
- TARGET: ...
- PAINS: ...
- PROOF: ...
- DIFF: ...
```
