Meridian

Architecture

How a customs declaration becomes an opportunity

Two halves that meet only through files on disk: a Python pipeline that turns raw extracts into typed, flagged rows, and a Next.js app that reads its committed output. The app never runs the pipeline and never reads its working files — only the payloads in public/. That is why Vercel can build this without Python, pandas, or the 540 MB intermediate CSV.

Declarationsloading
Declared valueUSD, observed trade — not addressable opportunity
Covered accountsloading
Committed payloads35.84 MBof 50 MB before these should move to Blob storage

Every number on these pages is measured from the artifacts, not typed into them. Sizes are read from disk on the server; counts come from a payload the page already loads. This exists because the numbers that were typed — all of them, in README.md — had drifted by two to three times.

Raw spreadsheet to rendered page

What produces what, and in which order?

Fig. 01

Dotted edges are side inputs and side outputs. The chain through dashboard-data.json is an ordering constraint, not a convenience: scripts.prospect.index reads that file to decide which companies count as accounts.

The payloads

PayloadBuilt bySize
Opportunity contract
public/dashboard-data.json
python3 -m scripts.opportunity.build4.65 MB
Per-account evidence
public/evidence/arkema.json
python3 -m scripts.opportunity.build0.45 MB
Analytics cube
public/analytics-cube.json
python3 -m scripts.analytics.cube4.86 MB
Entity cube
public/entity-cube.json
python3 -m scripts.analytics.entities3.43 MB
Prospect index
public/prospect-index.json
python3 -m scripts.prospect.index2.22 MB
Lanxess catalog
public/lanxess-catalog.json
python3 -m scripts.lanxess.catalog1.56 MB

The evidence/ row is one representative file. There are 40 of them, one per covered account, totalling 19.12 MB — fetched one account at a time, never as a set, which is why the directory can be larger than everything else combined without costing a page load. See what each payload holds and what breaks when it is stale →

Where to go next

PipelineNormalization, entity resolution, and the review queues where a human decides.
RunbookWhat to type, in order, from a fresh data drop to a deployed site.
ContractsEach payload: builder, readers, live size, and the failure it causes.
EngineBuckets, plays, coverage, and the defects found the hard way.
App mapEvery route, walked from the filesystem, plus the portfolio state model.