docs: refresh AGENTS.md, HANDOFF.md, and README with recent work

- Create AGENTS.md: comprehensive agent guide covering architecture,
  data flow, error bar convention (90% intervals), D3 usage patterns,
  null safety pitfalls, deployment checklist, and related repos
- Update HANDOFF.md: mark CORS as resolved, document chart improvements
  (Chart 5 ridgeline rewrite with synthetic draws + smooth rendering)
- Refresh README.md: accurate tech stack (D3 v7), updated file structure
  with new chart files, corrected API endpoint table
This commit is contained in:
2026-08-10 17:10:35 -04:00
parent 3fe004356e
commit 6c8e1e8409
3 changed files with 184 additions and 30 deletions
+18 -15
View File
@@ -20,19 +20,20 @@ npm run preview # serve built files locally
Visitors select a U.S. state, search for a school district (with suggestions of districts that have the most arrests), and see 6 charts comparing observed data against Bayesian model estimates:
1. **Arrests over time** — raw counts by CRDC wave with per-1k rate labels
2. **Rate by student group** — bar chart, most recent year (observed vs. modeled)
3. **District vs. national** — highest-rate group compared to the U.S. average
4. **Model predictions vs. observed** — four quadrants (one-year/three-year × baseline/covariate)
5. **Observed rate vs. model distribution** — ridgeline-style comparison across models
6. **Exceedance probability** — P(district > national) per student group
1. **Arrests over time** (Chart 1) — raw counts by CRDC wave with per-1k rate labels, built with inline SVG
2. **Rate by student group** (Chart 2) — bar chart, most recent year (observed vs. modeled), SVG
3. **District vs. national** (Chart 3) — highest-rate group compared to the U.S. average, SVG
4. **Model predictions vs. observed** (Chart 4) — four quadrants (one-year/three-year × baseline/covariate)
5. **Predicted rates by student group** (Chart 5) — D3 density ridges showing posterior distributions with diamond markers for observed rates
6. **Exceedance probability** (Chart 6) — P(district > national) per student group
## Architecture
### Tech Stack
- **React 19** + **Vite** (static site generation, no backend required)
- Plain CSS custom properties for styling (matches Civilytics design tokens exactly)
- SVG-based charts rendered inline (no D3 or charting library dependencies)
- D3.js v7 for data-driven visualizations (density ridges, scales, axes)
- Inline SVG rendering — charts are built with vanilla DOM/D3, not charting libraries
- Calls the public read-only API directly from the browser
### File Structure
@@ -40,6 +41,8 @@ Visitors select a U.S. state, search for a school district (with suggestions of
crdc-demo/
├── index.html # Entry point
├── vite.config.mjs # Vite build config
├── public/ # Static assets (wordmark, favicon)
│ └── civilytics-wordmark.svg # Civilytics wordmark from civilyticsR package
├── src/
│ ├── main.jsx # React entry
│ ├── App.jsx # Main router (state → search → loading → charts)
@@ -50,18 +53,17 @@ crdc-demo/
│ │ ├── LoadingAnimation.jsx # Animated histogram grid during data fetch
│ │ └── ChartPanel.jsx # Orchestrates all 6 charts + data fetching
│ ├── charts/
│ │ ├── ArrestsOverTime.jsx # Chart 1 — line chart by wave
│ │ ├── RateByGroupBar.jsx # Chart 2 — bar chart by group
│ │ ├── DistrictVsNational.jsx # Chart 3 — comparison vs. national avg
│ │ ├── ModelDrawsComparison.jsx # Chart 4 — quadrant model comparison
│ │ ├── ObservedRateDensity.jsx # Chart 5 — ridgeline proxy from intervals
│ │ └── ExceedanceProbability.jsx # Chart 6 — P(district > nat) per group
│ │ ├── ArrestsOverTime.jsx # Chart 1 — line chart by wave (SVG)
│ │ ├── RateByGroupBar.jsx # Chart 2 — bar chart by group (SVG)
│ │ ├── DistrictVsNational.jsx # Chart 3 — comparison vs. national avg
│ │ ├── ModelDrawsComparison.jsx # Chart 4 — quadrant model comparison (D3)
│ │ ├── RateDensityRidgeline.jsx # Chart 5 — density ridges per student group (D3)
│ │ └── ExceedanceProbability.jsx # Chart 6 — P(district > nat) per group
│ ├── styles/tokens.css # Civilytics design tokens (colors, fonts, spacing)
│ └── data/national_rates.json # Static national rates fixture for comparisons
├── Dockerfile # Multi-stage build → nginx static server
├── docker-compose.yml # Local dev / self-hosted deployment
├── _config.yml # Git Pages (self-hosted equivalent) config
└── .github/workflows/deploy.yml # CI/CD — builds and uploads artifacts
└── .gitea/workflows/pages.yml # CI/CD — builds and deploys to pages branch
```
### API Endpoints Used
@@ -70,6 +72,7 @@ crdc-demo/
| `/api/v1/models` | List available Bayesian model specs | Once (cached) |
| `/api/v1/districts?q=&state=` | District name/geo lookup → LEAID | On keystroke |
| `/api/v1/estimates/{leaid}?model=X&year=Y` | Estimates for one district/model/year/group | ~40 calls per district |
| `/api/v1/draws?...` | Locate raw-posterior Parquet shard (bulk only, not used in browser) | Not called from app |
| `/data/national_rates.json` | Static national rates fixture (committed) | Once per session |
## Deployment