🏛️ System architecture
⚙️ The 5-stage pipeline
warn_publish.run() executes.🔀 Data flow & change detection
🗓️ CI workflow — one scheduled run
monitor.yml,
end to end, including the two decision branches that gate email + commit.ETag / 304 caching
Every fetch sends If-None-Match + If-Modified-Since from
meta.json. A 304 short-circuits the download — bandwidth and
churn avoided while the dashboard still rebuilds from local data.
Anti-churn alert ledger
The EDD feed flip-flops its record count between two versions across consecutive fetches.
"New" is measured against the cumulative notified_keys.json ledger — not the
prior run — so each notice can fire at most one email, ever.
Cumulative store
EDD re-exports silently drop earlier notices. warn_cumulative.json is a union
of every notice ever observed (latest wins on conflict), so a filing never vanishes from the
dashboard once it has appeared.
Send-then-record + BCC batching
Keys are recorded only after the email actually sends, so a failed send retries next run instead of being lost. Subscribers are BCC'd in batches of ≤ 90 to stay under Gmail's per-message recipient cap.
Git as the database
No DB, no server. State lives in versioned JSON committed back to the repo; every run is an
auditable diff. [skip ci] on auto-commits prevents infinite workflow loops.
Single-file deployable site
Charts are pre-rendered to self-contained Plotly divs and inlined into one
index.html — the dashboard is a static asset GitHub Pages serves with zero
runtime dependencies.