Architecture and Deployment
The pattern every dashboard follows
fetch → build → dashboard → (health gate, on most) → commit → deploy
Each dashboard is its own folder (ons/, poc/, contratos/, flows/,
supply/, pld/, precos/, desk/) with the same shape: a <slug>_pipeline.py
CLI that fetches a public data source and aggregates it into a Parquet
store (fetch / build / sometimes all), and a dashboard.py (imported
by the CLI, or run standalone) that reads the store and writes one
self-contained index.html plus a sibling payload.json.gz. Nothing here
needs a database or a running server — the output is static files served by
GitHub Pages.
The browser fetches payload.json.gz and inflates it client-side via
DecompressionStream (Chrome/Edge 80+, Firefox 113+, Safari 16.4+; older
browsers get a message instead of a blank page) rather than the payload
being base64-embedded directly in the HTML — this keeps the HTML shell
small and the data cacheable separately from it.
Shared kit
shared/dashboard_kit.py and shared/theme.css are the single source of
truth for everything that used to be pasted into each dashboard by hand:
CSS custom properties for the light/dark palette, the Pacaembu font
embedding, the cross-dashboard nav bar (nav_links_html() — every
dashboard, the Wiki, and About, always in the same order, from one _SITES
dict), the theme toggle, i18n (GB_I18N, English/Portuguese), and shared
JS helpers (table sort, CSV/XLSX export, chart palette). A color, font, or
nav change is a one-line edit in one file that reaches every dashboard on
its next scheduled rebuild — see the site's ADR-001
for why this exists.
Deployment
Each dashboard has its own GitHub Actions workflow (.github/workflows/<slug>.yml)
on its own schedule — daily, every few hours, or monthly, matched to how
often that dashboard's source actually publishes new data — plus
workflow_dispatch for a manual run and a push trigger scoped to that
dashboard's own folder and shared/** (so a shared-kit change rebuilds
every dashboard, but a change to one dashboard's own code only rebuilds
that one). Most dashboards also run a health gate before publishing —
a sanity check on row counts, data age, and series coverage — so a broken
or stale fetch doesn't overwrite a working deploy; the previous build stays
live if the gate fails.
Every workflow commits straight to its own subfolder on main and retries
with git pull --rebase origin main on a push race, rather than using a
single repo-wide Pages-deploy API — that's what lets several dashboards'
workflows run and redeploy concurrently without stepping on each other.
Data architecture
Two tracks, by dashboard:
- Small/checked-in stores (poc, contratos): the tidy Parquet store is small enough to commit directly to git, so history lives in the repo itself.
- Large/gitignored stores with a private lake mirror (ons, flows,
supply, pld, precos):
raw/anddata/are gitignored (too large or fully rebuildable from source) and instead mirrored to a private Cloudflare R2 bucket viashared/data_kit.py, so a CI run that doesn't have local history can stillensure_lake()a copy when it needs one — this is how The Desk (which reads across several dashboards' data at once) gets its inputs without vendoring every sibling's Parquet file into its own workflow.
Every dashboard's public payload.json.gz is also published to a public R2
artifacts bucket, independent of the committed index.html — this is what
lets a dashboard's data refresh on its own schedule (a new payload) without
necessarily needing a full HTML rebuild.
The wiki you're reading
This wiki is markdown source (wiki-src/) rendered to static HTML (wiki/)
by build_wiki.py, deployed by its own workflow on any push to
wiki-src/** or build_wiki.py. It draws its palette from
shared/theme.css like every dashboard, but is otherwise independent —
pure documentation, no data pipeline behind it.