An edge-first early-warning system that predicts preventable weather-driven freeway incidents using Gemma 4 E-series models across a roadside Jetson, an in-car Jetson, and a human-approved cloud ops layer.
Submitted to: The Gemma 4 Good Hackathon — Global Resilience track (primary); Safety & Trust (secondary via human approval); Special Tech track: LiteRT.
License: CC BY 4.0 (see LICENSE).
A mother drives I-94 with her kids; a Jetson in the footwell watches over them. Miles ahead, a roadside Jetson detects a pile-up signature forming. An ops operator's phone buzzes; she approves a VMS billboard update on her mobile PWA. Her in-car Jetson chirps a slow-down warning. The billboard passes overhead. Exit. Baby still asleep.
Anchor event: the 2015 Galesburg pile-up (I-94, 193 vehicles, 1 fatality) — NHTSA FARS ST_CASE 260911. A near-identical 193-vehicle pile-up repeated at Zeeland 2026-01-19, proving the signature class. PEAS fires with ≥ 3-hour lead time on historical winter events and 0% on clear-day counter-fixtures.
Roadside Jetson (E4B on LiteRT) In-car Jetson (E4B on LiteRT)
│ ▲
│ detects signature │ voice/text warning
▼ │
Ops server (FastAPI) ──SSE──► Mobile PWA approval (one-tap)
│ Gemma 4 E2B composes brief via Ollama │
▼ │ approve
VMS billboard update ◄────────────────────────┘
Three surfaces. One Gemma 4 story. Full chain weights: ~10.6 GB (3.4 GB Jetson E4B via LiteRT + 7.2 GB Mac E2B via Ollama).
Full details: docs/architecture.md.
Locked in docs/requirements/01-program.md §6:
- §6.1 Gemma 4 runs locally at the edge; no cloud inference on critical path.
- §6.2 Human-in-the-loop for every actuation. No driver output without operator approval.
- §6.4 Only public data (FARS, ERA5, NWS, NIFC, EPA, MDOT, CDOT — all listed with licenses).
- §6.5 CC BY 4.0 — this repo is reusable.
- §6.8 Composer-not-classifier: Gemma 4 reasons about deterministic detector output; never learns to classify.
- §6.9 Not a black box: every prediction emits an OTel span with inputs, thresholds, and rationale.
One-time setup on a Mac with Ollama installed (first-time total ≈ 10–15 min, dominated by the 7.2 GB gemma4:e2b pull):
ollama pull gemma4:e2b # ~5–10 min on a fast connection
python3 -m venv .venv # ~3 s
.venv/bin/pip install -r requirements.txt # ~15–60 s
(cd peas-ops-pwa && npm install && npm run build) # ~30–60 s cold, ~5 s warmThen the demo itself fires in under 5 seconds (deterministic replay, no live Gemma 4 inference):
./demo.sh # local-only: ops server + PWA at http://127.0.0.1:8080/app/
./demo.sh --fire # auto-fires a deterministic Galesburg-analog scenario
./demo.sh --tunnel # additionally opens a public HTTPS URL via cloudflaredFire a scenario from a second terminal:
.venv/bin/python3 scripts/demo/play_scenario.py \
--scenario galesburg-analog --full-chainExpect a synced 3-surface JSONL triple under outputs/demo/<UTC-date>/ sharing one approval_request_id. Grep the ID to replay the full decision.
Documented in DEMO.md. Secondary scenario: outputs/demo-cache/east-troublesome-analog/ (Colorado fire hazard).
Note: the deterministic replay does not require a live Gemma 4 runtime. If you want to exercise the live 5-hop chain (real Ollama tool calls on your Mac, plus a live Jetson detect hop via SSH tunnel), see scripts/spike/run_chain.py — the measured wall on a warm chain is 79 s end-to-end (2026-04-22 re-bank), clearing the PH2 600 s gate by 87%.
- E4B on the Jetson via LiteRT (
gemma-4-E4B-it.litertlm, 3.4 GB) — edge detection + in-car warning composition. Cold-load 1.684 s, 8.29 tok/s decode, 383 MB peak swap. Bake-off vs Ollama: 127× faster cold-load, 1.44× faster decode, 20× less swap pressure. See outputs/bakeoffs/2026-04-19/summary.md. - E2B on the Mac via Ollama (
gemma4:e2b, 7.2 GB Q4_K_M) — ops brief composer + VMS + driver-notify tool-call hops. 5-shot stability eval: 5/5 schema pass, 59–64 word deterministic band, 100% Galesburg citation presence, 0% inline-label leak, 4.17× faster wall vs 8B variant. See outputs/deploys/e2b-stability-2026-04-22.md. - Function-calling multi-hop chain — 5 tool schemas (detect, compose, approval, VMS, notify) in scripts/tools/schemas.json. OTel spans per hop;
approval_request_idthreads across all three surfaces.
| Path | Purpose |
|---|---|
docs/requirements/ |
Frozen phase requirements (normative) |
docs/plans/ |
Phase plans (descriptive) + work breakdowns |
docs/architecture.md |
One-page system reference (architecture, governance, failure modes, audit trail) |
docs/science/ |
Canonical findings (detector validation, fire-weather rule, hazard observability) |
docs/research/ |
Pre-canonical research notes (corridor shortlists, anchor verification) |
scripts/roadside/monitor.py |
Weather-driven detection → brief compose → ops-server ingest |
scripts/ops/server.py |
FastAPI ops server + SSE fan-out + PWA static mount + VAPID Web Push |
scripts/incar/driver.py |
In-car Jetson SSE subscriber + HDMI display + Piper TTS |
scripts/spike/ |
PH2 multi-hop function-calling proof-of-concept |
scripts/demo/ |
Deterministic replay harness + sync-logging verifier |
peas-ops-pwa/ |
Mobile approval PWA (vite + preact-ts); installable, T1+T2 notifications |
outputs/demo-cache/galesburg-analog/ |
Frozen Gemma-4-composed demo scenario (crash hazard) |
outputs/demo-cache/east-troublesome-analog/ |
Frozen fire-hazard scenario |
outputs/deploys/ |
Dated evidence docs — every phase gate has one |
outputs/bakeoffs/ |
Runtime bake-off evidence (LiteRT vs Ollama) |
outputs/eval/ |
Model stability evaluations |
observe/ |
OTel substrate (PEAS-TEL-1..11 façade) + fallback JSONL tracker |
No observability stack required. Two examples using the committed corpus:
# Confirm the Galesburg 2015 anchor is in the federal record:
grep '^26,260911,' data/fars-mi-subset/mi_fatal_2015.csv | head -1
# Expected: ST_CASE 260911, Kalamazoo County (77), VE_TOTAL=58, WEATHER=4 (Snow)
# Count signature-class events per year across MI+CO+IA:
awk -F, 'NR>1 {print $1}' data/fars-mi-subset/fatal_mi-co-ia_2015-2021_signature_class.csv | sort | uniq -c
# Expected: MI 54, CO 30, IA 26 (110 total signature events 2015–2021)
# After running the demo, grep a single approval_request_id across all three surfaces:
grep "$(ls outputs/demo/*/roadside.jsonl | tail -1 | xargs python3 -c 'import sys,json; print(json.loads(open(sys.argv[1]).read().splitlines()[-1])[\"approval_request_id\"])')" outputs/demo/*/*.jsonl
# Expected: entries in roadside.jsonl + ops-server.trace.jsonl + incar.jsonl,
# all sharing one UUID — the PEAS chain-of-custody claim in one shell line.- Every on-screen Gemma 4 text in the demo comes from outputs/demo-cache/ — pre-composed, frozen. No live inference on camera (PH5 Risk #3).
- Every prediction produces a span tree in Tempo, log entries in Loki, and a JSONL fallback in
outputs/demo/<date>/. Any decision is grep-reachable from the JSONL layer alone:grep <approval_request_id> outputs/demo/<date>/*.jsonlreturns the full chain across all three surfaces without any observability backend running. - Detector thresholds validated against NOAA ERA5 hourly weather on 3 documented winter events (Galesburg 2015, Eisenhower Tunnel 2024, Zeeland 2026) + 3 clear-day counter-fixtures: 42/42 winter fire rate, 0/51 clear-day false positive rate. See docs/science/detector-validation-v1.md.
- Fire-hazard extension: same framework, different compound signals per hazard class. See docs/science/hazard-observability-v1.md and docs/science/fire-weather-alert-rule-v1.md.
| Source | Purpose | License |
|---|---|---|
| NHTSA FARS | Galesburg 2015 anchor verification | Public domain (US government work) |
| NOAA ERA5 | Historical hourly weather for detector calibration | Copernicus Climate Change Service (free, open) |
| NWS Alerts | Red Flag Warnings + winter weather for live corridors | Public domain |
| NIFC InciWeb | Wildfire perimeter + incident history | Public domain |
| EPA AirNow | Air quality index for fire-hazard rule | Public domain |
| MDOT open data | Michigan pavement condition metric (PCM) | Creative Commons / open (per MDOT terms) |
| CDOT CoTrip | Colorado real-time RWIS + VMS + Red Flag | Public (terms per COtrip.org) |
No restricted feeds, no scraped sources, no PII. See invariant §6.4 + data/experiments/<topic>/README.md for per-dataset provenance.
Neil Yashinsky — nyashinsky@gmail.com
For the full contest writeup: see docs/writeup/ once published.