Hero is now a data-native 0pt artifact (no photos, keeps grain/reveal/float); header is the 0pt wordmark with Swedish nav; layout is sv + loads JetBrains Mono + 0pt metadata; globals wires the loaded fonts. Adds CLAUDE.md + docs/01-09 as the build contract. next build green (5 routes). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.0 KiB
06 · Data engine
The engine turns sources into delivered, provenance-tagged data. It is the legal motor — the whole product positioning depends on it. Build on existing src/workers/ (adaptive_router, browser_worker, ocr_worker, db) but enforce the sourcing boundary below.
Sourcing chain (strict priority — this is the law)
For each field × market, acquire in this order; stop at the first that works:
- Official API / bulk — registries with programmatic/bulk access (highest confidence).
- Authorized public web — clearly public pages, respecting
robots.txtand rate limits, no auth walls. - Licensed partner data — contractually obtained.
- Otherwise → "not available / licensed only" — delivered honestly as level 0. Never guessed.
Prohibited (do not build, keep, or extend)
- WAF/bot-detection bypass — including FlareSolverr, "stealth"/patched browsers, undetected-* used to defeat detection.
- Proxy/fingerprint rotation to evade bans; scraping behind login walls; automated fake-account creation (Apollo etc.).
- Reconciliation: if
browser_worker.pyhas anHTTP → FlareSolverr → Playwrightchain, drop the FlareSolverr stage and keep only: polite HTTP fetch (identifies itself, obeys robots + rate limits) → optional Playwright for genuinely public JS-rendered pages (same politeness). Theadaptive_routerthen chooses among compliant methods only (API > bulk > authorized fetch), not bypass tiers.
Pipeline stages (per job)
sourcing → extraction/OCR → structuring → enrichment/fusion → QA
- Sourcing —
adaptive_routerpicks the compliant method per source; "source-racing" = query several authorized sources and take the fastest/freshest. Writes raw payloads + which source answered. - Extraction / OCR — API JSON parsed directly; documents (PDF/HTML/images) via Unstructured + OCR cascade (PaddleOCR → Tesseract → LLM fallback) in
ocr_worker. Output: candidate fields. - Structuring — NER + entity resolution (spaCy / transformers, sv/no/da/fi/de/fr), normalize to schema, link entities across markets via LEI (GLEIF golden key). Dedup with RapidFuzz.
- Enrichment / fusion — combine fields from multiple sources; compute multi-source agreement → confidence + level; append optional enrichment (NACE/SNI, sanctions/PEP flag from official lists, website/tech).
- QA — quality checks (Great Expectations or equivalent, run inside the worker): validity (format/range), completeness, freshness, uniqueness (post-dedup), corroboration. Drop rows below the order's
confidence_target(deliver fewer, never fabricate). Emit the quality report stored ondeliveries.
Each stage updates jobs (stage, pct, metrics incl. per-market progress, running counts, climbing confidence) so the production tracker / SSE can render it live.
Confidence & provenance scoring
- Per field, write a
bridge_provenancerow:source_id,freshness,confidence(0–1),level(0–3),agreed_sources. - Level: 0 = unavailable/licensed; 1 = single source; 2 = partial corroboration; 3 = ≥2 independent sources agree.
est_confidenceon the order = aggregate target; deliveredavg_confidence= actual.
sources.yaml (machine-readable registry)
Single registry that drives the router, the /sources API, and the public sourcing-map page. Schema per entry:
- source_id: gleif
name: GLEIF (LEI)
market: global
access: api # api | bulk | authorized_web | licensed
cadence: daily
licence: "open (CC0) — VERIFY current terms"
entities: [company]
fields: [lei, legal_name, address]
base_url: https://api.gleif.org
robots_ok: true # for authorized_web only; must be honored
rate_limit: "per provider terms"
notes: "cross-border key"
Starter set (VERIFY current access/licence per source before relying — terms change)
- GLEIF / LEI — typically open; cross-border key. (verify)
- Brønnøysund (Brreg, NO) — typically open API. (verify)
- Companies House (UK) — free API (key required). (verify)
- CVR / Virk (DK) — API access with terms. (verify)
- PRH (FI) — varies. (verify)
- Bolagsverket (SE) — more restricted for bulk; plan licensed/partner route. (verify)
- Vehicle: RDW (NL) open-ish, Traficom (FI), Transportstyrelsen (SE), DVLA (UK) — (verify each; vehicle/PII rules differ)
Do not hardcode access terms as fact. When building each connector, confirm the current API/licence/robots status and record it in
sources.yaml. If unclear or disallowed for bulk, route to licensed/partner or mark fields level 0.
Libraries used here
Crawlee or Scrapy (orchestration of authorized fetches) · Playwright (public JS pages, politely) · Unstructured (document parsing) · PaddleOCR/Tesseract (OCR) · spaCy / HF transformers (NER, multilingual) · Polars (fast tabular) · RapidFuzz (dedup/matching) · Great Expectations (quality) · dbt (optional transform layer). Versions/install: docs/07-dependencies.md.