Thunder Graph — Build Status

A living map of the relationship-intelligence platform: turning Thunder's network into answers about who fits an opportunity and who can warmly introduce us. Updated as we make progress.

Last updated:
Built In progress To build The goal
Current focus
SOURCES INGEST GRAPH ENGINE ACCESS OUTCOME Thunder App 10K firms · 73K cos nightly prod sync Cloud SQL (GCP) HubSpot contacts · engagements edges · personas private-app token Gmail · Calendar emails (both dirs) past meetings only service-acct DWD Fireflies meeting transcripts warm edges service-acct LinkedIn connections + messages AI thread topics upload → auto-pipeline WhatsApp chat history contact touch events export → inbox/ Client Sheets target lists outcomes Google Sheets Connectors & sync pull + refresh per source · community rosters (PEF · Luma) · nightly cron on EC2 Entity resolution dedupe people & orgs · nightly firm merge · re-link durable keys after rebuild Identity map + overrides link ext IDs · entity_overrides re-apply (edits survive rebuild) Identity graph firms · companies · people 13,678 orgs · 24,764 contacts hide/delete · suppression list Relationship graph 1,989 warm edges (HubSpot + Gmail + Fireflies + LinkedIn) edge_review · confirm/reject Signals & intel 51K touch events · Brave pulse Inven (28M cos, 430M contacts) theSwarm (2nd-degree network) Firm criteria sector · geo · check · stage 25 fields · 11,035 firms match_score prior (2.76M rows) Match & scoring engine rank = 100% Relevance · warmth + owner = separate sortable signals Relevance — the rank Relationship — sort Owner — sort ranked intros · why-fit · draft Relevance = compatibility engine (recommend.mjs): stage(40) + product(15) + sector(+40 / −40, imperative) + geo(±8 soft) + check(5) + bmodel(5) + revrange(4) + tags(3) + revstream(2) + acq-align(±20) → logistic sigmoid → 0-100 × firm-type weight · fit tiers 🎯 slam-dunk / possible / other · mandate verification per lane (covers/excludes/unclear) · learned ranker proposes weight nudges from funnel outcomes (human-adopted, ±20% max) Relationship = warmth (warmth.mjs): recency(30, 3yr) + frequency(30, 6mo) + depth(25: meeting=8/event=5/call=4/LI-msg=3/email=1) + directionality(15) · PEF community members +20 durable bonus · Owner: me=100 / @thunder.vc=80 / theSwarm 2nd-degree=70 / affiliate=40 · relationship + owner are SEPARATE sortable signals (NOT in the rank) · NL network search (search.mjs) answers plain-English questions over the whole graph · AI: Opus 4.6 drafts · Sonnet 4.6 extraction/classify/search · Haiku 4.5 pulse Ask anything & execute — manual match · NL network search · convert → curate → campaign wizard → funnel → one-click send → client reports · Docs (getting-started · definitions) Agents — proactive nightly: warmth rescore · persona enrich · reply triage · snapshots · EDGAR reconnect · origination scan · learned-ranker proposals · Inven/pulse enrich THE GOAL Owner reviews Warm intro sent Meeting Deal / win outcomes train scoring

Integrations & external services

Every external connection the graph depends on — data sources, enrichment APIs, and AI. Keys live in .env.prod on EC2.

Scheduled jobs & on-demand agents

Nightly cron runs automatically on EC2 (~2am UTC). On-demand workers can be triggered from the web UI or SSH.

🌙 Nightly cron (~2am UTC)
⚡ On-demand workers

Build roadmap

Sequenced for value-first: each step makes the answers warmer and the system more self-serve.

Progress log

What materially changed, newest first.

How the matching algorithm works

Every recommendation is one number combining three things: how relevant the firm is to the deal, how warm our relationship with it is, and who can make the intro. Below is every tunable knob, its live value, and what moving it does. Each knob has a NAME and a FILE so you can point me straight at it.

💬 To change anything, just tell me in plain English referencing the knob — e.g. "set W_RELATIONSHIP to 0.35", "make in-person meetings worth 10 in warmth", or "don't show VC funds for a buyout raise." I'll edit the source, verify, and redeploy.
1 · The ranking score

The ranking is now 100% match relevance (Jason, 2026-06-29). Relationship warmth and the strongest-connection / owner path are still computed per firm, but as separate, sortable signals + stats — never folded into the rank. recommend.mjs.

score = Relevance  · warmth + owner = side signals you sort & filter by
2 · Relevance — the compatibility engine

10 components earn raw points, summed → squashed through a logistic curve (sigmoid, midpoint 45, steepness k=0.05) → 0–100 → multiplied by the firm-type fit weight (§3). Company Quality (score_z) was removed (Jason, 2026-06-29) — fit drives relevance, not the company's own score. compatibility.mjs.

3 · Firm-type fit weights — the VC-vs-PE-vs-bank separator

A soft multiplier on relevance, by firm type and intent. 1.0 = ideal · partial · 0 = wrong type → deep-demoted to 5% (OFFTYPE_FLOOR) and sorted to the very bottom (never hard-deleted). recommend.mjs.

"Raise (any)" applies when no financing structure is chosen; picking venture / buyout / debt swaps in that more specific column. Acquire intent uses a separate target-company path (§8), not this table.

4 · The prod match_score prior (blend)

When the deal maps to a real Thunder-App company, we blend in the precomputed prod relevance prior as a cross-check. recommend.mjs + match-score.mjs.

5 · Relationship — warmth

Canonical warmth (warmth.mjs, faithful port of the prod WarmthScorer). Four components sum to 0–100, scored identically across every source.

Components
Depth weights by channel
Warmth labels (final 0–100)
Durable relationship bonuses (added on top of the computed score)

Some cohorts carry a fixed warmth boost that survives the nightly recompute — applied by score-warmth.mjs after the base recompute, capped at 100. So a member with no touch history still floors at the bonus value, and a touched member gets base + bonus.

6 · Owner — who can make the intro

"Me-first": your own relationships outrank teammates'. The best available path sets the score. recommend.mjs.

7 · Feedback loop & result tiers
👍 / 👎 learning signal
Result tiers
8 · Acquire path — finding target companies

The acquire intent matches target companies in our network (not investor firms), scored on its own additive scale (0–100). recommendTargets().

9 · Tuning cheat-sheet — "I want X → change Y"

The fastest way to brief me. Pick a row, tell me the value, I'll handle the edit + redeploy.