riprap-nyc / DEMO-PLAYBOOK.md
seriffic's picture
docs: playbook reflects final live-EO state with map polygons
4e0c875
# Riprap Demo Playbook
**For:** AMD Developer Cloud Hackathon Β· May 4–10, 2026
**Live URL:** https://lablab-ai-amd-developer-hackathon-riprap-nyc.hf.space
## What was fixed for the demo
1. **Cornerstone (Hazard Reader) latency** β€” DEP stormwater + Sandy 2012 join went from a 33-second cold-load to <100ms. Both layers are baked to compact GeoTIFFs (`data/baked/`, 7 MB total) sampled with rasterio. The 33s ReadTimeouts in batch testing are gone.
2. **Heavy register specialists no longer hang** β€” `step_nycha`, `step_doe_schools`, `step_doh_hospitals` previously did 20 polygonΓ—polygon intersections per query (8+ minute hang on HF Space CPU). They now read pre-computed exposure flags from `data/registers/*.json` (sub-millisecond). Hospitals don't have a pre-built register but read the 30 KB GeoJSON directly and sample the new Cornerstone rasters per hit.
3. **Live EO chain β€” every fine-tune renders on the map** β€” every Sentinel-2-driven specialist now produces polygons that surface as a MapLibre layer:
- `prithvi_live` (NYC Pluvial flood segmentation) β†’ blue water polygons (`prithvi-live-fill`/`-line`)
- `terramind_lulc` (NYC LoRA, 5-class land cover) β†’ categorical fill keyed on `fill_color` per feature (`terramind-lulc-fill`/`-line`)
- `terramind_buildings` (NYC LoRA, binary building mask) β†’ red building footprints (`terramind-buildings-fill`/`-line`)
- `terramind` (IBM v1 base synthesis, DEM→LULC) → uses the same LULC layer, falls back when the LoRA didn't fire
Wire format: droplet returns `pred_b64` (uint8 per-pixel argmax raster) + `pred_shape` + `class_labels`; HF polygonises against the chip's WGS84 bounds via `app/context/_polygonize.py`. Verified live on Beach Channel: 118 LULC polygons, 6 building polygons, 101 synthesis polygons; prithvi correctly returns 0 (address is inland and dry). Three droplet bugs fixed along the way: `_build_chip_tensor` 5D-input crash for the LoRAs; missing `synthesis` adapter route; DEM shape mismatch (`(1,H,W)` vs `(1,1,H,W)`).
4. **Misleading UI copy fixed** β€” three Stone specialists (TerraMind Buildings/LULC/Synthesis, Prithvi-NYC-Pluvial) previously claimed `RIPRAP_HEAVY_SPECIALISTS=0` when they silently skipped. Heavy specialists are actually enabled in production β€” the new copy reflects the actual cause (no recent <30% cloud Sentinel-2 chip / inference unavailable).
## The 3 demo queries
### 1. **2508 Beach Channel Drive, Queens** β€” full single_address activation
**What it shows:** the deep-data-density address from FRIDAY-REPORT. Single_address intent triggers every specialist:
- **Cornerstone:** Sandy outside, all DEP scenarios outside (this is Bayswater, just inland of the Sandy zone β€” useful counter-example)
- **Touchstone:** 2 FloodNet sensors (600m radius), 64 NYC 311 flood complaints, NOAA station 8516945 live, NWS hourly METAR
- **Lodestone:** Granite TTM forecasts (peak 0.47 ft surge ~2h ahead), TTM 311-forecast, NWS alerts
- **Keystone:** **7 MTA entrances** (6 in Sandy, 5 in DEP-2080), **2 NYCHA developments**, **5 schools** (4 in Sandy, 3 in DEP-2080), **1 hospital** β€” all with elevation/HAND from baked rasters
- **Live EO:** **`prithvi_eo_live`** fires with a real Sentinel-2 chip (≀30% cloud, ≀120 days old) β€” flood segmentation runs on the MI300X; **`eo_chip_fetch`** pulls multi-modal S2L2A + S1RTC chip
- **Capstone:** Granite Embedding RAG (3 hits) + GLiNER typed extraction + Mellea-grounded Granite-4.1 8B reconciliation (4/4 requirements pass)
**Talking points:** "This is what 'resilient infrastructure briefing' produces when every specialist fires. The Touchstone and Cornerstone disagree β€” Bayswater is just inland of Sandy 2012 but its subway entrances 200m away are deep in the zone. Live data: the Prithvi specialist just pulled a Sentinel-2 image from this month and ran flood segmentation on the MI300X."
### 2. **Coney Island I Houses, Brooklyn** β€” neighborhood path, narrative briefing
**What it shows:** neighborhood intent routing. Planner reads "Houses" with no street number β†’ resolves to NTA polygon, runs the neighborhood specialist set (sandy_nta, dep_*_nta, nyc311_nta).
- Returns a Markdown briefing structured as Status / Empirical / Modeled / Policy
- Uses NTA-aggregated metrics ("X% of the neighborhood was inundated during Sandy")
- ~7-second total latency
**Talking points:** "Same system, different intent. The planner picks neighborhood for queries that name an area without a house number. The briefing is denser narrative; the underlying data is NTA-aggregated, which is the right unit for emergency-management framing."
### 3. **80 Pioneer Street, Brooklyn (Red Hook)** β€” single_address, full activation
**What it shows:** Red Hook is canonical Sandy turf. All Stones populated:
- **Cornerstone:** Sandy inside, DEP-2080 outside, microtopo 0.83m elevation (low-lying)
- **NYCHA:** Red Hook Houses (East/West) β€” both inside Sandy
- **Schools:** PS 27, PS 30 β€” both inside Sandy
- **Hospitals:** 3 nearby
- **MTA:** entrances inside Sandy
- **Live Sentinel-2 chip + Prithvi flood segmentation** runs
**Talking points:** "Red Hook is the canonical 'this is what we got wrong in 2012' address. The system surfaces a NYCHA development, a school, a hospital, and a subway entrance β€” all at risk in the same query. That's the demo: one query, four asset classes, every Stone audited, plus a live Sentinel-2 chip."
## Reading the trace
The trace UI groups specialists by Stone. Each row shows status (fired / silent / errored) and a one-line skip reason if silent. Silent isn't broken β€” it's the engineering-honest contract: when a specialist's preconditions aren't met, it stays silent rather than fabricating.
**Honest skips you'll see in the demo:**
- *"FloodNet sensor recurrence: sensor has < silent-floor historical events; forecast omitted"* β€” sensor too new to forecast
- *"NPCC4 SLR projection: not yet wired into FSM"* β€” out of scope, listed for transparency
- *"NWS public alerts: no active flood-relevant alerts at this address"* β€” true, no active alert today
**Specialists that may show as skipped/errored on the demo:**
- **TerraMind LULC + Buildings: LIVE** β€” finetuned NYC adapters running on the MI300X. Hits live Sentinel-2 + Sentinel-1 chips. **TerraMind Synthesis** (the third terramind variant, DEM-driven LULC synthesis) has no remote inference route, so it returns a clean *"deps unavailable: terratorch (RuntimeError)"* on the HF Space β€” not on the demo critical path.
- **floodnet_forecast** β€” sensors with <5 historical events skip the forecast.
- **nws_alerts** β€” when no flood-relevant alert is active at the address (most days).
## Caveats to be ready for
- **NYCHA cards are binary, not pct-overlap** β€” per-query view shows `inside_sandy_2012: bool` and `dep_*_class: int` instead of `pct_inside_sandy_2012` floats. Same source data, less precise representation but fast (~ms instead of 8 min). The `/api/register/nycha` city-wide register is unchanged.
- **Heavy specialists are enabled** but may silently skip if Sentinel-2 chip fetch returns nothing recent for the query address. Prithvi-EO Live looks back 120 days for <30% cloud β€” most NYC addresses have a recent hit; very edge-of-NYC ones may not.
- **Inference is remote** on AMD MI300X via vLLM at `165.245.141.218:8001`. If the droplet is down, the reconciler will fail and the Capstone Stone won't render a paragraph; specialists will still fire and surface their data.
- **Bake re-runs** β€” `data/baked/*.tif` (7 MB) was generated once via `scripts/bake_cornerstone_rasters.py`. Re-bake when DEP scenarios get republished by NYC DEP (rare, ~every 5 years).
- **Register rebuilds** β€” `data/registers/*.json` are regenerated by `scripts/build_*_register.py` when the underlying NYCHA / DOE / NYS DOH datasets refresh.
## End-to-end smoke test
To verify before showtime:
```
.venv/bin/python scripts/probe_addresses.py \
--base https://lablab-ai-amd-developer-hackathon-riprap-nyc.hf.space \
--addresses "2508 Beach Channel Drive, Queens|Coney Island I Houses, Brooklyn|80 Pioneer Street, Brooklyn" \
--timeout 240
```
Expected: 3/3 PASS, each in 6–17 s after warm-up. If 2508 Beach Channel takes >60s, that's the post-restart pre-warm finishing β€” re-run.
## Final summary of changes shipped this cycle
| Change | Files | Effect |
|---|---|---|
| Cornerstone raster bake | `app/flood_layers/{dep_stormwater,sandy_inundation}.py`, `scripts/bake_cornerstone_rasters.py`, `data/baked/*.tif` | 33s β†’ <100ms cold; <5ms per query |
| Register refactor | `app/registers/{nycha,doe_schools,doh_hospitals}.py`, `app/registers/_loader.py` | 8+ min hang β†’ <100ms total |
| EO deps | `requirements.txt` (planetary-computer/pystac-client/rioxarray/xarray/einops) | Live Sentinel-2 + Prithvi remote inference |
| Deps gate split | `app/flood_layers/prithvi_live.py`, `app/context/terramind_synthesis.py`, `app/context/terramind_nyc.py` | Tier-1 chip-fetch separated from Tier-2 local-inference |
| UI honesty | `web/sveltekit/src/lib/data/stoneRegistry.ts`, `web/sveltekit/src/lib/client/registerAdapter.ts`, `web/sveltekit/src/routes/q/[queryId]/+page.svelte` | "RIPRAP_HEAVY_SPECIALISTS=0" copy gone; new NYCHA schema |
| HF env | `scripts/update_hf_env.sh` (RIPRAP_NYCHA_REGISTERS=1), set on live Space | Heavy register specialists actually attached to FSM |
| FSM consumers | `app/fsm.py`, `app/reconcile.py` | Match new NYCHA schema |
| Warmup hygiene | `web/main.py` | Drop 91 MB Sandy GeoJSON pre-load (no longer needed) |