A cited, continuously re-researched index of the AI industry — every company, model, product, investor and the people who move between them. Built as a read-mostly static site over Postgres, written entirely by Claude research agents.
Every figure below is read live from Postgres at render time — the site’s own copy is generated from these counts rather than typed in, so it can’t drift from the database.
| Entity | Records | Notes |
|---|---|---|
| Companies | 960 | 28 deep profiles, 932 master-list entries |
| Open roles | 4,761 | Synced daily from company boards |
| People | 501 | Cross-linked filmographies |
| Positions | 620 | The person ↔ company edge; powers talent flow |
| Briefs | 303 | Daily newsroom, five per run |
| Tools | 100 | Re-rated weekly |
| Models | 86 | Linked to their originating lab |
| Products | 41 | Shipping surfaces per company |
| Citations | 338 | Source-of-record for factual claims |
| Funding rounds | 27 | Valuation and investor edges |
| Categories | 21 | Sortable leaderboards |
| Investors | 19 | Portfolio views |
The interesting table isn’t companies — it’s roles. Because a position carries a person, a company and a time range, the same 620 rows that render a filmography also render the talent-flow graph: who left which lab for which, and when.
Every profile is produced by a research agent and re-produced on a schedule. The pipeline is the same whether a cron triggers it or an admin presses Refresh on a profile.
claude-sonnet-4-6 with live web search re-researches the entity against primary and major secondary sources.
The run must return JSON matching the entity schema. A malformed response fails the run rather than writing a partial record.
Every number lands as reported or estimated. The distinction is stored, not editorial — it renders on the profile.
Claims are written to citations with domain and date, so a reader can check any figure against its source.
The run is recorded in an admin-only changelog, then revalidateTag() drops the cached data and rebuilds only the pages that showed it.
Each profile carries a badge: green under 30 days, amber under 90, red beyond. Staleness is shown rather than hidden, so the index doesn’t quietly rot.
claude-sonnet-4-6 does deep research and debate. claude-haiku-4-5 handles the high-volume work — the newsroom, tool ratings, job classification and the reader-facing chat.
| Job | Cadence | Writes |
|---|---|---|
| Newsroom | daily · 13:00 UTC | Five briefs, grounded in a stored source set |
| Job sync | daily · 06:30 UTC | Refreshes open roles from company boards |
| Job agent | weekly · Wed 07:30 | Classifies and enriches new postings |
| Tool ratings | weekly · Mon 14:00 | Re-rates the 100-tool catalog |
AIDb is read-mostly: roughly 1,900 crawlable entity pages against a handful of writes a day. The rendering strategy follows that shape rather than fighting it.
All 6,686 pages are built at deploy with revalidate = false. Nothing expires on a timer — pages rebuild only when a write says they should.
The agent and the crons call revalidatePath() and revalidateTag() together, dropping the rendered page and the cached query behind it in one step.
Reads are wrapped in unstable_cache with entity tags, so even a page-cache miss re-renders from cached rows instead of hitting Postgres.
The admin check runs in the browser against a cookie-only endpoint. Reading it on the server would make every route dynamic — a single cookies() call in the root layout is enough to do that.
Neon bills compute by wake-time, not by query count. The database was awake 620 of the 621 hours in a billing period — it never once reached its five-minute idle timer, and logged no suspend events at all across five straight days.
The root cause wasn’t traffic volume. A cookies() read in the root layout forced every route to render dynamically, which quietly turned every per-page caching declaration in the codebase into a placebo. Ordinary crawler traffic across ~1,900 sitemap URLs then meant fresh SQL every few seconds, around the clock.
The counter-intuitive part: lengthening the cache window doesn’t fix this. At 1,900 pages, even a 24-hour window still means a regeneration roughly every 45 seconds once a crawler is sweeping — comfortably enough to hold the compute open forever. The fix has to remove the timer, not extend it.
The content surface now serves entirely from cache: every entity page returns a cache hit with no function invocation and no database read behind it.
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16.2 · React 19 | App Router; static export is the point |
| Database | Neon Postgres | Scale-to-zero suits a read-mostly index |
| Access | Drizzle · neon-http | Stateless one-shot reads, no idle pool |
| Agents | Anthropic SDK | Sonnet for research, Haiku for volume |
| Hosting | Vercel | Edge cache plus scheduled agent runs |