246d014a4e
The docs drifted as the project moved fast. Fixed: - Counts to the validator (canonical): 105,743 records, Jurisdiction 29,905 (README had 105,746/29,908 — the exact-GEOID backfill dropped 3 city nodes and only the commit message was updated, not the docs). - sources.md: officeholders-v2 → v3 (13,329); added the inputs that landed since — municipal GEOID backfill, place/district crosswalks, the complete U.S. Code corpus, the executive offices, and the authority edges. - board.md: it described income/population/poverty only; now names the partisan-lean lens, zoom/pan, and the district+representative drill-down. - data-model.md: stale '17,506 files' → 105,743. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
2.7 KiB
Markdown
61 lines
2.7 KiB
Markdown
# The Board
|
||
|
||
`viz/board.html` is the Board — a rebuilt *view* of the canonical tree, never a
|
||
source. It is a nationwide county choropleth with four lenses — **income,
|
||
population, poverty, and partisan lean** (the head-count D/R balance of each
|
||
county's covering state + federal representatives) — that is **zoomable and
|
||
pannable** (scroll to zoom 1×–16× toward the cursor, drag to pan, double-click to
|
||
reset). Any county opens a drill-down of its officeholders, its demographics, and
|
||
the **districts + representatives** that cover it (congressional, state senate,
|
||
and state house, each with the share of the county it covers).
|
||
|
||
## Pipeline
|
||
|
||
```text
|
||
data/officeholders-v3.jsonl ─┐
|
||
data/acs_county.jsonl ├─ scripts/build_viz.py ─> viz/board.html (inlined)
|
||
data/place_county_crosswalk ┤ └> viz/county_*.json (companions)
|
||
data/fec_candidates, acs_cd ─┘
|
||
```
|
||
|
||
`build_viz.py` resolves each official to a county with the **same**
|
||
`build.place_resolver` + `county_slug` the tree uses, so the Board and the tree
|
||
agree by construction. It then does two things:
|
||
|
||
1. Writes `viz/county_data.json` and `viz/county_detail.json` — human-readable,
|
||
diffable companions.
|
||
2. **Injects** the fresh data straight into `board.html`, rewriting three marked
|
||
lines: `const D` (per-county summary), `const CDETAIL` (per-county rosters),
|
||
and `const TOTALS` (the header counts).
|
||
|
||
## Why the data is inlined
|
||
|
||
The Board is deployed as a **self-contained Artifact**, and the Artifact CSP
|
||
blocks every external request — no `fetch`, no external scripts, styles, or
|
||
fonts. So the county data cannot be loaded at runtime; it must live inside
|
||
`board.html`. That is why `build_viz.py` writes into the file rather than letting
|
||
the page read the JSON companions (nothing reads them — they exist for diffs).
|
||
|
||
The header totals (officeholders, candidates, counties, districts) read from the
|
||
injected `const TOTALS` object — never hardcoded — so they cannot drift out of
|
||
sync on the next rebuild.
|
||
|
||
## Rebuild
|
||
|
||
```bash
|
||
make board # regenerate board.html + the JSON companions from the tree
|
||
```
|
||
|
||
`board.html` is deterministic: two runs are byte-identical.
|
||
|
||
## Redeploy the Artifact
|
||
|
||
Editing `board.html` in the repo does **not** update a live Artifact — the
|
||
deployed copy is a snapshot. After `make board`, the Board must be **republished**
|
||
to its existing Artifact URL for the change to appear on claude.ai.
|
||
|
||
Republishing to the *same* URL requires passing that URL back to the publish step
|
||
(`Artifact url=<existing-url>`); publishing without it mints a new URL. Keep the
|
||
current Board Artifact URL in the team's deployment record so redeploys stay in
|
||
place instead of scattering new links.
|