02-requirements.md
Scout Leadership Intel — Spec
Generated: 2026-05-05
Problem Statement
Account executives and BDRs doing pre-call research must manually navigate 2–4 pages of a company's website to find decision-makers — a repetitive, time-consuming task that interrupts sales workflow. There is no single command to ask "who leads this company?" and get a structured, sourced answer. The consequence is either skipped research (entering calls underprepared) or wasted time (5–10 minutes per company across dozens of accounts).
Research Sources + Technical Context
Source: Scout skill file at ~/.claude/commands/scout.md and Scout API source at /Users/arijitchowdhury/AI-Development/Scout/
Full research notes: docs/workspace/scout-leadership-intel/00-research.md
Key capabilities leveraged
Scout exposes 5 POST endpoints at http://localhost:8421, all authenticated via X-API-Key: dev-key:
| Endpoint | Use in this feature |
|---|---|
/health |
Pre-flight check before any call |
/map |
Discover all URLs on a company domain (returns urls: list[str]) |
/scrape |
Fetch a single page as markdown or raw HTML |
/extract |
Structured extraction via CSS schema or LLM instruction |
Core integration pattern (Map → Filter → Scrape):
1. POST /map with {"url": "https://company.com", "max_pages": 200} → get full URL list
2. Filter urls[] for leadership patterns: /team, /leadership, /management, /executives, /about/team, /about/leadership, /company/leadership
3. POST /scrape with {"url": "<best_match>", "use_js": true} → get markdown content
4. If markdown is sparse: retry with {"formats": ["raw_html"]} and parse name/title card patterns from HTML
Key constraints discovered in research:
- Scout is single-instance — no parallel crawls
- Cannot access authenticated or login-gated pages
- ATS SPA job boards (Workday, Greenhouse) fail — not relevant here, but same JS-interception gap could affect some team pages
- /extract with LLM requires LLM_API_KEY in Scout .env; fallback is css_schema mode
- Real-world eval (Algolia.com): 75% pass rate on exec extraction; raw_html fallback is the reliable path for JS-heavy card layouts
- Leadership page URLs vary wildly across companies — map-first is mandatory
- Timeout: /scrape default 30s, configurable via timeout_ms; /map up to 60s
Data model for exec output (from Scout skill):
## Executive Team — [Company]
| Name | Title |
|---|---|
| Jane Smith | CEO |
Response contract:
- ScrapeResponse.markdown: str — primary extraction target
- ScrapeResponse.raw_html: str — fallback for JS-card pages
- ScrapeResponse.links: list[str] — for LinkedIn URL harvesting
- ScrapeResponse.metadata.crawled_at — source timestamp for attribution
- ScrapeResponse.success: bool + error: str — for failure reporting
- ScrapeResponse.duration_ms: int — for latency tracking
Personas
Persona A — Account Executive (AE) - Job-to-be-done: Before a discovery call, identify the economic buyer, champion, and technical decision-maker at a target account - Pain: Spends 5–10 minutes per account manually navigating websites; results are inconsistent quality - Critical need: Title precision — "VP of Engineering" vs "SVP Engineering" matters for call strategy
Persona B — Business Development Representative (BDR) - Job-to-be-done: Build prospecting lists quickly; find any named executive to cold-reach at scale across many companies per day - Pain: Manual research doesn't scale across a large account list - Critical need: Volume — more names across more companies per hour, even with lower precision on titles
Value Props per Persona (Features-for-Whom)
Persona A — AE: | Feature | Value delivered | |---|---| | Map → Scrape → structured table | Get decision-maker names and titles from the authoritative source (the company's own website) in one command, not 5 clicks | | Source attribution on every result | Know whether to trust the data — "sourced from apple.com/leadership at 2026-05-05" vs "fallback: press release" changes call prep | | Partial results with fallback note | Still get something useful even when the primary page fails; AE can judge whether to dig further | | Error message with URLs tried | When it genuinely fails, know what was attempted so manual investigation has a starting point |
Persona B — BDR: | Feature | Value delivered | |---|---| | Single-command exec list | Research 10+ companies per hour instead of 3; scales prospecting velocity | | Name + title table (any source) | Any executive name beats zero names for cold outreach; source label tells BDR if they should verify first | | Written output file | Leave result files in workspace; reference later without re-running |
Functional Requirements
Must-Have (MVP)
- The skill shall accept a company domain (e.g.,
apple.com) as input and return a leadership table with at minimum: name, title, and source attribution - When a domain is provided, the skill shall call Scout
/healthto verify the service is running before any other API call - When Scout is unreachable, the skill shall return a clear error message: "Scout is not running at localhost:8421. Start it with:
cd /Users/arijitchowdhury/AI-Development/Scout && python3 -m uvicorn scout.api.main:app --host 0.0.0.0 --port 8421" - The skill shall call
/mapon the domain first (max_pages: 200) to discover the URL structure — it shall not guess or hardcode leadership URL patterns without mapping first - When
/mapreturns a URL matching leadership patterns (/team,/leadership,/management,/executives,/about/team,/about/leadership,/company/team,/company/leadership,/company/management), the skill shall scrape that URL - When the primary scrape returns sparse markdown (fewer than 100 words), the skill shall retry with
formats: ["raw_html"]and parse name/title card data from the HTML - The skill shall output a markdown table with columns: Name, Title, Source
- The skill shall label every row's source as either the page URL (if scraped from leadership page) or "Fallback: [source]" (if extracted from press release or other non-authoritative page)
- When no leadership page is found after mapping, the skill shall return: "No leadership page found for [domain]. URLs tried: [list]" — it shall not return an empty result silently
- When a partial result is available (execs found from a non-primary source), the skill shall return those results with a visible warning: "WARNING: data sourced from [fallback source], not a dedicated leadership page — may be incomplete or stale"
- The skill shall write output to
{company-slug}-leadership.mdin the current workspace directory - The skill shall print elapsed time and source URL in the output footer
- The total skill execution time shall target under 60 seconds for p90 of runs
Should-Have (fast-follow)
- When a LinkedIn URL is found in
ScrapeResponse.linksmatching the executive's name context, the skill shall include it as an optional fourth column - The skill shall support a company name as input (e.g., "Apple") in addition to a domain, resolving it to a domain via a simple search heuristic
- The skill shall support a
--rawflag to output JSON instead of markdown table (for programmatic consumption)
Nice-to-Have (future)
- Batch mode: accept a list of domains and run sequentially, producing one file per company
- Short bio extraction (1–2 sentences per executive)
- Confidence score per executive row (leadership page = high, press release = medium, inferred = low)
- Cron/scheduled batch runs against a pipeline list
UX Flow
Entry Point
Two invocation paths:
1. Slash command: /scout-leadership apple.com
2. Natural language in session: "get me the leadership team for Apple" → Claude routes to this skill
Primary Flow (happy path)
User: /scout-leadership apple.com
│
▼
[1] Health check — GET /health
→ if DOWN: print startup command, exit
│
▼
[2] Map domain — POST /map {"url": "https://apple.com", "max_pages": 200}
→ get urls[] list (may take 10–45s)
│
▼
[3] Filter urls[] for leadership patterns
→ rank: /leadership > /executives > /team > /management > /about/* variants
→ if no match: log "no leadership URL found", proceed to fallback
│
▼
[4a] Scrape best-match URL — POST /scrape {"url": "...", "use_js": true}
→ if markdown word_count < 100: go to [4b]
→ if scrape fails (success=false): go to fallback
│
[4b] Retry with raw_html — POST /scrape {"url": "...", "use_js": true, "formats": ["raw_html"]}
→ parse HTML for card patterns (h3/h4 names, role/title spans)
│
▼
[5] Structure output
→ Build markdown table: | Name | Title | Source |
→ If LinkedIn links found in ScrapeResponse.links: add | LinkedIn |
→ Attach source label per row
│
▼
[6] Write file: ./{company-slug}-leadership.md
│
▼
[7] Print to chat:
## Leadership Team — Apple (apple.com)
| Name | Title | Source |
|------|-------|--------|
| Tim Cook | CEO | apple.com/leadership |
...
Source: https://www.apple.com/leadership/
Scraped: 2026-05-05T14:32:00Z | Duration: 38s
Fallback Flow (no leadership page found)
[3] Filter returns no match
│
▼
[F1] Attempt common fallbacks: /about, /company, /about-us
→ scrape and scan for executive mentions
│
▼
[F2] If execs found in fallback:
→ return table with WARNING banner: "Data from [fallback URL], not a dedicated leadership page"
│
▼
[F3] If nothing found:
→ return: "No leadership page found for apple.com. URLs checked: [list]. Try: apple.com/leadership manually."
Back Navigation / Dead Ends
- If Scout is DOWN: exit immediately with startup instructions — no retry loop
- If
/maptimes out (>60s): return error, suggest--max-pages 50for faster mapping - If scrape returns 403: note bot detection, suggest manual verification
- No infinite retry loops — maximum 2 attempts per URL (primary + raw_html fallback)
Acceptance Criteria
Health check:
- When the skill is invoked, the system shall call GET http://localhost:8421/health before any other Scout API call
- If Scout returns a non-200 response or connection refused, then the system shall display the startup command and exit without making further API calls
Map-first enforcement:
- The system shall call /map on the target domain before attempting any /scrape call
- If /map returns success: false, then the system shall log the error and attempt scraping fallback URLs in priority order
Source attribution:
- The system shall label every executive row's source with the URL it was scraped from and the crawled_at timestamp
- If data is sourced from a non-leadership page, then the system shall display a WARNING label distinguishing it from authoritative leadership page data
Failure transparency: - If no leadership page is found after mapping and all fallback attempts, then the system shall return a list of all URLs attempted — it shall not return an empty result or generic error - While partial results are available (some execs found, leadership page incomplete), the system shall return those partial results with an explicit completeness warning
Output format:
- The system shall always write results to {company-slug}-leadership.md in the current working directory
- The system shall always print results to the chat session (markdown table format)
Performance:
- The system shall complete the full Map → Scrape → Extract pipeline in under 60 seconds for p90 of runs against Fortune 500 company domains
- Where Scout returns duration_ms in the response, the system shall display total elapsed time in the output footer
JavaScript rendering:
- When the initial /scrape returns markdown with fewer than 100 words, the system shall retry using formats: ["raw_html"] and parse the HTML for executive card patterns
- The system shall always include "use_js": true in all /scrape calls to handle JS-rendered pages
Data minimalism: - The system shall only source data from the target company's own public website — it shall not call LinkedIn, third-party APIs, or any authenticated resource
Success Metrics
Framework: HEART
| Metric | Definition | Target |
|---|---|---|
| Task Success Rate | % of queries against Fortune 500 companies that return ≥3 verified executives from a leadership/team page | ≥80% |
| Time-on-Task (p90) | Elapsed seconds from command invocation to result printed in chat | ≤60s |
| Adoption | Number of /scout-leadership invocations per week across AEs + BDRs |
Baseline in week 1; target 10+/week by week 4 |
| Error transparency | % of failed runs that return an explicit "here's what I tried" message (vs silent empty) | 100% |
North Star: An AE can walk into any discovery call having queried the skill for the company, confident they know the top 3 decision-makers' names and titles, in under 60 seconds.
MVP Scope
The following ships in v1:
- Scout health check pre-flight
/mapdomain → URL discovery- Leadership URL pattern matching and priority ranking
/scrapewithuse_js: trueprimary attempt- Raw HTML fallback when markdown is sparse
- Markdown table output: Name, Title, Source
- Source attribution label per row (authoritative vs fallback)
- Partial results with WARNING banner
- Explicit failure message with URLs-tried list
- File write to
{company-slug}-leadership.md - Elapsed time + source URL in output footer
Out of Scope
- LinkedIn scraping — ToS violation risk; explicitly excluded
- Batch / scheduled mode — sequential multi-company runs deferred to fast-follow
- Executive bios — text extraction beyond name + title is fast-follow
- Photos or avatars — not requested, excluded from MVP and fast-follow
- Authenticated pages — Scout cannot access login-gated leadership portals; behavior is to log and skip
- LLM-based
/extractendpoint — CSS schema fallback only for MVP to avoid LLM_API_KEY dependency; LLM extraction is fast-follow - BDR-specific output mode — different output density for BDR vs AE is post-MVP; both personas get the same table in v1
- Company name resolution — accepting "Apple" and resolving to "apple.com" automatically is fast-follow; MVP requires a domain as input
Risks + Assumptions
- [Assumption — confirm: apple.com and similar large-company sites render their team pages via JavaScript and will require
use_js: trueto return meaningful content] - [Assumption — confirm: Scout is running and accessible at localhost:8421 when the skill is invoked; skill does NOT auto-start Scout]
- [Risk — mitigate: Leadership page URLs vary widely across companies; the map-first pattern is mandatory — hardcoding URL guesses leads to 404s (observed in eval data)]
- [Risk — mitigate: Some companies (including Algolia, from eval data) do not have a dedicated
/leadershipor/teampage — press release fallback may be the only source, and it may be stale] - [Assumption — confirm:
X-API-Key: dev-keyis the correct API key for Scout in this environment; production may require a different key] - [Risk — mitigate: Scout's single-instance constraint means if a previous long
/mapor/crawlcall is in progress, this skill may timeout or return incomplete data] - [Assumption — confirm: The current working directory when the skill runs is the correct destination for
{company-slug}-leadership.md]
Open Questions
- Should the skill auto-start Scout if the health check fails, or only print the startup command?
- For the LinkedIn URL bonus feature (fast-follow): should it only include LinkedIn if it appears in the scraped page's own links, or is a LinkedIn search heuristic acceptable?
- Is
dev-keythe correct API key for all environments (local dev, CI, team members)? Should this be an env var? - When the map returns 100+ URLs and multiple match leadership patterns (e.g.,
/about/teamAND/about/leadershipAND/company/management), what ranking heuristic should determine the primary scrape target? - Should the output file be written to the current working directory, a fixed workspace path, or a configurable path?
Dependencies
- Scout at
http://localhost:8421— must be running before skill invocation - Scout API Key —
X-API-Key: dev-key(confirm env var strategy) - Scout source at
/Users/arijitchowdhury/AI-Development/Scout/ - Python/uvicorn — required to start Scout if not running
- Crawl4AI + Playwright — Scout's underlying stack; Playwright handles JS rendering and stealth mode
- Claude Code skill system — skill will be installed at
~/.claude/skills/scout-leadership/or as a command at~/.claude/commands/