Working title — rename freely. This repo started at Civic Tech DC Project Night, 2026-05-13.
A mobile-first map that answers one question for paddlers, rowers, and (where legal) swimmers across the inner DMV:
Is it safe to get in the water today?
We unify bacterial sampling from Riverkeeper networks, real-time DOEE sondes, USGS streamflow, EPA impairment status, and NOAA rainfall into a single traffic-light report card per recreation site. Everywhere else, this data is scattered across PDFs, dashboards, and dormant Water Reporter embeds. We are the consumer face that the data-producing orgs don't have the bandwidth to build themselves.
Phase 5 landed — keyless-data unlock + UI cohesion. A functional MVP is on the branch. Drop in a Swim Guide token + Cloudflare account to publish.
What works today:
- ✅ Next.js 16 App Router on React 19, MapLibre 5 with Mapbox
outdoors-v12raster tiles (falls back to OSM, both token paths supported). - ✅ Once UI System (
@once-ui-system/core) as the design language, with a tokenized radius/shadow/eyebrow scale inapp/globals.css. - ✅ Seven connectors wired up — USGS NWIS, USGS Water Quality Portal (regional bbox query, ~250 monitoring stations auto-discovered and snapped to nearest launch via haversine), NOAA precip, NOAA Tides & Currents, EPA How's My Waterway (all real APIs); Anacostia Riverkeeper and DC DOEE sondes (fixture-backed, real integration paths confirmed by Phase 2 spike).
- ✅ Deterministic grading rubric per
GRADING.md— 21 tests covering all 5 worked examples, 12 edge cases, and the 7/30/90-day staleness windows. - ✅ Stale-grade pin variant: bacteria past the 7-day freshness window but within 90 days drives a faded "last known" verdict instead of plain gray.
- ✅ Build pipeline: connectors → normalize → grade → emit
sites.geojson,grades.json,history/<id>.json,manifest.json,sources.json. - ✅ 34 inner-DMV sites in
data/sites.json, validated against the bounding box + DC swim prohibition. - ✅ Site detail cards with grade hero, reason sentence, freshness-stamped signal breakdown, 30-day sparkline, source attribution, share button, deep-linkable
/site/<id>pages. - ✅ Activity toggle (paddle ↔ swim) re-grades thresholds per FR-20.
- ✅ Methodology, About, Sources pages + first-visit disclaimer interstitial.
- ✅ PWA manifest + service worker (cache-first shell, network-first data).
- ✅ GitHub Actions workflows:
ci.yml(per-PR) andconnectors.yml(scheduled cron). - ✅ GitHub issue templates for non-technical contributors (suggest a site, report an incorrect grade, verify a site, bug report, feature request).
- ✅ Loading / refresh-error / empty / stale UI states; design tokens +
Eyebrowcomponent documented inapp/components/. - ✅ React error boundary + Sentry-ready wiring (drop in
NEXT_PUBLIC_SENTRY_DSN). - ✅ Recharts-based 30-day grade history on every detail card.
- ✅ Per-site Open Graph + Twitter card meta (deep-link previews).
- ✅ PWA manifest with shortcuts + maskable icon.
- ✅ Lighthouse CI on every PR (current scores: desktop 93 perf / 96 a11y / 100 best-practices / 100 SEO).
- ✅ Operational runbook at
docs/runbook.md. - ✅
GETTING_STARTED.mdonboarding guide for non-technical contributors. - ✅ 70 unit tests + sites validator + type-check all green.
What's queued for actual launch:
- Real Anacostia Riverkeeper data via Swim Guide API (token request pending — outreach plan in
docs/outreach.md§ 3.1). - Real DC DOEE sonde data via the EQuIS portal (next spike: interactive DevTools inspection on dcdoeepub.equisonline.com).
- EPA HMW assessment-unit IDs verified against ATTAINS (currently HTTP 422 because the AU strings in
data/sites.jsonhaven't been confirmed). - Site catalog expansion 34 → ~50 (community-sourced via issue templates).
- Production deploy on a real Cloudflare Pages + R2 setup (env vars + secrets — see
CONTRIBUTING.md§ 9). - Legal review of the disclaimer copy.
- Mobile Lighthouse Performance is currently 72 (LCP dominated by MapLibre bundle). Acceptable for civic-tech MVP; track via CI and revisit if it regresses.
git clone <this-repo>
cd dmv-water-watch
npm install
npm run pipeline # fetch + grade + write artifacts (≈3 sec)
npm run dev # http://localhost:3000Open the browser. You should see 34 pins across the inner DMV — a mix of green/yellow/red where bacterial data is fresh, faded "last-known" pins where it's stale, and dashed gray pins where no signal exists yet. Tap one to see its grade, reasoning, and 30-day history.
No API keys or accounts are required for local dev. The default map style is OpenStreetMap raster, served token-free; drop a Mapbox token into .env.local (see .env.example) for the higher-quality outdoors-v12 style.
For deeper testing and contribution guidance, see CONTRIBUTING.md and TESTING.md.
- Paddler-first, with a swimmer toggle for legal swim sites in MD/VA. DC prohibits swimming, so secondary-contact thresholds drive defaults. Nobody else owns this persona.
- First DMV product to automate the "48 hours after rain" rule with a CSO-aware overlay on top of bacterial results.
- Freshness as a first-class UI primitive. Every card shows lab-age, rain-age, sonde-age honestly. This is the single biggest credibility differentiator versus Swim Guide.
- Inner-DMV launches catalog — Bladensburg to National Harbor, the corridor every other product underserves.
- Open source, MIT, civic-tech ethos. Connectors are modular; new data sources are TypeScript files in
connectors/.
| Doc | What it answers |
|---|---|
GETTING_STARTED.md |
Friendly intro for non-technical contributors — site curators, paddlers, neighbors. Start here if you don't write code. |
CONTRIBUTING.md |
How to install, run, test, and extend the app. Start here if you do. |
TESTING.md |
Smoke tests, layered verification, troubleshooting. |
REQUIREMENTS.md |
Personas, user stories, functional + non-functional requirements, success metrics |
ROADMAP.md |
Phased delivery plan with acceptance criteria per phase |
ARCHITECTURE.md |
Tech stack, data flow, deployment topology |
GRADING.md |
The traffic-light rubric, thresholds, edge cases, worked examples |
DATA_SOURCES.md |
Per-source spec for every connector (live + planned) |
UX.md |
Screen inventory, states, accessibility, copy guidelines, disclaimer plan |
docs/adr/ |
Architecture Decision Records — why we chose what we chose |
docs/sites-curation.md |
How the ~50 hand-curated recreation sites get selected and maintained |
docs/outreach.md |
Cold-outreach plan for Riverkeeper / DOEE partnerships post-prototype |
npm run dev # start the dev server
npm run pipeline # regenerate public/data/ from all connectors
npm test # run unit tests (70 tests)
npm run grading:test # only the grading-rubric tests (21 tests)
npm run validate:sites # lint data/sites.json
npm run typecheck # tsc --noEmit
npm run build # pipeline + production static export to out/
npm run connector:run -- usgs-nwis # run a single connector to stdoutFull command reference: CONTRIBUTING.md § 4.
Read CONTRIBUTING.md for the full guide. The connector contract is in connectors/README.md. New data sources are isolated TypeScript modules that emit normalized records — drop one in, register it in pipeline/connectors.ts, and it appears on the map after the next pipeline run.
For non-code contributions, the highest-leverage work is:
- Site curation — physically verifying that a boat launch at
lat, lonis real, signed, and has parking. Seedocs/sites-curation.md. - Outreach — building relationships with Anacostia Riverkeeper, Potomac Riverkeeper Network, and DC DOEE so we eventually get direct data feeds instead of scrapes. See
docs/outreach.md.
MIT. © 2026 Samuel Giacinto and contributors.
This application provides aggregated water-quality information for educational and recreational planning purposes only. It does not guarantee safety. Sample-based water-quality data is by nature retrospective; conditions can change between samples. Always use your own judgment, observe posted signage, and follow guidance from local authorities. See UX.md § Disclaimer for the full user-facing copy.