MARS v3 · rewrite plan · 2026-07-24

Feed + spider, rewritten.

The current feed + spider were built in 2024, before we had a canonical entity layer, before OpenRouter, before Inferno, before Brave Search, before MCP. Every one of those tools invalidates a load-bearing assumption in the current architecture. This is the plan for rebuilding on the new stack — phased so we can keep processing the task list while it happens.

Why v1 exists in its current shape

In 2024, we had no canonical entity substrate. clean_domains was the entity layer — every deal keyed by md5(domain). Every downstream table was a projection of it. Feed classified with Grok because Qwen didn't exist yet at accessible pricing. Spider used Playwright + Bright Data for every domain because "search-first-then-fetch" wasn't obvious as a pattern. Signals categories (lawsuit / appointment / contract / etc.) got classified but never extracted — the extraction pipeline for those was scoped as a separate signals_pipeline/ vertical that never staffed.

All defensible calls at the time. All now obsolete.

What changed underneath

Canonical entity layer

v2 substrate exists. companies_v2 has 286K canonical entities with CIK, ticker, primary_domain, name_variants, integrity_tier. Feed no longer needs to "discover" companies — it looks them up.

Cheap extraction (Qwen)

OpenRouter Qwen 30B ≈ $0.0002/article. Inferno batch is ~$0 per token on our GPU cluster. Grok-4.3 was $0.005/article. That's a 25× cost reduction — enough to run extraction on all 8 previously-skipped signal categories inline.

Search-first crawling

Brave Search API + requests beats Playwright + Bright Data on 95% of sites. bios team proved it today: 15,695 firm sites, ~$40 total, 0 OOM, 0 rate-limit errors, ~14× faster.

v3 architecture (compact)

NEWS SOURCES (GDELT · RSS · Benzinga SQS · Brave discovery) │ ▼ CLASSIFY (Qwen body-aware, ~$0) │ route by event_type ├─ funding_round → extract → funding_deals_v2 (existing) ├─ merger → extract → merger_deals_v2 (existing) ├─ lawsuit → extract → lawsuit_events_v2 (NEW) ├─ appointment → extract → appointment_events_v2 (NEW) ├─ contract → extract → contract_events_v2 (NEW) ├─ product_announce → extract → product_events_v2 (NEW) ├─ financial_report → extract → financial_events_v2 (NEW) ├─ license_agreement → extract → license_events_v2 (NEW) ├─ government_issues → extract → government_events_v2 (NEW) ├─ dividend → extract → dividend_events_v2 (NEW) ├─ auditor_change → extract → auditor_change_events_v2 (NEW) └─ dropped → article_meta_v2 only (existing) SPIDER (crawl for domains we DON'T already have entity substrate for) │ ├─ 1. BRAVE DISCOVERY (find press / funding article URL up front) ├─ 2. REQUESTS FETCH (95% of sites — no browser, no proxy) ├─ 3. QWEN EXTRACT (in-text task, cheap) └─ 4. JS-TAIL BROWSER (small nightly pass, only for the 5% that need it — full OOM defense on this unit only) ENTITY RESOLUTION — up front, not downstream companies_v2 lookup by (CIK | ticker | primary_domain | name_variants) → 1 lookup replaces the whole clean_domains → v2 ETL propagation lag

The trade to be honest about

Grok vs Qwen on extraction. Round 4 calibration (task #183 PARKED) found Grok beats Qwen on funding-round extraction: composite 47.5% vs ~42%. But the noise concentrated in one field: funding_date, at 30% agreement, dominates the composite. Task #185 — the deterministic funding-date preprocessor — is estimated to lift Qwen composite 80% → 92%+ by regex-extracting dates and feeding them as candidates to the extractor.

v3 depends on shipping #185 first. Without it, we take a real quality hit for the cost savings. With it, Qwen composite should match or beat Grok at 25× lower cost.

Two ways to build this

Parallel rewriteIncremental replacement recommended
Shape Build mars_feed_v3 + mars_spider_v3 side-by-side, run in parallel, compare, then cutover Add Brave tier in front of current spider; add signals-inline inside existing classify.py; migrate clean_domains → v2 direct writes one event type at a time
Timeline ~2-3 months to first cutover ~1 month per event type, ~6 months total
Blast radius Higher at cutover Small each step, reversible
Managerial overhead Big project management surface Small each week, sustained
What breaks if we stall Everything — v3 is 60% done and unusable Nothing — each step is standalone

Incremental wins — smaller cost of a stall, matches how we've built everything else recently (bios, flare, lp_data all shipped incrementally on the existing platform).

Phased plan

Phase 0 — Foundations

~1 week · unblocks everything else

Phase 1 — Signals-inline in existing feed

~2-3 weeks · biggest single value unlock

Phase 2 — Brave-tier in front of spider

~3-4 weeks · biggest cost + reliability win

Phase 3 — Feed direct-to-v2 (per event type)

~1-2 months · migrates the "domain-first" primary key to entity-first

Phase 4 — Deprecate v1

~2-4 weeks · retire clean_domains + old feed

What we're NOT doing

Parallel full rewrite

Too much managerial surface for a small team. Every partial-completion state is unusable.

Retire clean_domains without dual-write

Raul's dataprep-cleaning + alerts consume clean_domains as source of truth. Coordination needed before Phase 4.

Ship v3 before #185 lands

Qwen extraction quality gap on funding_date will contaminate substrate. Preprocessor first.

Migrate Camoufox Cloudflare path

Anti-bot sites need the browser + rotating proxy. Keep that path as-is; it's ~5% of spider volume, not the cost driver.

Cost model (rough)

Pipeline stagev1 todayv3 targetΔ
Spider proxy (Bright Data residential)~$1,000/mo~$50/mo−95%
Feed extraction (Grok-4.3)~$400/mo~$20/mo−95%
Signals extraction (currently: nothing)$0 (not extracted)~$40/mo (Qwen)+new
Brave Search discovery (new)$0~$30/mo+new
Total substrate cost~$1,400/mo~$140/mo−90%

Plus the substrate-coverage gain: all 8 signal categories (lawsuit / appointment / contract / product / financial / license / government / dividend / auditor_change) become extracted substrate, not just classified metadata. That's material new product surface for MCP consumers.

Success criteria

Bottom line

v1 was correct in 2024 and is technically obsolete in 2026 because the tools that would have made it simpler didn't exist. The rewrite is real work — ~6 months total, ~$0 in project-cost extras (all the new tools are cheap-to-free by design). Payoff: substrate coverage expands to 8 new event categories, cost drops 90%, OOM class of failures goes away, and consumer-facing MCP responses inherit richer provenance because v3 writes carry canonical entity IDs from the start instead of resolving them downstream.

Doing this incrementally means the task list stays live — every Phase step is 1-3 tasks, individually reviewable, individually reversible.

Prereqs already met: v2 canonical substrate live (companies_v2, persons_v2, investors_v2, funds_v2, pension_funds_v2). Qwen body-aware classifier shipped. OpenRouter + Inferno access. Brave Search API subscribed ($100/mo). MCP-first delivery pattern established. bios_pipeline reference implementation at bios_pipeline/brave_crawl.py.

Blockers: none. #185 is prerequisite for Phase 3 extraction but not for Phase 0-2.

Related: task #216 (lp_data) shows the "mars-owned vertical writing direct-to-v2" pattern that Phase 3 generalizes across event types. #217 (SEC Daily Signal) shipping first will produce editorial demand for the new event categories — mutual reinforcement. #218 (Publish MCP catalog) benefits from richer event substrate.