Module: intel-company
THE FOUNDATION HUB. Runs first in every audit. All spoke modules depend on
its output. If this module fails, the entire audit aborts.
Identity
| Field |
Value |
| Name |
intel-company |
| Version |
0.2.0 |
| Layer |
Intelligence (Wave 1) |
| Wave |
1 (runs first, all others wait for it) |
| Description |
Foundation company intelligence via single Perplexity API call. Populates accounts table with company identity, executives, competitors, financials, and field-level source citations. |
| LLM Tier |
None (deterministic JSON parsing, no LLM enrichment) |
| Timeout |
120 seconds |
| Max Retries |
2 (module level) + 3 (collector level) |
| Gate Module |
YES. Failure aborts entire audit. |
Dependencies
None. This is the foundation module. All other modules depend on it.
Hub-and-Spoke: What Each Spoke Reads
intel-company writes to the accounts table. Every spoke module reads
specific columns from that table. This is the complete dependency map:
| Spoke Module |
Reads |
Why |
| intel-traffic |
Account.competitors |
Runs SimilarWeb on each competitor for comparative traffic |
| intel-hiring |
Account.executives, Account.competitors |
Exec names for buying committee; competitor domains for comparative hiring |
| intel-news |
Account.executives, Account.competitors |
Exec-specific news search; competitor news tracking |
| intel-social |
Account.executives, Account.competitors |
Exec LinkedIn/Twitter activity; competitor social signals |
| intel-investor |
executives (via context), competitor domains |
Exec names for earnings quote attribution; competitor tickers for financial comparison |
| intel-partner |
Account.industry, Account.competitors |
Industry for vertical SI relationships; competitor domains for partner overlap |
| intel-industry |
Account.industry, Account.sub_vertical |
Vertical classification for benchmarks, trends, pain points |
| intel-techstack |
Account.competitors (if populated) |
Fan-out: runs BuiltWith on each competitor domain |
| intel-queries |
Account.competitors, Account.product_categories |
Product categories for vertically-calibrated test queries; competitor domains for competitor query sets |
| intel-financial-public |
ticker (via ExecutionContext) |
Pulls Yahoo Finance data for the prospect ticker |
| intel-financial-private |
company_name, industry, employee_count |
Revenue waterfall estimation using company context |
| intel-competitors |
ALL upstream outputs via module_executions |
Pure synthesis. Depends on intel-company for the competitor list itself. |
| audit-browser |
has_search_bar |
Search bar detection tells browser module WHERE to look |
Why It's the Gate
If intel-company fails:
- No competitors = traffic, techstack, competitors modules have nothing to compare
- No executives = hiring, social, news modules can't find exec-specific intelligence
- No industry = industry, partner modules can't find vertical context
- No ticker = financial-public can't pull Yahoo Finance data
- No product_categories = queries module can't generate vertically-calibrated tests
Every spoke is structurally dependent on at least one field that only intel-company provides.
| Field |
Type |
Required |
Description |
| domain |
str |
yes |
Website domain to analyze, e.g. "dell.com" |
Output Schema: CompanyProfileOutput
Identity Fields
| Field |
Type |
Required |
Description |
| legal_name |
str |
yes |
Official registered company name |
| common_name |
str |
yes |
Name used in press/marketing |
| domain |
str |
yes |
Primary website domain |
| headquarters |
str |
yes |
HQ city and country, e.g. "Round Rock, Texas, USA" |
| employee_count |
int or None |
no |
Approximate headcount as integer (e.g. 133000) |
| employee_count_source |
str or None |
no |
Where count came from (e.g. "LinkedIn") |
| year_founded |
int or None |
no |
4-digit year (e.g. 1984) |
| business_model |
str |
yes |
3+ sentences: what they do, how they make money, who their customers are. Min 50 chars. |
| motto |
str or None |
no |
Company tagline if publicly stated |
Classification Fields
| Field |
Type |
Required |
Description |
| industry |
str |
yes |
Primary industry (e.g. "Enterprise Technology") |
| sub_vertical |
str or None |
no |
Specific sub-vertical (e.g. "Consumer Electronics") |
| is_public |
bool |
yes |
True if publicly traded |
| ticker |
str or None |
no |
Stock ticker if public (e.g. "DELL"). None if private. |
| parent_company |
str or None |
no |
Parent company if subsidiary. None if independent. |
| revenue_estimate |
float or None |
no |
Annual revenue in USD as float (e.g. 88400000000.0) |
| revenue_source |
str or None |
no |
Source of revenue figure (e.g. "SEC 10-K FY2025") |
People and Competitors
| Field |
Type |
Required |
Description |
| executives |
list[Executive] |
yes |
8-12 key executives. Min 3 for validation pass. |
| competitors |
list[Competitor] |
yes |
5-7 direct competitors with domains. Min 3 for validation. |
| recent_news |
list[NewsItem] |
no |
3-10 news items from last 90 days |
| recent_blog_posts |
list[BlogPost] |
no |
3-5 posts from company blog/newsroom |
Website Snapshot
| Field |
Type |
Required |
Description |
| has_search_bar |
bool or None |
no |
Whether homepage has a visible search bar (detected via HTML regex) |
| product_categories |
list[str] |
no |
Top-level product/service categories |
| search_experience_description |
str or None |
no |
Brief description of current search UX |
Sub-Schema: Executive
| Field |
Type |
Required |
Description |
| full_name |
str |
yes |
Full name |
| title |
str |
yes |
Current job title |
| linkedin_url |
str or None |
no |
LinkedIn profile URL (must be real, not fabricated) |
| headshot_url |
str or None |
no |
URL to headshot image |
| tenure_description |
str or None |
no |
How long in role (e.g. "Since 2019") |
| previous_company |
str or None |
no |
Most recent previous employer |
| previous_role |
str or None |
no |
Title at previous company |
Sub-Schema: Competitor
| Field |
Type |
Required |
Description |
| company_name |
str |
yes |
Competitor name |
| domain |
str |
yes |
Primary website domain |
| why_competitor |
str |
yes |
One sentence: why they compete |
| is_algolia_customer |
bool |
no |
True if in KNOWN_ALGOLIA_CUSTOMERS set (Golden Angle) |
Sub-Schema: NewsItem
| Field |
Type |
Required |
Description |
| headline |
str |
yes |
Article headline |
| source |
str |
yes |
Publication name |
| date |
str |
yes |
YYYY-MM-DD format |
| url |
str or None |
no |
Article URL |
| category |
str |
no |
One of: leadership_change, product_launch, partnership, financial, acquisition, technology, expansion, bankruptcy, other |
Sub-Schema: BlogPost
| Field |
Type |
Required |
Description |
| title |
str |
yes |
Post title |
| date |
str |
yes |
YYYY-MM-DD format |
| url |
str or None |
no |
Post URL |
| summary |
str |
no |
One-sentence summary |
Collection Strategy
Source 1: Perplexity API (sonar model)
| Aspect |
Detail |
| Client |
AgentAPIClient (httpx, Bearer token) |
| Endpoint |
POST https://api.perplexity.ai/chat/completions |
| Model |
sonar-pro ($3/M input tokens) |
| Input |
Resolved playbook.md — system prompt from ModuleConfig, user prompt from playbook |
| Output |
JSON string validated against CompanySeedOutput schema |
| Retry |
executor.max_retries = 2 |
| Evidence Tier |
WEBSEARCH (Perplexity searches the web) |
| Temperature |
0.1 (near-deterministic) |
| Max Tokens |
8192 |
Prompt strategy: The prompt is highly specific. It tells Perplexity exactly where to look for executives (LinkedIn, About Us page, Companies House, press releases). It demands minimums (5+ executives, 5+ competitors). It provides the exact JSON schema with example values. It specifies format rules (revenue as raw number, dates as YYYY-MM-DD, no fabricated LinkedIn URLs).
Source 2: Homepage HTML Fetch
| Aspect |
Detail |
| Removed in v2. Homepage fetch for search bar detection is deferred to audit-browser (Wave 2), which does live Playwright testing. |
|
The is_algolia_customer golden-angle signal is now detected via golden_angle_competitors in intel-techstack.
Enrichment Strategy
| Aspect |
Detail |
| Method |
ModuleExecutor (generic v2 harness) |
| LLM Model |
sonar-pro via AgentAPIClient |
| Pipeline |
PlaybookLoader → resolve template → AgentAPIClient.research() → strip_code_fences() → json.loads() → CompanySeedOutput.model_validate() → auto-generate ClaimRegistryEntry list |
Perplexity returns JSON. The executor strips any markdown fences, parses, and validates against the Pydantic schema. Claims are auto-generated from every output field that has a citation.
Perplexity annotates facts with inline citations in two formats:
1. [label](url) -- named citations. Extracted via regex.
2. [1][2] -- numbered citations. Stripped from field values to prevent "SHOP[1][2]" type corruption.
Citations become Source records with tier=WEBSEARCH and method=llm_extraction.
Post-Parse Processing
- Search bar injection --
has_search_bar from homepage fetch merged into output.
- Algolia customer cross-check -- Each competitor.domain checked against KNOWN_ALGOLIA_CUSTOMERS set. If match,
is_algolia_customer = True (Golden Angle detection).
Validation Rules (8 Checks)
| # |
Rule |
Severity |
Threshold |
| 1 |
legal_name is populated |
required |
Not empty string |
| 2 |
domain matches expected input |
required |
Exact case-insensitive match |
| 3 |
headquarters is populated |
required |
Not empty string |
| 4 |
Sufficient executives found |
required |
>= 3 executives |
| 5 |
Sufficient competitors found |
required |
>= 3 competitors |
| 6 |
At least 1 exec has LinkedIn URL |
warning |
Any exec with linkedin_url |
| 7 |
business_model is substantive |
required |
>= 50 characters |
| 8 |
At least 1 news item |
warning |
recent_news not empty |
Required failures -> status "partial". Warning failures -> logged but passed.
Evidence Requirements
| Field |
Minimum Tier |
Rationale |
| legal_name |
WEBSEARCH |
Perplexity synthesis is sufficient for company name |
| headquarters |
WEBSEARCH |
Perplexity synthesis is sufficient |
| executives |
WEBSEARCH |
Perplexity aggregates from LinkedIn + company pages |
| competitors |
WEBSEARCH |
Perplexity competitive research |
| revenue_estimate |
ESTIMATE |
Acceptable for initial context; financial modules refine later |
| has_search_bar |
WEBFETCH |
Direct HTML fetch, not search-based |
| ticker |
WEBSEARCH |
Cross-verified by intel-financial-public later |
Persistence (Hub -> Accounts Table)
intel-company is unique: it writes to the accounts table, not just module_executions. This is because spoke modules read from accounts columns directly.
Columns Written
legal_name, company_name, headquarters, employee_count, employee_count_source,
year_founded, business_model, motto, industry, sub_vertical, is_public, ticker,
parent_company, revenue_estimate, revenue_source, has_search_bar,
product_categories, # list[str] -> JSONB
executives, # list[Executive] -> JSONB array
competitors, # list[Competitor] -> JSONB array
recent_news, # list[NewsItem] -> JSONB array
recent_blog_posts, # list[BlogPost] -> JSONB array
sources, # list[citation dict] -> JSONB array
updated_at # timestamp
Cost Profile
| Metric |
Expected Value |
| LLM Calls |
1 (Perplexity sonar) |
| Estimated Cost |
~$0.01-0.03 (sonar is cheap) |
| Expected Duration |
5-15 seconds |
| External API Calls |
2 (Perplexity + homepage fetch) |
Retry Architecture (Two Layers)
Layer 1: Collector Retries (network errors)
| Trigger |
Retry |
Backoff |
| Timeout |
3 attempts |
Exponential (2^attempt seconds) |
| Rate limit (429) |
3 attempts |
Exponential |
| Connection error |
3 attempts |
Exponential |
| Other HTTP error |
No retry |
Fail immediately |
Layer 2: Module Retries (data quality)
| Trigger |
Retry |
Backoff |
| Empty Perplexity response |
3 attempts |
5 seconds fixed |
| JSON parse failure |
3 attempts |
5 seconds fixed |
| Pydantic validation failure |
3 attempts |
5 seconds fixed |
If all retries exhausted: return ModuleExecutorResult with status="failed".
Runtime Notes
SaaS Runtime (Temporal/Python)
- Config:
prism_platform/v2/modules/intel_company/config.py — INTEL_COMPANY_CONFIG (ModuleConfig)
- Playbook:
prism_platform/v2/modules/intel_company/playbook.md — research instructions
- Schema:
prism_platform/v2/modules/intel_company/schemas.py — CompanySeedOutput
- Hooks:
prism_platform/v2/modules/intel_company/hooks.py — intel_company_post_execute (accounts table write)
- Registry: registered in
prism_platform/v2/registry.py as ModuleHandle with post_execute hook
- Harness:
prism_platform/v2/executor.py — ModuleExecutor (generic, shared)
- Persistence: accounts table (via post_execute hook, keyed by domain) + module_executions table (via Temporal activity)
Skill Runtime (Claude Code)
- Skill file:
~/.claude/skills/algolia-intel-company/SKILL.md
- Reads from: nothing (foundation module)
- Writes to:
01-company-context.md, 01-company-context.json
- MCP tools used: WebSearch (for Perplexity-equivalent research), WebFetch (homepage)
What Makes This Production-Grade (vs a Simple Prompt)
| Aspect |
Simple Prompt |
PRISM Module |
| Data collection |
"Research this company" |
Specific composite prompt with field-level instructions, minimums, format rules |
| Structuring |
Hope the LLM returns good JSON |
Deterministic parser: extract citations, strip noise, json.loads(), Pydantic validate |
| Retry logic |
None |
Two layers: collector (network) + module (data quality). 3 attempts each. |
| Source provenance |
None |
Every fact tracked to source URL + evidence tier |
| Search bar detection |
Ask the LLM to guess |
Fetch actual HTML, run 5 regex patterns. Deterministic. |
| Golden Angle |
Not detected |
Competitor domains cross-checked against KNOWN_ALGOLIA_CUSTOMERS |
| Persistence |
Write a text file |
Normalized columns on accounts table; JSONB for structured arrays |
| Validation |
Trust the LLM |
8-point validation checklist (6 required, 2 warning) |
| Downstream value |
Text blob another prompt reads |
Typed columns that spoke modules query directly |
Changelog
| Date |
Change |
Reason |
| 2026-03-30 |
Initial implementation |
Session 1: backend phase 1 |
| 2026-04-02 |
Denormalized accounts table |
Migration 005: moved from intelligence JSONB to proper columns |
| 2026-04-08 |
Module spec written to vault |
Knowledge extraction for skill refactoring |
- Module-Catalog -- all 20 modules overview
- Module-Spec-Template -- the template this spec follows
- Evidence-Tier-Spec -- evidence tier system
- Adapter-Interfaces -- PerplexityAdapter and WebFetchAdapter