Module: intel-hiring
Wave 1 spoke module. The primary build-vs-buy oracle. Open roles map directly to ICP
tiers (economic buyer, technical evaluator, champion). Search-related hiring is the
highest-confidence signal that a company is actively solving a search problem — either
internally or via a vendor. The hiring_signal score feeds buying signal inference in
downstream synthesis.
Identity
| Field |
Value |
| Name |
intel-hiring |
| Version |
0.1.0 |
| Layer |
Intelligence (Wave 1) |
| Wave |
1 (runs concurrently with other spokes after intel-company completes) |
| Description |
Hiring signals, build-vs-buy assessment, and buying committee intelligence. Collects open roles via Apify LinkedIn Jobs Scraper (with Perplexity fallback), classifies by ICP tier, detects search-related roles, computes hiring velocity, maps buying committee, and compares against competitors. |
| LLM Tier |
Claude via Instructor (enrichment, classification, buying committee mapping) |
| Timeout |
300 seconds |
| Max Retries |
2 (module level) |
| Gate Module |
NO. Failure degrades hiring context but does not abort audit. |
Dependencies
| Module |
Reads |
Why |
| intel-company |
Account.executives |
Exec names for buying committee mapping and champion identification |
| intel-company |
Account.competitors |
Competitor domains for comparative hiring sweep |
Both fields are read from the accounts table via SQLAlchemy. If the account is not found,
the module falls back to basic domain-level job search (no exec enrichment or competitor comparison).
| Field |
Type |
Required |
Description |
| domain |
str |
yes |
Website domain to analyze, e.g. "dell.com" |
Output Schema: HiringOutput
Top-Level Fields
| Field |
Type |
Required |
Description |
| domain |
str |
yes |
Domain that was analyzed |
| open_roles |
list[OpenRole] |
no |
Open roles detected for the prospect company |
| role_count_by_tier |
dict[str, int] |
no |
Count of open roles by ICP tier, e.g. |
| search_related_count |
int |
no |
Total number of search-related open roles |
| hiring_velocity |
HiringVelocity or None |
no |
Hiring velocity metrics for the prospect |
| build_vs_buy |
BuildVsBuySignal or None |
no |
Assessment of build vs buy intent for search technology |
| buying_committee |
BuyingCommittee or None |
no |
Mapped buying committee for search technology decisions |
| competitor_hiring |
list[CompetitorHiring] |
no |
Hiring intelligence for competitor companies |
| comparative_summary |
str |
no |
Summary comparing prospect and competitor hiring patterns |
| hiring_summary |
str |
no |
Overall 2-4 sentence hiring intelligence summary |
Sub-Schema: OpenRole
| Field |
Type |
Required |
Description |
| title |
str |
yes |
Job title, e.g. "Senior Search Engineer" |
| department |
str |
no |
Department or team, e.g. "Engineering", "Product" |
| location |
str |
no |
Job location, e.g. "Austin, TX" or "Remote" |
| posted_date |
str or None |
no |
When the role was posted, YYYY-MM-DD or approximate |
| url |
str or None |
no |
URL to the job posting |
| icp_tier |
Literal |
no |
One of: tier1_economic (VP/Director budget holder), tier2_technical (architect/lead evaluator), tier3_champion (internal advocate/power user), tier4_user (end-user/individual contributor) |
| relevance_score |
float |
no |
How relevant to Algolia search/discovery, 0.0 to 1.0 |
| search_related |
bool |
no |
True if role is directly related to search, discovery, or personalization |
| signals |
list[str] |
no |
Signals extracted from role, e.g. "building internal search", "Algolia experience preferred" |
| source |
str |
no |
Where role was found: "linkedin", "perplexity", "careers_page" |
| company_name |
str |
no |
Which company this role belongs to |
Sub-Schema: HiringVelocity
| Field |
Type |
Required |
Description |
| roles_last_30d |
int |
no |
Number of roles posted in the last 30 days |
| roles_last_90d |
int |
no |
Number of roles posted in the last 90 days |
| trend |
Literal |
no |
One of: accelerating, steady, decelerating, insufficient_data |
| interpretation |
str |
no |
Human-readable interpretation of velocity and what it means for sales outreach |
Sub-Schema: BuildVsBuySignal
| Field |
Type |
Required |
Description |
| signal |
Literal |
no |
One of: build (hiring search engineers to build in-house), buy (likely to purchase search solution), mixed (both signals present), insufficient_data |
| evidence |
list[str] |
no |
Evidence items supporting the assessment |
| confidence |
Literal |
no |
One of: high, medium, low |
Sub-Schema: BuyingCommitteeMember
| Field |
Type |
Required |
Description |
| name |
str |
yes |
Full name of the person |
| title |
str |
yes |
Job title |
| role |
Literal |
no |
One of: economic_buyer, technical_evaluator, champion_candidate, influencer, blocker, unknown |
| linkedin_url |
str or None |
no |
LinkedIn profile URL if available |
| tenure_description |
str or None |
no |
How long they have been in this role |
| previous_company |
str or None |
no |
Previous employer if known |
| champion_signals |
list[str] |
no |
Signals this person could be an Algolia champion, e.g. "previously used Algolia at prior company" |
Sub-Schema: BuyingCommittee
| Field |
Type |
Required |
Description |
| members |
list[BuyingCommitteeMember] |
no |
Identified buying committee members |
| confidence |
Literal |
no |
One of: high, medium, low |
| methodology |
str |
no |
How the buying committee was identified, e.g. "executive list + open roles + Perplexity enrichment" |
Sub-Schema: CompetitorHiring
| Field |
Type |
Required |
Description |
| company_name |
str |
yes |
Competitor company name |
| domain |
str |
yes |
Competitor domain |
| open_roles |
list[OpenRole] |
no |
Open roles detected for this competitor |
| search_related_count |
int |
no |
Number of search-related roles at this competitor |
| hiring_velocity |
HiringVelocity or None |
no |
Hiring velocity for this competitor |
Collection Strategy
The module runs a two-phase pipeline: job data collection (Apify primary, Perplexity fallback), then Claude enrichment.
Phase 1: Apify LinkedIn Jobs Scraper (Primary) — Direct Job Data
| Aspect |
Detail |
| Adapter |
HiringCollector via Apify API |
| Evidence Tier |
VERIFIED (direct LinkedIn data, not search-derived) |
| Source Label |
"Apify LinkedIn Jobs Scraper" |
| Method |
direct_api |
| Confidence |
high |
| Fallback |
Perplexity sonar-pro if APIFY_API_KEY not set |
When Apify is available, the collector fetches live LinkedIn job postings directly. This is the highest-confidence source for open role data — it reflects actual postings, not search-engine-indexed summaries.
Phase 1 Fallback: Perplexity API (sonar-pro) — When Apify Unavailable
| Aspect |
Detail |
| Adapter |
PerplexityAdapter via HiringCollector |
| Evidence Tier |
WEBSEARCH (Perplexity searches LinkedIn, Glassdoor, Indeed, company careers pages) |
| Source Label |
"Perplexity sonar-pro" |
| Method |
llm_extraction |
| Confidence |
medium |
Perplexity fallback queries:
| Query |
Purpose |
| Prospect open roles (search/engineering/product) |
Job titles, departments, seniority for ICP mapping |
| Competitor hiring for each domain |
Parallel job data for each intel-company competitor |
| Search-specific roles |
Targeted: site search, discovery, search engineer, Elasticsearch, Algolia |
| Exec-level roles at prospect |
VP/Director/Head-of-level postings for economic buyer identification |
Phase 2: Claude via Instructor — Enrichment and Classification
| Aspect |
Detail |
| Adapter |
Claude claude-3-5-sonnet / claude-opus via Instructor |
| Evidence Tier |
ESTIMATE (inference on collected role data) |
| Purpose |
ICP tier classification, search_related detection, build_vs_buy assessment, buying committee mapping, hiring velocity computation |
Enrichment pipeline:
1. Raw role data -> per-role classification: icp_tier, relevance_score, search_related, signals
2. role_count_by_tier computed from tier classifications
3. HiringVelocity derived from posted_date distribution
4. BuildVsBuySignal determined: build if search engineers hired for internal platform, buy if search vendor evaluation roles or minimal engineering search roles
5. BuyingCommittee mapped from exec list (intel-company) cross-referenced with open roles
6. CompetitorHiring built per competitor domain
7. comparative_summary and hiring_summary generated
ICP Tier Classification
The ICP tier system maps open roles to MEDDPICC buying committee roles:
| Tier |
Label |
Example Roles |
MEDDPICC Role |
| tier1_economic |
Economic Buyer |
VP Digital, VP Engineering, Director of Search, CTO |
Economic Buyer (E) |
| tier2_technical |
Technical Evaluator |
Search Architect, Principal Engineer, Platform Lead |
Decision Criteria (D) |
| tier3_champion |
Champion Candidate |
Product Manager (Search), UX Engineer (Discovery) |
Champion (C) |
| tier4_user |
End User |
Frontend Developer, Search Analyst |
Users |
Roles with relevance_score >= 0.7 AND search_related=True are the primary buying signal.
Build vs Buy Signal Logic
| Signal |
Indicators |
Sales Implication |
| build |
Multiple search engineer roles, Elasticsearch/OpenSearch/Solr mentioned, "build internal search" in JD signals |
Prospect building in-house. Positioning: "Why build when you can buy Algolia?" |
| buy |
0-1 search engineers, vendor evaluation role posted, Algolia/Typesense/Solr in JD as tools |
Prospect likely evaluating. Positioning: "We win vendor evaluations. Here's why." |
| mixed |
Both search engineers AND vendor-evaluation signals |
Complex picture. Positioning: hybrid/extensibility angle. |
| insufficient_data |
Too few roles, no search-specific roles found |
Cannot determine. Fall back to techstack signals. |
Hiring Velocity Interpretation
| Trend |
Definition |
Sales Signal |
| accelerating |
roles_last_30d > (roles_last_90d / 3) * 1.5 |
Ramp event likely. Outreach now. |
| steady |
roles_last_30d approximately equals roles_last_90d / 3 |
Stable investment. Standard qualification. |
| decelerating |
roles_last_30d < (roles_last_90d / 3) * 0.5 |
Potential freeze or strategic shift. Probe budget. |
| insufficient_data |
Fewer than 3 roles with dates |
Cannot determine. |
Validation Rules
| # |
Rule |
Severity |
Threshold |
| 1 |
At least 1 open role found |
required |
open_roles not empty |
| 2 |
All roles have a title |
required |
Every OpenRole.title is non-empty |
| 3 |
build_vs_buy is populated |
warning |
build_vs_buy not None |
| 4 |
hiring_summary is populated |
warning |
hiring_summary not empty string |
| 5 |
Competitor hiring populated if competitors were provided |
warning |
competitor_hiring not empty if competitor list non-empty |
| 6 |
Buying committee populated |
warning |
buying_committee.members not empty |
Required failures -> status "partial". Warning failures -> logged but passed.
Evidence Requirements
| Field |
Minimum Tier |
Rationale |
| open_roles (Apify path) |
VERIFIED |
Direct LinkedIn data |
| open_roles (Perplexity path) |
WEBSEARCH |
Perplexity searches job boards |
| icp_tier classification |
ESTIMATE |
Claude inference on role text |
| build_vs_buy |
ESTIMATE |
Claude inference on role signals |
| hiring_velocity |
ESTIMATE |
Computed from posted_date distribution |
| buying_committee |
WEBSEARCH |
Perplexity enrichment + exec list cross-reference |
| competitor_hiring |
VERIFIED or WEBSEARCH |
Depends on whether Apify or Perplexity used |
Cost Profile
| Metric |
Expected Value |
| LLM Calls |
2-4 (Perplexity fallback) + 3-6 (Claude Instructor enrichment) |
| Apify Cost |
~$0.01-0.05 per run (LinkedIn Jobs actor) |
| Claude Cost |
~$0.05-0.15 (sonnet tier for enrichment) |
| Expected Duration |
60-180 seconds |
| External API Calls |
1 (Apify) or 3-5 (Perplexity fallback) |
Retry Architecture (Two Layers)
Layer 1: Collector Retries (network errors)
| Trigger |
Retry |
Backoff |
| Apify timeout |
3 attempts |
Exponential (2^attempt seconds) |
| Apify rate limit |
3 attempts |
Exponential |
| Perplexity timeout |
3 attempts |
Exponential |
| Perplexity rate limit (429) |
3 attempts |
Exponential |
| Other HTTP error |
No retry |
Fail immediately |
Layer 2: Module Retries (data quality)
| Trigger |
Retry |
Backoff |
| Empty role response |
2 attempts |
5 seconds fixed |
| Instructor parse failure |
2 attempts |
5 seconds fixed |
If all retries exhausted: return ModuleExecutorResult with status="partial" (not "failed" — partial hiring data is non-blocking).
Persistence
intel-hiring writes to module_executions table only (not the accounts table). Downstream synthesis modules read module_executions.output JSONB directly.
Key fields consumed by downstream synthesis:
- open_roles with icp_tier and search_related flags
- search_related_count — primary buying signal input
- build_vs_buy.signal — used in positioning logic
- buying_committee.members — directly feeds power map in AE playbook
- hiring_velocity.trend — feeds outreach timing recommendations
Runtime Notes
SaaS Runtime (Temporal/Python)
- Module:
prism_platform/v2/modules/intel_hiring/module.py
- Collector:
prism_platform/v2/modules/intel_hiring/collector.py
- Enricher:
prism_platform/v2/modules/intel_hiring/enricher.py (Claude via Instructor)
- Validator:
prism_platform/v2/modules/intel_hiring/validator.py
- Schemas:
prism_platform/v2/modules/intel_hiring/schemas.py
- Persistence: module_executions table only
Skill Runtime (Claude Code)
- Skill file:
~/.claude/skills/algolia-intel-hiring/SKILL.md
- Reads from:
01-company-context.json (for exec names and competitor domains)
- Writes to:
09d-hiring-signals.md, 09d-hiring-signals.json
- MCP tools used: WebSearch (Perplexity-equivalent job search), WebFetch (careers page)
Evolution: v1 → v2
v1 Architecture (Current)
| Aspect |
v1 Design |
| Job collection |
Apify LinkedIn Jobs Scraper (primary) + Perplexity sonar-pro (fallback) |
| Enrichment |
Claude via Instructor — ICP tier, build_vs_buy, buying committee classification |
| Context source |
Account.executives and Account.competitors JSONB columns (from intel-company) |
| ICP tier |
Claude inference on role title and description |
| Build vs buy |
Claude inference on aggregated role signals |
| Buying committee |
Exec list cross-referenced with open roles via Perplexity enrichment |
| Competitor hiring |
Per-domain query (Apify or Perplexity) |
v2 Architecture (Planned — Playbook-Based Agent)
In v2, intel-hiring becomes a playbook-driven agent. The config object holds a hiring_playbook that instructs data collection with precise source, role classification, and signal extraction rules:
| Aspect |
v2 Design |
| Job collection |
Perplexity with explicit source constraints in system prompt |
| Primary sources |
LinkedIn Jobs, Glassdoor, Indeed, company careers page (direct WebFetch) |
| Citation requirements |
Every role must include job title + URL. No URL = role flagged as unverified. |
| ICP tier |
Playbook defines exact title-to-tier mapping rules (not left to Claude inference) |
| Build vs buy |
Playbook defines keyword triggers per signal type — "Elasticsearch" = build, "evaluate search vendors" = buy |
| Buying committee |
Playbook specifies MEDDPICC role mapping from title patterns; champion_signals expanded to include prior employer detection |
| Hiring velocity |
Computed deterministically from date-stamped roles, not inferred |
| Competitive context |
Automatic diff: prospect search_related_count vs each competitor search_related_count |
| hiring_signal score |
Numeric score (0-100) computed from: search_related_count, icp_tier distribution, velocity trend, build_vs_buy confidence |
| Schema |
Extended HiringOutput adds hiring_signal: float (0.0-1.0) for synthesis modules |
| Agent memory |
Can carry prior-run role lists for trend tracking over time |
The hiring_signal score is the key v2 addition. It normalizes all hiring intelligence into a single float that synthesis modules can use in buying signal inference without re-parsing raw role data.
What Makes This Production-Grade (vs a Simple LinkedIn Search)
| Aspect |
Simple LinkedIn Search |
PRISM intel-hiring |
| Collection |
Manual search, one query |
Apify LinkedIn Jobs actor (direct data) + Perplexity multi-query fallback |
| ICP mapping |
None |
Every role classified: tier1_economic through tier4_user |
| Search signal |
None |
search_related flag + relevance_score (0-1) per role |
| Build vs buy |
Guesswork |
Evidence-backed classification with confidence level and supporting signals |
| Buying committee |
None |
Members mapped from exec list + role patterns, with MEDDPICC roles assigned |
| Velocity |
None |
roles_last_30d vs roles_last_90d trend with sales interpretation |
| Competitor comparison |
None |
Parallel collection per competitor domain, comparative_summary generated |
| Champion detection |
None |
previous_company cross-check, champion_signals per committee member |
| Downstream use |
Text blob |
Typed fields: search_related_count feeds scoring, build_vs_buy feeds positioning, buying_committee feeds AE power map |
| Source provenance |
None |
Evidence tier per field: VERIFIED (Apify) vs WEBSEARCH (Perplexity) vs ESTIMATE (Claude) |
Changelog
| Date |
Change |
Reason |
| 2026-03-30 |
Initial implementation |
Session 1: backend phase 1 |
| 2026-04-14 |
Module spec written to vault |
Knowledge extraction — module catalog completion |
- Module-Catalog — all 20 modules overview
- Intel-Company — foundation module this depends on
- Intel-News — sibling spoke module
- Module-Spec-Template — the template this spec follows
- Evidence-Tier-Spec — evidence tier system
- Adapter-Interfaces — Apify and PerplexityAdapter