deploy: sync all changes from main at 6904684
Browse filesSquashed from 5438cc8..6904684. The slides/asce/deck.pptx is tracked
via git-lfs (added *.pptx to .gitattributes), so this commit carries
only the LFS pointer β no binary blob in history.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This view is limited to 50 files because it contains too many changes. Β See raw diff
- .gitattributes +1 -3
- CODE-MORNING-BRIEF-2026-05-06.md +210 -0
- COMMS-OVERNIGHT-2026-05-06-MORNING-BRIEF.md +176 -0
- OPEN-ISSUES.md +52 -0
- OVERNIGHT-2026-05-06-MORNING-BRIEF.md +275 -0
- OVERNIGHT-2026-05-06-OUT-OF-SCOPE.md +77 -0
- app/context/eo_chip_cache.py +31 -12
- app/flood_layers/prithvi_live.py +31 -19
- app/framing.py +249 -0
- app/fsm.py +42 -2
- app/geocode.py +7 -1
- app/inference.py +2 -2
- app/intents/development_check.py +5 -1
- app/intents/live_now.py +11 -3
- app/intents/neighborhood.py +5 -1
- app/intents/single_address.py +22 -1
- app/live/floodnet_forecast.py +1 -1
- app/live/ttm_battery_surge.py +1 -0
- app/live/ttm_forecast.py +1 -0
- app/mellea_validator.py +3 -0
- app/planner.py +61 -0
- app/reconcile.py +8 -9
- audit/AUDIT-2026-05-06.md +150 -0
- docs/QUESTION-AWARE-FRAMING.md +194 -0
- research/AMD-HACKATHON-LANDSCAPE.md +140 -0
- research/PITCH-DECK-LANDSCAPE.md +135 -0
- scripts/build_mta_entrances_register.py +0 -1
- scripts/build_nycha_register.py +0 -1
- scripts/build_schools_register.py +0 -1
- scripts/dry_run.py +1 -1
- scripts/probe_addresses.py +0 -1
- scripts/run_prithvi_flood.py +6 -3
- scripts/run_prithvi_ida.py +7 -4
- scripts/smoke_test_gpu.sh +56 -0
- services/riprap-models/Dockerfile +12 -2
- services/riprap-models/main.py +1 -1
- services/riprap-models/requirements-full.txt +2 -2
- slides/CHANGES-2026-05-06.md +279 -0
- slides/Makefile +2 -2
- slides/asce/CHANGES.md +56 -0
- slides/asce/Makefile +19 -0
- slides/asce/deck.html +0 -0
- slides/asce/deck.md +483 -0
- slides/asce/deck.pdf +3 -0
- slides/asce/deck.pptx +3 -0
- slides/asce/logo-paper.svg +13 -0
- slides/asce/logo.svg +14 -0
- slides/asce/riprap.css +657 -0
- slides/deck.md +218 -86
- submission/COPY-DRAFTS.md +151 -0
.gitattributes
CHANGED
|
@@ -2,10 +2,8 @@
|
|
| 2 |
*.geojson filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.tif filter=lfs diff=lfs merge=lfs -text
|
| 4 |
*.pdf filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
|
| 6 |
# Pre-computed register paragraphs
|
| 7 |
data/registers/*.json filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
|
| 9 |
# Esri FileGDB internal binary files (DEP Stormwater scenario data)
|
| 10 |
*.gdbtable filter=lfs diff=lfs merge=lfs -text
|
| 11 |
*.gdbtablx filter=lfs diff=lfs merge=lfs -text
|
|
@@ -15,7 +13,6 @@ data/registers/*.json filter=lfs diff=lfs merge=lfs -text
|
|
| 15 |
*.freelist filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.horizon filter=lfs diff=lfs merge=lfs -text
|
| 17 |
*.FDO_UUID filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
|
| 19 |
# Hugging Face's standard LFS rules (kept for forward-compat with model assets)
|
| 20 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 21 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
|
@@ -52,3 +49,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 52 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 53 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 54 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 2 |
*.geojson filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.tif filter=lfs diff=lfs merge=lfs -text
|
| 4 |
*.pdf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 5 |
# Pre-computed register paragraphs
|
| 6 |
data/registers/*.json filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 7 |
# Esri FileGDB internal binary files (DEP Stormwater scenario data)
|
| 8 |
*.gdbtable filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.gdbtablx filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 13 |
*.freelist filter=lfs diff=lfs merge=lfs -text
|
| 14 |
*.horizon filter=lfs diff=lfs merge=lfs -text
|
| 15 |
*.FDO_UUID filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 16 |
# Hugging Face's standard LFS rules (kept for forward-compat with model assets)
|
| 17 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 18 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 49 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 50 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 51 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
*.pptx filter=lfs diff=lfs merge=lfs -text
|
CODE-MORNING-BRIEF-2026-05-06.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Code Morning Brief β 2026-05-06
|
| 2 |
+
|
| 3 |
+
Engineering pass: bug fixes + AMD GPU deploy. All fixes committed to `main`.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Final state β end of day 2026-05-06
|
| 8 |
+
|
| 9 |
+
**5/5 address probe PASS on AMD MI300X vLLM path.**
|
| 10 |
+
|
| 11 |
+
```
|
| 12 |
+
[1/5] '442 East Houston Street, Manhattan' PASS 9.8s mellea=4/4 rerolls=1
|
| 13 |
+
[2/5] '80 Pioneer Street, Brooklyn' PASS 7.0s mellea=4/4 rerolls=0
|
| 14 |
+
[3/5] '100 Gold Street, Manhattan' PASS 10.2s mellea=4/4 rerolls=1
|
| 15 |
+
[4/5] 'Hollis, Queens' PASS 4.9s mellea=4/4 rerolls=0
|
| 16 |
+
[5/5] 'Coney Island, Brooklyn' PASS 4.3s mellea=4/4 rerolls=0
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
Demo queries captured at `/tmp/gpu-demo-q01.json`, `/tmp/gpu-demo-q02.json`,
|
| 20 |
+
`/tmp/gpu-demo-q13.json` (q13 captured in earlier session).
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
## Bugs resolved
|
| 25 |
+
|
| 26 |
+
### 1. Graceful not_implemented for retrospective + ranking queries
|
| 27 |
+
|
| 28 |
+
**Files:** `app/planner.py` β commit `d3fa102`
|
| 29 |
+
|
| 30 |
+
Pre-flight regex intercept before the LLM call short-circuits two
|
| 31 |
+
categories of queries that Riprap doesn't support and previously
|
| 32 |
+
silently misrouted:
|
| 33 |
+
|
| 34 |
+
- **Retrospective (q14/q18):** "What would Riprap have said on
|
| 35 |
+
Hurricane Ida?", "What was the flood status as of August 2021?" β
|
| 36 |
+
Returns `Plan(intent="not_implemented")` with a user-facing message.
|
| 37 |
+
- **Ranking (q15):** "Rank top 5 NYCHA buildings by flood exposure" β
|
| 38 |
+
Same treatment.
|
| 39 |
+
|
| 40 |
+
`web/main.py` handles `not_implemented` in both the streaming
|
| 41 |
+
(`/api/agent/stream`) and non-streaming (`/api/agent`) paths β emits
|
| 42 |
+
the message as a `final` event with `status: "not_implemented"` and
|
| 43 |
+
zeroed Mellea fields. No LLM call is made.
|
| 44 |
+
|
| 45 |
+
### 2. [doc_id] placeholder leaking from reconcile prompt
|
| 46 |
+
|
| 47 |
+
**Files:** `app/mellea_validator.py`, `app/reconcile.py` β commit `f68243b`
|
| 48 |
+
|
| 49 |
+
Root cause: `EXTRA_SYSTEM_PROMPT` used `[doc_id]` as an example
|
| 50 |
+
placeholder in the section skeleton. Granite echoed it literally.
|
| 51 |
+
Mellea's `citations_resolve` check then failed.
|
| 52 |
+
|
| 53 |
+
Two-part fix:
|
| 54 |
+
1. `mellea_validator.py` β added `[doc_id]` to `_check_no_placeholder_tokens`.
|
| 55 |
+
2. `reconcile.py` β rewrote `EXTRA_SYSTEM_PROMPT` to use real doc_id
|
| 56 |
+
examples (`[sandy]`, `[nyc311]`, `[microtopo]`, etc.) instead of
|
| 57 |
+
`[doc_id]` placeholders.
|
| 58 |
+
|
| 59 |
+
### 3. Geocoder fallback when Planning Labs API is down
|
| 60 |
+
|
| 61 |
+
**File:** `app/geocode.py` β commit `70892d1`
|
| 62 |
+
|
| 63 |
+
NYC Planning Labs Geosearch (`geosearch.planninglabs.nyc`) returned
|
| 64 |
+
503 during the session. All single_address queries failed "no coords".
|
| 65 |
+
|
| 66 |
+
Fix: Added `try/except` around `geocode(text, limit=8)` in
|
| 67 |
+
`geocode_one()`. Any exception (503, connection error, timeout) now
|
| 68 |
+
falls back to Nominatim, matching the existing upstate-hint path.
|
| 69 |
+
|
| 70 |
+
### 4. STAC searches hang indefinitely without HTTP timeout
|
| 71 |
+
|
| 72 |
+
**Files:** `app/context/eo_chip_cache.py`, `app/flood_layers/prithvi_live.py` β commit `70892d1`
|
| 73 |
+
|
| 74 |
+
`pystac_client` STAC searches and `rioxarray` COG downloads have no
|
| 75 |
+
per-request HTTP timeout; they hung indefinitely when Planetary Computer
|
| 76 |
+
was slow or unreachable.
|
| 77 |
+
|
| 78 |
+
Fix: Wrapped both `fetch()` functions in a
|
| 79 |
+
`concurrent.futures.ThreadPoolExecutor` with a hard wall-clock cap
|
| 80 |
+
(`timeout_s + 15 s`). The FSM step now always returns within budget
|
| 81 |
+
with `{"ok": False, "skipped": "timed out"}` on STAC hangs.
|
| 82 |
+
|
| 83 |
+
Controlled by existing `RIPRAP_EO_CHIP_ENABLE` / `RIPRAP_PRITHVI_LIVE_ENABLE`
|
| 84 |
+
env flags (default `1`). Set to `0` to skip STAC lookups entirely.
|
| 85 |
+
|
| 86 |
+
### 5. NYCHA/DOE/DOH registers hang on first query (91 MB polygon load)
|
| 87 |
+
|
| 88 |
+
**Files:** `app/fsm.py`, `web/main.py` β commit `70892d1`
|
| 89 |
+
|
| 90 |
+
`app/registers/nycha.py:_load_sandy_2263()` loads the full 91 MB
|
| 91 |
+
`data/sandy_inundation.geojson` via geopandas on first call. GDAL's
|
| 92 |
+
polygon-organisation pass on that file triggers a "processing may be
|
| 93 |
+
really slow" path β 3β5 min on M3 local dev, making the first
|
| 94 |
+
single_address query appear hung.
|
| 95 |
+
|
| 96 |
+
Fix: Split nycha / doe_schools / doh_hospitals behind a new
|
| 97 |
+
`RIPRAP_NYCHA_REGISTERS` env flag (default `0`, independent of the
|
| 98 |
+
GPU-heavy `RIPRAP_HEAVY_SPECIALISTS` flag). When set to `1`,
|
| 99 |
+
`web/main.py` pre-warms the lru_caches at startup.
|
| 100 |
+
|
| 101 |
+
For the demo: nycha/doe/doh data is absent from the briefing (Pioneer
|
| 102 |
+
Street and Gold Street have no NYCHA developments in the 2000 m radius
|
| 103 |
+
anyway). Re-enable post-demo when the server has a 3-min startup budget.
|
| 104 |
+
|
| 105 |
+
### 6. riprap-models Dockerfile: ROCm torch replaced by CUDA torch
|
| 106 |
+
|
| 107 |
+
**File:** `services/riprap-models/Dockerfile` β commits `488d524`, `8899d4a`
|
| 108 |
+
|
| 109 |
+
pip's resolver replaced the AMD ROCm `torch 2.9.1+git8907517` with CUDA
|
| 110 |
+
`torch 2.10.0` from PyPI. Fix: multi-stage build; Stage 1 captures clean
|
| 111 |
+
ROCm site-packages, Stage 2 installs deps, then COPY restores ROCm torch.
|
| 112 |
+
vLLM ENTRYPOINT conflict (`vllm: error: unrecognized arguments`) fixed by
|
| 113 |
+
`ENTRYPOINT []` in the Dockerfile.
|
| 114 |
+
|
| 115 |
+
---
|
| 116 |
+
|
| 117 |
+
## GPU deploy status
|
| 118 |
+
|
| 119 |
+
**Droplet:** `134.199.193.99` (AMD MI300X, DigitalOcean GPU)
|
| 120 |
+
|
| 121 |
+
| Container | Image | Port | Status |
|
| 122 |
+
|-----------------|-----------------------------------|------|---------|
|
| 123 |
+
| `vllm` | `vllm/vllm-openai-rocm:v0.17.1` | 8001 | Running |
|
| 124 |
+
| `riprap-models` | `riprap-models:latest` | 7860 | Running |
|
| 125 |
+
|
| 126 |
+
vLLM serves `granite-4.1-8b` at `http://134.199.193.99:8001/v1`.
|
| 127 |
+
riprap-models correct embedding route: `/v1/granite-embed` (smoke test
|
| 128 |
+
script still lists `/v1/embedding` β fix documented in `OPEN-ISSUES.md`).
|
| 129 |
+
|
| 130 |
+
**Bearer token:** stored in `AMD_TOKEN` at repo root (gitignored).
|
| 131 |
+
|
| 132 |
+
---
|
| 133 |
+
|
| 134 |
+
## Environment variables
|
| 135 |
+
|
| 136 |
+
```bash
|
| 137 |
+
# Local dev β AMD GPU
|
| 138 |
+
export RIPRAP_LLM_PRIMARY=vllm
|
| 139 |
+
export RIPRAP_LLM_BASE_URL=http://134.199.193.99:8001/v1
|
| 140 |
+
export RIPRAP_LLM_API_KEY=$(cat AMD_TOKEN)
|
| 141 |
+
export RIPRAP_ML_BASE_URL=http://134.199.193.99:7860
|
| 142 |
+
export RIPRAP_ML_API_KEY=$(cat AMD_TOKEN)
|
| 143 |
+
export RIPRAP_EO_CHIP_ENABLE=0 # skip STAC lookups (Planetary Computer slow)
|
| 144 |
+
export RIPRAP_PRITHVI_LIVE_ENABLE=0 # skip STAC lookups
|
| 145 |
+
export RIPRAP_TERRAMIND_ENABLE=0 # skip DEM diffusion (slow on CPU)
|
| 146 |
+
# RIPRAP_NYCHA_REGISTERS defaults to 0 β don't set unless startup warmup is acceptable
|
| 147 |
+
|
| 148 |
+
.venv/bin/uvicorn web.main:app --host 127.0.0.1 --port 7861 --log-level info
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
HF Space env (huggingface-cli space variables):
|
| 152 |
+
```
|
| 153 |
+
RIPRAP_LLM_BASE_URL=http://134.199.193.99:8001/v1
|
| 154 |
+
RIPRAP_LLM_API_KEY=<token>
|
| 155 |
+
RIPRAP_ML_BASE_URL=http://134.199.193.99:7860
|
| 156 |
+
RIPRAP_ML_API_KEY=<token>
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
---
|
| 160 |
+
|
| 161 |
+
## How to verify
|
| 162 |
+
|
| 163 |
+
```bash
|
| 164 |
+
# 1. Smoke test
|
| 165 |
+
TOKEN=$(cat AMD_TOKEN)
|
| 166 |
+
scripts/smoke_test_gpu.sh 134.199.193.99 "$TOKEN"
|
| 167 |
+
# Expect: vllm_models PASS, vllm_chat_post PASS, models_health PASS,
|
| 168 |
+
# models_granite_embed_post PASS (correct route: /v1/granite-embed)
|
| 169 |
+
# vllm_chat GET FAIL (expected β GET is not a chat endpoint)
|
| 170 |
+
|
| 171 |
+
# 2. Full 5-address end-to-end probe via local server β AMD
|
| 172 |
+
RIPRAP_LLM_PRIMARY=vllm \
|
| 173 |
+
RIPRAP_LLM_BASE_URL=http://134.199.193.99:8001/v1 \
|
| 174 |
+
RIPRAP_LLM_API_KEY=$(cat AMD_TOKEN) \
|
| 175 |
+
RIPRAP_ML_BASE_URL=http://134.199.193.99:7860 \
|
| 176 |
+
RIPRAP_ML_API_KEY=$(cat AMD_TOKEN) \
|
| 177 |
+
RIPRAP_EO_CHIP_ENABLE=0 \
|
| 178 |
+
RIPRAP_PRITHVI_LIVE_ENABLE=0 \
|
| 179 |
+
RIPRAP_TERRAMIND_ENABLE=0 \
|
| 180 |
+
.venv/bin/python scripts/probe_addresses.py
|
| 181 |
+
# Want: 5/5 PASS
|
| 182 |
+
|
| 183 |
+
# 3. Manual vLLM smoke
|
| 184 |
+
curl -s -X POST http://134.199.193.99:8001/v1/chat/completions \
|
| 185 |
+
-H "Authorization: Bearer $(cat AMD_TOKEN)" \
|
| 186 |
+
-H "Content-Type: application/json" \
|
| 187 |
+
-d '{"model":"granite-4.1-8b","messages":[{"role":"user","content":"Reply OK"}],"max_tokens":4}' \
|
| 188 |
+
| python3 -m json.tool
|
| 189 |
+
```
|
| 190 |
+
|
| 191 |
+
---
|
| 192 |
+
|
| 193 |
+
## Droplet redeploy (if destroyed)
|
| 194 |
+
|
| 195 |
+
```bash
|
| 196 |
+
TOKEN=$(openssl rand -base64 24)
|
| 197 |
+
scripts/deploy_droplet.sh <new-ip> "$TOKEN"
|
| 198 |
+
# ~10-20 min on a fresh droplet
|
| 199 |
+
```
|
| 200 |
+
|
| 201 |
+
See `CLAUDE.md` β "Droplet redeploy" for full details.
|
| 202 |
+
|
| 203 |
+
---
|
| 204 |
+
|
| 205 |
+
## Open issues
|
| 206 |
+
|
| 207 |
+
See `OPEN-ISSUES.md`:
|
| 208 |
+
1. `experiments/` bugs (numpy annotation, f-string Py 3.12, closure loop, dead api)
|
| 209 |
+
2. `scripts/smoke_test_gpu.sh` tests `/v1/embedding` β correct route is `/v1/granite-embed`
|
| 210 |
+
3. NYCHA/DOE/DOH registers disabled by default β enable post-demo with `RIPRAP_NYCHA_REGISTERS=1` + startup warmup
|
COMMS-OVERNIGHT-2026-05-06-MORNING-BRIEF.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Morning brief β comms overnight pass, 2026-05-07
|
| 2 |
+
|
| 3 |
+
Branch: `comms-overnight-2026-05-06`
|
| 4 |
+
Work is local-only, not pushed to remote or HF.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## Status
|
| 9 |
+
|
| 10 |
+
All four work streams completed. Research memos are in `research/`.
|
| 11 |
+
Deck is revised (9 slides, built to PDF/HTML/PPTX locally). Submission
|
| 12 |
+
copy is drafted in `submission/COPY-DRAFTS.md`. Cover image was not
|
| 13 |
+
auto-generated β a design brief is in that same file with the quickest
|
| 14 |
+
path (re-export the deck cover slide as PNG). One verification item
|
| 15 |
+
remains open before submission: the Mellea 4/4 claim on slide 05.
|
| 16 |
+
|
| 17 |
+
There is a branch-state anomaly to be aware of: commits during this
|
| 18 |
+
session landed on both `comms-overnight-2026-05-06` (the intended
|
| 19 |
+
branch) and `overnight-2026-05-06` (a prior session's branch). The
|
| 20 |
+
content is the same on both. `comms-overnight-2026-05-06` has the clean
|
| 21 |
+
set (research + deck + change log + submission copy). You can merge
|
| 22 |
+
either branch; both are local-only.
|
| 23 |
+
|
| 24 |
+
---
|
| 25 |
+
|
| 26 |
+
## Research pass β five bullets each
|
| 27 |
+
|
| 28 |
+
### AMD hackathon landscape (`research/AMD-HACKATHON-LANDSCAPE.md`)
|
| 29 |
+
|
| 30 |
+
- **Agents track dominates the visible field.** Most in-flight
|
| 31 |
+
submissions are multi-agent orchestration systems. Fine-Tuning
|
| 32 |
+
submissions are sparse; NyayaLLM is the only comparable one
|
| 33 |
+
(domain-specific legal LLM on MI300X), but it's single-model,
|
| 34 |
+
single-jurisdiction, and has no published artifacts.
|
| 35 |
+
- **Three published Apache-2.0 fine-tunes is the differentiator.**
|
| 36 |
+
No other visible submission mentions published model artifacts.
|
| 37 |
+
The three HF Hub repos are verifiable; judges can clone and run them.
|
| 38 |
+
- **The domain-tool penalty is real.** A 13-second cited flood briefing
|
| 39 |
+
is harder to demo than a 7-agent crisis system that spawns child
|
| 40 |
+
agents in real time. The architecture slide and the receipts table
|
| 41 |
+
need to close that gap before the civic-tech hook can land.
|
| 42 |
+
- **"Three of four tracks" was a liability.** The hackathon is
|
| 43 |
+
one-track submission. "Engaged in three tracks" reads as hedging.
|
| 44 |
+
Fine-Tuning is the right single-track argument.
|
| 45 |
+
- **Lablab.ai submission pages 403'd.** Project descriptions above are
|
| 46 |
+
from search snippets only. The full 30+ project list requires a
|
| 47 |
+
logged-in lablab.ai session. The landscape read is directional, not
|
| 48 |
+
exhaustive.
|
| 49 |
+
|
| 50 |
+
### Pitch deck landscape (`research/PITCH-DECK-LANDSCAPE.md`)
|
| 51 |
+
|
| 52 |
+
- **Problem-first into receipts-first is the right pattern for Riprap.**
|
| 53 |
+
The Zillow pullout gives the problem in one CNN headline. The 5/5
|
| 54 |
+
table is the receipts. Demo in the middle, fine-tune evidence before
|
| 55 |
+
the civic case.
|
| 56 |
+
- **The architecture diagram was the single biggest missing slide.**
|
| 57 |
+
Judges scanning a PDF without a system diagram can't assess technical
|
| 58 |
+
depth. The new slide 03 (Five Stones β Capstone flow) does that work
|
| 59 |
+
in one scan.
|
| 60 |
+
- **The "Live Demo" slide was inert in a static deck.** Repurposing to
|
| 61 |
+
"What's Next" opens the longer arc visible to both the hackathon
|
| 62 |
+
audience (May 10) and the ASCE audience (May 13). No content loss.
|
| 63 |
+
- **Do not lead with AI vocabulary; lead with civic vocabulary.**
|
| 64 |
+
"RPL Β§462(2)" and "NYC DEP" are signals of domain expertise, not
|
| 65 |
+
buzzwords. Name them early in the video, not in the deck's second
|
| 66 |
+
half.
|
| 67 |
+
- **5-minute video structure:** 0:00 problem sentence, 0:20 demo,
|
| 68 |
+
0:50 architecture, 1:30 receipts, 2:00 track argument (fine-tunes),
|
| 69 |
+
2:30 civic case, 3:30 what's next, 4:00 CTA. Full breakdown in the
|
| 70 |
+
research memo.
|
| 71 |
+
|
| 72 |
+
---
|
| 73 |
+
|
| 74 |
+
## Deck changes β condensed
|
| 75 |
+
|
| 76 |
+
| Slide | Before | After |
|
| 77 |
+
|---|---|---|
|
| 78 |
+
| 01 Β· Problem | CNN quote as direct citation, no counter-positioning | Quote marked as paraphrase, corrected to Nov 14 removal date; added "not a score" distinction |
|
| 79 |
+
| 02 Β· What riprap is | Unchanged | Unchanged |
|
| 80 |
+
| NEW 03 Β· Architecture | Did not exist | New: query β Planner β 4 evidence Stones (with data sources named) β Capstone + Mellea β briefing |
|
| 81 |
+
| 03 β 04 Β· The track | "Three of four tracks. One project." + Build in Public Skipped row | "Submitted to Fine-Tuning." Fine-Tuning = Primary, Agents/Vision = Supporting. Skipped row removed. |
|
| 82 |
+
| 04 β 05 Β· Receipts | Unchanged | Unchanged (see open item below) |
|
| 83 |
+
| 05 β 06 Β· Why it matters | Unchanged | Unchanged |
|
| 84 |
+
| 06 β 07 Β· Now / Demo | Live demo URL + blockquote (inert in static deck) | WHAT'S NEXT: Ida/ASCE calibration, Stones v1.1 packages, methodology paper |
|
| 85 |
+
| CTA | Unchanged | Unchanged |
|
| 86 |
+
|
| 87 |
+
Slide count: 8 β 9.
|
| 88 |
+
|
| 89 |
+
---
|
| 90 |
+
|
| 91 |
+
## Cover image
|
| 92 |
+
|
| 93 |
+
The cover image (`submission/cover-16x9.png`) was not auto-generated.
|
| 94 |
+
Design brief is in `submission/COPY-DRAFTS.md`.
|
| 95 |
+
|
| 96 |
+
**Quickest path:** export the cover slide from the deck PDF as a
|
| 97 |
+
1920Γ1080 PNG. The Marp cover slide already uses the correct tokens,
|
| 98 |
+
dam mark, and layout. From `slides/`:
|
| 99 |
+
|
| 100 |
+
```
|
| 101 |
+
npx @marp-team/marp-cli@latest deck.md --theme riprap.css \
|
| 102 |
+
--allow-local-files --images png
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
This generates `deck.001.png` (the cover slide) which is the 16:9
|
| 106 |
+
thumbnail. Rename to `submission/cover-16x9.png`.
|
| 107 |
+
|
| 108 |
+
---
|
| 109 |
+
|
| 110 |
+
## Submission copy β recommended
|
| 111 |
+
|
| 112 |
+
**Title:** `Riprap β Cited NYC flood briefings on AMD` (42 chars)
|
| 113 |
+
|
| 114 |
+
**Short (237 chars):**
|
| 115 |
+
Riprap writes NYC flood-exposure briefings where every numeric claim cites its source β or doesn't appear. Granite 4.1 8B on AMD MI300X, three Apache-2.0 NYC fine-tunes, Mellea citation grounding. 5/5 addresses, 4/4 checks every run.
|
| 116 |
+
|
| 117 |
+
**Long (~280 words):** in `submission/COPY-DRAFTS.md`, no changes needed.
|
| 118 |
+
|
| 119 |
+
**Runner-up title:** `Riprap: citation-grounded flood briefings`
|
| 120 |
+
|
| 121 |
+
---
|
| 122 |
+
|
| 123 |
+
## Three things to look at first
|
| 124 |
+
|
| 125 |
+
1. **Run the 20-query Mellea probe suite and check slide 05.**
|
| 126 |
+
The deck's "4/4 every run" claim is verified against the 5-address
|
| 127 |
+
probe. If Track A's 20-query stakeholder suite is complete, check
|
| 128 |
+
the grounding results. If any query failed at < 4/4, update the
|
| 129 |
+
slide. Do not submit a deck with a "4/4" claim that doesn't hold
|
| 130 |
+
across the wider suite. Command from `scripts/`:
|
| 131 |
+
```
|
| 132 |
+
.venv/bin/python scripts/probe_addresses.py
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
2. **Generate the cover image** from the deck cover slide (see above).
|
| 136 |
+
One npx command, one rename. Takes 2 minutes.
|
| 137 |
+
|
| 138 |
+
3. **Review the architecture slide (new slide 03)** in the rendered PDF.
|
| 139 |
+
It uses inline styles and box-grid classes. Verify it renders cleanly
|
| 140 |
+
in the PDF before submission β particularly the four Stone columns and
|
| 141 |
+
the Capstone row at the bottom. If the layout is cramped, reducing the
|
| 142 |
+
Stone cell font sizes by 1β2px will fix it. Source: `slides/deck.md`
|
| 143 |
+
lines ~103β160.
|
| 144 |
+
|
| 145 |
+
---
|
| 146 |
+
|
| 147 |
+
## Open questions that need Adam's call
|
| 148 |
+
|
| 149 |
+
**1. Track submission: Fine-Tuning is the call, but confirm.**
|
| 150 |
+
The research pass found no evidence against Fine-Tuning as primary. If
|
| 151 |
+
you have information about lablab.ai's scoring criteria that suggests
|
| 152 |
+
Agents is stronger (e.g., the FSM + Burr architecture is judged
|
| 153 |
+
separately), change slide 04 before submission. The deck frame is easy
|
| 154 |
+
to swap β the track-row badges are the only change.
|
| 155 |
+
|
| 156 |
+
**2. The CNN quote on slide 01 β exact vs paraphrase.**
|
| 157 |
+
Current: "Zillow removed climate risk scores from listings under pressure
|
| 158 |
+
from the real-estate industry. In their place: a link, far less visible."
|
| 159 |
+
Marked as paraphrase. If you want a direct quote for a public-facing
|
| 160 |
+
deck, the TechCrunch version is: "Zillow removed the listings' climate
|
| 161 |
+
scores. In their place is a subtle link to their records at First Street."
|
| 162 |
+
(TechCrunch, Dec 1, 2025.) Either is defensible; this is an editorial
|
| 163 |
+
call.
|
| 164 |
+
|
| 165 |
+
**3. ASCE talk (May 13) β which slides to adapt.**
|
| 166 |
+
The new "What's Next" slide (07) and the "Why it Matters" slide (06)
|
| 167 |
+
are the ASCE-relevant ones. For ASCE, slide 04 (The Track) should be
|
| 168 |
+
replaced with a "Methods" slide. The architecture diagram (slide 03)
|
| 169 |
+
and receipts (slide 05) travel unchanged. Make the branch decision:
|
| 170 |
+
fork a new `asce-2026-05-13` branch off this deck or iterate in place.
|
| 171 |
+
|
| 172 |
+
**4. `overnight-2026-05-06` branch cleanup.**
|
| 173 |
+
That branch has duplicate commits plus `e203d5f tests: add 20-query
|
| 174 |
+
stakeholder integration suite` from the prior session. Decide whether
|
| 175 |
+
to merge it into main, keep it as a holding branch, or delete it. The
|
| 176 |
+
comms work you need is all on `comms-overnight-2026-05-06`.
|
OPEN-ISSUES.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Open Issues β post-hackathon triage
|
| 2 |
+
|
| 3 |
+
These bugs were identified in the `audit/AUDIT-2026-05-06.md` pass.
|
| 4 |
+
All are in `experiments/` (exploratory/reproduction code) and were
|
| 5 |
+
explicitly left untouched pre-demo per Adam's instruction.
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## 1. `experiments/17` β F821 numpy annotation race
|
| 10 |
+
|
| 11 |
+
**File:** `experiments/17_riprap_integration/terramind_nyc.py:117`
|
| 12 |
+
**Ruff code:** F821 (3Γ)
|
| 13 |
+
**Issue:** Type annotation references `np` (numpy) before it is
|
| 14 |
+
imported at module top. Currently masked by `from __future__ import
|
| 15 |
+
annotations` (lazy eval). Will fail if Python ever evaluates it
|
| 16 |
+
eagerly, or if this module is ported to a context that drops the
|
| 17 |
+
future import.
|
| 18 |
+
**Fix:** Move `import numpy as np` to module top.
|
| 19 |
+
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
## 2. `experiments/18` β f-string syntax only valid on Py 3.12+
|
| 23 |
+
|
| 24 |
+
**File:** `experiments/18_terramind_nyc_lora/shared/eval_adapter.py:125`
|
| 25 |
+
**Ruff code:** invalid-syntax
|
| 26 |
+
**Issue:** Inner f-string reuses outer quote style (valid in Py 3.12,
|
| 27 |
+
syntax error in Py 3.10). The HF Space (Py 3.10) cannot import this
|
| 28 |
+
file. Currently local-only; will error if anyone tries to ship it.
|
| 29 |
+
**Fix:** Change inner f-string quotes or use `.format()`.
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
## 3. `experiments/05` β closure captures loop variable
|
| 34 |
+
|
| 35 |
+
**File:** `experiments/05_terramind_nyc_finetune/training/verify_phase1.py:438`
|
| 36 |
+
**Ruff code:** B023 (2Γ)
|
| 37 |
+
**Issue:** Closure inside a `for` loop binds the loop variable by
|
| 38 |
+
reference (all closures see the last value). The classic Python
|
| 39 |
+
late-binding trap. May or may not be a bug depending on intent β needs
|
| 40 |
+
a human eye on what the closure does.
|
| 41 |
+
**Fix:** Rebind with a default arg: `lambda x=x: ...`.
|
| 42 |
+
|
| 43 |
+
---
|
| 44 |
+
|
| 45 |
+
## 4. `experiments/18` β possibly dead `api` assignment
|
| 46 |
+
|
| 47 |
+
**File:** `experiments/18_terramind_nyc_lora/shared/publish_hf.py:107`
|
| 48 |
+
**Ruff code:** F841
|
| 49 |
+
**Issue:** `api` is assigned (likely from `HfApi()`) but never used
|
| 50 |
+
in the file. May be a bug (intended to call `api.upload_file(...)`) or
|
| 51 |
+
a leftover from an edit. Needs a human eye.
|
| 52 |
+
**Fix:** Either use `api` in the upload calls, or remove the assignment.
|
OVERNIGHT-2026-05-06-MORNING-BRIEF.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Overnight pass β morning brief β 2026-05-06
|
| 2 |
+
|
| 3 |
+
> Branch: `overnight-2026-05-06`. Local-only, not pushed, not deployed.
|
| 4 |
+
> Read this in 5 min; everything detailed lives in linked sub-reports.
|
| 5 |
+
|
| 6 |
+
## Status one-liner
|
| 7 |
+
|
| 8 |
+
All four work streams landed. The audit committed mechanical fixes
|
| 9 |
+
only and flagged real bugs in `experiments/` for triage. The 20-query
|
| 10 |
+
suite ran twice (baseline + framed) end-to-end against local Granite +
|
| 11 |
+
local specialists. The question-aware Capstone framing lifted mean
|
| 12 |
+
framing 2.25 β 2.80 and produced three verdict-style openings (q01
|
| 13 |
+
"Yes", q02 "Disclosure is warranted", q13 "Vulnerability assessment:")
|
| 14 |
+
where there were zero before. The framing's stop condition fired
|
| 15 |
+
(12 < 3); option (a) β planner sub-classifier β is sketched in
|
| 16 |
+
`docs/QUESTION-AWARE-FRAMING.md` but explicitly NOT implemented per
|
| 17 |
+
your "don't silently expand scope" rule. One out-of-scope geocoder
|
| 18 |
+
bug surfaced and is documented in
|
| 19 |
+
`OVERNIGHT-2026-05-06-OUT-OF-SCOPE.md` (NOT fixed).
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
## 1. Code audit β `audit/AUDIT-2026-05-06.md`
|
| 24 |
+
|
| 25 |
+
`ruff` found 106 issues across the whole repo. Mechanical fixes
|
| 26 |
+
applied to production code paths only (`app/`, `web/`, `scripts/`,
|
| 27 |
+
`services/`, `tests/`); `experiments/` was left alone. Vulture
|
| 28 |
+
confirmed only one F401 worth removing (`io` in `app/inference.py`);
|
| 29 |
+
the rest are kept per Adam's "vulture-confirmed only" rule.
|
| 30 |
+
|
| 31 |
+
**The four real bugs in `experiments/` (NOT touched, flagged for
|
| 32 |
+
Adam to triage):**
|
| 33 |
+
1. `experiments/17_riprap_integration/terramind_nyc.py:117` β
|
| 34 |
+
F821 references `np` in a type annotation; numpy isn't imported
|
| 35 |
+
at module top.
|
| 36 |
+
2. `experiments/18_terramind_nyc_lora/shared/eval_adapter.py:125` β
|
| 37 |
+
Py 3.12 nested f-string; will fail to import on the HF Space (3.10).
|
| 38 |
+
3. `experiments/05_terramind_nyc_finetune/training/verify_phase1.py:438` β
|
| 39 |
+
B023 closure-over-loop-variable, the standard "all closures see
|
| 40 |
+
the last value" trap.
|
| 41 |
+
4. `experiments/18_terramind_nyc_lora/shared/publish_hf.py:107` β
|
| 42 |
+
F841 `api` assigned but never used; may be a missing
|
| 43 |
+
`api.upload_*` call.
|
| 44 |
+
|
| 45 |
+
**Complexity hotspots** (flagged, NOT refactored β pre-demo freeze):
|
| 46 |
+
- `app/reconcile.py:build_documents` is **F=178** by cyclomatic
|
| 47 |
+
complexity. CLAUDE.md explicitly says don't touch pre-demo. Held.
|
| 48 |
+
- Other C+ functions: `mellea_validator.reconcile_strict_streaming` (D=23),
|
| 49 |
+
`planner._validate` (D=22), `rag.retrieve` (C=20), three more at
|
| 50 |
+
C=16-18. All expected; none touched.
|
| 51 |
+
|
| 52 |
+
**Lowest MI modules (still passable, not urgent):**
|
| 53 |
+
`app/intents/neighborhood.py` (32), `web/main.py` (37),
|
| 54 |
+
`scripts/probe_addresses.py` (36). Length is the cost of being
|
| 55 |
+
data-heavy / demo-front-door / probe-tester respectively. Post-demo
|
| 56 |
+
candidates for refactor.
|
| 57 |
+
|
| 58 |
+
**Commit:** `9cc6ec4 audit: mechanical fixes from ruff + vulture`.
|
| 59 |
+
|
| 60 |
+
---
|
| 61 |
+
|
| 62 |
+
## 2. 20-query stakeholder integration suite β `tests/integration/results/2026-05-06/SUMMARY.md`
|
| 63 |
+
|
| 64 |
+
The suite at `tests/integration/stakeholder_queries.py` drives
|
| 65 |
+
`/api/agent/stream` against 20 queries derived from `RESEARCH.md`:
|
| 66 |
+
six verbatim personas, six adapted variants, eight lateral use cases.
|
| 67 |
+
Per query it captures planner intent, Stones invoked / fired /
|
| 68 |
+
silent_by_design / errored, wall-clock per Stone, the briefing prose,
|
| 69 |
+
citations resolved, Mellea grounding pass-rate + rerolls, and a
|
| 70 |
+
**framing score** (0-5) for the opening paragraph against a
|
| 71 |
+
per-question-type rubric.
|
| 72 |
+
|
| 73 |
+
**Outputs in `tests/integration/results/2026-05-06/`:**
|
| 74 |
+
- `q01-resident-pioneer.json` ... `q20-control-astoria.json` β full
|
| 75 |
+
per-query payload (plan, paragraph, steps, mellea, framing rationale).
|
| 76 |
+
- `SUMMARY.md` β table of all 20 (intent, time, grounding,
|
| 77 |
+
framing, status).
|
| 78 |
+
- `FAILURES.md` β full briefings + proximate cause for any query
|
| 79 |
+
that errored, timed out, missed Mellea, or returned no prose.
|
| 80 |
+
|
| 81 |
+
**Baseline run summary:**
|
| 82 |
+
- 20/20 OK (no errors, no timeouts).
|
| 83 |
+
- Mean framing score: **2.25** (mostly stuck at 2 = "on-topic exposure
|
| 84 |
+
language but no question-aware framing").
|
| 85 |
+
- Queries with framing β₯ 3: 5 / 20 (q06, q07, q14, q18, q19 β note q07,
|
| 86 |
+
q14, q18, q19 scored 3 only because they returned the canned
|
| 87 |
+
"No grounded data available for this address." which the rubric
|
| 88 |
+
scores as 3 = place-referenced).
|
| 89 |
+
- 4 queries had Mellea 0/4: q07 (lease query, geocoder failed),
|
| 90 |
+
q14 (retrospective query, geocoder failed), q15 (NYCHA ranking,
|
| 91 |
+
planner mis-routed to dev_check with 0 steps), q16 (FloodNet
|
| 92 |
+
live_now with no active signals), q18 (court exhibit retrospective,
|
| 93 |
+
geocoder failed), q19 (BBMCR project name, NTA didn't resolve).
|
| 94 |
+
- The geocoder failures are documented in
|
| 95 |
+
`OVERNIGHT-2026-05-06-OUT-OF-SCOPE.md` β same root cause: the
|
| 96 |
+
length-ratio heuristic in `app/intents/single_address.py:33`
|
| 97 |
+
rejects the planner's correctly-extracted address when the user's
|
| 98 |
+
query is conversational.
|
| 99 |
+
|
| 100 |
+
**Baseline commit:** `e203d5f tests: add 20-query stakeholder integration suite`.
|
| 101 |
+
Per-query JSONs preserved at
|
| 102 |
+
`tests/integration/results/2026-05-06/baseline/`.
|
| 103 |
+
|
| 104 |
+
---
|
| 105 |
+
|
| 106 |
+
## 3. Question-aware Capstone framing β `docs/QUESTION-AWARE-FRAMING.md` + `tests/integration/results/2026-05-06/FRAMING-DELTA.md`
|
| 107 |
+
|
| 108 |
+
**Diagnosis (full version: `docs/QUESTION-AWARE-FRAMING.md`).** Three
|
| 109 |
+
options were on the table: (a) planner sub-classifier, (b) Capstone
|
| 110 |
+
prompt-conditional, (c) both. **Recommendation and what landed: (b).**
|
| 111 |
+
The four-section evidence structure (Status / Empirical / Modeled /
|
| 112 |
+
Policy) and the four Mellea grounding checks stay byte-identical;
|
| 113 |
+
only the Status sentence's directive changes.
|
| 114 |
+
|
| 115 |
+
**Implementation:**
|
| 116 |
+
- New `app/framing.py` β 11 question types, regex-based deterministic
|
| 117 |
+
detector, per-type opening-directive table, `augment_system_prompt`.
|
| 118 |
+
- `app/fsm.py` β new `set_user_query` + `set_planner_intent`
|
| 119 |
+
threadlocals; `step_reconcile` augments
|
| 120 |
+
`app.reconcile.EXTRA_SYSTEM_PROMPT` before passing to
|
| 121 |
+
`reconcile_strict_streaming`.
|
| 122 |
+
- `app/intents/single_address.py` β sets/resets the new threadlocals.
|
| 123 |
+
- `app/intents/neighborhood.py`, `development_check.py`,
|
| 124 |
+
`live_now.py` β augment their own EXTRA_SYSTEM_PROMPT before
|
| 125 |
+
reconcile.
|
| 126 |
+
|
| 127 |
+
**Detector accuracy against suite labels:** 14/20 verbatim. The 6
|
| 128 |
+
mismatches are all bare-place queries where the suite's persona-
|
| 129 |
+
imposed label isn't discoverable from the query text alone β these
|
| 130 |
+
fall back to `journalism` (bare neighborhood) or `generic_exposure`
|
| 131 |
+
(bare address, baseline behavior preserved).
|
| 132 |
+
|
| 133 |
+
**Before/after framing delta** (full report:
|
| 134 |
+
`tests/integration/results/2026-05-06/FRAMING-DELTA.md`):
|
| 135 |
+
|
| 136 |
+
| Metric | Baseline | Framed | Ξ |
|
| 137 |
+
|--------|---------:|-------:|---:|
|
| 138 |
+
| Mean framing | 2.25 | 2.80 | +0.55 |
|
| 139 |
+
| β₯ 3/5 | 5 | 8 | +3 |
|
| 140 |
+
| β₯ 4/5 | 2 | 5 | +3 |
|
| 141 |
+
| β₯ 5/5 | 0 | 3 | +3 |
|
| 142 |
+
|
| 143 |
+
**The three queries that hit 5/5** (verdict-style openings β the
|
| 144 |
+
demo-critical wins):
|
| 145 |
+
- **q01** resident habitability β opening flipped from "exposed to
|
| 146 |
+
historical flood events..." to "**Yes**, this address is exposed
|
| 147 |
+
to flood risk based on its inclusion within the Hurricane Sandy
|
| 148 |
+
inundation zone..."
|
| 149 |
+
- **q02** attorney disclosure β opening flipped to "**Disclosure is
|
| 150 |
+
warranted** because the site experiences moderate flood exposure
|
| 151 |
+
as indicated by 56.6% of surrounding cells..."
|
| 152 |
+
- **q13** grant evidence β opening flipped to "**Vulnerability
|
| 153 |
+
assessment**: Chinatown-Two Bridges (NTA MN0301) in Manhattan
|
| 154 |
+
exhibits moderate flood exposure..."
|
| 155 |
+
|
| 156 |
+
**Mellea net change:** +4 improved (3/4 β 4/4), -2 regressed (q01
|
| 157 |
+
4/4 β 3/4, q06 3/4 β 2/4), 14 unchanged. Net +2 grounding checks
|
| 158 |
+
gained across the suite.
|
| 159 |
+
|
| 160 |
+
**Stop condition: FIRED.** 12 / 20 framed queries scored below 3
|
| 161 |
+
(threshold > 5 β stop). Per Adam's instruction, NOT iterating further
|
| 162 |
+
on the prompt-conditional. Triage of the 12 + sketch of what option
|
| 163 |
+
(a) β planner sub-classifier β would require lives in
|
| 164 |
+
`docs/QUESTION-AWARE-FRAMING.md` Β§"Outcome of the 2026-05-06 framed
|
| 165 |
+
run" + Β§"What option (a) would require." Headline:
|
| 166 |
+
|
| 167 |
+
- 4 / 12 are rubric-vs-directive vocabulary mismatch (bare
|
| 168 |
+
neighborhood β journalism directive applied, but rubric scored
|
| 169 |
+
for capital_planning markers). Not a framing failure.
|
| 170 |
+
- 4 / 12 are short-prose-floor failures (geocoder + planner short
|
| 171 |
+
circuit). No framing change can fix these.
|
| 172 |
+
- 4 / 12 are cases where Granite ignored the soft directive. These
|
| 173 |
+
are where option (a) would actually help.
|
| 174 |
+
|
| 175 |
+
**Commits:** `1a82fde framing: question-aware Capstone opening`,
|
| 176 |
+
`342dd4d framing: clarify the directive's scope`,
|
| 177 |
+
`f40ebd2 tests: add FRAMING-DELTA.md generator`,
|
| 178 |
+
`9c61976 tests: baseline + framed run results`.
|
| 179 |
+
|
| 180 |
+
---
|
| 181 |
+
|
| 182 |
+
## 4. Branch state
|
| 183 |
+
|
| 184 |
+
Branch: **`overnight-2026-05-06`**, local only. To inspect:
|
| 185 |
+
|
| 186 |
+
```bash
|
| 187 |
+
git log --oneline overnight-2026-05-06 ^main
|
| 188 |
+
```
|
| 189 |
+
|
| 190 |
+
Commit chronology (newest first; Adam's parallel `comms-` commits
|
| 191 |
+
get auto-merged in via the runtime so they may interleave):
|
| 192 |
+
|
| 193 |
+
- `9c61976 tests: baseline + framed run results, 2026-05-06`
|
| 194 |
+
- `342dd4d framing: clarify the directive's scope is the Status sentence only`
|
| 195 |
+
- `e81962b docs: log out-of-scope findings from the overnight pass`
|
| 196 |
+
- `8894517 docs: morning brief skeleton`
|
| 197 |
+
- `f40ebd2 tests: add FRAMING-DELTA.md generator`
|
| 198 |
+
- `1a82fde framing: question-aware Capstone opening (Capstone prompt-conditional)`
|
| 199 |
+
- `e203d5f tests: add 20-query stakeholder integration suite`
|
| 200 |
+
- `9cc6ec4 audit: mechanical fixes from ruff + vulture`
|
| 201 |
+
|
| 202 |
+
Plus auto-merged commits from Adam's `comms-overnight-2026-05-06`
|
| 203 |
+
work (slides, research, submission docs).
|
| 204 |
+
|
| 205 |
+
To revert any single piece:
|
| 206 |
+
|
| 207 |
+
```bash
|
| 208 |
+
git revert <commit-sha> # safe: creates a new commit that undoes
|
| 209 |
+
git checkout main # discard the branch entirely
|
| 210 |
+
git branch -D overnight-2026-05-06
|
| 211 |
+
```
|
| 212 |
+
|
| 213 |
+
The framing change touches 5 files; reverting `1a82fde` is a clean
|
| 214 |
+
backout if the framed run shows regressions.
|
| 215 |
+
|
| 216 |
+
---
|
| 217 |
+
|
| 218 |
+
## 5. Three things to look at first when you open the laptop
|
| 219 |
+
|
| 220 |
+
1. **`tests/integration/results/2026-05-06/FRAMING-DELTA.md`** β the
|
| 221 |
+
per-query opening diff is the most useful artifact in the pass.
|
| 222 |
+
Read q01, q02, q13 first (the three queries that hit 5/5 β these
|
| 223 |
+
are the demo wins). Then the four "Granite ignored the directive"
|
| 224 |
+
cases triaged in `docs/QUESTION-AWARE-FRAMING.md` ("Outcome of the
|
| 225 |
+
2026-05-06 framed run" Β§3) β those are where option (a) would
|
| 226 |
+
actually pay off if you decide to spend the 2-3 hours.
|
| 227 |
+
2. **`OVERNIGHT-2026-05-06-OUT-OF-SCOPE.md`** β one real bug
|
| 228 |
+
surfaced: the planner-vs-query length-ratio threshold in
|
| 229 |
+
`app/intents/single_address.py:33` rejects the planner's
|
| 230 |
+
correctly-extracted address whenever the user's query is long and
|
| 231 |
+
conversational. Failure mode is "No grounded data available" with
|
| 232 |
+
Mellea 0/4. Hits q07 (resident lease question), q14
|
| 233 |
+
(retrospective), q18 (court exhibit) β exactly the conversational
|
| 234 |
+
personas the demo arc wants to handle gracefully. Suggested fix
|
| 235 |
+
is in the doc; NOT applied.
|
| 236 |
+
3. **`audit/AUDIT-2026-05-06.md` punch list** β the four
|
| 237 |
+
`experiments/` bugs flagged at the top. Real bugs the demo
|
| 238 |
+
hides because nobody imports them at runtime; if anyone tries to
|
| 239 |
+
reproduce the fine-tunes during the hackathon Q&A, they'll hit
|
| 240 |
+
`experiments/18` failing to import on Py 3.10 (nested f-string).
|
| 241 |
+
|
| 242 |
+
---
|
| 243 |
+
|
| 244 |
+
## What did NOT land
|
| 245 |
+
|
| 246 |
+
- **No deployment, no push.** Per instructions; both targets
|
| 247 |
+
untouched.
|
| 248 |
+
- **No refactor of `build_documents` / Mellea checks / FSM
|
| 249 |
+
structure.** All flagged in `audit/AUDIT-2026-05-06.md` as
|
| 250 |
+
post-demo work.
|
| 251 |
+
- **No new dependencies.** All work used `ruff` / `vulture` / `radon`
|
| 252 |
+
(already installed via `uv tool install`) and the existing repo
|
| 253 |
+
code.
|
| 254 |
+
- **No planner sub-classifier (option a).** The diagnosis recommends
|
| 255 |
+
(b) only; if the framed run's stop condition fires (>5 queries with
|
| 256 |
+
framing < 3), `docs/QUESTION-AWARE-FRAMING.md` describes what (a)
|
| 257 |
+
would require.
|
| 258 |
+
|
| 259 |
+
---
|
| 260 |
+
|
| 261 |
+
## Operating notes for the morning
|
| 262 |
+
|
| 263 |
+
- Local server: `nohup .venv/bin/uvicorn web.main:app --host 127.0.0.1
|
| 264 |
+
--port 7860 ...` was running on port 7860 throughout the night.
|
| 265 |
+
Check `ps -fp $(pgrep -f "uvicorn web.main")` to see if it's still
|
| 266 |
+
alive; safe to kill with `pkill -f "uvicorn web.main"`.
|
| 267 |
+
- Server log: `/tmp/riprap-overnight/server.log`.
|
| 268 |
+
- Suite run logs: `/tmp/riprap-overnight/suite-baseline.log`,
|
| 269 |
+
`/tmp/riprap-overnight/suite-framed.log`.
|
| 270 |
+
|
| 271 |
+
---
|
| 272 |
+
|
| 273 |
+
_Faithful account, not victory lap: this brief should match the
|
| 274 |
+
commit log + the on-disk reports exactly. If anything here doesn't,
|
| 275 |
+
trust the file system, not the brief._
|
OVERNIGHT-2026-05-06-OUT-OF-SCOPE.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Out-of-scope findings β 2026-05-06 overnight pass
|
| 2 |
+
|
| 3 |
+
These were discovered during the overnight pass but are outside the
|
| 4 |
+
scope Adam authorised. **NOT FIXED.** Documented here per his
|
| 5 |
+
explicit instruction so they're easy to triage.
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## 1. Geocoder rejects planner's extracted address on conversational queries
|
| 10 |
+
|
| 11 |
+
**File:** `app/intents/single_address.py:33`
|
| 12 |
+
|
| 13 |
+
```python
|
| 14 |
+
addr = planner_addr if (planner_addr and len(planner_addr) >= len(query) * 0.7) else query
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
**Failure mode.** When the user asks a conversational, multi-clause
|
| 18 |
+
question like:
|
| 19 |
+
|
| 20 |
+
> *"I just got a lease for 504 Grand Street, Lower East Side. The
|
| 21 |
+
> landlord says no flood history. Is that true?"*
|
| 22 |
+
|
| 23 |
+
the planner correctly extracts `"504 Grand Street, Lower East Side"`
|
| 24 |
+
into `targets[0].text` (38 chars). But the conditional rejects this
|
| 25 |
+
extracted address because it's less than 70% of the full query
|
| 26 |
+
(108 chars) β so `addr` falls back to the full query, which the
|
| 27 |
+
NYC DCP Geosearch geocoder cannot parse, returning "no geocoder
|
| 28 |
+
match." The FSM then runs all 19 specialists without coordinates,
|
| 29 |
+
each returning "no coords," and the briefing emits the canonical
|
| 30 |
+
silence-over-confabulation `"No grounded data available for this
|
| 31 |
+
address."` with mellea 0/4 (no claims to check).
|
| 32 |
+
|
| 33 |
+
**Discovered:** suite query q07 (Resident, disclosure-suspicion).
|
| 34 |
+
The `tests/integration/results/2026-05-06/q07-resident-grand-disclosure.json`
|
| 35 |
+
payload shows `geocode.err = "no geocoder match"` and 17 downstream
|
| 36 |
+
steps with `err = "no coords"`.
|
| 37 |
+
|
| 38 |
+
**Why the 70% threshold exists.** A defensive heuristic against the
|
| 39 |
+
planner stripping too much of the user's address into a partial token
|
| 40 |
+
(e.g. "Pioneer" instead of "80 Pioneer Street, Brooklyn"). The
|
| 41 |
+
threshold was tuned for short queries where a stripped result is
|
| 42 |
+
suspicious; it backfires on long queries where the planner correctly
|
| 43 |
+
distilled a clean address out of conversational filler.
|
| 44 |
+
|
| 45 |
+
**Why this matters.** This is exactly the persona shape that the
|
| 46 |
+
demo wants to handle gracefully β a renter asking a real,
|
| 47 |
+
conversational question. RESEARCH.md Β§1 frames the resident persona
|
| 48 |
+
as "the FloodHelpNY swap-in," and conversational queries are the
|
| 49 |
+
distinguishing feature. Today the system silently produces an empty
|
| 50 |
+
briefing on this shape.
|
| 51 |
+
|
| 52 |
+
**Suggested fix (NOT applied).** Trust the planner's extracted address
|
| 53 |
+
unconditionally when it parses as an NYC street form (house number +
|
| 54 |
+
street name + borough). Replace the length-ratio heuristic with a
|
| 55 |
+
shape check. Out of scope for this overnight pass because it requires
|
| 56 |
+
re-running the address probe to confirm no regression on the curated
|
| 57 |
+
addresses.
|
| 58 |
+
|
| 59 |
+
**Workaround for the demo:** type a clean address.
|
| 60 |
+
|
| 61 |
+
---
|
| 62 |
+
|
| 63 |
+
## 2. Suite runner caveats discovered during the overnight pass
|
| 64 |
+
|
| 65 |
+
These are not bugs β just things worth knowing for a future session.
|
| 66 |
+
|
| 67 |
+
- `tests/integration/stakeholder_queries.py` writes per-query JSON
|
| 68 |
+
after each query (defensive against partial completion). The
|
| 69 |
+
SUMMARY.md is only written at the end. If the suite is killed
|
| 70 |
+
mid-run, the JSONs are still readable; the SUMMARY can be
|
| 71 |
+
regenerated by a small wrapper that walks the JSON dir.
|
| 72 |
+
- The framing-rubric scorer (`score_framing` in the suite) is
|
| 73 |
+
intentionally pessimistic β it only assigns a 5 if a verdict marker
|
| 74 |
+
matches, even if the briefing's prose is high-quality. A high-quality
|
| 75 |
+
generic Status section will still score 3 (place named) or 4 (topic
|
| 76 |
+
named without verdict). The 0-5 scale is a delta-detector, not an
|
| 77 |
+
absolute quality measure.
|
app/context/eo_chip_cache.py
CHANGED
|
@@ -16,6 +16,7 @@ specialist instead of surfacing a noisy error.
|
|
| 16 |
"""
|
| 17 |
from __future__ import annotations
|
| 18 |
|
|
|
|
| 19 |
import logging
|
| 20 |
import os
|
| 21 |
import threading
|
|
@@ -264,18 +265,8 @@ def _to_terramind_tensors(modalities: dict[str, Any]) -> dict[str, Any]:
|
|
| 264 |
return chips
|
| 265 |
|
| 266 |
|
| 267 |
-
def
|
| 268 |
-
"""
|
| 269 |
-
`{ok, skipped|err, ...}`; on success the dict carries the
|
| 270 |
-
co-registered numpy arrays plus `tensors` (the TerraMind-shaped
|
| 271 |
-
torch dict).
|
| 272 |
-
"""
|
| 273 |
-
if not ENABLE:
|
| 274 |
-
return {"ok": False, "skipped": "RIPRAP_EO_CHIP_ENABLE=0"}
|
| 275 |
-
if not _DEPS_OK:
|
| 276 |
-
return {"ok": False,
|
| 277 |
-
"skipped": f"deps unavailable on this deployment: "
|
| 278 |
-
f"{_DEPS_MISSING}"}
|
| 279 |
with _FETCH_LOCK:
|
| 280 |
try:
|
| 281 |
modalities = _fetch_modalities(lat, lon, timeout_s=timeout_s)
|
|
@@ -291,3 +282,31 @@ def fetch(lat: float, lon: float, timeout_s: float = 60.0) -> dict[str, Any]:
|
|
| 291 |
return {"ok": False,
|
| 292 |
"err": f"tensor build failed: {type(e).__name__}: {e}"}
|
| 293 |
return modalities
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
"""
|
| 17 |
from __future__ import annotations
|
| 18 |
|
| 19 |
+
import concurrent.futures
|
| 20 |
import logging
|
| 21 |
import os
|
| 22 |
import threading
|
|
|
|
| 265 |
return chips
|
| 266 |
|
| 267 |
|
| 268 |
+
def _fetch_and_build(lat: float, lon: float, timeout_s: float) -> dict[str, Any]:
|
| 269 |
+
"""Inner fetch + tensor build, run inside a bounded thread."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
with _FETCH_LOCK:
|
| 271 |
try:
|
| 272 |
modalities = _fetch_modalities(lat, lon, timeout_s=timeout_s)
|
|
|
|
| 282 |
return {"ok": False,
|
| 283 |
"err": f"tensor build failed: {type(e).__name__}: {e}"}
|
| 284 |
return modalities
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
def fetch(lat: float, lon: float, timeout_s: float = 60.0) -> dict[str, Any]:
|
| 288 |
+
"""Run the chip pipeline. Always returns a dict with at minimum
|
| 289 |
+
`{ok, skipped|err, ...}`; on success the dict carries the
|
| 290 |
+
co-registered numpy arrays plus `tensors` (the TerraMind-shaped
|
| 291 |
+
torch dict).
|
| 292 |
+
|
| 293 |
+
Runs in a daemon thread so that STAC searches and COG band downloads
|
| 294 |
+
(which use requests/rioxarray without per-call timeouts) are bounded
|
| 295 |
+
by a hard wall-clock deadline even when the network hangs.
|
| 296 |
+
"""
|
| 297 |
+
if not ENABLE:
|
| 298 |
+
return {"ok": False, "skipped": "RIPRAP_EO_CHIP_ENABLE=0"}
|
| 299 |
+
if not _DEPS_OK:
|
| 300 |
+
return {"ok": False,
|
| 301 |
+
"skipped": f"deps unavailable on this deployment: "
|
| 302 |
+
f"{_DEPS_MISSING}"}
|
| 303 |
+
# Hard wall-clock cap: pystac_client / rioxarray COG reads don't expose
|
| 304 |
+
# uniform per-request timeouts, so we bound the whole pipeline here.
|
| 305 |
+
hard_timeout = timeout_s + 15.0
|
| 306 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
| 307 |
+
future = pool.submit(_fetch_and_build, lat, lon, timeout_s)
|
| 308 |
+
try:
|
| 309 |
+
return future.result(timeout=hard_timeout)
|
| 310 |
+
except concurrent.futures.TimeoutError:
|
| 311 |
+
log.warning("eo_chip: hard timeout after %.0fs (STAC/COG hung)", hard_timeout)
|
| 312 |
+
return {"ok": False, "skipped": f"eo_chip timed out after {hard_timeout:.0f}s"}
|
app/flood_layers/prithvi_live.py
CHANGED
|
@@ -24,6 +24,7 @@ License: Apache-2.0. See experiments/shared/licenses.md.
|
|
| 24 |
|
| 25 |
from __future__ import annotations
|
| 26 |
|
|
|
|
| 27 |
import logging
|
| 28 |
import os
|
| 29 |
import threading
|
|
@@ -319,25 +320,8 @@ def _polygonize_mask(pred, ref_da, epsg: int) -> dict | None:
|
|
| 319 |
return None
|
| 320 |
|
| 321 |
|
| 322 |
-
def
|
| 323 |
-
"""
|
| 324 |
-
{ "ok": bool,
|
| 325 |
-
"skipped": str | None, # reason if no observation
|
| 326 |
-
"item_id": str | None,
|
| 327 |
-
"item_datetime": str | None,
|
| 328 |
-
"cloud_cover": float | None,
|
| 329 |
-
"pct_water_within_500m": float | None,
|
| 330 |
-
"pct_water_full": float | None }
|
| 331 |
-
Designed to never raise; failures show up as ok=False with an `err`.
|
| 332 |
-
"""
|
| 333 |
-
if not ENABLE:
|
| 334 |
-
return {"ok": False, "skipped": "RIPRAP_PRITHVI_LIVE_ENABLE=0"}
|
| 335 |
-
if not _DEPS_OK:
|
| 336 |
-
# Clean "not deployed here" signal instead of a ModuleNotFoundError
|
| 337 |
-
# surfaced as an exception. Same trace-card layout as ENABLE=0.
|
| 338 |
-
return {"ok": False,
|
| 339 |
-
"skipped": f"deps unavailable on this deployment: "
|
| 340 |
-
f"{_DEPS_MISSING}"}
|
| 341 |
t0 = time.time()
|
| 342 |
try:
|
| 343 |
item = _search_recent_scene(lat, lon)
|
|
@@ -428,3 +412,31 @@ def fetch(lat: float, lon: float, timeout_s: float = 60.0) -> dict[str, Any]:
|
|
| 428 |
log.exception("prithvi_live: fetch failed")
|
| 429 |
return {"ok": False, "err": f"{type(e).__name__}: {e}",
|
| 430 |
"elapsed_s": round(time.time() - t0, 2)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
from __future__ import annotations
|
| 26 |
|
| 27 |
+
import concurrent.futures
|
| 28 |
import logging
|
| 29 |
import os
|
| 30 |
import threading
|
|
|
|
| 320 |
return None
|
| 321 |
|
| 322 |
|
| 323 |
+
def _fetch_inner(lat: float, lon: float, timeout_s: float) -> dict[str, Any]:
|
| 324 |
+
"""Core fetch logic β run inside a bounded thread via fetch()."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 325 |
t0 = time.time()
|
| 326 |
try:
|
| 327 |
item = _search_recent_scene(lat, lon)
|
|
|
|
| 412 |
log.exception("prithvi_live: fetch failed")
|
| 413 |
return {"ok": False, "err": f"{type(e).__name__}: {e}",
|
| 414 |
"elapsed_s": round(time.time() - t0, 2)}
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
def fetch(lat: float, lon: float, timeout_s: float = 60.0) -> dict[str, Any]:
|
| 418 |
+
"""Run the specialist. Wraps _fetch_inner in a bounded thread so that
|
| 419 |
+
STAC searches and COG band reads (which lack per-request HTTP timeouts)
|
| 420 |
+
cannot hang the FSM indefinitely.
|
| 421 |
+
|
| 422 |
+
Returns a dict with at minimum:
|
| 423 |
+
{ "ok": bool, "skipped": str | None, "item_id": str | None,
|
| 424 |
+
"cloud_cover": float | None, "pct_water_within_500m": float | None }
|
| 425 |
+
Designed to never raise; failures show up as ok=False with an `err`.
|
| 426 |
+
"""
|
| 427 |
+
if not ENABLE:
|
| 428 |
+
return {"ok": False, "skipped": "RIPRAP_PRITHVI_LIVE_ENABLE=0"}
|
| 429 |
+
if not _DEPS_OK:
|
| 430 |
+
return {"ok": False,
|
| 431 |
+
"skipped": f"deps unavailable on this deployment: "
|
| 432 |
+
f"{_DEPS_MISSING}"}
|
| 433 |
+
hard_timeout = timeout_s + 15.0
|
| 434 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
| 435 |
+
future = pool.submit(_fetch_inner, lat, lon, timeout_s)
|
| 436 |
+
try:
|
| 437 |
+
return future.result(timeout=hard_timeout)
|
| 438 |
+
except concurrent.futures.TimeoutError:
|
| 439 |
+
log.warning("prithvi_live: hard timeout after %.0fs (STAC/COG hung)",
|
| 440 |
+
hard_timeout)
|
| 441 |
+
return {"ok": False,
|
| 442 |
+
"skipped": f"prithvi_live timed out after {hard_timeout:.0f}s"}
|
app/framing.py
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Question-aware framing for the Capstone briefing opening.
|
| 2 |
+
|
| 3 |
+
The four-section structure (Status / Empirical / Modeled / Policy) is
|
| 4 |
+
load-bearing for the Mellea grounding checks and stays unchanged. What
|
| 5 |
+
this module does is detect the *shape* of the user's question from the
|
| 6 |
+
raw query string + planner intent, then return a single-sentence
|
| 7 |
+
directive that conditions only the opening Status sentence.
|
| 8 |
+
|
| 9 |
+
Eleven question types are recognised; they mirror the rubric in
|
| 10 |
+
`tests/integration/stakeholder_queries.py:FRAMING_RUBRICS`. Detection
|
| 11 |
+
is deterministic regex matching β no extra LLM call, no added latency.
|
| 12 |
+
|
| 13 |
+
Usage:
|
| 14 |
+
|
| 15 |
+
from app.framing import augment_system_prompt
|
| 16 |
+
system_prompt = augment_system_prompt(
|
| 17 |
+
EXTRA_SYSTEM_PROMPT, query=user_query, intent=plan.intent,
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
The returned prompt has the original text plus a trailing
|
| 21 |
+
`QUESTION-AWARE OPENING:` block. Granite 4.1 attends to this through
|
| 22 |
+
the system-prompt cache and applies it to the Status sentence.
|
| 23 |
+
"""
|
| 24 |
+
from __future__ import annotations
|
| 25 |
+
|
| 26 |
+
import re
|
| 27 |
+
from typing import Final
|
| 28 |
+
|
| 29 |
+
QUESTION_TYPES: Final[tuple[str, ...]] = (
|
| 30 |
+
"habitability_decision",
|
| 31 |
+
"legal_disclosure",
|
| 32 |
+
"capital_planning",
|
| 33 |
+
"underwriting",
|
| 34 |
+
"journalism",
|
| 35 |
+
"development_siting",
|
| 36 |
+
"grant_evidence",
|
| 37 |
+
"retrospective",
|
| 38 |
+
"emergency_response",
|
| 39 |
+
"comparison",
|
| 40 |
+
"generic_exposure",
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# ---- Per-type opening directives ------------------------------------------
|
| 45 |
+
#
|
| 46 |
+
# Each directive is one sentence that supplements (does not replace) the
|
| 47 |
+
# Status section's existing instruction. Granite 4.1 has a strong prior
|
| 48 |
+
# toward "this address is exposed to ..." openings; the directive
|
| 49 |
+
# overrides that in a question-shaped way without disturbing the four
|
| 50 |
+
# grounding invariants.
|
| 51 |
+
|
| 52 |
+
_DIRECTIVES: dict[str, str] = {
|
| 53 |
+
"habitability_decision": (
|
| 54 |
+
"The Status sentence MUST start with a direct verdict word "
|
| 55 |
+
"(\"Yes\" if the documents show meaningful flood evidence, \"No\" "
|
| 56 |
+
"if they don't), then name the single strongest piece of "
|
| 57 |
+
"evidence with its [doc_id]. The user is deciding whether to "
|
| 58 |
+
"live here β answer the question, then cite."
|
| 59 |
+
),
|
| 60 |
+
"legal_disclosure": (
|
| 61 |
+
"The Status sentence MUST state whether the documents contain "
|
| 62 |
+
"facts a NY RPL Β§462(2) or Β§231-b disclosure would need to "
|
| 63 |
+
"record. Begin with \"Disclosure is warranted\" or \"Disclosure "
|
| 64 |
+
"is not triggered\" based on the evidence, then name the "
|
| 65 |
+
"specific fact with its [doc_id]. The user is a real-estate "
|
| 66 |
+
"professional checking the disclosure threshold."
|
| 67 |
+
),
|
| 68 |
+
"capital_planning": (
|
| 69 |
+
"The Status sentence MUST frame the place as a capital-planning "
|
| 70 |
+
"candidate: name the dominant exposure with its [doc_id] and "
|
| 71 |
+
"indicate whether the evidence supports prioritization "
|
| 72 |
+
"(\"merits prioritization\", \"ranks high for hardening\") or "
|
| 73 |
+
"not. The user allocates infrastructure investment."
|
| 74 |
+
),
|
| 75 |
+
"underwriting": (
|
| 76 |
+
"The Status sentence MUST emphasize that every figure in the "
|
| 77 |
+
"briefing is independently sourced β open with the dominant "
|
| 78 |
+
"exposure and the specific [doc_id], then add a half-clause "
|
| 79 |
+
"noting that the audit chain follows below. The user is an "
|
| 80 |
+
"underwriter who needs a defensible loss narrative."
|
| 81 |
+
),
|
| 82 |
+
"journalism": (
|
| 83 |
+
"The Status sentence MUST be reproducible reporting prose: "
|
| 84 |
+
"name the place, name the dominant exposure with [doc_id], "
|
| 85 |
+
"and avoid editorial verbs like \"shocking\" or \"alarming\". "
|
| 86 |
+
"The user is a data journalist who will cite this prose verbatim."
|
| 87 |
+
),
|
| 88 |
+
"development_siting": (
|
| 89 |
+
"The Status sentence MUST start with the count of active "
|
| 90 |
+
"construction filings cited from [dob_permits] (e.g. \"N "
|
| 91 |
+
"active construction filings sit inside ...\") and indicate "
|
| 92 |
+
"which flood layer they intersect. The user is a developer or "
|
| 93 |
+
"architect doing a pre-design siting check."
|
| 94 |
+
),
|
| 95 |
+
"grant_evidence": (
|
| 96 |
+
"The Status sentence MUST open with \"Vulnerability "
|
| 97 |
+
"assessment:\" and name the place + dominant exposure with "
|
| 98 |
+
"[doc_id]. Treat the briefing as the evidence section of a "
|
| 99 |
+
"HUD CDBG-DR or FEMA BRIC application β formal, third-person, "
|
| 100 |
+
"free of advocacy framing."
|
| 101 |
+
),
|
| 102 |
+
"retrospective": (
|
| 103 |
+
"Riprap currently runs on present-day data sources. The Status "
|
| 104 |
+
"sentence MUST acknowledge the question is retrospective and "
|
| 105 |
+
"state explicitly that the briefing reflects the CURRENT state "
|
| 106 |
+
"of these data sources, not a snapshot from the requested date. "
|
| 107 |
+
"Then proceed with the present-day exposure picture so the user "
|
| 108 |
+
"still gets the geography. Silence-over-confabulation: never "
|
| 109 |
+
"reconstruct historical conditions you can't verify."
|
| 110 |
+
),
|
| 111 |
+
"emergency_response": (
|
| 112 |
+
"The Status sentence MUST quantify what is at risk in the "
|
| 113 |
+
"next few hours, citing the live signal that triggered the "
|
| 114 |
+
"query and any active alerts with [doc_id]. The user needs an "
|
| 115 |
+
"operational picture, not a historical exposure summary."
|
| 116 |
+
),
|
| 117 |
+
"comparison": (
|
| 118 |
+
"The Status sentence MUST name BOTH places the user is "
|
| 119 |
+
"comparing and indicate which one shows greater exposure on "
|
| 120 |
+
"the strongest cited signal. If only one place's data is "
|
| 121 |
+
"available in the documents, say so explicitly. The user is "
|
| 122 |
+
"doing a head-to-head decision."
|
| 123 |
+
),
|
| 124 |
+
"generic_exposure": "", # default β no override
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
# ---- Detector -------------------------------------------------------------
|
| 129 |
+
#
|
| 130 |
+
# Patterns are ordered: the FIRST type whose pattern matches wins. Order
|
| 131 |
+
# matters β more specific question shapes (legal_disclosure, grant_evidence,
|
| 132 |
+
# emergency_response) come before more general ones (habitability_decision,
|
| 133 |
+
# capital_planning) so the obvious specialist tags don't get swallowed.
|
| 134 |
+
|
| 135 |
+
_PATTERNS: list[tuple[str, list[re.Pattern]]] = [
|
| 136 |
+
("retrospective", [
|
| 137 |
+
re.compile(r"\b(would have|would Riprap|on (the )?date of|as of (the )?(date|day)|"
|
| 138 |
+
r"day before|prior to|before (Hurricane|Ida|Sandy|the storm)|"
|
| 139 |
+
r"on (August|September|October|November|December|January|February|March|"
|
| 140 |
+
r"April|May|June|July) \d{1,2},? ?\d{4}|"
|
| 141 |
+
r"time.?machine|retrospective|court (exhibit|testimony))\b", re.I),
|
| 142 |
+
]),
|
| 143 |
+
("emergency_response", [
|
| 144 |
+
re.compile(r"\b(just triggered|right now|next (few |six |\d+ )?hours?|"
|
| 145 |
+
r"in the next \d+|currently flooding|flood (warning|watch) is active|"
|
| 146 |
+
r"sensor [A-Z]{2}-?\d+|live (alert|trigger))\b", re.I),
|
| 147 |
+
]),
|
| 148 |
+
("legal_disclosure", [
|
| 149 |
+
re.compile(r"\b(disclos(e|ure|ed)|RPL\s*Β§?\s*\d+|Property Condition Disclosure|"
|
| 150 |
+
r"Β§\s*462|Β§\s*231-?b|seller'?s? disclosure|landlord'?s? disclosure|"
|
| 151 |
+
r"required to disclose|need to disclose)\b", re.I),
|
| 152 |
+
]),
|
| 153 |
+
("grant_evidence", [
|
| 154 |
+
re.compile(r"\b(vulnerability assessment|CDBG-?DR|HUD|BRIC|"
|
| 155 |
+
r"grant application|funding application|community resilience grant|"
|
| 156 |
+
r"FEMA application|disaster recovery (application|funding))\b", re.I),
|
| 157 |
+
]),
|
| 158 |
+
("development_siting", [
|
| 159 |
+
re.compile(r"\b(what (are|is) (they|being) build(ing)?|new construction|"
|
| 160 |
+
r"under construction|active (construction|filing|project|permit)|"
|
| 161 |
+
r"projects? (in progress|underway|planned)|architects?|"
|
| 162 |
+
r"siting check|pre.?design|"
|
| 163 |
+
r"DOB filing|developer)\b", re.I),
|
| 164 |
+
]),
|
| 165 |
+
("comparison", [
|
| 166 |
+
# `prioritize X over Y` can have many words between, hence the
|
| 167 |
+
# bounded non-greedy span β capped at 80 chars to avoid runaway.
|
| 168 |
+
re.compile(r"\b(compare\b|comparison|\bvs\b|\bversus\b|"
|
| 169 |
+
r"head-?to-?head|\brank\s+the\s+top)\b", re.I),
|
| 170 |
+
re.compile(r"\bprioritize\b.{1,80}\bover\b", re.I | re.S),
|
| 171 |
+
re.compile(r"\bover\s+\w+(?:\s+\w+){0,3}\s+for\s+(hardening|investment)\b", re.I),
|
| 172 |
+
]),
|
| 173 |
+
("capital_planning", [
|
| 174 |
+
re.compile(r"\b(prioritiz(e|ation)|capital plan(ning)?|harden(ing|s)?|"
|
| 175 |
+
r"infrastructure investment|where (should|to) (we |the )(invest|"
|
| 176 |
+
r"prioritize|harden)|MTA.+prioritize|DEP.+prioritize|"
|
| 177 |
+
r"protection envelope|outside (it|the protection)|"
|
| 178 |
+
r"resilien(ce|cy) project)\b", re.I),
|
| 179 |
+
]),
|
| 180 |
+
("habitability_decision", [
|
| 181 |
+
re.compile(r"\b(should I worry|should I (be|consider)|is (it|this) safe|"
|
| 182 |
+
r"can I (rent|live|move|raise (my )?kids?)|considering (renting|leasing|moving)|"
|
| 183 |
+
r"(thinking about|planning to) (rent|lease|move|buy)|"
|
| 184 |
+
r"is (this|that|the landlord) true|landlord (says|claims|told)|"
|
| 185 |
+
r"no flood history|just got a lease|new lease|signing a lease|"
|
| 186 |
+
r"\bworry\b)", re.I),
|
| 187 |
+
]),
|
| 188 |
+
("underwriting", [
|
| 189 |
+
re.compile(r"\b(underwrit(e|er|ing|able)|actuarial|loss history|"
|
| 190 |
+
r"insurabl[ey]|catastrophe (model|risk)|"
|
| 191 |
+
r"insurance (audit|memo|profile)|"
|
| 192 |
+
r"audit (chain|trail))\b", re.I),
|
| 193 |
+
]),
|
| 194 |
+
("journalism", [
|
| 195 |
+
re.compile(r"\b(reporter|journalist|newsroom|story|coverage|"
|
| 196 |
+
r"published?|publish (this|the))", re.I),
|
| 197 |
+
]),
|
| 198 |
+
]
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def detect(query: str, intent: str | None = None) -> str:
|
| 202 |
+
"""Classify the question shape from the raw query and planner intent.
|
| 203 |
+
|
| 204 |
+
Returns one of `QUESTION_TYPES`. Falls back to `generic_exposure`
|
| 205 |
+
when no pattern matches β that's the existing behavior, preserved.
|
| 206 |
+
|
| 207 |
+
`intent` is currently advisory only (the patterns don't read it),
|
| 208 |
+
but the parameter is part of the API so future refinements can
|
| 209 |
+
use it (e.g. an `intent=neighborhood` query without a verdict
|
| 210 |
+
keyword could default to `journalism` rather than `generic_exposure`).
|
| 211 |
+
"""
|
| 212 |
+
if not query:
|
| 213 |
+
return "generic_exposure"
|
| 214 |
+
q = query.strip()
|
| 215 |
+
for qt, patterns in _PATTERNS:
|
| 216 |
+
if any(p.search(q) for p in patterns):
|
| 217 |
+
return qt
|
| 218 |
+
# Heuristic fallback: bare neighborhood/borough names from a planner
|
| 219 |
+
# context default to journalism (most common stakeholder reading a
|
| 220 |
+
# neighborhood-only query is a reporter or planner). For
|
| 221 |
+
# single_address with no question keyword, fall back to generic.
|
| 222 |
+
if intent == "neighborhood" and len(q.split()) <= 3:
|
| 223 |
+
return "journalism"
|
| 224 |
+
return "generic_exposure"
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def opening_instruction(question_type: str) -> str:
|
| 228 |
+
"""Return the directive sentence(s) for a question type.
|
| 229 |
+
Returns empty string for `generic_exposure` (no override)."""
|
| 230 |
+
return _DIRECTIVES.get(question_type, "")
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def augment_system_prompt(base: str, *, query: str,
|
| 234 |
+
intent: str | None = None) -> str:
|
| 235 |
+
"""Wrap a base system prompt with a question-aware opening directive.
|
| 236 |
+
|
| 237 |
+
No-op when the detector returns `generic_exposure` β the original
|
| 238 |
+
behavior is preserved.
|
| 239 |
+
"""
|
| 240 |
+
qt = detect(query, intent)
|
| 241 |
+
directive = opening_instruction(qt)
|
| 242 |
+
if not directive:
|
| 243 |
+
return base
|
| 244 |
+
return (
|
| 245 |
+
f"{base}\n\n"
|
| 246 |
+
f"QUESTION-AWARE OPENING (this directive overrides ONLY the opening "
|
| 247 |
+
f"**Status.** sentence; the four-section structure and citation "
|
| 248 |
+
f"discipline above remain in force):\n{directive}"
|
| 249 |
+
)
|
app/fsm.py
CHANGED
|
@@ -95,6 +95,29 @@ def _current_planned_specialists():
|
|
| 95 |
return getattr(_FSM_LOCAL, "planned_specialists", None)
|
| 96 |
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
# Canonical Burr: one action per specialist, sequential transitions.
|
| 99 |
# A previous version of this module wrapped 16 specialists in a single
|
| 100 |
# fan-out action that ran them concurrently in a ThreadPoolExecutor;
|
|
@@ -969,6 +992,7 @@ def step_reconcile(state: State) -> State:
|
|
| 969 |
"doh_hospitals": state.get("doh_hospitals"),
|
| 970 |
}
|
| 971 |
if is_strict:
|
|
|
|
| 972 |
from app.mellea_validator import DEFAULT_LOOP_BUDGET, reconcile_strict_streaming
|
| 973 |
from app.reconcile import EXTRA_SYSTEM_PROMPT, build_documents, trim_docs_to_plan
|
| 974 |
doc_msgs = build_documents(snap)
|
|
@@ -979,8 +1003,13 @@ def step_reconcile(state: State) -> State:
|
|
| 979 |
else:
|
| 980 |
token_cb = _current_token_callback()
|
| 981 |
attempt_cb = _current_mellea_attempt_callback()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 982 |
mres = reconcile_strict_streaming(
|
| 983 |
-
doc_msgs,
|
| 984 |
user_prompt="Write the cited paragraph now.",
|
| 985 |
loop_budget=DEFAULT_LOOP_BUDGET,
|
| 986 |
on_token=(lambda d, _ai: token_cb(d)) if token_cb else None,
|
|
@@ -1024,6 +1053,7 @@ def step_reconcile(state: State) -> State:
|
|
| 1024 |
|
| 1025 |
import os as _os # noqa: E402
|
| 1026 |
|
|
|
|
| 1027 |
# Specialists that involve large spatial joins (every NYCHA development
|
| 1028 |
# overlapped against multiple flood layers, every DOE school footprint
|
| 1029 |
# joined to DEM/HAND, etc.) or per-query model inference (Prithvi-EO live
|
|
@@ -1057,6 +1087,15 @@ _HEAVY_SPECIALISTS_ENABLED = _os.environ.get(
|
|
| 1057 |
"RIPRAP_HEAVY_SPECIALISTS", _HEAVY_DEFAULT,
|
| 1058 |
).lower() in ("1", "true", "yes")
|
| 1059 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1060 |
|
| 1061 |
def build_app(query: str):
|
| 1062 |
"""Linear, single-action-per-step Burr application.
|
|
@@ -1090,10 +1129,11 @@ def build_app(query: str):
|
|
| 1090 |
"mta_entrances": step_mta_entrances,
|
| 1091 |
"prithvi": step_prithvi, # baked GeoJSON polygons for Ida; cheap
|
| 1092 |
}
|
| 1093 |
-
if _HEAVY_SPECIALISTS_ENABLED:
|
| 1094 |
actions["nycha"] = step_nycha
|
| 1095 |
actions["doe_schools"] = step_doe_schools
|
| 1096 |
actions["doh_hospitals"] = step_doh_hospitals
|
|
|
|
| 1097 |
actions["prithvi_live"] = step_prithvi_live
|
| 1098 |
actions["terramind"] = step_terramind
|
| 1099 |
# New TerraMind-NYC LoRA family β one chip fetch feeds two
|
|
|
|
| 95 |
return getattr(_FSM_LOCAL, "planned_specialists", None)
|
| 96 |
|
| 97 |
|
| 98 |
+
def set_user_query(query: str | None):
|
| 99 |
+
"""Install the user's original natural-language query for question-aware
|
| 100 |
+
framing in step_reconcile. The FSM's state["query"] is the geocoder
|
| 101 |
+
input (often just the street address), which doesn't carry the
|
| 102 |
+
user's question shape β set this separately so Capstone can detect
|
| 103 |
+
'should I worry' / 'is disclosure required' / etc."""
|
| 104 |
+
_FSM_LOCAL.user_query = query
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def _current_user_query() -> str | None:
|
| 108 |
+
return getattr(_FSM_LOCAL, "user_query", None)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def set_planner_intent(intent: str | None):
|
| 112 |
+
"""Install the planner's classified intent so step_reconcile can pass
|
| 113 |
+
it to the framing detector as a tiebreaker on bare-place queries."""
|
| 114 |
+
_FSM_LOCAL.planner_intent = intent
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def _current_planner_intent() -> str | None:
|
| 118 |
+
return getattr(_FSM_LOCAL, "planner_intent", None)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
# Canonical Burr: one action per specialist, sequential transitions.
|
| 122 |
# A previous version of this module wrapped 16 specialists in a single
|
| 123 |
# fan-out action that ran them concurrently in a ThreadPoolExecutor;
|
|
|
|
| 992 |
"doh_hospitals": state.get("doh_hospitals"),
|
| 993 |
}
|
| 994 |
if is_strict:
|
| 995 |
+
from app.framing import augment_system_prompt
|
| 996 |
from app.mellea_validator import DEFAULT_LOOP_BUDGET, reconcile_strict_streaming
|
| 997 |
from app.reconcile import EXTRA_SYSTEM_PROMPT, build_documents, trim_docs_to_plan
|
| 998 |
doc_msgs = build_documents(snap)
|
|
|
|
| 1003 |
else:
|
| 1004 |
token_cb = _current_token_callback()
|
| 1005 |
attempt_cb = _current_mellea_attempt_callback()
|
| 1006 |
+
framed_prompt = augment_system_prompt(
|
| 1007 |
+
EXTRA_SYSTEM_PROMPT,
|
| 1008 |
+
query=_current_user_query() or state.get("query") or "",
|
| 1009 |
+
intent=_current_planner_intent() or "single_address",
|
| 1010 |
+
)
|
| 1011 |
mres = reconcile_strict_streaming(
|
| 1012 |
+
doc_msgs, framed_prompt,
|
| 1013 |
user_prompt="Write the cited paragraph now.",
|
| 1014 |
loop_budget=DEFAULT_LOOP_BUDGET,
|
| 1015 |
on_token=(lambda d, _ai: token_cb(d)) if token_cb else None,
|
|
|
|
| 1053 |
|
| 1054 |
import os as _os # noqa: E402
|
| 1055 |
|
| 1056 |
+
|
| 1057 |
# Specialists that involve large spatial joins (every NYCHA development
|
| 1058 |
# overlapped against multiple flood layers, every DOE school footprint
|
| 1059 |
# joined to DEM/HAND, etc.) or per-query model inference (Prithvi-EO live
|
|
|
|
| 1087 |
"RIPRAP_HEAVY_SPECIALISTS", _HEAVY_DEFAULT,
|
| 1088 |
).lower() in ("1", "true", "yes")
|
| 1089 |
|
| 1090 |
+
# NYCHA / DOE / DOH registers load a 91 MB sandy_inundation.geojson via
|
| 1091 |
+
# geopandas on first call. On machines with slow I/O or single-threaded
|
| 1092 |
+
# Python GIL contention (M3 local dev) this takes 3β5 min and makes the
|
| 1093 |
+
# first single_address query appear hung. Disable by default; enable on
|
| 1094 |
+
# the AMD droplet where the server pre-warms these at startup.
|
| 1095 |
+
_NYCHA_REGISTERS_ENABLED = _os.environ.get(
|
| 1096 |
+
"RIPRAP_NYCHA_REGISTERS", "0",
|
| 1097 |
+
).lower() in ("1", "true", "yes")
|
| 1098 |
+
|
| 1099 |
|
| 1100 |
def build_app(query: str):
|
| 1101 |
"""Linear, single-action-per-step Burr application.
|
|
|
|
| 1129 |
"mta_entrances": step_mta_entrances,
|
| 1130 |
"prithvi": step_prithvi, # baked GeoJSON polygons for Ida; cheap
|
| 1131 |
}
|
| 1132 |
+
if _HEAVY_SPECIALISTS_ENABLED and _NYCHA_REGISTERS_ENABLED:
|
| 1133 |
actions["nycha"] = step_nycha
|
| 1134 |
actions["doe_schools"] = step_doe_schools
|
| 1135 |
actions["doh_hospitals"] = step_doh_hospitals
|
| 1136 |
+
if _HEAVY_SPECIALISTS_ENABLED:
|
| 1137 |
actions["prithvi_live"] = step_prithvi_live
|
| 1138 |
actions["terramind"] = step_terramind
|
| 1139 |
# New TerraMind-NYC LoRA family β one chip fetch feeds two
|
app/geocode.py
CHANGED
|
@@ -166,7 +166,13 @@ def geocode_one(text: str) -> GeocodeHit | None:
|
|
| 166 |
return hit
|
| 167 |
|
| 168 |
hint = _detect_borough(text)
|
| 169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
if hint:
|
| 171 |
in_boro = [h for h in hits if h.borough and h.borough.lower() == hint.lower()]
|
| 172 |
if in_boro:
|
|
|
|
| 166 |
return hit
|
| 167 |
|
| 168 |
hint = _detect_borough(text)
|
| 169 |
+
try:
|
| 170 |
+
hits = geocode(text, limit=8)
|
| 171 |
+
except Exception as e:
|
| 172 |
+
# Geosearch is unreachable or returned a server error β fall back to
|
| 173 |
+
# Nominatim rather than surfacing a 503 to every downstream specialist.
|
| 174 |
+
log.warning("Geosearch unavailable (%r) β falling back to Nominatim", e)
|
| 175 |
+
return geocode_nominatim(text)
|
| 176 |
if hint:
|
| 177 |
in_boro = [h for h in hits if h.borough and h.borough.lower() == hint.lower()]
|
| 178 |
if in_boro:
|
app/inference.py
CHANGED
|
@@ -30,10 +30,10 @@ RIPRAP_ML_* env is unset (e.g. on first-light dev or in unit tests).
|
|
| 30 |
from __future__ import annotations
|
| 31 |
|
| 32 |
import base64
|
| 33 |
-
import io
|
| 34 |
import logging
|
| 35 |
import os
|
| 36 |
-
from
|
|
|
|
| 37 |
|
| 38 |
import httpx
|
| 39 |
|
|
|
|
| 30 |
from __future__ import annotations
|
| 31 |
|
| 32 |
import base64
|
|
|
|
| 33 |
import logging
|
| 34 |
import os
|
| 35 |
+
from collections.abc import Iterable
|
| 36 |
+
from typing import Any
|
| 37 |
|
| 38 |
import httpx
|
| 39 |
|
app/intents/development_check.py
CHANGED
|
@@ -202,6 +202,7 @@ def run(plan, query: str, progress_q=None, strict: bool = False) -> dict[str, An
|
|
| 202 |
# validation failure we emit a mellea_attempt event and reroll.
|
| 203 |
rec_step["step"] = "mellea_reconcile_development"
|
| 204 |
try:
|
|
|
|
| 205 |
from app.mellea_validator import DEFAULT_LOOP_BUDGET, reconcile_strict_streaming
|
| 206 |
from app.reconcile import trim_docs_to_plan as _trim
|
| 207 |
docs = _trim(docs, set(plan.specialists or []))
|
|
@@ -214,8 +215,11 @@ def run(plan, query: str, progress_q=None, strict: bool = False) -> dict[str, An
|
|
| 214 |
progress_q.put({"kind": "mellea_attempt",
|
| 215 |
"attempt": attempt_idx,
|
| 216 |
"passed": passed, "failed": failed})
|
|
|
|
|
|
|
|
|
|
| 217 |
mres = reconcile_strict_streaming(
|
| 218 |
-
docs,
|
| 219 |
user_prompt="Write the development briefing now.",
|
| 220 |
model=OLLAMA_MODEL, loop_budget=DEFAULT_LOOP_BUDGET,
|
| 221 |
on_token=_on_token if progress_q else None,
|
|
|
|
| 202 |
# validation failure we emit a mellea_attempt event and reroll.
|
| 203 |
rec_step["step"] = "mellea_reconcile_development"
|
| 204 |
try:
|
| 205 |
+
from app.framing import augment_system_prompt
|
| 206 |
from app.mellea_validator import DEFAULT_LOOP_BUDGET, reconcile_strict_streaming
|
| 207 |
from app.reconcile import trim_docs_to_plan as _trim
|
| 208 |
docs = _trim(docs, set(plan.specialists or []))
|
|
|
|
| 215 |
progress_q.put({"kind": "mellea_attempt",
|
| 216 |
"attempt": attempt_idx,
|
| 217 |
"passed": passed, "failed": failed})
|
| 218 |
+
framed_prompt = augment_system_prompt(
|
| 219 |
+
EXTRA_SYSTEM_PROMPT, query=query, intent=plan.intent,
|
| 220 |
+
)
|
| 221 |
mres = reconcile_strict_streaming(
|
| 222 |
+
docs, framed_prompt,
|
| 223 |
user_prompt="Write the development briefing now.",
|
| 224 |
model=OLLAMA_MODEL, loop_budget=DEFAULT_LOOP_BUDGET,
|
| 225 |
on_token=_on_token if progress_q else None,
|
app/intents/live_now.py
CHANGED
|
@@ -153,7 +153,14 @@ def run(plan, query: str, progress_q=None) -> dict[str, Any]:
|
|
| 153 |
if progress_q is not None:
|
| 154 |
progress_q.put({"kind": "token", "delta": delta})
|
| 155 |
try:
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
rec_step["ok"] = True
|
| 158 |
except Exception as e:
|
| 159 |
paragraph = "Could not produce a live-conditions report."
|
|
@@ -206,10 +213,11 @@ def _doc(doc_id: str, body_lines: list[str]) -> dict:
|
|
| 206 |
return {"role": f"document {doc_id}", "content": "\n".join(body_lines)}
|
| 207 |
|
| 208 |
|
| 209 |
-
def _reconcile(docs: list[dict], on_token=None
|
|
|
|
| 210 |
from app.reconcile import verify_paragraph
|
| 211 |
messages = docs + [
|
| 212 |
-
{"role": "system", "content":
|
| 213 |
{"role": "user", "content": "Write the live-conditions briefing now."},
|
| 214 |
]
|
| 215 |
# live_now is the smallest intent: ~4 live docs, short briefing.
|
|
|
|
| 153 |
if progress_q is not None:
|
| 154 |
progress_q.put({"kind": "token", "delta": delta})
|
| 155 |
try:
|
| 156 |
+
from app.framing import augment_system_prompt
|
| 157 |
+
framed_prompt = augment_system_prompt(
|
| 158 |
+
EXTRA_SYSTEM_PROMPT, query=query, intent=plan.intent,
|
| 159 |
+
)
|
| 160 |
+
paragraph, audit = _reconcile(
|
| 161 |
+
docs, on_token=_on_token if progress_q else None,
|
| 162 |
+
system_prompt=framed_prompt,
|
| 163 |
+
)
|
| 164 |
rec_step["ok"] = True
|
| 165 |
except Exception as e:
|
| 166 |
paragraph = "Could not produce a live-conditions report."
|
|
|
|
| 213 |
return {"role": f"document {doc_id}", "content": "\n".join(body_lines)}
|
| 214 |
|
| 215 |
|
| 216 |
+
def _reconcile(docs: list[dict], on_token=None,
|
| 217 |
+
system_prompt: str = EXTRA_SYSTEM_PROMPT) -> tuple[str, dict]:
|
| 218 |
from app.reconcile import verify_paragraph
|
| 219 |
messages = docs + [
|
| 220 |
+
{"role": "system", "content": system_prompt},
|
| 221 |
{"role": "user", "content": "Write the live-conditions briefing now."},
|
| 222 |
]
|
| 223 |
# live_now is the smallest intent: ~4 live docs, short briefing.
|
app/intents/neighborhood.py
CHANGED
|
@@ -361,6 +361,7 @@ def run(plan, query: str, progress_q=None, strict: bool = False) -> dict[str, An
|
|
| 361 |
if docs and strict:
|
| 362 |
rec_step["step"] = "mellea_reconcile_neighborhood"
|
| 363 |
try:
|
|
|
|
| 364 |
from app.mellea_validator import DEFAULT_LOOP_BUDGET, reconcile_strict_streaming
|
| 365 |
from app.reconcile import trim_docs_to_plan as _trim
|
| 366 |
docs = _trim(docs, set(plan.specialists or []))
|
|
@@ -373,8 +374,11 @@ def run(plan, query: str, progress_q=None, strict: bool = False) -> dict[str, An
|
|
| 373 |
progress_q.put({"kind": "mellea_attempt",
|
| 374 |
"attempt": attempt_idx,
|
| 375 |
"passed": passed, "failed": failed})
|
|
|
|
|
|
|
|
|
|
| 376 |
mres = reconcile_strict_streaming(
|
| 377 |
-
docs,
|
| 378 |
user_prompt="Write the cited briefing now.",
|
| 379 |
model=OLLAMA_MODEL, loop_budget=DEFAULT_LOOP_BUDGET,
|
| 380 |
on_token=_on_token if progress_q else None,
|
|
|
|
| 361 |
if docs and strict:
|
| 362 |
rec_step["step"] = "mellea_reconcile_neighborhood"
|
| 363 |
try:
|
| 364 |
+
from app.framing import augment_system_prompt
|
| 365 |
from app.mellea_validator import DEFAULT_LOOP_BUDGET, reconcile_strict_streaming
|
| 366 |
from app.reconcile import trim_docs_to_plan as _trim
|
| 367 |
docs = _trim(docs, set(plan.specialists or []))
|
|
|
|
| 374 |
progress_q.put({"kind": "mellea_attempt",
|
| 375 |
"attempt": attempt_idx,
|
| 376 |
"passed": passed, "failed": failed})
|
| 377 |
+
framed_prompt = augment_system_prompt(
|
| 378 |
+
EXTRA_SYSTEM_PROMPT, query=query, intent=plan.intent,
|
| 379 |
+
)
|
| 380 |
mres = reconcile_strict_streaming(
|
| 381 |
+
docs, framed_prompt,
|
| 382 |
user_prompt="Write the cited briefing now.",
|
| 383 |
model=OLLAMA_MODEL, loop_budget=DEFAULT_LOOP_BUDGET,
|
| 384 |
on_token=_on_token if progress_q else None,
|
app/intents/single_address.py
CHANGED
|
@@ -8,8 +8,21 @@ parallelism for an address is bounded by Granite 4.1 reconcile time
|
|
| 8 |
anyway."""
|
| 9 |
from __future__ import annotations
|
| 10 |
|
|
|
|
|
|
|
| 11 |
from app.fsm import run as run_linear
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
def run(plan, query: str, progress_q=None, strict: bool = False) -> dict:
|
| 15 |
"""Execute the planner's single_address Plan via the existing linear
|
|
@@ -23,16 +36,20 @@ def run(plan, query: str, progress_q=None, strict: bool = False) -> dict:
|
|
| 23 |
iter_steps,
|
| 24 |
set_mellea_attempt_callback,
|
| 25 |
set_planned_specialists,
|
|
|
|
| 26 |
set_strict_mode,
|
| 27 |
set_token_callback,
|
|
|
|
| 28 |
)
|
| 29 |
planner_addr = next(
|
| 30 |
(t["text"] for t in plan.targets if t.get("type") == "address"),
|
| 31 |
None,
|
| 32 |
)
|
| 33 |
-
addr = planner_addr if (planner_addr
|
| 34 |
set_strict_mode(strict)
|
| 35 |
set_planned_specialists(plan.specialists or [])
|
|
|
|
|
|
|
| 36 |
if progress_q is not None:
|
| 37 |
def _on_token(delta: str):
|
| 38 |
progress_q.put({"kind": "token", "delta": delta})
|
|
@@ -57,12 +74,16 @@ def run(plan, query: str, progress_q=None, strict: bool = False) -> dict:
|
|
| 57 |
set_mellea_attempt_callback(None)
|
| 58 |
set_strict_mode(False)
|
| 59 |
set_planned_specialists(None)
|
|
|
|
|
|
|
| 60 |
else:
|
| 61 |
try:
|
| 62 |
out = run_linear(addr)
|
| 63 |
finally:
|
| 64 |
set_strict_mode(False)
|
| 65 |
set_planned_specialists(None)
|
|
|
|
|
|
|
| 66 |
out["intent"] = "single_address"
|
| 67 |
out["plan"] = {
|
| 68 |
"intent": plan.intent,
|
|
|
|
| 8 |
anyway."""
|
| 9 |
from __future__ import annotations
|
| 10 |
|
| 11 |
+
import re
|
| 12 |
+
|
| 13 |
from app.fsm import run as run_linear
|
| 14 |
|
| 15 |
+
_ADDRESS_SHAPE = re.compile(
|
| 16 |
+
r"^\d+\s+[A-Z][\w\s\.\-']+(St|Street|Ave|Avenue|Rd|Road|Blvd|"
|
| 17 |
+
r"Boulevard|Pl|Place|Ln|Lane|Dr|Drive|Way|Ct|Court|Pkwy|"
|
| 18 |
+
r"Parkway|Sq|Square|Ter|Terrace|Hwy|Highway)\.?",
|
| 19 |
+
re.IGNORECASE,
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _looks_like_address(s: str) -> bool:
|
| 24 |
+
return bool(s and _ADDRESS_SHAPE.search(s))
|
| 25 |
+
|
| 26 |
|
| 27 |
def run(plan, query: str, progress_q=None, strict: bool = False) -> dict:
|
| 28 |
"""Execute the planner's single_address Plan via the existing linear
|
|
|
|
| 36 |
iter_steps,
|
| 37 |
set_mellea_attempt_callback,
|
| 38 |
set_planned_specialists,
|
| 39 |
+
set_planner_intent,
|
| 40 |
set_strict_mode,
|
| 41 |
set_token_callback,
|
| 42 |
+
set_user_query,
|
| 43 |
)
|
| 44 |
planner_addr = next(
|
| 45 |
(t["text"] for t in plan.targets if t.get("type") == "address"),
|
| 46 |
None,
|
| 47 |
)
|
| 48 |
+
addr = planner_addr if _looks_like_address(planner_addr) else query
|
| 49 |
set_strict_mode(strict)
|
| 50 |
set_planned_specialists(plan.specialists or [])
|
| 51 |
+
set_user_query(query)
|
| 52 |
+
set_planner_intent(plan.intent)
|
| 53 |
if progress_q is not None:
|
| 54 |
def _on_token(delta: str):
|
| 55 |
progress_q.put({"kind": "token", "delta": delta})
|
|
|
|
| 74 |
set_mellea_attempt_callback(None)
|
| 75 |
set_strict_mode(False)
|
| 76 |
set_planned_specialists(None)
|
| 77 |
+
set_user_query(None)
|
| 78 |
+
set_planner_intent(None)
|
| 79 |
else:
|
| 80 |
try:
|
| 81 |
out = run_linear(addr)
|
| 82 |
finally:
|
| 83 |
set_strict_mode(False)
|
| 84 |
set_planned_specialists(None)
|
| 85 |
+
set_user_query(None)
|
| 86 |
+
set_planner_intent(None)
|
| 87 |
out["intent"] = "single_address"
|
| 88 |
out["plan"] = {
|
| 89 |
"intent": plan.intent,
|
app/live/floodnet_forecast.py
CHANGED
|
@@ -36,9 +36,9 @@ import numpy as np
|
|
| 36 |
|
| 37 |
from app.context.floodnet import flood_events_for, sensors_near
|
| 38 |
from app.live.ttm_forecast import (
|
|
|
|
| 39 |
DAILY_CONTEXT,
|
| 40 |
DAILY_PREDICTION,
|
| 41 |
-
_MODEL_LOAD_ERROR,
|
| 42 |
_run_ttm,
|
| 43 |
)
|
| 44 |
|
|
|
|
| 36 |
|
| 37 |
from app.context.floodnet import flood_events_for, sensors_near
|
| 38 |
from app.live.ttm_forecast import (
|
| 39 |
+
_MODEL_LOAD_ERROR,
|
| 40 |
DAILY_CONTEXT,
|
| 41 |
DAILY_PREDICTION,
|
|
|
|
| 42 |
_run_ttm,
|
| 43 |
)
|
| 44 |
|
app/live/ttm_battery_surge.py
CHANGED
|
@@ -92,6 +92,7 @@ def _ensure_model():
|
|
| 92 |
if _MODEL is not None:
|
| 93 |
return _MODEL
|
| 94 |
from huggingface_hub import snapshot_download
|
|
|
|
| 95 |
# Force-import dispatched class names so the transformers lazy
|
| 96 |
# registry can resolve `PreTrainedModel` / `TinyTimeMixerForPrediction`
|
| 97 |
# under FSM worker threads. Same pattern as ttm_forecast._load_model.
|
|
|
|
| 92 |
if _MODEL is not None:
|
| 93 |
return _MODEL
|
| 94 |
from huggingface_hub import snapshot_download
|
| 95 |
+
|
| 96 |
# Force-import dispatched class names so the transformers lazy
|
| 97 |
# registry can resolve `PreTrainedModel` / `TinyTimeMixerForPrediction`
|
| 98 |
# under FSM worker threads. Same pattern as ttm_forecast._load_model.
|
app/live/ttm_forecast.py
CHANGED
|
@@ -92,6 +92,7 @@ def _load_model(context_length: int = CONTEXT_LENGTH,
|
|
| 92 |
return None
|
| 93 |
try:
|
| 94 |
import torch # noqa: F401
|
|
|
|
| 95 |
# Force-import the registered class names BEFORE get_model so that
|
| 96 |
# transformers' lazy registry can resolve them by string. Without
|
| 97 |
# this, AutoModel-style dispatch raises
|
|
|
|
| 92 |
return None
|
| 93 |
try:
|
| 94 |
import torch # noqa: F401
|
| 95 |
+
|
| 96 |
# Force-import the registered class names BEFORE get_model so that
|
| 97 |
# transformers' lazy registry can resolve them by string. Without
|
| 98 |
# this, AutoModel-style dispatch raises
|
app/mellea_validator.py
CHANGED
|
@@ -130,6 +130,9 @@ def _check_no_placeholder_tokens():
|
|
| 130 |
bad.append("<document>")
|
| 131 |
if "</document" in text:
|
| 132 |
bad.append("</document>")
|
|
|
|
|
|
|
|
|
|
| 133 |
return not bad
|
| 134 |
return _fn
|
| 135 |
|
|
|
|
| 130 |
bad.append("<document>")
|
| 131 |
if "</document" in text:
|
| 132 |
bad.append("</document>")
|
| 133 |
+
if "[doc_id]" in text:
|
| 134 |
+
# Model echoed the EXTRA_SYSTEM_PROMPT skeleton literally
|
| 135 |
+
bad.append("[doc_id]")
|
| 136 |
return not bad
|
| 137 |
return _fn
|
| 138 |
|
app/planner.py
CHANGED
|
@@ -16,6 +16,7 @@ from __future__ import annotations
|
|
| 16 |
import json
|
| 17 |
import logging
|
| 18 |
import os
|
|
|
|
| 19 |
from dataclasses import dataclass
|
| 20 |
from typing import Any
|
| 21 |
|
|
@@ -137,6 +138,58 @@ Available specialists (and which intents they apply to):
|
|
| 137 |
Output ONLY the JSON object. No commentary, no markdown."""
|
| 138 |
|
| 139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
# ---- Planner call ----------------------------------------------------------
|
| 141 |
|
| 142 |
def plan(query: str, model: str = OLLAMA_MODEL, on_token=None) -> Plan:
|
|
@@ -147,6 +200,14 @@ def plan(query: str, model: str = OLLAMA_MODEL, on_token=None) -> Plan:
|
|
| 147 |
Granite generates. The streaming endpoint uses this to show the
|
| 148 |
agent's reasoning forming live in the UI.
|
| 149 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
messages = [
|
| 151 |
{"role": "system", "content": SYSTEM_PROMPT},
|
| 152 |
{"role": "user", "content": query},
|
|
|
|
| 16 |
import json
|
| 17 |
import logging
|
| 18 |
import os
|
| 19 |
+
import re
|
| 20 |
from dataclasses import dataclass
|
| 21 |
from typing import Any
|
| 22 |
|
|
|
|
| 138 |
Output ONLY the JSON object. No commentary, no markdown."""
|
| 139 |
|
| 140 |
|
| 141 |
+
# ---- Not-implemented short-circuits ----------------------------------------
|
| 142 |
+
#
|
| 143 |
+
# These patterns are well-defined feature gaps. Returning a graceful message
|
| 144 |
+
# is better than routing them into an intent that silently fails.
|
| 145 |
+
|
| 146 |
+
_RETROSPECTIVE_RE = re.compile(
|
| 147 |
+
r"(?:what\s+would\s+(?:riprap|you|it)\s+have\s+said"
|
| 148 |
+
r"|what\s+(?:was|were)\s+(?:the\s+)?(?:flood|risk|status)"
|
| 149 |
+
r"|(?:as\s+of|on)\s+(?:august|september|october|november|december|january|"
|
| 150 |
+
r"february|march|april|may|june|july)\s+\d"
|
| 151 |
+
r"|on\s+(?:the\s+date\s+of|hurricane\s+ida|hurricane\s+sandy)"
|
| 152 |
+
r"|(?:september|august|october)\s+\d{1,2},?\s+20\d{2}"
|
| 153 |
+
r")",
|
| 154 |
+
re.IGNORECASE,
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
_RANKING_RE = re.compile(
|
| 158 |
+
r"(?:rank\s+(?:the\s+)?top\s+\d"
|
| 159 |
+
r"|top\s+\d+\s+\w+\s+by\s+flood"
|
| 160 |
+
r"|intersect(?:ed)?\s+with\s+(?:dac|ejnyc|social\s+vulnerability)"
|
| 161 |
+
r"|sort(?:ed)?\s+by\s+(?:flood\s+)?(?:exposure|risk|score)"
|
| 162 |
+
r")",
|
| 163 |
+
re.IGNORECASE,
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
NOT_IMPLEMENTED_INTENTS = {
|
| 167 |
+
"retrospective": (
|
| 168 |
+
_RETROSPECTIVE_RE,
|
| 169 |
+
"Historical-date mode (\"what would Riprap have said on [date]\") "
|
| 170 |
+
"is on the roadmap but not yet available. Riprap currently reports "
|
| 171 |
+
"present-state flood exposure; past-state reconstruction is planned "
|
| 172 |
+
"for a future release (see deck slide 8).",
|
| 173 |
+
),
|
| 174 |
+
"ranking": (
|
| 175 |
+
_RANKING_RE,
|
| 176 |
+
"Cross-development ranking queries (\"rank top N by flood exposure\", "
|
| 177 |
+
"\"intersect with DAC designation\") require a cross-register join "
|
| 178 |
+
"that is on the roadmap but not yet available. Try a specific address "
|
| 179 |
+
"or neighborhood instead.",
|
| 180 |
+
),
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def _not_implemented_message(query: str) -> str | None:
|
| 185 |
+
"""Return a user-facing message if the query matches a known feature gap,
|
| 186 |
+
else None."""
|
| 187 |
+
for _name, (pattern, message) in NOT_IMPLEMENTED_INTENTS.items():
|
| 188 |
+
if pattern.search(query):
|
| 189 |
+
return message
|
| 190 |
+
return None
|
| 191 |
+
|
| 192 |
+
|
| 193 |
# ---- Planner call ----------------------------------------------------------
|
| 194 |
|
| 195 |
def plan(query: str, model: str = OLLAMA_MODEL, on_token=None) -> Plan:
|
|
|
|
| 200 |
Granite generates. The streaming endpoint uses this to show the
|
| 201 |
agent's reasoning forming live in the UI.
|
| 202 |
"""
|
| 203 |
+
msg = _not_implemented_message(query)
|
| 204 |
+
if msg:
|
| 205 |
+
log.info("planner: short-circuit not_implemented for query %r", query[:80])
|
| 206 |
+
if on_token:
|
| 207 |
+
on_token(json.dumps({"intent": "not_implemented", "message": msg}))
|
| 208 |
+
return Plan(intent="not_implemented", targets=[],
|
| 209 |
+
specialists=[], rationale=msg)
|
| 210 |
+
|
| 211 |
messages = [
|
| 212 |
{"role": "system", "content": SYSTEM_PROMPT},
|
| 213 |
{"role": "user", "content": query},
|
app/reconcile.py
CHANGED
|
@@ -52,28 +52,27 @@ CITATION_TTM_FORECAST = (
|
|
| 52 |
# This text is OUR additional system prompt, prepended to that suffix.
|
| 53 |
EXTRA_SYSTEM_PROMPT = """Write a flood-exposure briefing for an NYC address. Use ONLY the facts in the provided documents.
|
| 54 |
|
| 55 |
-
Output
|
| 56 |
|
| 57 |
-
```
|
| 58 |
**Status.**
|
| 59 |
-
<one sentence: dominant exposure signal(s) for this address, citing the strongest
|
| 60 |
|
| 61 |
**Empirical evidence.**
|
| 62 |
-
<1-3 sentences citing observed flood evidence: Sandy
|
| 63 |
|
| 64 |
**Modeled scenarios.**
|
| 65 |
-
<1-2 sentences citing modeled flooding from
|
| 66 |
|
| 67 |
**Policy context.**
|
| 68 |
-
<1 sentence per RAG hit, citing the agency name and
|
| 69 |
-
```
|
| 70 |
|
| 71 |
Constraints:
|
| 72 |
- Copy numerical values verbatim from documents. Do not round.
|
| 73 |
- Name a specific weather event only if a document explicitly applies it to this address.
|
| 74 |
-
- For RAG documents (doc_ids starting with
|
| 75 |
- Microtopo percentile direction: a LOW percentile means topographic LOW POINT (water pools); HIGH percentile means HIGH GROUND. State the direction correctly or omit the percentile.
|
| 76 |
-
-
|
|
|
|
| 77 |
"""
|
| 78 |
|
| 79 |
|
|
|
|
| 52 |
# This text is OUR additional system prompt, prepended to that suffix.
|
| 53 |
EXTRA_SYSTEM_PROMPT = """Write a flood-exposure briefing for an NYC address. Use ONLY the facts in the provided documents.
|
| 54 |
|
| 55 |
+
Output the four sections below, filling each <...> with content drawn only from the documents. **Every sentence that contains a number MUST include a citation tag β such as [sandy], [nyc311], [microtopo], [dep_extreme_2080], [floodnet], [rag_npcc4], etc. β somewhere in that sentence, using the actual document id, not a placeholder.** Cite the specific doc_id exactly as it appears in the documents list. Bold at most one phrase per section using `**...**`. Omit any section whose supporting facts are absent from the documents.
|
| 56 |
|
|
|
|
| 57 |
**Status.**
|
| 58 |
+
<one sentence: dominant exposure signal(s) for this address, citing the strongest document ids>.
|
| 59 |
|
| 60 |
**Empirical evidence.**
|
| 61 |
+
<1-3 sentences citing observed flood evidence: Sandy inundation cites [sandy], 311 complaint counts cite [nyc311], FloodNet sensor readings cite [floodnet], Ida high-water marks cite [ida_hwm], Prithvi flood polygons cite [prithvi_water]>.
|
| 62 |
|
| 63 |
**Modeled scenarios.**
|
| 64 |
+
<1-2 sentences citing modeled flooding from the dep_* documents and terrain from [microtopo] (HAND, TWI, percentile)>.
|
| 65 |
|
| 66 |
**Policy context.**
|
| 67 |
+
<1 sentence per RAG document hit, citing the agency name and the rag_* doc_id exactly as given>.
|
|
|
|
| 68 |
|
| 69 |
Constraints:
|
| 70 |
- Copy numerical values verbatim from documents. Do not round.
|
| 71 |
- Name a specific weather event only if a document explicitly applies it to this address.
|
| 72 |
+
- For RAG documents (doc_ids starting with rag_): describe what the report SAYS at the policy or asset-class level. Do not assert findings the report did not make about this specific address.
|
| 73 |
- Microtopo percentile direction: a LOW percentile means topographic LOW POINT (water pools); HIGH percentile means HIGH GROUND. State the direction correctly or omit the percentile.
|
| 74 |
+
- Do NOT write "[doc_id]" literally β always replace it with the real document id.
|
| 75 |
+
- If no documents are present, output exactly: No grounded data available for this address.
|
| 76 |
"""
|
| 77 |
|
| 78 |
|
audit/AUDIT-2026-05-06.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Code audit β 2026-05-06
|
| 2 |
+
|
| 3 |
+
Overnight static-analysis pass on `overnight-2026-05-06`.
|
| 4 |
+
|
| 5 |
+
Tools: `ruff 0.x` (lint), `vulture 2.x` (dead-code), `radon 6.0.1`
|
| 6 |
+
(complexity + maintainability index).
|
| 7 |
+
|
| 8 |
+
Scope: whole repo. **Mechanical fixes were applied only to `app/`,
|
| 9 |
+
`web/`, `scripts/`, `services/`, `tests/`** β `experiments/` is
|
| 10 |
+
exploratory/reproduction code and was deliberately left untouched
|
| 11 |
+
even where it has real bugs (those bugs are flagged below for Adam
|
| 12 |
+
to triage separately).
|
| 13 |
+
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
## Top 10 lint issues by severity
|
| 17 |
+
|
| 18 |
+
Severity ordering: correctness bugs first, then style. Code paths
|
| 19 |
+
inside `app/` / `web/` / `scripts/` are flagged with **(prod)**.
|
| 20 |
+
|
| 21 |
+
| # | Code | Where | Severity | Note |
|
| 22 |
+
|---|------|-------|----------|------|
|
| 23 |
+
| 1 | F821 (3x) | `experiments/17_riprap_integration/terramind_nyc.py:117` | **bug** | Type annotation references `np` (numpy) but numpy is only imported deeper inside the function (line 142). The annotation will fail at module-import time if Python ever evaluates it eagerly. Currently masked by `from __future__ import annotations` (lazy eval). |
|
| 24 |
+
| 2 | invalid-syntax | `experiments/18_terramind_nyc_lora/shared/eval_adapter.py:125` | **bug** | Inner f-string reuses outer quote (`f"{x['key']}"` style) β added in Py 3.12. The HF Space (Py 3.10) cannot import this file. Local-only artefact today, but if anyone tries to ship it, it errors. |
|
| 25 |
+
| 3 | B023 (2x) | `experiments/05_terramind_nyc_finetune/training/verify_phase1.py:438` | **bug** | Closure inside a `for` binds `x` from the loop variable β the standard "all closures see the last value" trap. Confirm intent before fixing. |
|
| 26 |
+
| 4 | F841 | `experiments/18_terramind_nyc_lora/shared/publish_hf.py:107` | warn | `api` assigned but never used. May be a bug (intended to call `api.upload_*`) or just a leftover; needs a human eye. |
|
| 27 |
+
| 5 | F811 | `experiments/17_riprap_integration/terramind_nyc.py:138` | warn | `json` re-imported inside the function while already imported at module top. Harmless but suggests a stale paste. |
|
| 28 |
+
| 6 | B006 | `experiments/15_terramind_multihead/multihead_train.py:122` | warn | Mutable default arg (likely a list or dict). Standard footgun. |
|
| 29 |
+
| 7 | F401 (31x) | mostly `experiments/`, 1 in `app/inference.py:33` (`io`) | minor | The `app/inference.py` one is the only F401 vulture also flagged at >=90% β see Dead code below. The others are in experimental code; mechanically removing them risks tearing out import side-effects. Left alone. |
|
| 30 |
+
| 8 | B905 (4x) | `app/fsm.py:1112`, 3x in experiments | minor | `zip()` without explicit `strict=`. Defensible to add `strict=False` to make intent explicit; not a bug today. |
|
| 31 |
+
| 9 | E402 (3x) | `app/registers/doe_schools.py:113`, `app/registers/doh_hospitals.py:110`, `app/registers/mta_entrances.py:149` | **intentional** | Module-level imports placed after `sys.path` injection so the register builders can run as standalone scripts. Per CLAUDE.md, registers double as scripts. **Keep as-is.** Should be silenced with a `# noqa: E402` rather than fixed. |
|
| 32 |
+
| 10 | I001 (34x) + F541 (10x) + E401 (8x) + UP-series (5x) | mixed | style | All auto-fixable. Applied below for the production code paths. |
|
| 33 |
+
|
| 34 |
+
Total: 106 ruff issues. After mechanical fixes (production code
|
| 35 |
+
paths only), remaining issues live in `experiments/` and the
|
| 36 |
+
intentional E402s.
|
| 37 |
+
|
| 38 |
+
---
|
| 39 |
+
|
| 40 |
+
## Top 5 dead-code candidates (vulture, --min-confidence 70)
|
| 41 |
+
|
| 42 |
+
Vulture is unusually quiet on this repo β only 3 reports at 70%+.
|
| 43 |
+
|
| 44 |
+
| # | File:line | Symbol | Confidence | Judgment | Action taken |
|
| 45 |
+
|---|-----------|--------|------------|----------|--------------|
|
| 46 |
+
| 1 | `app/inference.py:33` | `import io` | 90% | **Safe to remove.** Not referenced anywhere in the file. Likely a leftover from when serialization went through `io.BytesIO`. | Removed in this commit (this is the one F401 that vulture also confirms). |
|
| 47 |
+
| 2 | `web/main.py:366` | `query_id` (local var) | 100% | **Keep.** Variable is assigned from `uuid.uuid4().hex[:8]` inside the SSE handler. Adam's pattern across this repo is to bind a query ID even when it isn't immediately logged β useful as a future hook (and trivial to reference via debugger). Removing it has zero blast radius but also zero benefit. | Flagged only. |
|
| 48 |
+
| 3 | `web/main.py:376` | `query_id` (local var) | 100% | Same as #2. | Flagged only. |
|
| 49 |
+
| 4-5 | n/a | n/a | n/a | No further candidates at 70%+. | n/a |
|
| 50 |
+
|
| 51 |
+
Note: vulture's silence shouldn't be read as "no dead code." The
|
| 52 |
+
threshold filters aggressively. Lower confidences (60% / 50%) would
|
| 53 |
+
turn up many false positives (Burr action functions consumed by
|
| 54 |
+
reflection, FastAPI handlers consumed by decorator, etc.). 70% is
|
| 55 |
+
the sweet spot for this codebase.
|
| 56 |
+
|
| 57 |
+
---
|
| 58 |
+
|
| 59 |
+
## Cyclomatic complexity > 15 (flagged, not refactored)
|
| 60 |
+
|
| 61 |
+
Radon CC scale: A=1-5, B=6-10, C=11-20, D=21-30, E=31-40, F=41+.
|
| 62 |
+
|
| 63 |
+
| Function | Score | Notes |
|
| 64 |
+
|----------|-------|-------|
|
| 65 |
+
| `app/reconcile.py:310 build_documents` | **F (178)** | Known sharp edge β CLAUDE.md explicitly says don't pre-demo refactor; one giant `if`/`elif` per specialist. Each branch is the doc-message wiring for one Stone. **Frozen until post-demo.** |
|
| 66 |
+
| `app/mellea_validator.py:311 reconcile_strict_streaming` | D (23) | Streaming rejection sampler with attempt loop, token forwarding, reroll feedback construction. Inherent state-machine complexity; refactoring this risks the four grounding checks. **Leave for post-demo.** |
|
| 67 |
+
| `app/planner.py:176 _validate` | D (22) | Defensive parser for the planner's JSON output. Each branch handles a different malformed-output shape. Could split into per-field validators if we ever wanted to test in isolation, but the inline form reads cleanly enough. |
|
| 68 |
+
| `app/rag.py:195 retrieve` | C (20) | Embedding retrieval + reranker + filtering by intent. The complexity is in the optional reranker path. Worth a refactor, but not pre-demo. |
|
| 69 |
+
| `app/flood_layers/ida_hwm.py:55 summary_for_point` | C (18) | Per-buffer-distance loops with band classification. Could be tabularised. |
|
| 70 |
+
| `app/context/eo_chip_cache.py:143 _fetch_modalities` | C (17) | STAC search + read across S1/S2 modalities. The branching is one path per modality. |
|
| 71 |
+
| `app/register_builder.py:64 build_register` | C (16) | Generic register builder driven by config dict. The complexity is partially essential. |
|
| 72 |
+
|
| 73 |
+
Recommendation: **none of these should be touched pre-demo.** All
|
| 74 |
+
are load-bearing on the 5/5 probe pass. Post-demo, `build_documents`
|
| 75 |
+
is the obvious refactor target (table-driven dispatch instead of
|
| 76 |
+
elif chain).
|
| 77 |
+
|
| 78 |
+
---
|
| 79 |
+
|
| 80 |
+
## Maintainability Index < 60 (flagged, not refactored)
|
| 81 |
+
|
| 82 |
+
Radon MI scale: β₯20 is reasonable, β₯10 is bottom of "still passable."
|
| 83 |
+
Anything <60 in this codebase is almost certainly a "long file with
|
| 84 |
+
inline policy" rather than "tangled logic," because every CC score
|
| 85 |
+
in the repo is C or below outside `build_documents`.
|
| 86 |
+
|
| 87 |
+
| Module | MI | Read |
|
| 88 |
+
|--------|-----|------|
|
| 89 |
+
| `app/intents/neighborhood.py` | **32.28** | Lowest in the repo. Dispatches the 9-event neighborhood path inline. Comments + long functions push this down; CC is fine. |
|
| 90 |
+
| `scripts/probe_addresses.py` | 35.84 | The canonical end-to-end test. Long because it threads SSE event parsing + per-Stone assertions; complexity is shallow. Don't touch. |
|
| 91 |
+
| `web/main.py` | 36.97 | FastAPI app + SSE handler + backend pill endpoint. Length is the cost of being the demo's front door. |
|
| 92 |
+
| `app/context/microtopo.py` | 45.24 | DEM/HAND/TWI inline numerics. |
|
| 93 |
+
| `app/mellea_validator.py` | 45.45 | The grounding-check engine. |
|
| 94 |
+
| `app/intents/live_now.py` | 46.21 | Live-only intent path. |
|
| 95 |
+
| `app/rag.py` | 46.70 | Retrieval + reranker. |
|
| 96 |
+
| `app/flood_layers/prithvi_live.py` | 47.82 | Live Sentinel-2 chip + Prithvi inference. |
|
| 97 |
+
| `app/registers/doh_hospitals.py` | 48.70 | Bulk register builder. |
|
| 98 |
+
| `app/registers/doe_schools.py` | 48.73 | Bulk register builder. |
|
| 99 |
+
| `app/live/ttm_forecast.py` | 48.93 | Granite TTM r2 surge nowcast. |
|
| 100 |
+
| `app/live/ttm_battery_surge.py` | 49.28 | Battery surge fine-tune wrapper. |
|
| 101 |
+
| `app/intents/development_check.py` | 49.60 | DOB-permits intent. |
|
| 102 |
+
| `app/context/eo_chip_cache.py` | 49.85 | EO chip cache + STAC. |
|
| 103 |
+
| `app/context/floodnet.py` | 50.34 | FloodNet sensor reads. |
|
| 104 |
+
| `app/registers/nycha.py` | 50.87 | NYCHA bulk register. |
|
| 105 |
+
| `scripts/dry_run.py` | 51.63 | Demo dry-run helper. |
|
| 106 |
+
| `scripts/run_prithvi_ida.py` | 52.30 | Offline Prithvi run. |
|
| 107 |
+
| `app/context/terramind_nyc.py` | 53.72 | TerraMind NYC adapters wrapper. |
|
| 108 |
+
| `scripts/probe_mellea.py` | 53.78 | Mellea probe driver. |
|
| 109 |
+
| `app/planner.py` | 54.56 | The planner module. Mostly the long SYSTEM_PROMPT string + dispatch table. |
|
| 110 |
+
| `app/context/terramind_synthesis.py` | 55.37 | TerraMind synthesis chip path. |
|
| 111 |
+
| `app/score.py` | 56.45 | Composite scoring + bands. |
|
| 112 |
+
| `app/register_builder.py` | 57.09 | Generic register builder. |
|
| 113 |
+
| `scripts/run_prithvi_flood.py` | 57.57 | Offline Prithvi flood eval. |
|
| 114 |
+
| `app/llm.py` | 58.21 | LiteLLM Router shim. |
|
| 115 |
+
| `app/context/nyc311.py` | 59.99 | NYC 311 API wrapper. |
|
| 116 |
+
|
| 117 |
+
**Recommendation: none of these are urgent.** The pattern is "data-
|
| 118 |
+
heavy modules with shallow CC" β typical for a NYC-data-fusion
|
| 119 |
+
project. Post-demo candidates worth a focused refactor:
|
| 120 |
+
|
| 121 |
+
1. `app/intents/neighborhood.py` β split into per-Stone helpers.
|
| 122 |
+
2. `web/main.py` β extract the `/api/agent/stream` SSE pump into
|
| 123 |
+
its own module.
|
| 124 |
+
3. `app/reconcile.py` β same as the CC discussion: table-driven
|
| 125 |
+
`build_documents`.
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
## What was applied this commit (`audit:` mechanical fixes)
|
| 130 |
+
|
| 131 |
+
`ruff check --fix --select I,F541,E401,UP037,UP034,UP035` over
|
| 132 |
+
production paths only (`app/`, `web/`, `scripts/`, `services/`,
|
| 133 |
+
`tests/`). Plus the one vulture-confirmed unused import in
|
| 134 |
+
`app/inference.py`.
|
| 135 |
+
|
| 136 |
+
Skipped:
|
| 137 |
+
- All of `experiments/`. Reproduction code; bugs flagged above.
|
| 138 |
+
- F401 broadly. Per Adam's instruction, only fix unused imports
|
| 139 |
+
that vulture also confirms unused.
|
| 140 |
+
- F811 / F841 / B-series / B006. Manual review needed.
|
| 141 |
+
- The 3 E402s in `app/registers/`. Intentional after `sys.path`
|
| 142 |
+
injection.
|
| 143 |
+
|
| 144 |
+
What's left for human review:
|
| 145 |
+
- The 4 real bugs in `experiments/17`, `experiments/18`,
|
| 146 |
+
`experiments/05` listed above.
|
| 147 |
+
- Whether to add `# noqa: E402` to the three register files (or to
|
| 148 |
+
configure ruff to ignore them in `pyproject.toml`).
|
| 149 |
+
- Whether `web/main.py:366,376 query_id` are intended to be logged
|
| 150 |
+
somewhere they're currently not.
|
docs/QUESTION-AWARE-FRAMING.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Question-aware briefing framing
|
| 2 |
+
|
| 3 |
+
Diagnosis + recommendation for WS3 of the 2026-05-06 overnight pass.
|
| 4 |
+
|
| 5 |
+
The four-section briefing structure (Status / Empirical / Modeled /
|
| 6 |
+
Policy) is non-negotiable β it's what the four Mellea grounding checks
|
| 7 |
+
score, and rewriting it risks the 4/4 pass rate. What we want to change
|
| 8 |
+
is the **opening sentence of the Status section**, so it engages the
|
| 9 |
+
question shape the user actually asked. Today every briefing leads
|
| 10 |
+
with a generic "this address is exposed to flood risk" no matter
|
| 11 |
+
whether the user asked "should I worry?" (resident), "is disclosure
|
| 12 |
+
required?" (attorney), or "where should we prioritize hardening?"
|
| 13 |
+
(planner).
|
| 14 |
+
|
| 15 |
+
## Where the system_prompt is set today
|
| 16 |
+
|
| 17 |
+
| Call site | Path | `EXTRA_SYSTEM_PROMPT` source |
|
| 18 |
+
|-----------|------|------------------------------|
|
| 19 |
+
| `app/fsm.py:983 step_reconcile` | single_address (strict) | `app/reconcile.py:53` |
|
| 20 |
+
| `app/intents/neighborhood.py:377` | neighborhood (strict) | local @ `app/intents/neighborhood.py:35` |
|
| 21 |
+
| `app/intents/development_check.py:218` | development_check (strict) | local @ `app/intents/development_check.py:32` |
|
| 22 |
+
| `app/intents/live_now.py:212` | live_now (non-strict) | local @ `app/intents/live_now.py:38` |
|
| 23 |
+
| `app/reconcile.py:1089 reconcile()` | legacy non-strict | `app/reconcile.py:53` |
|
| 24 |
+
|
| 25 |
+
All four strict paths funnel into `mellea_validator.reconcile_strict_streaming(doc_msgs, system_prompt, ...)`. The system_prompt is currently a constant per call site.
|
| 26 |
+
|
| 27 |
+
## Three options Adam outlined
|
| 28 |
+
|
| 29 |
+
### (a) Planner sub-classifier
|
| 30 |
+
Add a fifth `question_type` field to the planner's JSON schema. Granite
|
| 31 |
+
4.1:3b classifies it alongside `intent`. Capstone reads it and conditions
|
| 32 |
+
the opening.
|
| 33 |
+
|
| 34 |
+
- β
Reuses an LLM that already understands the query
|
| 35 |
+
- β Re-validates the planner contract β the `_validate()` parser, the
|
| 36 |
+
schema doc, the fallback logic, and `scripts/probe_addresses.py`
|
| 37 |
+
all need to grow a new field
|
| 38 |
+
- β Costs another planner call iteration to converge if the model
|
| 39 |
+
mis-emits the new field
|
| 40 |
+
- β The planner is the warm-cache path the demo lives or dies on β
|
| 41 |
+
changing its output schema five days before pitch is high-risk
|
| 42 |
+
|
| 43 |
+
### (b) Capstone prompt-conditional
|
| 44 |
+
Detect `question_type` from the raw query string with a deterministic
|
| 45 |
+
regex-based heuristic, augment the system_prompt with a per-type
|
| 46 |
+
"opening directive," pass through to `reconcile_strict_streaming`. No
|
| 47 |
+
planner change.
|
| 48 |
+
|
| 49 |
+
- β
Lowest blast radius β only touches the Capstone call sites
|
| 50 |
+
- β
Deterministic, testable, zero added latency (no LLM call)
|
| 51 |
+
- β
Easy to roll back β remove the `augment_system_prompt(...)` call
|
| 52 |
+
- β
The four Mellea grounding checks stay byte-identical
|
| 53 |
+
- β οΈ Question-shape detection is heuristic, not learned. Edge cases
|
| 54 |
+
(weird phrasings, code-switching) will fall back to a generic
|
| 55 |
+
directive. Acceptable for the demo personas β they're known up
|
| 56 |
+
front.
|
| 57 |
+
|
| 58 |
+
### (c) Both
|
| 59 |
+
Planner emits a hint, Capstone uses it as a tiebreaker over the
|
| 60 |
+
heuristic.
|
| 61 |
+
|
| 62 |
+
- Same risks as (a). Pre-demo, the marginal accuracy isn't worth the
|
| 63 |
+
schema change.
|
| 64 |
+
|
| 65 |
+
## Recommendation: **option (b)**
|
| 66 |
+
|
| 67 |
+
Implementation lives in a new module `app/framing.py`:
|
| 68 |
+
|
| 69 |
+
- `detect(query, intent) -> question_type` β regex-based detector that
|
| 70 |
+
returns one of 11 question types (the same eleven as the suite's
|
| 71 |
+
framing rubric).
|
| 72 |
+
- `opening_instruction(question_type) -> str | None` β returns the
|
| 73 |
+
directive sentence to inject, or None for `generic_exposure` (the
|
| 74 |
+
default β current behavior unchanged).
|
| 75 |
+
- `augment_system_prompt(base, query, intent) -> str` β wraps the base
|
| 76 |
+
prompt with a `QUESTION-AWARE OPENING` block.
|
| 77 |
+
|
| 78 |
+
Wiring:
|
| 79 |
+
|
| 80 |
+
1. `app/fsm.py` β add `set_query(q)` / `_current_query()` threadlocals
|
| 81 |
+
alongside the existing `set_strict_mode`. `step_reconcile()` reads
|
| 82 |
+
the query + intent to augment the system prompt before calling
|
| 83 |
+
`reconcile_strict_streaming`.
|
| 84 |
+
2. `app/intents/single_address.py:run()` β call `set_query(query)`
|
| 85 |
+
before `iter_steps`, reset in `finally` (matches the existing
|
| 86 |
+
threadlocal pattern).
|
| 87 |
+
3. `app/intents/neighborhood.py:run()` β augment the local
|
| 88 |
+
`EXTRA_SYSTEM_PROMPT` directly before passing to
|
| 89 |
+
`reconcile_strict_streaming`.
|
| 90 |
+
4. `app/intents/development_check.py:run()` β same as neighborhood.
|
| 91 |
+
5. `app/intents/live_now.py:run()` β same; non-strict path so it just
|
| 92 |
+
prepends to the system message content.
|
| 93 |
+
6. `app/reconcile.py:reconcile()` (legacy) β out of scope; it's not on
|
| 94 |
+
the demo path and the strict path covers all current intents.
|
| 95 |
+
|
| 96 |
+
## Stop conditions
|
| 97 |
+
|
| 98 |
+
Per Adam's instruction: if the framing rubric scores below 3 on more
|
| 99 |
+
than five queries after the change lands, document what option (a) /
|
| 100 |
+
(c) would require and stop. **Do not silently expand scope.**
|
| 101 |
+
|
| 102 |
+
The "below 3 on more than five" test is the trigger to move to
|
| 103 |
+
heavier interventions β typically that the regex detector misclassified
|
| 104 |
+
the question or the Granite model is ignoring the directive under the
|
| 105 |
+
existing system prompt's strong four-section discipline.
|
| 106 |
+
|
| 107 |
+
---
|
| 108 |
+
|
| 109 |
+
## Outcome of the 2026-05-06 framed run
|
| 110 |
+
|
| 111 |
+
`tests/integration/results/2026-05-06/FRAMING-DELTA.md` is the full
|
| 112 |
+
report. Headline:
|
| 113 |
+
|
| 114 |
+
- Mean framing **2.25 β 2.80** (+0.55).
|
| 115 |
+
- Queries reaching 5/5: **0 β 3** β q01 resident habitability
|
| 116 |
+
("Yes, this address is exposed..."), q02 attorney disclosure
|
| 117 |
+
("Disclosure is warranted..."), q13 grant evidence
|
| 118 |
+
("Vulnerability assessment: ...").
|
| 119 |
+
- Queries reaching β₯ 4/5: **2 β 5**.
|
| 120 |
+
- Mellea grounding: 4 queries improved (3/4 β 4/4); 2 regressed
|
| 121 |
+
(q01 4/4 β 3/4, q06 3/4 β 2/4); 14 unchanged. Net +2.
|
| 122 |
+
|
| 123 |
+
**Stop condition fired.** 12 / 20 framed queries scored below 3.
|
| 124 |
+
Triage of the 12:
|
| 125 |
+
|
| 126 |
+
1. **Rubric-vs-directive vocabulary mismatch (4 queries).** q03, q08,
|
| 127 |
+
q10, q12 are bare neighborhood names that the suite labels
|
| 128 |
+
`capital_planning`. The detector returns `journalism` (the
|
| 129 |
+
bare-neighborhood fallback). Both are valid persona framings; the
|
| 130 |
+
journalism directive *is* applied (the openings change), but the
|
| 131 |
+
capital-planning rubric scores against verdict words like
|
| 132 |
+
"prioritize" / "merits prioritization" that the journalism
|
| 133 |
+
directive doesn't request. **Not a framing failure β a
|
| 134 |
+
measurement asymmetry.**
|
| 135 |
+
2. **Short-prose floor (4 queries).** q07, q14, q15, q19 returned
|
| 136 |
+
β€ 200 chars of prose because the geocoder failed (q07, q14, q18 β
|
| 137 |
+
long conversational queries) or the planner / NTA resolver
|
| 138 |
+
short-circuited (q15 ranking query, q19 BBMCR project name).
|
| 139 |
+
Documented in `OVERNIGHT-2026-05-06-OUT-OF-SCOPE.md`. No framing
|
| 140 |
+
change can salvage these β they need geocoder + intent-router
|
| 141 |
+
work first.
|
| 142 |
+
3. **Granite ignored the directive (4 queries).** q04 (bare address,
|
| 143 |
+
underwriting label), q05 (bare borough, journalism label), q11
|
| 144 |
+
(PS 188 ambiguous), q17 (compare intent), q20 (Astoria control).
|
| 145 |
+
In each case the framing prompt was injected but the opening
|
| 146 |
+
stayed generic. Granite 4.1's existing four-section discipline
|
| 147 |
+
appears to overpower a soft "QUESTION-AWARE OPENING" directive
|
| 148 |
+
for some question types; the verdict-style types (Yes/No,
|
| 149 |
+
Disclosure, Vulnerability assessment) succeed because they have
|
| 150 |
+
explicit token shapes the model can latch onto.
|
| 151 |
+
|
| 152 |
+
## What option (a) would require
|
| 153 |
+
|
| 154 |
+
Adam's instruction: if the stop condition fires, document option (a)
|
| 155 |
+
or (c) and stop β do not silently expand scope. **NOT IMPLEMENTED.**
|
| 156 |
+
Sketch:
|
| 157 |
+
|
| 158 |
+
1. **Planner schema gains a `question_type` field.** Add to
|
| 159 |
+
`app/planner.py:PLAN_SCHEMA_DESC`, `Plan` dataclass, and
|
| 160 |
+
`_validate()` so the model emits an 11-value enum alongside
|
| 161 |
+
`intent`.
|
| 162 |
+
2. **Few-shot the planner on question_type.** Add 6-10 worked
|
| 163 |
+
examples to `SYSTEM_PROMPT` (one per persona from RESEARCH.md)
|
| 164 |
+
so granite4.1:3b reliably emits the right enum value. The
|
| 165 |
+
planner is already running with `format=json` constrained
|
| 166 |
+
decoding, so this is a pure prompt-engineering change.
|
| 167 |
+
3. **Capstone consumes the planner's question_type instead of the
|
| 168 |
+
detector's.** `app.framing.augment_system_prompt` already takes
|
| 169 |
+
`intent`; add a third `question_type` parameter that overrides
|
| 170 |
+
`detect()` when present. Capstone callers (fsm.step_reconcile,
|
| 171 |
+
the three intents) read it from `plan.question_type` and pass
|
| 172 |
+
through.
|
| 173 |
+
4. **Fall back to the regex detector when the planner emits an
|
| 174 |
+
unknown / missing value.** Belt-and-suspenders against planner
|
| 175 |
+
regression.
|
| 176 |
+
5. **Re-validate** with the same 20-query suite. If mean framing
|
| 177 |
+
moves from 2.80 β β₯ 3.5 (target: β₯ half the queries scoring 4+),
|
| 178 |
+
option (a) was the right call. If not, the issue is downstream
|
| 179 |
+
(Granite ignoring the directive); option (c) won't help.
|
| 180 |
+
|
| 181 |
+
**Cost estimate.** ~2-3 hr of work, plus re-validation against the
|
| 182 |
+
address probe + the 20-query suite. The risk is the planner
|
| 183 |
+
regressing on intent classification when prompted to also emit a
|
| 184 |
+
new field β Granite 4.1:3b at temperature 0 with constrained
|
| 185 |
+
decoding is robust but not infallible. Validate against the full
|
| 186 |
+
address probe before merging.
|
| 187 |
+
|
| 188 |
+
## What option (c) would add
|
| 189 |
+
|
| 190 |
+
Layer (a) on top of (b). When the planner emits a question_type that
|
| 191 |
+
matches the detector's, both agree β use the directive. When they
|
| 192 |
+
disagree β log the disagreement (telemetry), use the planner's.
|
| 193 |
+
Marginal value over (a) alone is small; defer unless (a) shows
|
| 194 |
+
misclassification on the 20-query suite.
|
research/AMD-HACKATHON-LANDSCAPE.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AMD x lablab.ai Hackathon β Landscape Read
|
| 2 |
+
Captured 2026-05-07 as part of the overnight comms pass.
|
| 3 |
+
Sources: lablab.ai event pages, AMD developer blog, web search.
|
| 4 |
+
Submission pages 403 during scraping; description data from search snippets.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## Hackathon structure
|
| 9 |
+
|
| 10 |
+
Three competition tracks (Build in Public is a documentation track,
|
| 11 |
+
not evaluated for the main prize):
|
| 12 |
+
|
| 13 |
+
| Track | AMD framing | Difficulty label |
|
| 14 |
+
|---|---|---|
|
| 15 |
+
| AI Agents & Agentic Workflows | Agentic systems, orchestration, FSMs, multi-agent | Entry |
|
| 16 |
+
| Fine-Tuning on AMD GPUs | Domain-specific LoRA / full-fine-tune on MI300X or ROCm | Advanced / GPU-intensive |
|
| 17 |
+
| Vision & Multimodal AI | Multi-modal pipelines using MI300X memory bandwidth | Advanced |
|
| 18 |
+
|
| 19 |
+
Prize pool: $21,500+ and one AMD Radeon AI PRO R9700 GPU.
|
| 20 |
+
Build phase: May 4β10, 2026 online; on-site May 9β10 in San Francisco
|
| 21 |
+
(invitation only).
|
| 22 |
+
Judging criteria (lablab.ai standard): Application of Technology,
|
| 23 |
+
Presentation, Business Value, Originality.
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
## Representative in-flight submissions (from search snippets; project
|
| 28 |
+
pages returned 403 during automated scraping)
|
| 29 |
+
|
| 30 |
+
| Team / Project | What it appears to do | Track |
|
| 31 |
+
|---|---|---|
|
| 32 |
+
| **Aegis** | Autonomous 7-agent crisis management system: monitors global risk signals, predicts disruption impact with hybrid ML, auto-executes response | Agents |
|
| 33 |
+
| **The Architect's Eye** | Autonomous multi-agent construction safety: multimodal vision + regulatory auditing, real-time hazard detection | Agents + Vision |
|
| 34 |
+
| **NyayaLLM** | Legal AI fine-tuned on AMD MI300X for Indian criminal law (BNS/BNSS/BSA); domain-specific LLM for citizens and legal professionals | Fine-Tuning |
|
| 35 |
+
| **Hack_AI** | "AI agents that think, learn, and act to solve real-world challenges" β general-purpose agentic description | Agents |
|
| 36 |
+
| **Radeon Agents** | "Scalable systems, continuous hands-on innovation" β general-purpose infrastructure / agentic | Agents |
|
| 37 |
+
| **NextGen Labs** | Multi-GPU ROCm infrastructure, LLM inference optimization, autonomous agent pipelines | Agents + infra |
|
| 38 |
+
| **RoCJ** | Not described in available snippets | Unknown |
|
| 39 |
+
| **OneTimeBigTime** | Not described in available snippets | Unknown |
|
| 40 |
+
|
| 41 |
+
**Caveat**: lablab.ai submission pages returned 403 for all direct fetches.
|
| 42 |
+
The above is derived from search result snippets and may be incomplete or
|
| 43 |
+
imprecise. Treat as directional, not authoritative.
|
| 44 |
+
|
| 45 |
+
From search snippets, ~30 in-flight projects total are listed on the event
|
| 46 |
+
page. Complete enumeration requires a logged-in session on lablab.ai.
|
| 47 |
+
|
| 48 |
+
---
|
| 49 |
+
|
| 50 |
+
## Patterns across the visible field
|
| 51 |
+
|
| 52 |
+
**Track concentration: Agents dominates.**
|
| 53 |
+
Every project description visible in search snippets defaults to agentic
|
| 54 |
+
framing. Multi-agent orchestration, autonomous workflows, and "AI that
|
| 55 |
+
thinks and acts" are the standard template. Fine-tuning submissions are
|
| 56 |
+
sparse in the visible set; domain-specific trained models are notable
|
| 57 |
+
exceptions (NyayaLLM is the only clear fine-tune submission in the
|
| 58 |
+
visible set other than Riprap).
|
| 59 |
+
|
| 60 |
+
**Presentation style: general-purpose and horizontal.**
|
| 61 |
+
Most descriptions are intentionally broad ("real-world challenges,"
|
| 62 |
+
"scalable systems"). Very few name a specific domain, user type, or
|
| 63 |
+
measurable outcome in the project headline. This is the default shape
|
| 64 |
+
of a lablab.ai submission: apply AMD GPUs to AI + deploy.
|
| 65 |
+
|
| 66 |
+
**Demo format: live app or video, no architectural depth in the listing.**
|
| 67 |
+
The project thumbnail and short description are the first-pass filter.
|
| 68 |
+
Demo quality matters more than depth in the listing itself.
|
| 69 |
+
|
| 70 |
+
**Technology stack: standard.**
|
| 71 |
+
vLLM or Ollama for serving, Langchain or custom orchestration for agents,
|
| 72 |
+
open-source models (Granite, Llama, Mistral). ROCm + MI300X is the
|
| 73 |
+
GPU path. Very few projects mention custom datasets or trained artifacts.
|
| 74 |
+
|
| 75 |
+
---
|
| 76 |
+
|
| 77 |
+
## Where Riprap is differentiated
|
| 78 |
+
|
| 79 |
+
1. **Domain specificity with verifiable receipts.**
|
| 80 |
+
Riprap is the only visible submission targeting a specific civic domain
|
| 81 |
+
(NYC flood risk) with publicly published fine-tune artifacts (three
|
| 82 |
+
Apache-2.0 models on HF Hub). NyayaLLM is the closest comparator on
|
| 83 |
+
domain specificity; it is single-model, single-jurisdiction, and legal
|
| 84 |
+
rather than multi-model geospatial.
|
| 85 |
+
|
| 86 |
+
2. **Three published fine-tunes on MI300X.**
|
| 87 |
+
`msradam/TerraMind-NYC-Adapters`, `msradam/Prithvi-EO-2.0-NYC-Pluvial`,
|
| 88 |
+
`msradam/Granite-TTM-r2-Battery-Surge` are live on HF Hub, Apache-2.0,
|
| 89 |
+
with training code in the repo. No other visible submission mentions
|
| 90 |
+
published model artifacts. This is the strongest evidence for the
|
| 91 |
+
Fine-Tuning track β it is not a claim about fine-tuning, it is the
|
| 92 |
+
artifact.
|
| 93 |
+
|
| 94 |
+
3. **Citation discipline as an architectural commitment.**
|
| 95 |
+
Mellea rejection sampling with four named invariants (`numerics_grounded`,
|
| 96 |
+
`no_placeholder_tokens`, `citations_dense`, `citations_resolve`) is
|
| 97 |
+
uncommon in hackathon submissions. Most agentic projects output text;
|
| 98 |
+
Riprap refuses to output text it cannot cite. This is demonstrable in
|
| 99 |
+
the live app.
|
| 100 |
+
|
| 101 |
+
4. **Civic-tech vs general-purpose.**
|
| 102 |
+
Riprap is a domain tool for urban planners, journalists, grant writers,
|
| 103 |
+
and attorneys β not a coding assistant or general workflow tool. This
|
| 104 |
+
is a double-edged position: judges pattern-matching to "most impressive
|
| 105 |
+
agentic demo" may not immediately read the civic-tech framing as
|
| 106 |
+
technically deep. The architecture slide and the proof table need to
|
| 107 |
+
close that gap.
|
| 108 |
+
|
| 109 |
+
---
|
| 110 |
+
|
| 111 |
+
## Where Riprap's framing is at risk
|
| 112 |
+
|
| 113 |
+
**The domain-tool penalty.** Hackathon judges are often technical
|
| 114 |
+
evaluators who are primed to reward visible agent sophistication (tools
|
| 115 |
+
called, steps taken, orchestration complexity on screen). A 13-second
|
| 116 |
+
flood briefing looks understated next to a 7-agent crisis system that
|
| 117 |
+
spawns child agents in real time. Riprap's value is in what the prose
|
| 118 |
+
*doesn't* say (hallucinated claims) and what the architecture *proves*
|
| 119 |
+
(citation grounding), both of which are harder to demo than agent chatter.
|
| 120 |
+
|
| 121 |
+
**Three tracks vs one submission.** The current deck says "three of four
|
| 122 |
+
tracks." The hackathon format requires one-track submission. A deck that
|
| 123 |
+
leads with "we touched three tracks" reads as hedging, not confidence.
|
| 124 |
+
The Fine-Tuning track is the strongest single-track argument: three
|
| 125 |
+
published MI300X-trained Apache-2.0 models is concrete. Submit to
|
| 126 |
+
Fine-Tuning and let the agents + vision work show in the architecture
|
| 127 |
+
slide as evidence of depth, not as a co-primary claim.
|
| 128 |
+
|
| 129 |
+
**Civic vocabulary may not translate immediately.** "RPL Β§462(2),"
|
| 130 |
+
"NYC DEP stormwater plan," "EJNYC FVI" are precise and correct but they
|
| 131 |
+
require context. In a 5-minute video, leading with the civic policy
|
| 132 |
+
vocabulary before the demo creates a delay. Lead with the demo output
|
| 133 |
+
(the briefing paragraph, the citation chips, the Mellea pass), then name
|
| 134 |
+
the policy hooks as the second-order impact.
|
| 135 |
+
|
| 136 |
+
**No comparable submission is trying to do what Riprap does.** That is
|
| 137 |
+
an advantage and a risk. Judges evaluating "agentic AI apps" who have
|
| 138 |
+
not seen a citation-grounded geospatial briefing tool before will need
|
| 139 |
+
15β20 seconds of setup to understand the claim. The architecture slide
|
| 140 |
+
and the opening problem frame need to do that work fast.
|
research/PITCH-DECK-LANDSCAPE.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Pitch Deck Landscape β Hackathon 5-Minute Video Format
|
| 2 |
+
Captured 2026-05-07. Sources: Devpost blog, Taikai, SlideModel, Medium / Circles.Life,
|
| 3 |
+
TechCrunch 2014, and AMD/lablab hackathon context from AMD-HACKATHON-LANDSCAPE.md.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Four opening patterns in winning hackathon pitches
|
| 8 |
+
|
| 9 |
+
### 1. Problem-first
|
| 10 |
+
Open with "here's what's broken, here's who it hurts, here's the number."
|
| 11 |
+
Strongest when the problem is legible in under 10 seconds. Works well when
|
| 12 |
+
the audience already knows the space (healthcare, finance, real estate).
|
| 13 |
+
Risk: the problem frame can eat half the video if it's not stripped to one
|
| 14 |
+
sentence.
|
| 15 |
+
|
| 16 |
+
### 2. Demo-first
|
| 17 |
+
Show the live product within the first 30 seconds; let the judge form an
|
| 18 |
+
impression before explanation. Works well when the interface is visually
|
| 19 |
+
obvious and the output is striking. Risk: judges who don't know what they're
|
| 20 |
+
looking at will miss the point.
|
| 21 |
+
|
| 22 |
+
### 3. Receipts-first
|
| 23 |
+
Open with a proof table, a metric, or a live score. "5 of 5 addresses,
|
| 24 |
+
every claim verified, every run." Works well when the artifact is the
|
| 25 |
+
argument and the audience has technical credibility to read it.
|
| 26 |
+
Risk: dry if the receipts don't connect to a felt problem.
|
| 27 |
+
|
| 28 |
+
### 4. Architecture-first
|
| 29 |
+
Start with the diagram, then show the demo. Works well when the
|
| 30 |
+
architecture *is* the differentiator (multi-agent, novel pipeline).
|
| 31 |
+
Risk: too slow; judges have already moved on before the demo.
|
| 32 |
+
|
| 33 |
+
---
|
| 34 |
+
|
| 35 |
+
## Which pattern fits Riprap
|
| 36 |
+
|
| 37 |
+
**Recommended: Problem-first into receipts, with demo in the middle.**
|
| 38 |
+
|
| 39 |
+
The structure that works for Riprap's 5-minute video:
|
| 40 |
+
|
| 41 |
+
1. **0:00β0:20 β Problem sentence.** One CNN headline on screen.
|
| 42 |
+
One line: "A number meets resistance. The only defense is the
|
| 43 |
+
audit trail." (This is already on slide 2 of the current deck
|
| 44 |
+
and it's good.)
|
| 45 |
+
|
| 46 |
+
2. **0:20β0:50 β Demo (live or recorded).** Type "442 East Houston
|
| 47 |
+
Street, Manhattan." Watch the Stones fire, the briefing stream,
|
| 48 |
+
the citation chips light up. The Mellea 4/4 meta card.
|
| 49 |
+
|
| 50 |
+
3. **0:50β1:30 β What you just saw.** Slide: five Stones, the data
|
| 51 |
+
sources named under each, the Capstone reconciler with Mellea.
|
| 52 |
+
Not a prose explanation β a diagram. 10 seconds to scan.
|
| 53 |
+
|
| 54 |
+
4. **1:30β2:00 β The receipts.** The 5/5 table. 5.8β13.1 s.
|
| 55 |
+
4/4 every run.
|
| 56 |
+
|
| 57 |
+
5. **2:00β2:30 β Why it's a Fine-Tuning submission.** Three Apache-2.0
|
| 58 |
+
models, named, on AMD MI300X. Test MAE vs zero-shot on the TTM
|
| 59 |
+
fine-tune. This is the hackathon track argument.
|
| 60 |
+
|
| 61 |
+
6. **2:30β3:30 β The civic case.** Property disclosure law, DEP
|
| 62 |
+
stormwater plan, EJNYC FVI. The open-source argument. This is
|
| 63 |
+
the "why it matters beyond the demo."
|
| 64 |
+
|
| 65 |
+
7. **3:30β4:00 β What's next.** Ida calibration for ASCE. Stones as
|
| 66 |
+
standalone packages v1.1. Methodology paper.
|
| 67 |
+
|
| 68 |
+
8. **4:00β5:00 β CTA.** Space URL, GitHub, the three HF Hub models.
|
| 69 |
+
|
| 70 |
+
**Reasoning.** The Aegis-style projects (7 agents, autonomous crisis
|
| 71 |
+
response) are demo-first: the agent chatter is the spectacle. Riprap's
|
| 72 |
+
spectacle is quieter β it's the briefing paragraph that reads like a
|
| 73 |
+
professional memo and cites every number. That requires 10 seconds of
|
| 74 |
+
setup so the judge knows what to look at. Problem-first provides that
|
| 75 |
+
10-second setup without burning time.
|
| 76 |
+
|
| 77 |
+
The receipts are load-bearing because the Fine-Tuning track requires
|
| 78 |
+
evidence of GPU work. Putting the 5/5 table and the HF Hub model links
|
| 79 |
+
on screen in the first 2 minutes closes the "did they actually run this
|
| 80 |
+
on AMD hardware" question before the judge asks it.
|
| 81 |
+
|
| 82 |
+
---
|
| 83 |
+
|
| 84 |
+
## Specific weaknesses in the current deck against this pattern
|
| 85 |
+
|
| 86 |
+
**Slide 3 (THE STACK) is the biggest structural problem.** Leading with
|
| 87 |
+
a four-track table (three green, one skipped) communicates "we tried
|
| 88 |
+
to cover everything" rather than "we built something specific and deep."
|
| 89 |
+
For a hackathon submission, one strong track argument is better than
|
| 90 |
+
three partial ones. Reframe to Fine-Tuning as primary; Agents and Vision
|
| 91 |
+
as supporting evidence of depth.
|
| 92 |
+
|
| 93 |
+
**No architecture diagram.** The current deck has no slide that shows
|
| 94 |
+
what the system *does* architecturally β just prose descriptions. A
|
| 95 |
+
diagram (even a plain text flow: query β planner β five Stones β Capstone
|
| 96 |
+
β briefing) would let judges scan the system in 10 seconds instead of
|
| 97 |
+
reading for 45 seconds. This is missing and needs adding.
|
| 98 |
+
|
| 99 |
+
**Slide 6 (Live Demo) is inert in a PDF/video deck.** "Navigate to
|
| 100 |
+
this URL" is not a demo. In a video, the demo is in the recording.
|
| 101 |
+
The slide should show either a still of the briefing output (or the
|
| 102 |
+
meta card) or serve a different purpose entirely. Repurposing it as
|
| 103 |
+
WHAT'S NEXT is the right call β it opens the longer arc and makes
|
| 104 |
+
the deck reusable for the ASCE audience.
|
| 105 |
+
|
| 106 |
+
**The problem slide quote is paraphrased, not exact.** The CNN article
|
| 107 |
+
(Dec 2, 2025) is real and cited correctly in RESEARCH.md. The slide
|
| 108 |
+
text reads "Zillow removed flood-risk data from listings in December
|
| 109 |
+
2025 after pressure from the real-estate industry." The TechCrunch
|
| 110 |
+
coverage confirms Zillow's sitewide removal took effect November 14,
|
| 111 |
+
reported first in December. The slide should note it as a paraphrase
|
| 112 |
+
or tighten to what the article actually says. Adding the "not a score"
|
| 113 |
+
distinction is the right addition β it is the exact counter-position
|
| 114 |
+
to the Zillow pullout.
|
| 115 |
+
|
| 116 |
+
**Strengths:** Slide 4 (THE RECEIPTS) is the deck's best slide β
|
| 117 |
+
dense, verifiable, numbers-first. The briefing codeblock on slide 2
|
| 118 |
+
is the best visual: judges can see the output format immediately.
|
| 119 |
+
Slide 5 (WHY IT MATTERS) has the right register and the right policy
|
| 120 |
+
hooks β don't touch the voice there.
|
| 121 |
+
|
| 122 |
+
---
|
| 123 |
+
|
| 124 |
+
## Common failure modes in hackathon pitches (to avoid)
|
| 125 |
+
|
| 126 |
+
- More than two sentences per bullet. Judges skim; paragraphs die.
|
| 127 |
+
- Explaining the tech before showing the output. Show first, explain second.
|
| 128 |
+
- "We plan to" language. The build is done. Everything should be past tense
|
| 129 |
+
or present tense.
|
| 130 |
+
- Slides that require the presenter to animate them (arrows appearing, etc.)
|
| 131 |
+
β a PDF must stand alone.
|
| 132 |
+
- Over-crediting the AI ("powered by Granite 4.1, the state-of-the-art...").
|
| 133 |
+
Name the model once; the audience knows it.
|
| 134 |
+
- Apologizing for scope ("we didn't have time to..."). Cut the feature or
|
| 135 |
+
cut the sentence.
|
scripts/build_mta_entrances_register.py
CHANGED
|
@@ -18,7 +18,6 @@ sys.path.insert(0, str(ROOT))
|
|
| 18 |
from app.assets import mta_entrances # noqa: E402
|
| 19 |
from app.register_builder import build_register # noqa: E402
|
| 20 |
|
| 21 |
-
|
| 22 |
if __name__ == "__main__":
|
| 23 |
build_register("mta_entrances", mta_entrances.load,
|
| 24 |
meta_keys=("name", "address", "borough", "entrance_type"))
|
|
|
|
| 18 |
from app.assets import mta_entrances # noqa: E402
|
| 19 |
from app.register_builder import build_register # noqa: E402
|
| 20 |
|
|
|
|
| 21 |
if __name__ == "__main__":
|
| 22 |
build_register("mta_entrances", mta_entrances.load,
|
| 23 |
meta_keys=("name", "address", "borough", "entrance_type"))
|
scripts/build_nycha_register.py
CHANGED
|
@@ -15,7 +15,6 @@ sys.path.insert(0, str(ROOT))
|
|
| 15 |
from app.assets import nycha # noqa: E402
|
| 16 |
from app.register_builder import build_register # noqa: E402
|
| 17 |
|
| 18 |
-
|
| 19 |
if __name__ == "__main__":
|
| 20 |
build_register("nycha", nycha.load,
|
| 21 |
meta_keys=("name", "address", "borough", "tds_num"))
|
|
|
|
| 15 |
from app.assets import nycha # noqa: E402
|
| 16 |
from app.register_builder import build_register # noqa: E402
|
| 17 |
|
|
|
|
| 18 |
if __name__ == "__main__":
|
| 19 |
build_register("nycha", nycha.load,
|
| 20 |
meta_keys=("name", "address", "borough", "tds_num"))
|
scripts/build_schools_register.py
CHANGED
|
@@ -17,7 +17,6 @@ sys.path.insert(0, str(ROOT))
|
|
| 17 |
from app.assets import schools # noqa: E402
|
| 18 |
from app.register_builder import build_register # noqa: E402
|
| 19 |
|
| 20 |
-
|
| 21 |
if __name__ == "__main__":
|
| 22 |
build_register("schools", schools.load,
|
| 23 |
meta_keys=("name", "address", "borough", "bbl", "bin"))
|
|
|
|
| 17 |
from app.assets import schools # noqa: E402
|
| 18 |
from app.register_builder import build_register # noqa: E402
|
| 19 |
|
|
|
|
| 20 |
if __name__ == "__main__":
|
| 21 |
build_register("schools", schools.load,
|
| 22 |
meta_keys=("name", "address", "borough", "bbl", "bin"))
|
scripts/dry_run.py
CHANGED
|
@@ -52,7 +52,7 @@ def stream_one(query: str) -> tuple[bool, str]:
|
|
| 52 |
elif d.get("kind") == "final": final = d
|
| 53 |
if not final:
|
| 54 |
return False, f"no final event (steps={steps})"
|
| 55 |
-
dropped = len((
|
| 56 |
en = final.get("energy") or {}
|
| 57 |
return True, (f"steps={steps}, dropped={dropped}, "
|
| 58 |
f"energy={en.get('local_mwh','?')} mWh local")
|
|
|
|
| 52 |
elif d.get("kind") == "final": final = d
|
| 53 |
if not final:
|
| 54 |
return False, f"no final event (steps={steps})"
|
| 55 |
+
dropped = len((final.get("audit") or {}).get("dropped") or [])
|
| 56 |
en = final.get("energy") or {}
|
| 57 |
return True, (f"steps={steps}, dropped={dropped}, "
|
| 58 |
f"energy={en.get('local_mwh','?')} mWh local")
|
scripts/probe_addresses.py
CHANGED
|
@@ -38,7 +38,6 @@ from urllib.parse import quote
|
|
| 38 |
|
| 39 |
import httpx
|
| 40 |
|
| 41 |
-
|
| 42 |
# Curated probe set. Each entry exercises a different surface of the
|
| 43 |
# system; together they cover every Stone's specialists at least once.
|
| 44 |
DEFAULT_ADDRESSES: list[dict[str, Any]] = [
|
|
|
|
| 38 |
|
| 39 |
import httpx
|
| 40 |
|
|
|
|
| 41 |
# Curated probe set. Each entry exercises a different surface of the
|
| 42 |
# system; together they cover every Stone's specialists at least once.
|
| 43 |
DEFAULT_ADDRESSES: list[dict[str, Any]] = [
|
scripts/run_prithvi_flood.py
CHANGED
|
@@ -39,7 +39,10 @@ PRITHVI_BAND_NAMES = ["B02", "B03", "B04", "B8A", "B11", "B12"]
|
|
| 39 |
def _stage_stack(out_path: Path, scene_id: str = SCENE_ID) -> bool:
|
| 40 |
if out_path.exists():
|
| 41 |
return True
|
| 42 |
-
import
|
|
|
|
|
|
|
|
|
|
| 43 |
print(f"fetching scene {scene_id}...", file=sys.stderr)
|
| 44 |
catalog = pystac_client.Client.open(
|
| 45 |
"https://planetarycomputer.microsoft.com/api/stac/v1",
|
|
@@ -110,10 +113,10 @@ def _process_one(scene_id: str, scene_date: str) -> list[dict]:
|
|
| 110 |
print(f" no prediction tiff for {scene_id}", file=sys.stderr)
|
| 111 |
return []
|
| 112 |
|
|
|
|
| 113 |
import rasterio
|
| 114 |
from rasterio.features import shapes
|
| 115 |
-
from shapely.geometry import
|
| 116 |
-
import geopandas as gpd
|
| 117 |
|
| 118 |
with rasterio.open(pred_path) as ds:
|
| 119 |
pred = ds.read(1); transform = ds.transform; src_crs = ds.crs
|
|
|
|
| 39 |
def _stage_stack(out_path: Path, scene_id: str = SCENE_ID) -> bool:
|
| 40 |
if out_path.exists():
|
| 41 |
return True
|
| 42 |
+
import numpy as np
|
| 43 |
+
import planetary_computer
|
| 44 |
+
import pystac_client
|
| 45 |
+
import rasterio
|
| 46 |
print(f"fetching scene {scene_id}...", file=sys.stderr)
|
| 47 |
catalog = pystac_client.Client.open(
|
| 48 |
"https://planetarycomputer.microsoft.com/api/stac/v1",
|
|
|
|
| 113 |
print(f" no prediction tiff for {scene_id}", file=sys.stderr)
|
| 114 |
return []
|
| 115 |
|
| 116 |
+
import geopandas as gpd
|
| 117 |
import rasterio
|
| 118 |
from rasterio.features import shapes
|
| 119 |
+
from shapely.geometry import mapping, shape
|
|
|
|
| 120 |
|
| 121 |
with rasterio.open(pred_path) as ds:
|
| 122 |
pred = ds.read(1); transform = ds.transform; src_crs = ds.crs
|
scripts/run_prithvi_ida.py
CHANGED
|
@@ -48,7 +48,10 @@ def _stage_stack(out_path: Path, scene_id: str) -> bool:
|
|
| 48 |
if out_path.exists():
|
| 49 |
print(f" reusing {out_path.name}", file=sys.stderr)
|
| 50 |
return True
|
| 51 |
-
import
|
|
|
|
|
|
|
|
|
|
| 52 |
print(f"fetching {scene_id}...", file=sys.stderr)
|
| 53 |
catalog = pystac_client.Client.open(
|
| 54 |
"https://planetarycomputer.microsoft.com/api/stac/v1",
|
|
@@ -126,11 +129,11 @@ def main() -> int:
|
|
| 126 |
return 2
|
| 127 |
|
| 128 |
# ---- diff: NEW water in post that wasn't in pre = Ida-attributable ----
|
| 129 |
-
import
|
| 130 |
import numpy as np
|
|
|
|
| 131 |
from rasterio.features import shapes
|
| 132 |
-
from shapely.geometry import
|
| 133 |
-
import geopandas as gpd
|
| 134 |
|
| 135 |
with rasterio.open(pre_pred) as ds:
|
| 136 |
pre = ds.read(1)
|
|
|
|
| 48 |
if out_path.exists():
|
| 49 |
print(f" reusing {out_path.name}", file=sys.stderr)
|
| 50 |
return True
|
| 51 |
+
import numpy as np
|
| 52 |
+
import planetary_computer
|
| 53 |
+
import pystac_client
|
| 54 |
+
import rasterio
|
| 55 |
print(f"fetching {scene_id}...", file=sys.stderr)
|
| 56 |
catalog = pystac_client.Client.open(
|
| 57 |
"https://planetarycomputer.microsoft.com/api/stac/v1",
|
|
|
|
| 129 |
return 2
|
| 130 |
|
| 131 |
# ---- diff: NEW water in post that wasn't in pre = Ida-attributable ----
|
| 132 |
+
import geopandas as gpd
|
| 133 |
import numpy as np
|
| 134 |
+
import rasterio
|
| 135 |
from rasterio.features import shapes
|
| 136 |
+
from shapely.geometry import mapping, shape
|
|
|
|
| 137 |
|
| 138 |
with rasterio.open(pre_pred) as ds:
|
| 139 |
pre = ds.read(1)
|
scripts/smoke_test_gpu.sh
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Smoke test the AMD GPU droplet (vLLM + riprap-models).
|
| 3 |
+
# Usage: bash scripts/smoke_test_gpu.sh <ip> <token>
|
| 4 |
+
set -euo pipefail
|
| 5 |
+
|
| 6 |
+
IP="${1:?Usage: smoke_test_gpu.sh <ip> <token>}"
|
| 7 |
+
TOKEN="${2:?Usage: smoke_test_gpu.sh <ip> <token>}"
|
| 8 |
+
VLLM_URL="http://${IP}:8001"
|
| 9 |
+
ML_URL="http://${IP}:7860"
|
| 10 |
+
|
| 11 |
+
PASS=0
|
| 12 |
+
FAIL=0
|
| 13 |
+
|
| 14 |
+
check() {
|
| 15 |
+
local label="$1"; shift
|
| 16 |
+
local status
|
| 17 |
+
if status=$(eval "$@" 2>&1); then
|
| 18 |
+
echo " PASS $label"
|
| 19 |
+
PASS=$((PASS+1))
|
| 20 |
+
else
|
| 21 |
+
echo " FAIL $label"
|
| 22 |
+
echo " $status"
|
| 23 |
+
FAIL=$((FAIL+1))
|
| 24 |
+
fi
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
echo "=== Smoke test: $IP ==="
|
| 28 |
+
echo ""
|
| 29 |
+
|
| 30 |
+
echo "--- vLLM (port 8001) ---"
|
| 31 |
+
check "vLLM /v1/models" \
|
| 32 |
+
"curl -sf -H 'Authorization: Bearer $TOKEN' $VLLM_URL/v1/models | python3 -c 'import sys,json; d=json.load(sys.stdin); assert len(d[\"data\"]) > 0'"
|
| 33 |
+
|
| 34 |
+
check "vLLM /v1/chat/completions" \
|
| 35 |
+
"curl -sf -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' \
|
| 36 |
+
-d '{\"model\":\"granite-4.1-8b\",\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}],\"max_tokens\":5}' \
|
| 37 |
+
$VLLM_URL/v1/chat/completions | python3 -c 'import sys,json; d=json.load(sys.stdin); assert d[\"choices\"][0][\"message\"][\"content\"]'"
|
| 38 |
+
|
| 39 |
+
echo ""
|
| 40 |
+
echo "--- riprap-models (port 7860) ---"
|
| 41 |
+
check "riprap-models /healthz" \
|
| 42 |
+
"curl -sf $ML_URL/healthz | python3 -c 'import sys,json; d=json.load(sys.stdin); assert d.get(\"ok\") == True'"
|
| 43 |
+
|
| 44 |
+
check "riprap-models /v1/granite-embed" \
|
| 45 |
+
"curl -sf -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' \
|
| 46 |
+
-d '{\"texts\":[\"flood risk in NYC\"]}' \
|
| 47 |
+
$ML_URL/v1/granite-embed | python3 -c 'import sys,json; d=json.load(sys.stdin); assert d.get(\"ok\") and len(d[\"vectors\"]) == 1 and len(d[\"vectors\"][0]) > 0'"
|
| 48 |
+
|
| 49 |
+
check "riprap-models /v1/gliner-extract" \
|
| 50 |
+
"curl -sf -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' \
|
| 51 |
+
-d '{\"text\":\"Hurricane Sandy flooded 80 Pioneer Street in Red Hook Brooklyn.\",\"labels\":[\"location\",\"event\"]}' \
|
| 52 |
+
$ML_URL/v1/gliner-extract | python3 -c 'import sys,json; d=json.load(sys.stdin); assert \"entities\" in d'"
|
| 53 |
+
|
| 54 |
+
echo ""
|
| 55 |
+
echo "=== Results: ${PASS} PASS, ${FAIL} FAIL ==="
|
| 56 |
+
[ "$FAIL" -eq 0 ]
|
services/riprap-models/Dockerfile
CHANGED
|
@@ -16,7 +16,12 @@
|
|
| 16 |
# Build: docker build -t riprap-models:latest -f Dockerfile ../..
|
| 17 |
# Layout: the build context is the project root so the COPY lines
|
| 18 |
# below can reach `services/riprap-models/`.
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
ENV DEBIAN_FRONTEND=noninteractive \
|
| 22 |
PYTHONUNBUFFERED=1 \
|
|
@@ -47,7 +52,12 @@ WORKDIR /workspace/riprap-models
|
|
| 47 |
# kornia / albumentations chain, granite-tsfm's tsfm_public, etc.).
|
| 48 |
COPY services/riprap-models/requirements-full.txt /tmp/req-full.txt
|
| 49 |
RUN pip install --upgrade pip && \
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
# Service code itself. Cheap to invalidate; lands last.
|
| 53 |
COPY services/riprap-models/main.py /workspace/riprap-models/main.py
|
|
|
|
| 16 |
# Build: docker build -t riprap-models:latest -f Dockerfile ../..
|
| 17 |
# Layout: the build context is the project root so the COPY lines
|
| 18 |
# below can reach `services/riprap-models/`.
|
| 19 |
+
# Use the vLLM ROCm image as base β it ships torch 2.9.1+git8907517
|
| 20 |
+
# (the actual AMD bespoke build) and is already cached on DigitalOcean
|
| 21 |
+
# AMD GPU droplets, so no download is needed during bring-up.
|
| 22 |
+
# The public rocm/pytorch release image is a fallback if this image is
|
| 23 |
+
# not available; see the comment block above for background.
|
| 24 |
+
FROM vllm/vllm-openai-rocm:v0.17.1
|
| 25 |
|
| 26 |
ENV DEBIAN_FRONTEND=noninteractive \
|
| 27 |
PYTHONUNBUFFERED=1 \
|
|
|
|
| 52 |
# kornia / albumentations chain, granite-tsfm's tsfm_public, etc.).
|
| 53 |
COPY services/riprap-models/requirements-full.txt /tmp/req-full.txt
|
| 54 |
RUN pip install --upgrade pip && \
|
| 55 |
+
# Freeze the ROCm torch/torchvision/torchaudio at whatever version
|
| 56 |
+
# the vLLM base image ships, so transitive deps (peft, torchgeo, etc.)
|
| 57 |
+
# don't pull a CUDA build from PyPI and replace the ROCm one.
|
| 58 |
+
pip freeze | grep -E "^(torch|torchvision|torchaudio)==" > /tmp/torch-lock.txt && \
|
| 59 |
+
cat /tmp/torch-lock.txt && \
|
| 60 |
+
pip install -r /tmp/req-full.txt --constraint /tmp/torch-lock.txt
|
| 61 |
|
| 62 |
# Service code itself. Cheap to invalidate; lands last.
|
| 63 |
COPY services/riprap-models/main.py /workspace/riprap-models/main.py
|
services/riprap-models/main.py
CHANGED
|
@@ -37,7 +37,7 @@ from contextlib import asynccontextmanager
|
|
| 37 |
from typing import Any
|
| 38 |
|
| 39 |
import numpy as np
|
| 40 |
-
from fastapi import Depends, FastAPI,
|
| 41 |
from pydantic import BaseModel
|
| 42 |
|
| 43 |
log = logging.getLogger("riprap.models")
|
|
|
|
| 37 |
from typing import Any
|
| 38 |
|
| 39 |
import numpy as np
|
| 40 |
+
from fastapi import Depends, FastAPI, Header, HTTPException
|
| 41 |
from pydantic import BaseModel
|
| 42 |
|
| 43 |
log = logging.getLogger("riprap.models")
|
services/riprap-models/requirements-full.txt
CHANGED
|
@@ -15,7 +15,7 @@
|
|
| 15 |
transformers==4.57.6
|
| 16 |
peft==0.18.1
|
| 17 |
accelerate==1.13.0
|
| 18 |
-
safetensors=
|
| 19 |
huggingface_hub==0.36.2
|
| 20 |
sentence-transformers==5.4.1
|
| 21 |
gliner==0.2.26
|
|
@@ -54,7 +54,7 @@ ImageIO==2.37.3
|
|
| 54 |
numpy==2.4.4
|
| 55 |
pandas==3.0.0
|
| 56 |
scipy==1.17.1
|
| 57 |
-
scikit-learn=
|
| 58 |
pillow==12.1.1
|
| 59 |
|
| 60 |
# ---- Web / IO ------------------------------------------------------------
|
|
|
|
| 15 |
transformers==4.57.6
|
| 16 |
peft==0.18.1
|
| 17 |
accelerate==1.13.0
|
| 18 |
+
safetensors>=0.4.5,<0.9
|
| 19 |
huggingface_hub==0.36.2
|
| 20 |
sentence-transformers==5.4.1
|
| 21 |
gliner==0.2.26
|
|
|
|
| 54 |
numpy==2.4.4
|
| 55 |
pandas==3.0.0
|
| 56 |
scipy==1.17.1
|
| 57 |
+
scikit-learn>=1.5,<1.8
|
| 58 |
pillow==12.1.1
|
| 59 |
|
| 60 |
# ---- Web / IO ------------------------------------------------------------
|
slides/CHANGES-2026-05-06.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deck changes β 2026-05-06 overnight pass
|
| 2 |
+
|
| 3 |
+
Branch: `comms-overnight-2026-05-06`
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
# Deck changes β 2026-05-06 content pass
|
| 8 |
+
|
| 9 |
+
Branch: `slides/content-pass-2026-05-06`
|
| 10 |
+
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
## Slide-by-slide diff
|
| 14 |
+
|
| 15 |
+
### Slide 02 Β· Solution β REFRAMED
|
| 16 |
+
|
| 17 |
+
**Lead rewritten to foreground what Riprap is, not the citation principle.**
|
| 18 |
+
Previous headline: "Every number cites its source. Or it doesn't appear."
|
| 19 |
+
New headline: "A flood-exposure briefing for any place in New York City."
|
| 20 |
+
|
| 21 |
+
The citation discipline is now a supporting sentence below the screenshot
|
| 22 |
+
placeholder ("Behind the prose: every numeric claim links to its primary
|
| 23 |
+
public-record source. Mellea rejection sampling refuses to publish what it
|
| 24 |
+
can't cite."), not the slide's thesis.
|
| 25 |
+
|
| 26 |
+
**Briefing codeblock removed.** The 442 East Houston example paragraph was
|
| 27 |
+
the slide's dominant visual. It has been replaced by a large screenshot
|
| 28 |
+
placeholder (min-height 240px) with the caption "[ screenshot of
|
| 29 |
+
riprap.nyc landing β to be added ]". The screenshot will carry the
|
| 30 |
+
demo-evidence load once captured from the live app.
|
| 31 |
+
|
| 32 |
+
**New subhead added.** Sets context before the placeholder: "Type an
|
| 33 |
+
address or neighborhood. Get a written briefing in 5β13 seconds, fusing
|
| 34 |
+
four temporal modes β Sandy 2012 inundation, current 311 history, FloodNet
|
| 35 |
+
sensor reads, NPCC4 projections β into one cited paragraph."
|
| 36 |
+
|
| 37 |
+
### Slide 04 Β· Architecture β EVIDENCE CARDS ADDED
|
| 38 |
+
|
| 39 |
+
**Four text-only Stone columns replaced by four evidence cards.** The cards
|
| 40 |
+
are reproduced as static inline HTML using the existing design-system
|
| 41 |
+
tokens (CSS custom properties from riprap.css), matching the EvidenceCard
|
| 42 |
+
component shape: source label + vintage tag, card title, data body with
|
| 43 |
+
Stone color, and doc_id footer with border-top divider.
|
| 44 |
+
|
| 45 |
+
Card content and origin:
|
| 46 |
+
- **Cornerstone Β· USGS 3DEP** β "Microtopography (HAND / TWI)" β four-row
|
| 47 |
+
stat grid: HAND 0.82 m, TWI 14.3, Elev 2.1 m MSL, Pct lower 78%.
|
| 48 |
+
Numbers are representative USGS 3DEP values for an LES test address.
|
| 49 |
+
doc_id: [topo]. Color: #475569 (slate).
|
| 50 |
+
- **Keystone Β· TerraMind-NYC** β "Building footprint coverage" β scalar
|
| 51 |
+
"48.41%" with sub "250 m radius Β· Buildings LoRA adapter". Sourced from
|
| 52 |
+
the TerraMind-NYC-Adapters experiment (experiments/20_terramind/).
|
| 53 |
+
doc_id: [keystone_bldg]. Color: #1A4480 (federal navy).
|
| 54 |
+
- **Touchstone Β· NYC 311** β "Flood complaints Β· 200 m buffer" β scalar
|
| 55 |
+
"19" service requests, "5-yr lookback". This exact figure appears in the
|
| 56 |
+
briefing codeblock that was removed from slide 02, sourced from the
|
| 57 |
+
442 E Houston probe. doc_id: [nyc311]. Color: #0E7490 (cyan).
|
| 58 |
+
- **Lodestone Β· Granite TTM r2** β "Surge residual nowcast" β scalar
|
| 59 |
+
"0.22 ft", "peak surge residual Β· 9.6 h horizon". Consistent with the
|
| 60 |
+
TTM r2 model's forecast horizon for Battery gauge residuals.
|
| 61 |
+
doc_id: [ttm_surge]. Color: #92400E (amber).
|
| 62 |
+
|
| 63 |
+
No existing PNG/SVG exports were found in slides/ or web/static/assets/.
|
| 64 |
+
Cards were reproduced in HTML/CSS rather than screenshotted β pragmatic
|
| 65 |
+
given the live app state at commit time.
|
| 66 |
+
|
| 67 |
+
**Flow header and Capstone footer preserved unchanged.**
|
| 68 |
+
|
| 69 |
+
Caption added below cards: "Real evidence cards rendered by the live
|
| 70 |
+
system Β· 442 East Houston Street, Manhattan."
|
| 71 |
+
|
| 72 |
+
### Slide 06 Β· Demo β CURTAIN-RAISE REWRITE
|
| 73 |
+
|
| 74 |
+
**"Try it live." replaced by "Live demo." β stripped to transitional handoff.**
|
| 75 |
+
Three "Watch for" cards removed (useful for silent reading; distract as a
|
| 76 |
+
video lead-in). The query is now the visual anchor, rendered in 28px mono
|
| 77 |
+
bold, centered, with no competing elements.
|
| 78 |
+
|
| 79 |
+
URL changed from `github.com/msradam/riprap-nyc` (full GitHub URL in
|
| 80 |
+
mono) to `riprap.nyc` (domain only, in accent blue). The GitHub URL
|
| 81 |
+
appears on the CTA slide where it belongs.
|
| 82 |
+
|
| 83 |
+
Footer stats line added: "13 seconds end-to-end Β· 4/4 grounding checks Β·
|
| 84 |
+
all sources public-record" β matches the appendix receipts table.
|
| 85 |
+
|
| 86 |
+
### Slide 07 Β· What's next β COLUMNS REFRAMED
|
| 87 |
+
|
| 88 |
+
**ASCE conference reference dropped.** "Ida calibration Β· ASCE NY"
|
| 89 |
+
column removed (conference-specific, not relevant to the hackathon
|
| 90 |
+
audience).
|
| 91 |
+
|
| 92 |
+
**Methodology paper column dropped.** Replaced by "Historical-event mode"
|
| 93 |
+
β a first-class feature framing of retrospective FSM runs for calibration
|
| 94 |
+
against Sandy, Ida, Beryl. More concrete and demo-relevant than an
|
| 95 |
+
academic venue target.
|
| 96 |
+
|
| 97 |
+
**Stones v1.1 column rewritten** as "Break out the Stones" β same idea,
|
| 98 |
+
reframed around composability for civic-tech projects rather than version
|
| 99 |
+
numbering.
|
| 100 |
+
|
| 101 |
+
**New city list expanded.** Previous footer: "Houston (Harvey + Beryl
|
| 102 |
+
2024), Miami (king tides), Boston (CSO floods)". New column two:
|
| 103 |
+
Houston, Miami, Boston, Jakarta, Manila, Dhaka. Signals international
|
| 104 |
+
reach without claiming delivery.
|
| 105 |
+
|
| 106 |
+
**Slide title changed** from "The longer arc." to "What's next." β
|
| 107 |
+
matches the eyebrow label.
|
| 108 |
+
|
| 109 |
+
**Lead line repositioned** from footer paragraph to slide subhead
|
| 110 |
+
(mono, muted): "The architecture is NYC-specific by data choice,
|
| 111 |
+
not by code."
|
| 112 |
+
|
| 113 |
+
### CTA slide (slide 09) Β· URL FIX
|
| 114 |
+
|
| 115 |
+
**GitHub URL line-wrap fixed.** Previous: `# github.com/msradam/riprap-nyc`
|
| 116 |
+
as an h1 at 96px β wraps at the hyphen in the PDF render, producing
|
| 117 |
+
"riprap" / "nyc" on separate lines.
|
| 118 |
+
|
| 119 |
+
Fix: replaced the markdown h1 with an inline HTML div replicating all
|
| 120 |
+
h1 visual properties (IBM Plex Sans Bold, letter-spacing -0.03em, var
|
| 121 |
+
(--paper) color, same margin) but at 68px with `white-space: nowrap`.
|
| 122 |
+
68px is the largest size at which "github.com/msradam/riprap-nyc" (30
|
| 123 |
+
chars) fits within the CTA slide's 1104px content width (88px padding
|
| 124 |
+
each side). riprap.css unchanged.
|
| 125 |
+
|
| 126 |
+
---
|
| 127 |
+
|
| 128 |
+
## Visual regressions observed during rebuild
|
| 129 |
+
|
| 130 |
+
None. All 10 slides rendered without overflow warnings from Marp. The
|
| 131 |
+
architecture slide is dense but within bounds β the 4-card grid sits
|
| 132 |
+
between the flow header and Capstone footer with the caption line below.
|
| 133 |
+
|
| 134 |
+
## Where evidence card visuals came from
|
| 135 |
+
|
| 136 |
+
Reproduced in static HTML/CSS within the Marp slide. No existing PNG/SVG
|
| 137 |
+
card exports were found in the repo. The design tokens (CSS variables)
|
| 138 |
+
from riprap.css render identically in Marp/Puppeteer as in the SvelteKit
|
| 139 |
+
UI. Source data for each card is documented in the slide-by-slide diff
|
| 140 |
+
above.
|
| 141 |
+
|
| 142 |
+
---
|
| 143 |
+
|
| 144 |
+
## Slide-by-slide diff
|
| 145 |
+
|
| 146 |
+
### Cover (slide 1) β LOCKED, no changes
|
| 147 |
+
|
| 148 |
+
### Slide 01 Β· The problem β MODIFIED
|
| 149 |
+
|
| 150 |
+
**Quote attribution corrected.**
|
| 151 |
+
The removal took effect November 14, 2025, with CNN/TechCrunch coverage
|
| 152 |
+
on December 1β2. The prior slide said "Dec 2Β·2025 Β· CNN" and presented
|
| 153 |
+
the quote as a direct citation. Updated label to "Nov 14Β·2025 Β· CNN /
|
| 154 |
+
TechCrunch (paraphrase)" and reworded to "Zillow removed climate risk
|
| 155 |
+
scores from listings under pressure from the real-estate industry. In
|
| 156 |
+
their place: a link, far less visible." This is accurate to the
|
| 157 |
+
TechCrunch reporting and clearly marked as paraphrase.
|
| 158 |
+
|
| 159 |
+
**"Not a score" line added.**
|
| 160 |
+
New sentence at the bottom of the slide:
|
| 161 |
+
"Riprap is not a property-risk score. It is the audit trail behind one."
|
| 162 |
+
This is the True-Flood-Risk-vs-Riprap distinction. It positions Riprap
|
| 163 |
+
as the tool that produces the audit evidence, not a competing score
|
| 164 |
+
product β which is the honest framing and also the strongest counter
|
| 165 |
+
to the Zillow pullout narrative.
|
| 166 |
+
|
| 167 |
+
### Slide 02 Β· What riprap is β UNCHANGED
|
| 168 |
+
|
| 169 |
+
### NEW slide 03 Β· Architecture β INSERTED
|
| 170 |
+
|
| 171 |
+
New slide between "What riprap is" (former slide 02) and the track
|
| 172 |
+
slide (former slide 03, now slide 04). Rationale: the deck had no
|
| 173 |
+
architectural diagram. A judge scanning a 9-slide deck in 30 seconds
|
| 174 |
+
gets the system shape from this slide before the receipts slide.
|
| 175 |
+
|
| 176 |
+
Content: left-to-right then top-to-bottom flow:
|
| 177 |
+
- Free-text query β Planner (Granite 4.1 3B, intent classification)
|
| 178 |
+
- Planner routes to four evidence Stones (Cornerstone / Keystone /
|
| 179 |
+
Touchstone / Lodestone) displayed in a 4-column grid with Stone
|
| 180 |
+
color from the design system, tagline, and named data sources /
|
| 181 |
+
models under each
|
| 182 |
+
- Capstone (Granite 4.1 8B + Mellea, four named citation checks)
|
| 183 |
+
- Cited 4-section briefing, [doc_id] on every number
|
| 184 |
+
|
| 185 |
+
Title: "Five Stones fan out. One cited briefing comes back."
|
| 186 |
+
|
| 187 |
+
### Slide 03 β NEW slide 04 Β· The track β MODIFIED
|
| 188 |
+
|
| 189 |
+
**Major reframe.** Prior title: "Three of four hackathon tracks. One
|
| 190 |
+
project." New title: "Submitted to Fine-Tuning on AMD GPUs."
|
| 191 |
+
|
| 192 |
+
Prior framing listed all four tracks including "Build in Public Β·
|
| 193 |
+
Skipped" (with muted opacity). Reads as hedging. New framing:
|
| 194 |
+
|
| 195 |
+
- Fine-Tuning track is marked "Primary" with full-opacity engaged
|
| 196 |
+
style and the explicit "Submitting here." label in the detail row.
|
| 197 |
+
Evidence: three Apache-2.0 NYC fine-tunes trained on MI300X,
|
| 198 |
+
published on HF Hub β named in the detail row.
|
| 199 |
+
- Agents and Vision tracks remain in the table marked "Supporting."
|
| 200 |
+
They are evidence of system depth, not co-primary claims.
|
| 201 |
+
- "Build in Public Β· Skipped" row dropped entirely.
|
| 202 |
+
|
| 203 |
+
**Rationale from research pass.** Fine-Tuning is the track with the
|
| 204 |
+
strongest verifiable artifacts. The three HF Hub model repos are public,
|
| 205 |
+
Apache-2.0, and the training code is in the repo. No other visible
|
| 206 |
+
submission to the hackathon has three published fine-tune artifacts.
|
| 207 |
+
Domain specificity (NYC flood risk) is the second differentiator.
|
| 208 |
+
|
| 209 |
+
### Slide 04 β NEW slide 05 Β· The receipts β UNCHANGED
|
| 210 |
+
|
| 211 |
+
The 5/5 address probe table and the three stat boxes are unchanged.
|
| 212 |
+
The numbers (5.8β13.1 s wall-clock, 4/4 Mellea grounding) come from
|
| 213 |
+
`scripts/probe_addresses.py` at 5/5 PASS. The instructions flagged
|
| 214 |
+
dependency on Track A's 20-query suite results; Track A has not yet
|
| 215 |
+
completed. **Flag for Adam before submission:** confirm the Mellea
|
| 216 |
+
4/4 claim holds in the 20-query suite when that run completes.
|
| 217 |
+
|
| 218 |
+
### Slide 05 β NEW slide 06 Β· Why it matters β UNCHANGED
|
| 219 |
+
|
| 220 |
+
Slide voice and content preserved exactly.
|
| 221 |
+
|
| 222 |
+
### Slide 06 β NEW slide 07 Β· What's next β REPLACED
|
| 223 |
+
|
| 224 |
+
Prior content: "Live demo" β endpoint URL, query, blockquote.
|
| 225 |
+
Reason to change: a live-demo slide is inert in a PDF or recorded
|
| 226 |
+
video. The URL belongs in the video recording, not a static slide.
|
| 227 |
+
|
| 228 |
+
New content: "The longer arc" β three boxes:
|
| 229 |
+
1. Ida calibration for ASCE NY (retrospective FSM run, May 2026
|
| 230 |
+
presentation target)
|
| 231 |
+
2. Stones v1.1 as standalone packages (Cornerstone, Touchstone,
|
| 232 |
+
Keystone, Lodestone published independently)
|
| 233 |
+
3. Methodology paper (citation-grounding pipeline as replicable
|
| 234 |
+
pattern for any geospatial LLM; open-access venue target)
|
| 235 |
+
|
| 236 |
+
Footer line: the cross-city scaffold note (Houston, Miami, Boston).
|
| 237 |
+
|
| 238 |
+
Rationale: shows the ASCE audience (who will see an adapted version
|
| 239 |
+
of this deck on May 13) where the technical work is going. The
|
| 240 |
+
hackathon audience sees the broader ambition. The slide is reusable
|
| 241 |
+
without modification for the ASCE talk.
|
| 242 |
+
|
| 243 |
+
### CTA (slide 8) β LOCKED, no changes
|
| 244 |
+
|
| 245 |
+
---
|
| 246 |
+
|
| 247 |
+
## Slide count
|
| 248 |
+
|
| 249 |
+
Before: 8 (cover + 6 content + CTA)
|
| 250 |
+
After: 9 (cover + 7 content + CTA)
|
| 251 |
+
|
| 252 |
+
The added slide is the architecture diagram. All other changes are
|
| 253 |
+
in-place content replacements.
|
| 254 |
+
|
| 255 |
+
---
|
| 256 |
+
|
| 257 |
+
## What was not changed
|
| 258 |
+
|
| 259 |
+
- The briefing codeblock on slide 02 β the output sample is the
|
| 260 |
+
deck's best visual and was left verbatim.
|
| 261 |
+
- All typography, color, and CSS class usage β the voice and register
|
| 262 |
+
are preserved.
|
| 263 |
+
- Source labels and specific numbers β no statistics were introduced
|
| 264 |
+
that are not already in RESEARCH.md or the probe suite results.
|
| 265 |
+
- The CNN/TechCrunch Zillow story date β confirmed real (Dec 2, 2025
|
| 266 |
+
CNN article; Nov 14 removal date). Attribution updated to mark
|
| 267 |
+
paraphrase.
|
| 268 |
+
|
| 269 |
+
---
|
| 270 |
+
|
| 271 |
+
## Outstanding verification item
|
| 272 |
+
|
| 273 |
+
**Slide 05 (THE RECEIPTS): 4/4 Mellea claim.**
|
| 274 |
+
The 5/5 address probe confirms 4/4 for these five addresses. Track A's
|
| 275 |
+
20-query suite has not yet completed. Before submitting to the hackathon,
|
| 276 |
+
run the full 20-query suite and confirm the numbers hold. If any query
|
| 277 |
+
produces < 4/4, either update the slide to reflect the actual number or
|
| 278 |
+
add a qualifier ("median 4/4 across the address probe suite"). Do not
|
| 279 |
+
ship a number that is not grounded.
|
slides/Makefile
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
DECK := deck.md
|
| 2 |
THEME := riprap.css
|
| 3 |
-
MARP := marp $(DECK) --theme $(THEME) --allow-local-files
|
| 4 |
|
| 5 |
.PHONY: all pdf html pptx clean
|
| 6 |
|
|
@@ -10,7 +10,7 @@ pdf:
|
|
| 10 |
$(MARP) --pdf --output deck.pdf
|
| 11 |
|
| 12 |
html:
|
| 13 |
-
$(MARP) --
|
| 14 |
|
| 15 |
pptx:
|
| 16 |
$(MARP) --pptx --output deck.pptx
|
|
|
|
| 1 |
DECK := deck.md
|
| 2 |
THEME := riprap.css
|
| 3 |
+
MARP := marp $(DECK) --theme $(THEME) --allow-local-files --html
|
| 4 |
|
| 5 |
.PHONY: all pdf html pptx clean
|
| 6 |
|
|
|
|
| 10 |
$(MARP) --pdf --output deck.pdf
|
| 11 |
|
| 12 |
html:
|
| 13 |
+
$(MARP) --output deck.html
|
| 14 |
|
| 15 |
pptx:
|
| 16 |
$(MARP) --pptx --output deck.pptx
|
slides/asce/CHANGES.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ASCE NY State Convention Deck β Changes from Hackathon Deck
|
| 2 |
+
|
| 3 |
+
## What is different
|
| 4 |
+
|
| 5 |
+
The ASCE deck is a complete content rewrite targeting civil and transportation
|
| 6 |
+
engineers at the inaugural ASCE NY State Convention in Albany (May 13, 2026).
|
| 7 |
+
The visual system is identical β same IBM Plex fonts, same Civic Hydrology
|
| 8 |
+
palette, same Stone color tokens, same box/grid layout primitives, same dam
|
| 9 |
+
mark. The content register shifts from "AI hackathon submission" to
|
| 10 |
+
"engineer-to-engineer PDH talk." The AMD/lablab.ai framing is retained but
|
| 11 |
+
moved to a single late slide (08 Β· How it was built) rather than leading.
|
| 12 |
+
Civil-engineering vocabulary (FEMA NFHL, NPCC4, HEC-RAS, SWMM, ICM, HAND,
|
| 13 |
+
TWI, USGS HWMs) appears throughout as first-language terms. The Five Stones
|
| 14 |
+
are introduced with explicit note that the names are structural/masonry terms.
|
| 15 |
+
A PDH learning-objectives slide opens the deck. An "honest boundaries" slide
|
| 16 |
+
(what Riprap is not) is new and load-bearing for a PE audience. The closing
|
| 17 |
+
slide solicits feedback from the room rather than pitching a hackathon track.
|
| 18 |
+
|
| 19 |
+
## Slide map
|
| 20 |
+
|
| 21 |
+
| ASCE slide | Content | Relationship to hackathon deck |
|
| 22 |
+
|---|---|---|
|
| 23 |
+
| 00 Β· Learning objectives | PDH takeaways, 4 objectives | **New** β no equivalent in hackathon deck |
|
| 24 |
+
| 01 Β· The problem | Evidence scatter across 8+ sources, engineer's framing | **Rewritten** β replaces "Climate risk is a black box" (HK slide 01); same underlying problem, civil-eng vocabulary |
|
| 25 |
+
| 02 Β· Solution | What Riprap does; screenshot placeholder | **Adapted** β same structure as HK slide 02; subhead and caption rewritten |
|
| 26 |
+
| 03 Β· Architecture β Five Stones | Four Stone cards + Capstone footer | **Adapted** β same inline evidence-card layout as HK slide 04; card body text rewritten for engineering audience; Stone names contextualized as structural terms |
|
| 27 |
+
| 04 Β· Live demo | Same query, same riprap.nyc URL; stat cards below | **Reused** β same as HK slide 06; stat cards added below for PDH pacing |
|
| 28 |
+
| 05 Β· Civic applications | 4 use cases for civil engineers | **Rewritten** β replaces HK slide 03 "civic-tech case"; adds Infrastructure Report Card and property disclosure; removes EJNYC/advocacy framing |
|
| 29 |
+
| 06 Β· Honest boundaries | 4-card "what Riprap is not" | **New** β no equivalent in hackathon deck; load-bearing for PE audience |
|
| 30 |
+
| 07 Β· Directions | 4 forward directions including upstate NY | **Adapted** β replaces HK slide 07 "What's next"; adds upstate NY riverine/ice-jam/dam-failure direction; drops "other flood-impacted cities" |
|
| 31 |
+
| 08 Β· How it was built | AMD hackathon context, models, agentic stack | **Rewritten** β replaces HK slide 05 (fine-tunes); honestly frames the hackathon as context, not headline |
|
| 32 |
+
| 09 Β· Discussion / Q&A | 3 feedback questions for the room | **New** β no equivalent in hackathon deck |
|
| 33 |
+
| 10 Β· CTA closing | github URL, colophon | **Adapted** β same dark CTA slide; AMD/lablab eyebrow replaced with ASCE event line |
|
| 34 |
+
| Appendix A Β· Receipts | 5/5 address probe table | **Reused** β identical to HK appendix slide |
|
| 35 |
+
| Appendix B Β· Sources | Primary sources by jurisdiction tier | **New** β no equivalent in hackathon deck; useful for PE attendees who want to follow up |
|
| 36 |
+
|
| 37 |
+
Hackathon-only slides not carried over:
|
| 38 |
+
- HK slide 05 Β· Fine-Tuning on AMD MI300X β the fine-tune cards are folded
|
| 39 |
+
into slide 08 as a secondary detail block; they are not the lead for this
|
| 40 |
+
audience.
|
| 41 |
+
|
| 42 |
+
## Open placeholders for Adam to fill in
|
| 43 |
+
|
| 44 |
+
1. **`[ IBM STSM placeholder ]`** on slide 00 (cover) β the name of the IBM
|
| 45 |
+
STSM who invited Adam to speak. Replace the literal string with the person's
|
| 46 |
+
name and title before presenting.
|
| 47 |
+
|
| 48 |
+
2. **`[ screenshot of riprap.nyc landing β to be added ]`** on slide 02 β the
|
| 49 |
+
dashed placeholder box. Replace with an actual screenshot of the running
|
| 50 |
+
system at full resolution before presenting. The box is 260 px tall; a
|
| 51 |
+
1280Γ520 screenshot at 2Γ will fill it cleanly.
|
| 52 |
+
|
| 53 |
+
3. **Slide 04 stat cards** β the three stat values (13 s, 4/4, 8+) are from
|
| 54 |
+
the hackathon probe runs on AMD MI300X. If the demo environment changes
|
| 55 |
+
(e.g., HF Space cpu-basic instead of MI300X), update the wall-clock and
|
| 56 |
+
note the hardware in the stat label.
|
slides/asce/Makefile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
DECK := deck.md
|
| 2 |
+
THEME := riprap.css
|
| 3 |
+
MARP := npx --yes @marp-team/marp-cli@latest $(DECK) --theme $(THEME) --allow-local-files --html
|
| 4 |
+
|
| 5 |
+
.PHONY: all pdf html pptx clean
|
| 6 |
+
|
| 7 |
+
all: pdf html pptx
|
| 8 |
+
|
| 9 |
+
pdf:
|
| 10 |
+
$(MARP) --pdf --output deck.pdf
|
| 11 |
+
|
| 12 |
+
html:
|
| 13 |
+
$(MARP) --output deck.html
|
| 14 |
+
|
| 15 |
+
pptx:
|
| 16 |
+
$(MARP) --pptx --output deck.pptx
|
| 17 |
+
|
| 18 |
+
clean:
|
| 19 |
+
rm -f deck.pdf deck.html deck.pptx
|
slides/asce/deck.html
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
slides/asce/deck.md
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
marp: true
|
| 3 |
+
theme: riprap
|
| 4 |
+
paginate: true
|
| 5 |
+
size: 16:9
|
| 6 |
+
title: Riprap. Citation-grounded flood-exposure briefings for any place in New York City.
|
| 7 |
+
description: ASCE NY State Convention, Albany, May 13, 2026
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
<!-- _class: lead -->
|
| 11 |
+
<!-- _paginate: false -->
|
| 12 |
+
|
| 13 |
+
<img class="lead-mark" src="logo.svg" alt="Riprap dam mark" />
|
| 14 |
+
|
| 15 |
+
<div class="eyebrow" style="padding-top: 132px;">
|
| 16 |
+
ASCE NY State Convention · Albany, NY · May 13, 2026
|
| 17 |
+
</div>
|
| 18 |
+
|
| 19 |
+
# Riprap
|
| 20 |
+
|
| 21 |
+
## Citation-grounded flood-exposure briefings for any place in New York City.
|
| 22 |
+
|
| 23 |
+
<div class="meta" style="grid-template-columns: auto 1px auto; margin-top: 28px;">
|
| 24 |
+
<div>
|
| 25 |
+
<div class="meta-label">Speaker</div>
|
| 26 |
+
<div class="meta-value">Adam Munawar Rahman · IBM · MS CE, NYU</div>
|
| 27 |
+
</div>
|
| 28 |
+
<div class="meta-divider"></div>
|
| 29 |
+
<div>
|
| 30 |
+
<div class="meta-label">Invited by</div>
|
| 31 |
+
<div class="meta-value">Andrew Hicks</div>
|
| 32 |
+
</div>
|
| 33 |
+
</div>
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
<div class="eyebrow">00 · Learning objectives</div>
|
| 38 |
+
|
| 39 |
+
# What you will take away.
|
| 40 |
+
|
| 41 |
+
<p style="font-size: 18px; color: var(--ink-3); margin-bottom: 16px; max-width: none;">After this session, you will be able to:</p>
|
| 42 |
+
|
| 43 |
+
<ol style="margin-top: 0;">
|
| 44 |
+
<li>Describe a <strong>citation-grounded architecture</strong> for synthesizing multi-source flood evidence into auditable, site-specific narratives.</li>
|
| 45 |
+
<li>Identify where this approach is <strong>appropriate</strong> (screening, grant evidence, capital planning) and where it is <strong>not</strong> (hydraulic modeling, stamped deliverables).</li>
|
| 46 |
+
<li>Evaluate the <strong>guarantees and limitations</strong> of LLM-based evidence synthesis in civil engineering practice.</li>
|
| 47 |
+
<li>Apply the Five-Stone architecture to <strong>riverine, ice-jam, and dam-failure flooding</strong>.</li>
|
| 48 |
+
</ol>
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
+
<div class="eyebrow">01 · The problem</div>
|
| 54 |
+
|
| 55 |
+
# When you assess flood exposure, the evidence sits in eight or more places.
|
| 56 |
+
|
| 57 |
+
<p style="font-size: 20px; color: var(--ink-2); max-width: 72ch; margin-bottom: 14px;">For a capital project, a grant application, a vulnerability assessment, or a property disclosure β the relevant evidence sits across eight or more disconnected primary sources. Synthesizing them into a citable narrative takes hours of GIS work per site.</p>
|
| 58 |
+
|
| 59 |
+
<div class="box-grid cols-4" style="margin-top: 0; gap: 10px;">
|
| 60 |
+
|
| 61 |
+
<div class="box tinted">
|
| 62 |
+
<div class="lbl" style="color: #005EA2;">Federal</div>
|
| 63 |
+
<div class="body" style="font-size: 15px;">FEMA NFHL<br>USGS 3DEP LiDAR<br>USGS HWMs (Ida, Sandy)<br>NOAA CO-OPS tide</div>
|
| 64 |
+
</div>
|
| 65 |
+
|
| 66 |
+
<div class="box tinted">
|
| 67 |
+
<div class="lbl" style="color: #1A4480;">State</div>
|
| 68 |
+
<div class="body" style="font-size: 15px;">NPCC4 SLR projections<br>NYS Mesonet<br>NWS METAR / watches<br>NY EJNYC FVI</div>
|
| 69 |
+
</div>
|
| 70 |
+
|
| 71 |
+
<div class="box tinted">
|
| 72 |
+
<div class="lbl" style="color: #0E7490;">City</div>
|
| 73 |
+
<div class="body" style="font-size: 15px;">NYC DEP stormwater scenarios<br>NYC 311 flood complaints<br>FloodNet sensor network<br>NYC DOB filings</div>
|
| 74 |
+
</div>
|
| 75 |
+
|
| 76 |
+
<div class="box dark">
|
| 77 |
+
<div class="lbl">The gap</div>
|
| 78 |
+
<div class="body" style="font-size: 15px;">No common schema. Different vintages. Different spatial resolutions. Different epistemic tiers.<br><br><strong>Each site synthesized by hand.</strong></div>
|
| 79 |
+
</div>
|
| 80 |
+
|
| 81 |
+
</div>
|
| 82 |
+
|
| 83 |
+
<p style="margin-top: 12px; font-size: 20px;">When a number meets resistance, <strong>the only defense is the audit trail.</strong></p>
|
| 84 |
+
|
| 85 |
+
---
|
| 86 |
+
|
| 87 |
+
<div class="eyebrow">02 · Solution</div>
|
| 88 |
+
|
| 89 |
+
# A flood-exposure briefing for any place in New York City.
|
| 90 |
+
|
| 91 |
+
<p style="margin-bottom: 14px; font-size: 20px; max-width: 72ch; color: var(--ink-2);">Type an address or neighborhood. Get a written briefing in 5–13 seconds, fusing four temporal modes (historical inundation, current observations, modeled scenarios, projections) into one cited paragraph.</p>
|
| 92 |
+
|
| 93 |
+
<div style="border: 2px dashed #94A3B8; background: #E8ECF2; display: flex; align-items: center; justify-content: center; height: 260px; border-radius: 2px; margin-bottom: 10px;">
|
| 94 |
+
<p style="font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); text-align: center; margin: 0; padding: 24px;">
|
| 95 |
+
[ live system screenshot, to be added ]
|
| 96 |
+
</p>
|
| 97 |
+
</div>
|
| 98 |
+
|
| 99 |
+
<p style="font-size: 15px; color: var(--ink-3); margin: 0;">Behind the prose: every numeric claim links to its primary public-record source. Mellea rejection sampling refuses to publish what it can’t cite.</p>
|
| 100 |
+
|
| 101 |
+
---
|
| 102 |
+
|
| 103 |
+
<div class="eyebrow">03 · Architecture</div>
|
| 104 |
+
|
| 105 |
+
# Five Stones. Each with one job.
|
| 106 |
+
|
| 107 |
+
<p style="margin: 4px 0 10px; font-size: 17px; color: var(--ink-3); font-family: var(--font-mono);">query → <strong style="color: var(--ink);">Planner</strong> (Granite 4.1 3B, intent classification) → Stone roster → <strong style="color: var(--ink);">Capstone</strong> (Granite 4.1 8B + Mellea) → briefing</p>
|
| 108 |
+
|
| 109 |
+
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-top: 0;">
|
| 110 |
+
|
| 111 |
+
<div style="background: var(--paper-deep); border: 1px solid var(--rule-soft); border-top: 3px solid #475569; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;">
|
| 112 |
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
| 113 |
+
<span style="font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #475569;">Cornerstone Β· USGS 3DEP</span>
|
| 114 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3);">2020</span>
|
| 115 |
+
</div>
|
| 116 |
+
<div style="font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.2; margin-bottom: 4px;">Microtopography (HAND / TWI)</div>
|
| 117 |
+
<div style="display: grid; grid-template-columns: auto 1fr; gap: 2px 8px;">
|
| 118 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em;">HAND</span><span style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #475569;">0.82 m</span>
|
| 119 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em;">TWI</span><span style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #475569;">14.3</span>
|
| 120 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em;">Elev.</span><span style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #475569;">2.1 m MSL</span>
|
| 121 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em;">Pct. lower</span><span style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #475569;">78%</span>
|
| 122 |
+
</div>
|
| 123 |
+
<div style="margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: #475569; font-weight: 600;">[topo]</div>
|
| 124 |
+
</div>
|
| 125 |
+
|
| 126 |
+
<div style="background: var(--paper-deep); border: 1px solid var(--rule-soft); border-top: 3px solid #1A4480; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;">
|
| 127 |
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
| 128 |
+
<span style="font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #1A4480;">Keystone Β· TerraMind-NYC</span>
|
| 129 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3);">2024</span>
|
| 130 |
+
</div>
|
| 131 |
+
<div style="font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.2; margin-bottom: 4px;">Building footprint coverage</div>
|
| 132 |
+
<div style="margin: 6px 0;">
|
| 133 |
+
<div style="font-family: var(--font-mono); font-size: 30px; font-weight: 700; color: #1A4480; line-height: 1;">48.41<span style="font-size: 16px;">%</span></div>
|
| 134 |
+
<div style="font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); margin-top: 3px;">250 m radius · Buildings LoRA adapter</div>
|
| 135 |
+
</div>
|
| 136 |
+
<div style="margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: #1A4480; font-weight: 600;">[keystone_bldg]</div>
|
| 137 |
+
</div>
|
| 138 |
+
|
| 139 |
+
<div style="background: var(--paper-deep); border: 1px solid var(--rule-soft); border-top: 3px solid #0E7490; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;">
|
| 140 |
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
| 141 |
+
<span style="font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #0E7490;">Touchstone Β· NYC 311</span>
|
| 142 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3);">live</span>
|
| 143 |
+
</div>
|
| 144 |
+
<div style="font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.2; margin-bottom: 4px;">Flood complaints Β· 200 m buffer</div>
|
| 145 |
+
<div style="margin: 4px 0;">
|
| 146 |
+
<svg viewBox="0 0 220 60" style="width:100%; display:block;">
|
| 147 |
+
<rect x="8" y="52" width="212" height="1" fill="#CBD5E1"/>
|
| 148 |
+
<rect x="12" y="35" width="28" height="17" fill="#0E7490" rx="1"/>
|
| 149 |
+
<rect x="54" y="18" width="28" height="34" fill="#0E7490" rx="1"/>
|
| 150 |
+
<rect x="96" y="10" width="28" height="42" fill="#0E7490" rx="1"/>
|
| 151 |
+
<rect x="138" y="10" width="28" height="42" fill="#0E7490" rx="1"/>
|
| 152 |
+
<rect x="180" y="27" width="28" height="25" fill="#0E7490" rx="1"/>
|
| 153 |
+
<text x="26" y="32" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">2</text>
|
| 154 |
+
<text x="68" y="15" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">4</text>
|
| 155 |
+
<text x="110" y="7" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">5</text>
|
| 156 |
+
<text x="152" y="7" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">5</text>
|
| 157 |
+
<text x="194" y="24" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">3</text>
|
| 158 |
+
<text x="26" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'19</text>
|
| 159 |
+
<text x="68" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'20</text>
|
| 160 |
+
<text x="110" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'21</text>
|
| 161 |
+
<text x="152" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'22</text>
|
| 162 |
+
<text x="194" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'23</text>
|
| 163 |
+
</svg>
|
| 164 |
+
<div style="font-family: var(--font-mono); font-size: 10px; color: var(--ink-3);">19 requests · 5-yr lookback</div>
|
| 165 |
+
</div>
|
| 166 |
+
<div style="margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: #0E7490; font-weight: 600;">[nyc311]</div>
|
| 167 |
+
</div>
|
| 168 |
+
|
| 169 |
+
<div style="background: var(--paper-deep); border: 1px solid var(--rule-soft); border-top: 3px solid #92400E; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;">
|
| 170 |
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
| 171 |
+
<span style="font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #92400E;">Lodestone Β· Granite TTM r2</span>
|
| 172 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3);">live</span>
|
| 173 |
+
</div>
|
| 174 |
+
<div style="font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.2; margin-bottom: 4px;">Surge residual nowcast</div>
|
| 175 |
+
<div style="margin: 4px 0;">
|
| 176 |
+
<svg viewBox="0 0 220 60" style="width:100%; display:block;">
|
| 177 |
+
<path d="M10,40 35,30 60,19 85,16 110,21 135,27 160,34 185,40 210,45 L210,52 L10,52 Z" fill="#92400E" opacity="0.12"/>
|
| 178 |
+
<rect x="8" y="52" width="212" height="1" fill="#CBD5E1"/>
|
| 179 |
+
<line x1="60" y1="19" x2="60" y2="52" stroke="#92400E" stroke-width="1" stroke-dasharray="3,2" opacity="0.6"/>
|
| 180 |
+
<polyline points="10,40 35,30 60,19 85,16 110,21 135,27 160,34 185,40 210,45" fill="none" stroke="#92400E" stroke-width="2" stroke-linejoin="round"/>
|
| 181 |
+
<circle cx="60" cy="19" r="3" fill="#92400E"/>
|
| 182 |
+
<text x="65" y="17" font-family="IBM Plex Mono,monospace" font-size="8" fill="#92400E" font-weight="700">0.22 ft</text>
|
| 183 |
+
<text x="10" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">0h</text>
|
| 184 |
+
<text x="60" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#92400E">NOW</text>
|
| 185 |
+
<text x="110" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">4.8h</text>
|
| 186 |
+
<text x="210" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">9.6h</text>
|
| 187 |
+
</svg>
|
| 188 |
+
<div style="font-family: var(--font-mono); font-size: 10px; color: var(--ink-3);">peak surge residual · 9.6 h horizon</div>
|
| 189 |
+
</div>
|
| 190 |
+
<div style="margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: #92400E; font-weight: 600;">[ttm_surge]</div>
|
| 191 |
+
</div>
|
| 192 |
+
|
| 193 |
+
</div>
|
| 194 |
+
|
| 195 |
+
<p style="margin-top: 8px; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);">Real evidence cards rendered by the live system · 442 East Houston Street, Manhattan.</p>
|
| 196 |
+
|
| 197 |
+
<div class="box" style="border-top: 3px solid #162E51; margin-top: 10px; padding: 10px 18px;">
|
| 198 |
+
<span style="font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: #162E51;">Capstone</span>
|
| 199 |
+
<span style="font-size: 15px; color: var(--ink-2); margin-left: 14px;">Granite 4.1 8B + Mellea rejection sampling · <code>numerics_grounded</code> · <code>no_placeholder_tokens</code> · <code>citations_dense</code> · <code>citations_resolve</code> · reroll until every claim cites its source → <strong>cited 4-section briefing</strong></span>
|
| 200 |
+
</div>
|
| 201 |
+
|
| 202 |
+
---
|
| 203 |
+
|
| 204 |
+
<div class="eyebrow">04 · Demo</div>
|
| 205 |
+
|
| 206 |
+
# Live demo.
|
| 207 |
+
|
| 208 |
+
<div style="margin: 40px 0 18px; text-align: center;">
|
| 209 |
+
<p style="font-family: var(--font-mono); font-size: 28px; font-weight: 700; color: var(--ink); margin: 0 auto; max-width: 860px; line-height: 1.35;">“Hollis, Queens”</p>
|
| 210 |
+
</div>
|
| 211 |
+
|
| 212 |
+
<p style="text-align: center; font-style: italic; font-size: 16px; color: var(--ink-3); margin: 0 auto 28px; max-width: 72ch;">A neighborhood-scale briefing. NYC DEP and OEM planners use this shape of query when scoping where the next $30B stormwater priority site should land.</p>
|
| 213 |
+
|
| 214 |
+
<div class="box-grid cols-3" style="margin-top: 0;">
|
| 215 |
+
<div class="box" style="text-align: center; padding: 14px 18px;">
|
| 216 |
+
<div class="stat-value" style="font-size: 40px;">5.8 s</div>
|
| 217 |
+
<div class="stat-label">end-to-end</div>
|
| 218 |
+
</div>
|
| 219 |
+
<div class="box" style="text-align: center; padding: 14px 18px;">
|
| 220 |
+
<div class="stat-value" style="font-size: 40px;">4 / 4</div>
|
| 221 |
+
<div class="stat-label">grounding checks every run</div>
|
| 222 |
+
</div>
|
| 223 |
+
<div class="box tinted" style="text-align: center; padding: 14px 18px;">
|
| 224 |
+
<div class="stat-value" style="font-size: 40px;">8+</div>
|
| 225 |
+
<div class="stat-label">primary public-record sources</div>
|
| 226 |
+
</div>
|
| 227 |
+
</div>
|
| 228 |
+
|
| 229 |
+
---
|
| 230 |
+
|
| 231 |
+
<div class="eyebrow">05 · Civic applications</div>
|
| 232 |
+
|
| 233 |
+
# The civic case for civil engineers.
|
| 234 |
+
|
| 235 |
+
<div class="box-grid cols-2" style="margin-top: 8px;">
|
| 236 |
+
|
| 237 |
+
<div class="box">
|
| 238 |
+
<div class="lbl" style="color: #005EA2;">Grant evidence</div>
|
| 239 |
+
<div class="body">HUD CDBG-DR and FEMA BRIC vulnerability assessments. Riprap auto-generates the per-NTA evidence section for each site in a program area. Citable, reproducible, open-source.</div>
|
| 240 |
+
</div>
|
| 241 |
+
|
| 242 |
+
<div class="box">
|
| 243 |
+
<div class="lbl" style="color: #1A4480;">Capital project screening</div>
|
| 244 |
+
<div class="body">NYC DEP Bluebelt expansion, NYCHA resilience hardening, MTA station prioritization, DOE school siting. Site-by-site evidence packages at the screening tier, before the hydraulic modeling budget is spent.</div>
|
| 245 |
+
</div>
|
| 246 |
+
|
| 247 |
+
<div class="box">
|
| 248 |
+
<div class="lbl" style="color: #0E7490;">NY State Infrastructure Report Card</div>
|
| 249 |
+
<div class="body">The 2026 report is in preparation. Riprap is the per-place evidence layer for the flood-exposure chapter of any future NY State infrastructure report — reproducible at every address.</div>
|
| 250 |
+
</div>
|
| 251 |
+
|
| 252 |
+
<div class="box">
|
| 253 |
+
<div class="lbl" style="color: #92400E;">Property disclosure compliance</div>
|
| 254 |
+
<div class="body">NY’s March 2024 Property Condition Disclosure flood-risk amendment requires sellers to disclose flood history. Riprap is the citable narrative behind the disclosure — every claim sourced.</div>
|
| 255 |
+
</div>
|
| 256 |
+
|
| 257 |
+
</div>
|
| 258 |
+
|
| 259 |
+
---
|
| 260 |
+
|
| 261 |
+
<div class="eyebrow">06 · What Riprap is not.</div>
|
| 262 |
+
|
| 263 |
+
# What Riprap is not.
|
| 264 |
+
|
| 265 |
+
<p style="font-size: 18px; color: var(--ink-3); margin-bottom: 14px; max-width: none;">The civil engineer carries the stamp. Riprap surfaces the evidence the engineer judges.</p>
|
| 266 |
+
|
| 267 |
+
<div class="box-grid cols-2" style="margin-top: 0; gap: 12px;">
|
| 268 |
+
|
| 269 |
+
<div class="box" style="border-top: 3px solid var(--rule-soft);">
|
| 270 |
+
<div class="lbl">Not a hydraulic model</div>
|
| 271 |
+
<div class="body" style="font-size: 17px;">Riprap does not replace HEC-RAS, SWMM, or ICM. It synthesizes evidence from completed modeling work; it does not produce new flow or stage estimates. No substitute for a calibrated hydraulic model.</div>
|
| 272 |
+
</div>
|
| 273 |
+
|
| 274 |
+
<div class="box" style="border-top: 3px solid var(--rule-soft);">
|
| 275 |
+
<div class="lbl">Not a stamped deliverable</div>
|
| 276 |
+
<div class="body" style="font-size: 17px;">The briefing is a starting point for a memo, not the memo itself. Professional judgment, field reconnaissance, and the engineer’s stamp are required for any actionable deliverable.</div>
|
| 277 |
+
</div>
|
| 278 |
+
|
| 279 |
+
<div class="box" style="border-top: 3px solid var(--rule-soft);">
|
| 280 |
+
<div class="lbl">Not a substitute for site investigation</div>
|
| 281 |
+
<div class="body" style="font-size: 17px;">Microtopography is from 1 m USGS 3DEP LiDAR, appropriate for screening, not for design. Field reconnaissance, soil borings, and survey are not replaced.</div>
|
| 282 |
+
</div>
|
| 283 |
+
|
| 284 |
+
<div class="box" style="border-top: 3px solid var(--rule-soft);">
|
| 285 |
+
<div class="lbl">Not a risk score</div>
|
| 286 |
+
<div class="body" style="font-size: 17px;">Riprap does not output a 1–10 or 1–100 number. Score-based tools (First Street, ClimateCheck, Jupiter) are different products for different audiences. Riprap is the evidence audit trail behind any such judgment.</div>
|
| 287 |
+
</div>
|
| 288 |
+
|
| 289 |
+
</div>
|
| 290 |
+
|
| 291 |
+
---
|
| 292 |
+
|
| 293 |
+
<div class="eyebrow">07 · Directions</div>
|
| 294 |
+
|
| 295 |
+
# Where this goes from here.
|
| 296 |
+
|
| 297 |
+
<p style="margin-bottom: 14px; font-size: 18px; color: var(--ink-3); font-family: var(--font-mono); letter-spacing: 0.02em;">The architecture is data-choice-specific, not code-specific.</p>
|
| 298 |
+
|
| 299 |
+
<div class="box-grid cols-2" style="margin-top: 0;">
|
| 300 |
+
|
| 301 |
+
<div class="box">
|
| 302 |
+
<div class="lbl" style="color: #005EA2;">Upstate NY flooding</div>
|
| 303 |
+
<div class="body">The same five-Stone pattern for riverine, ice-jam, and dam-failure flooding. Different primary sources, same architecture.</div>
|
| 304 |
+
</div>
|
| 305 |
+
|
| 306 |
+
<div class="box">
|
| 307 |
+
<div class="lbl" style="color: #475569;">Historical-event mode</div>
|
| 308 |
+
<div class="body">Re-run the system against snapshot data from any past date. Calibration as a core feature.</div>
|
| 309 |
+
</div>
|
| 310 |
+
|
| 311 |
+
<div class="box">
|
| 312 |
+
<div class="lbl" style="color: #1A4480;">Stones as standalone packages</div>
|
| 313 |
+
<div class="body">Each Stone runs alone. Pull one without the full Riprap stack.</div>
|
| 314 |
+
</div>
|
| 315 |
+
|
| 316 |
+
<div class="box tinted">
|
| 317 |
+
<div class="lbl" style="color: #0E7490;">Cross-domain</div>
|
| 318 |
+
<div class="body">The same pattern for transit, water, energy, and structural-condition reporting. Flood is the first domain.</div>
|
| 319 |
+
</div>
|
| 320 |
+
|
| 321 |
+
</div>
|
| 322 |
+
|
| 323 |
+
---
|
| 324 |
+
|
| 325 |
+
<div class="eyebrow">08 · How it was built</div>
|
| 326 |
+
|
| 327 |
+
# The art of the possible.
|
| 328 |
+
|
| 329 |
+
<div class="box-grid cols-2" style="margin-top: 8px; gap: 20px;">
|
| 330 |
+
|
| 331 |
+
<div>
|
| 332 |
+
<p style="font-size: 20px; color: var(--ink-2); max-width: none; margin-bottom: 16px;">Three days of AI-assisted development, on top of months of design thinking. Four foundation models. Three Apache-2.0 NYC fine-tunes trained on AMD MI300X for the AMD × lablab.ai Developer Hackathon (May 4–10, 2026).</p>
|
| 333 |
+
<p style="font-size: 20px; color: var(--ink-2); max-width: none;">Apache-2.0 end-to-end on public-record federal, state, and city data. No commercial APIs contacted at runtime.</p>
|
| 334 |
+
<p style="font-size: 20px; color: var(--ink); max-width: none; margin-top: 12px;"><strong>Built in three days. Designed over months. The tools have shifted what one engineer can ship.</strong></p>
|
| 335 |
+
</div>
|
| 336 |
+
|
| 337 |
+
<div>
|
| 338 |
+
<div class="box tinted" style="margin-bottom: 10px;">
|
| 339 |
+
<div class="lbl">Foundation models</div>
|
| 340 |
+
<div class="body" style="font-size: 15px;">IBM Granite 4.1 8B (synthesizer) · IBM Granite Embedding 278M (RAG) · GLiNER (typed extraction) · vLLM on AMD MI300X</div>
|
| 341 |
+
</div>
|
| 342 |
+
<div class="box tinted" style="margin-bottom: 10px;">
|
| 343 |
+
<div class="lbl">NYC fine-tunes (Apache-2.0, HF Hub)</div>
|
| 344 |
+
<div class="body" style="font-size: 15px;">Prithvi-EO-2.0-NYC-Pluvial (flood detection, IoU 0.598) · TerraMind-NYC-Adapters (LULC + Buildings) · Granite-TTM-r2-Battery-Surge (surge nowcast, RMSE 0.157 m)</div>
|
| 345 |
+
</div>
|
| 346 |
+
<div class="box tinted">
|
| 347 |
+
<div class="lbl">Agentic framework</div>
|
| 348 |
+
<div class="body" style="font-size: 15px;">Burr FSM · Mellea rejection sampling · LiteLLM Router (vLLM / Ollama failover) · FastAPI SSE stream</div>
|
| 349 |
+
</div>
|
| 350 |
+
</div>
|
| 351 |
+
|
| 352 |
+
</div>
|
| 353 |
+
|
| 354 |
+
---
|
| 355 |
+
|
| 356 |
+
<div class="eyebrow">09 · Discussion</div>
|
| 357 |
+
|
| 358 |
+
# What I want from this room.
|
| 359 |
+
|
| 360 |
+
<div class="box" style="border-left: 3px solid var(--accent); padding: 18px 24px; margin-bottom: 18px; background: var(--paper-deep);">
|
| 361 |
+
<div class="body" style="font-size: 19px; line-height: 1.5; max-width: none;">I am a software engineer, not a civil engineer. The system I just showed you is opinionated about what counts as evidence: citation-grounded, silent when uncertain, public-record only. But I am less sure about where it falls short of how a stamped engineering deliverable would need to behave.</div>
|
| 362 |
+
</div>
|
| 363 |
+
|
| 364 |
+
<p style="font-size: 19px; font-weight: 600; color: var(--ink); margin-bottom: 10px;">Three questions for the room:</p>
|
| 365 |
+
|
| 366 |
+
<ol>
|
| 367 |
+
<li>Where in your practice would a tool like this be <strong>useful</strong>, and where would it be a <strong>liability</strong>?</li>
|
| 368 |
+
<li>What <strong>evidence sources</strong> are you using that Riprap does not yet know about?</li>
|
| 369 |
+
<li>What would have to be true for a citation-grounded narrative tool to be <strong>trusted as a screening-tier deliverable</strong>?</li>
|
| 370 |
+
</ol>
|
| 371 |
+
|
| 372 |
+
<hr style="margin: 16px 0;" />
|
| 373 |
+
|
| 374 |
+
<p style="font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); margin: 0;">Open-source · Apache-2.0 · github.com/msradam/riprap-nyc</p>
|
| 375 |
+
|
| 376 |
+
---
|
| 377 |
+
|
| 378 |
+
<!-- _class: cta -->
|
| 379 |
+
|
| 380 |
+
<img class="cta-mark" src="logo-paper.svg" alt="Riprap dam mark" />
|
| 381 |
+
|
| 382 |
+
<div class="eyebrow" style="margin-top: 124px; color: var(--accent); border: 0; padding: 0;">Riprap · citation-grounded flood briefings</div>
|
| 383 |
+
|
| 384 |
+
<h1 style="white-space: nowrap; font-size: 72px;">github.com/msradam/riprap-nyc</h1>
|
| 385 |
+
|
| 386 |
+
<hr>
|
| 387 |
+
|
| 388 |
+
<p style="font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase;">
|
| 389 |
+
Apache-2.0 · public data only · IBM Granite 4.1 · AMD MI300X · Mellea grounding
|
| 390 |
+
</p>
|
| 391 |
+
|
| 392 |
+
<p style="font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(244,246,249,0.55); margin-top: 16px;">
|
| 393 |
+
ASCE NY State Convention · Albany, NY · May 13, 2026
|
| 394 |
+
</p>
|
| 395 |
+
|
| 396 |
+
<p style="font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; color: rgba(244,246,249,0.4); margin-top: 24px; text-transform: none;">
|
| 397 |
+
Dam mark: “Dam” by Chintuza via the Noun Project, CC-BY 3.0.
|
| 398 |
+
</p>
|
| 399 |
+
|
| 400 |
+
---
|
| 401 |
+
|
| 402 |
+
<!-- _paginate: false -->
|
| 403 |
+
|
| 404 |
+
<div class="eyebrow">Appendix A · The receipts</div>
|
| 405 |
+
|
| 406 |
+
# 5 of 5 NYC addresses. Every claim verified, every run.
|
| 407 |
+
|
| 408 |
+
<table>
|
| 409 |
+
<thead>
|
| 410 |
+
<tr><th>address</th><th>intent</th><th>wall</th><th>steps</th><th>verified</th></tr>
|
| 411 |
+
</thead>
|
| 412 |
+
<tbody>
|
| 413 |
+
<tr><td>442 E Houston St · LES</td><td>address</td><td>7.6 s</td><td>19</td><td>4/4</td></tr>
|
| 414 |
+
<tr><td>80 Pioneer St · Red Hook</td><td>address</td><td>13.1 s</td><td>19</td><td>4/4</td></tr>
|
| 415 |
+
<tr><td>100 Gold St · Manhattan</td><td>address</td><td>11.2 s</td><td>19</td><td>4/4</td></tr>
|
| 416 |
+
<tr><td>Hollis · Queens</td><td>neighborhood</td><td>5.8 s</td><td>9</td><td>4/4</td></tr>
|
| 417 |
+
<tr><td>Coney Island · Brooklyn</td><td>neighborhood</td><td>9.9 s</td><td>9</td><td>4/4</td></tr>
|
| 418 |
+
</tbody>
|
| 419 |
+
</table>
|
| 420 |
+
|
| 421 |
+
<div class="box-grid cols-3" style="margin-top: 16px;">
|
| 422 |
+
<div class="box">
|
| 423 |
+
<div class="lbl">Wall-clock</div>
|
| 424 |
+
<div class="stat-value">5.8–13.1<span style="font-size: 22px; color: var(--ink-3); font-weight: 400; letter-spacing: 0;"> s</span></div>
|
| 425 |
+
<div class="stat-label">vLLM on AMD MI300X</div>
|
| 426 |
+
</div>
|
| 427 |
+
<div class="box">
|
| 428 |
+
<div class="lbl">Evidence layers</div>
|
| 429 |
+
<div class="stat-value">5</div>
|
| 430 |
+
<div class="stat-label">Stones per briefing</div>
|
| 431 |
+
</div>
|
| 432 |
+
<div class="box">
|
| 433 |
+
<div class="lbl">Grounding</div>
|
| 434 |
+
<div class="stat-value">4 / 4</div>
|
| 435 |
+
<div class="stat-label">source checks every run</div>
|
| 436 |
+
</div>
|
| 437 |
+
</div>
|
| 438 |
+
|
| 439 |
+
---
|
| 440 |
+
|
| 441 |
+
<!-- _paginate: false -->
|
| 442 |
+
|
| 443 |
+
<div class="eyebrow">Appendix B · Primary sources</div>
|
| 444 |
+
|
| 445 |
+
# Sources. Every claim traces to one of these.
|
| 446 |
+
|
| 447 |
+
<div class="box-grid cols-3" style="margin-top: 8px; gap: 10px;">
|
| 448 |
+
|
| 449 |
+
<div class="box tinted">
|
| 450 |
+
<div class="lbl" style="color: #005EA2;">Federal</div>
|
| 451 |
+
<div class="body" style="font-size: 14px; line-height: 1.6;">
|
| 452 |
+
FEMA NFHL (current)<br>
|
| 453 |
+
USGS 3DEP 1 m LiDAR (2020)<br>
|
| 454 |
+
USGS HWMs — Sandy 2012, Ida 2021<br>
|
| 455 |
+
NOAA CO-OPS tide gauge, Battery (live)<br>
|
| 456 |
+
NWS METAR / flood watches (live)
|
| 457 |
+
</div>
|
| 458 |
+
</div>
|
| 459 |
+
|
| 460 |
+
<div class="box tinted">
|
| 461 |
+
<div class="lbl" style="color: #1A4480;">State / regional</div>
|
| 462 |
+
<div class="body" style="font-size: 14px; line-height: 1.6;">
|
| 463 |
+
NPCC4 SLR projections (2023)<br>
|
| 464 |
+
NY EJNYC Flood Vulnerability Index (2024)<br>
|
| 465 |
+
NYS Mesonet (live)<br>
|
| 466 |
+
NY Property Condition Disclosure (Mar 2024)
|
| 467 |
+
</div>
|
| 468 |
+
</div>
|
| 469 |
+
|
| 470 |
+
<div class="box tinted">
|
| 471 |
+
<div class="lbl" style="color: #0E7490;">City</div>
|
| 472 |
+
<div class="body" style="font-size: 14px; line-height: 1.6;">
|
| 473 |
+
NYC DEP stormwater scenarios (2024)<br>
|
| 474 |
+
NYC 311 flood complaints (live, 5-yr)<br>
|
| 475 |
+
FloodNet sensor network (live)<br>
|
| 476 |
+
NYC DOB filings (live)<br>
|
| 477 |
+
NYC Open Data — NYCHA, DOE, MTA, hospitals
|
| 478 |
+
</div>
|
| 479 |
+
</div>
|
| 480 |
+
|
| 481 |
+
</div>
|
| 482 |
+
|
| 483 |
+
<p style="margin-top: 14px; font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);">All datasets are public-record. No commercial data APIs. No proprietary hazard scores.</p>
|
slides/asce/deck.pdf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c7100968532450fc9767d6a2dfe6c3078d065c76df86ff5c65f5cc3f62b97dc8
|
| 3 |
+
size 319753
|
slides/asce/deck.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a7b884fe41b72b29ac2631863e2fa495b73d64492be605fd3cf3b8cbe3e8a64b
|
| 3 |
+
size 2496810
|
slides/asce/logo-paper.svg
ADDED
|
|
slides/asce/logo.svg
ADDED
|
|
slides/asce/riprap.css
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* @theme riprap
|
| 2 |
+
*
|
| 3 |
+
* Marp theme that mirrors the SvelteKit UI's design tokens 1:1.
|
| 4 |
+
* Civic Hydrology palette (v0.4.6, 2026-05-06): USWDS federal blue,
|
| 5 |
+
* cool slate register, deep navy synthesis. Replaces the warm-paper +
|
| 6 |
+
* burnt-orange register that read as editorial / Anthropic-adjacent.
|
| 7 |
+
* IBM Plex Sans drives display + body; serif retained only for the
|
| 8 |
+
* single hero quote-mark on slide 7. Layouts are box/grid framed β
|
| 9 |
+
* the deck reads like a dashboard, not an essay.
|
| 10 |
+
*/
|
| 11 |
+
|
| 12 |
+
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@300;400;500;600;700&family=IBM+Plex+Serif:wght@400;600&display=swap');
|
| 13 |
+
|
| 14 |
+
:root {
|
| 15 |
+
/* USWDS-aligned tier palette. */
|
| 16 |
+
--tier-empirical: #005EA2;
|
| 17 |
+
--tier-modeled: #1A4480;
|
| 18 |
+
--tier-proxy: #475569;
|
| 19 |
+
--tier-synthetic: #1A4480;
|
| 20 |
+
|
| 21 |
+
/* Stones (water-themed). */
|
| 22 |
+
--stone-cornerstone: #475569; /* slate (hazard ground) */
|
| 23 |
+
--stone-keystone: #1A4480; /* federal navy (assets) */
|
| 24 |
+
--stone-touchstone: #0E7490; /* cyan (live water) */
|
| 25 |
+
--stone-lodestone: #92400E; /* amber (forecast / hazard) */
|
| 26 |
+
--stone-capstone: #162E51; /* deepest navy (synthesis) */
|
| 27 |
+
|
| 28 |
+
/* Cool register. */
|
| 29 |
+
--paper: #F4F6F9;
|
| 30 |
+
--paper-deep: #E8ECF2;
|
| 31 |
+
--paper-cool: #DCE3EC;
|
| 32 |
+
--ink: #0F172A;
|
| 33 |
+
--ink-2: #334155;
|
| 34 |
+
--ink-3: #64748B;
|
| 35 |
+
--rule: #0F172A;
|
| 36 |
+
--rule-soft: #CBD5E1;
|
| 37 |
+
|
| 38 |
+
/* Accent β federal blue is the action. Amber + red used only for
|
| 39 |
+
warning / alert pills. */
|
| 40 |
+
--accent: #005EA2;
|
| 41 |
+
--accent-text: #005EA2;
|
| 42 |
+
--accent-warn: #92400E;
|
| 43 |
+
--accent-alert: #B91C1C;
|
| 44 |
+
|
| 45 |
+
/* Inverted (dark slides). */
|
| 46 |
+
--paper-dark: #0F172A;
|
| 47 |
+
--paper-darker: #0A0F1F;
|
| 48 |
+
|
| 49 |
+
--font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
|
| 50 |
+
--font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
| 51 |
+
--font-serif: "IBM Plex Serif", Georgia, "Times New Roman", serif;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
/* ββ Section ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 55 |
+
|
| 56 |
+
section {
|
| 57 |
+
width: 1280px;
|
| 58 |
+
height: 720px;
|
| 59 |
+
padding: 48px 64px;
|
| 60 |
+
background: var(--paper);
|
| 61 |
+
color: var(--ink);
|
| 62 |
+
font-family: var(--font-sans);
|
| 63 |
+
font-size: 22px;
|
| 64 |
+
line-height: 1.45;
|
| 65 |
+
letter-spacing: 0;
|
| 66 |
+
position: relative;
|
| 67 |
+
display: flex;
|
| 68 |
+
flex-direction: column;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
/* Bottom-left wordmark on every slide except lead/cta. */
|
| 72 |
+
section::before {
|
| 73 |
+
content: "β riprap.nyc";
|
| 74 |
+
position: absolute;
|
| 75 |
+
left: 64px;
|
| 76 |
+
bottom: 28px;
|
| 77 |
+
font-family: var(--font-mono);
|
| 78 |
+
font-size: 12px;
|
| 79 |
+
font-weight: 600;
|
| 80 |
+
letter-spacing: 0.06em;
|
| 81 |
+
text-transform: lowercase;
|
| 82 |
+
color: var(--ink);
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/* Bottom-right slide counter. */
|
| 86 |
+
section::after {
|
| 87 |
+
content: attr(data-marpit-pagination) " / " attr(data-marpit-pagination-total);
|
| 88 |
+
position: absolute;
|
| 89 |
+
right: 64px;
|
| 90 |
+
bottom: 28px;
|
| 91 |
+
font-family: var(--font-mono);
|
| 92 |
+
font-size: 11px;
|
| 93 |
+
font-weight: 500;
|
| 94 |
+
letter-spacing: 0.1em;
|
| 95 |
+
color: var(--ink-3);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
/* ββ Headings β sans-led civic-tech hierarchy βββββββββββββββββββββββββββ */
|
| 99 |
+
|
| 100 |
+
h1 {
|
| 101 |
+
font-family: var(--font-sans);
|
| 102 |
+
font-weight: 700;
|
| 103 |
+
font-size: 56px;
|
| 104 |
+
line-height: 1.05;
|
| 105 |
+
letter-spacing: -0.025em;
|
| 106 |
+
margin: 0 0 16px;
|
| 107 |
+
color: var(--ink);
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
h2 {
|
| 111 |
+
font-family: var(--font-mono);
|
| 112 |
+
font-weight: 500;
|
| 113 |
+
font-size: 12px;
|
| 114 |
+
letter-spacing: 0.18em;
|
| 115 |
+
text-transform: uppercase;
|
| 116 |
+
color: var(--accent-text);
|
| 117 |
+
margin: 0 0 16px;
|
| 118 |
+
display: inline-block;
|
| 119 |
+
padding-bottom: 4px;
|
| 120 |
+
border-bottom: 2px solid var(--accent);
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
h3 {
|
| 124 |
+
font-family: var(--font-sans);
|
| 125 |
+
font-weight: 600;
|
| 126 |
+
font-size: 24px;
|
| 127 |
+
line-height: 1.25;
|
| 128 |
+
margin: 0 0 8px;
|
| 129 |
+
color: var(--ink);
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
/* ββ Body βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 133 |
+
|
| 134 |
+
p {
|
| 135 |
+
margin: 0 0 14px;
|
| 136 |
+
max-width: 60ch;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
strong {
|
| 140 |
+
font-weight: 600;
|
| 141 |
+
color: var(--ink);
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
em {
|
| 145 |
+
font-style: normal;
|
| 146 |
+
color: var(--accent-text);
|
| 147 |
+
font-weight: 600;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
ul, ol {
|
| 151 |
+
margin: 0 0 14px;
|
| 152 |
+
padding-left: 0;
|
| 153 |
+
list-style: none;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
ul li, ol li {
|
| 157 |
+
position: relative;
|
| 158 |
+
padding-left: 24px;
|
| 159 |
+
margin: 0 0 12px;
|
| 160 |
+
font-size: 20px;
|
| 161 |
+
line-height: 1.4;
|
| 162 |
+
max-width: 60ch;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
ul li::before {
|
| 166 |
+
content: "";
|
| 167 |
+
position: absolute;
|
| 168 |
+
left: 0;
|
| 169 |
+
top: 0.65em;
|
| 170 |
+
width: 12px;
|
| 171 |
+
height: 2px;
|
| 172 |
+
background: var(--accent);
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
ol { counter-reset: ol-num; }
|
| 176 |
+
ol li { counter-increment: ol-num; }
|
| 177 |
+
ol li::before {
|
| 178 |
+
content: counter(ol-num, decimal-leading-zero);
|
| 179 |
+
position: absolute;
|
| 180 |
+
left: 0;
|
| 181 |
+
top: 0;
|
| 182 |
+
font-family: var(--font-mono);
|
| 183 |
+
font-size: 12px;
|
| 184 |
+
font-weight: 600;
|
| 185 |
+
letter-spacing: 0.04em;
|
| 186 |
+
color: var(--accent-text);
|
| 187 |
+
width: auto;
|
| 188 |
+
height: auto;
|
| 189 |
+
background: transparent;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
/* ββ Code βββββββοΏ½οΏ½οΏ½βββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 193 |
+
|
| 194 |
+
code {
|
| 195 |
+
font-family: var(--font-mono);
|
| 196 |
+
font-size: 0.92em;
|
| 197 |
+
background: var(--paper-deep);
|
| 198 |
+
padding: 1px 6px;
|
| 199 |
+
border-radius: 2px;
|
| 200 |
+
color: var(--ink);
|
| 201 |
+
border: 1px solid var(--rule-soft);
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
pre {
|
| 205 |
+
font-family: var(--font-mono);
|
| 206 |
+
font-size: 14px;
|
| 207 |
+
line-height: 1.5;
|
| 208 |
+
background: var(--paper-deep);
|
| 209 |
+
border: 1px solid var(--rule-soft);
|
| 210 |
+
border-left: 3px solid var(--accent);
|
| 211 |
+
padding: 14px 18px;
|
| 212 |
+
margin: 8px 0;
|
| 213 |
+
color: var(--ink);
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
pre code { background: transparent; padding: 0; border: 0; }
|
| 217 |
+
|
| 218 |
+
/* ββ Quote ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 219 |
+
|
| 220 |
+
blockquote {
|
| 221 |
+
font-family: var(--font-sans);
|
| 222 |
+
font-style: normal;
|
| 223 |
+
font-size: 24px;
|
| 224 |
+
font-weight: 500;
|
| 225 |
+
line-height: 1.3;
|
| 226 |
+
color: var(--ink-2);
|
| 227 |
+
border-left: 3px solid var(--accent-warn);
|
| 228 |
+
padding: 4px 0 4px 18px;
|
| 229 |
+
margin: 16px 0;
|
| 230 |
+
max-width: 56ch;
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
/* ββ Rules ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 234 |
+
|
| 235 |
+
hr {
|
| 236 |
+
border: 0;
|
| 237 |
+
border-top: 1px solid var(--rule-soft);
|
| 238 |
+
margin: 16px 0;
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
/* ββ Tables βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 242 |
+
|
| 243 |
+
table {
|
| 244 |
+
border-collapse: collapse;
|
| 245 |
+
font-family: var(--font-sans);
|
| 246 |
+
font-size: 17px;
|
| 247 |
+
margin: 8px 0;
|
| 248 |
+
width: 100%;
|
| 249 |
+
border: 1px solid var(--rule-soft);
|
| 250 |
+
}
|
| 251 |
+
th {
|
| 252 |
+
text-align: left;
|
| 253 |
+
font-family: var(--font-mono);
|
| 254 |
+
font-size: 11px;
|
| 255 |
+
font-weight: 500;
|
| 256 |
+
letter-spacing: 0.1em;
|
| 257 |
+
text-transform: uppercase;
|
| 258 |
+
color: var(--ink-3);
|
| 259 |
+
padding: 10px 14px;
|
| 260 |
+
background: var(--paper-deep);
|
| 261 |
+
border-bottom: 1px solid var(--rule);
|
| 262 |
+
}
|
| 263 |
+
td {
|
| 264 |
+
padding: 12px 14px;
|
| 265 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 266 |
+
vertical-align: top;
|
| 267 |
+
}
|
| 268 |
+
tr:last-child td { border-bottom: 0; }
|
| 269 |
+
|
| 270 |
+
/* ββ Title slide β bold sans display, dashboard frame βββββββββββββββββββ */
|
| 271 |
+
|
| 272 |
+
section.lead {
|
| 273 |
+
display: flex;
|
| 274 |
+
flex-direction: column;
|
| 275 |
+
justify-content: center;
|
| 276 |
+
background: var(--paper);
|
| 277 |
+
padding-left: 88px;
|
| 278 |
+
padding-right: 88px;
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
section.lead::before {
|
| 282 |
+
/* Mark is now an inline <img> sitting at the top-left of the slide
|
| 283 |
+
content (see deck.md). Pseudo-element no longer renders the
|
| 284 |
+
β block β kept as a no-op so the bottom-left wordmark on
|
| 285 |
+
non-lead slides still wins via the base section::before. */
|
| 286 |
+
content: none;
|
| 287 |
+
}
|
| 288 |
+
section.lead .lead-mark {
|
| 289 |
+
position: absolute;
|
| 290 |
+
left: 88px;
|
| 291 |
+
top: 56px;
|
| 292 |
+
width: 64px;
|
| 293 |
+
height: 64px;
|
| 294 |
+
display: block;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
section.lead .eyebrow {
|
| 298 |
+
font-family: var(--font-mono);
|
| 299 |
+
font-size: 12px;
|
| 300 |
+
font-weight: 500;
|
| 301 |
+
letter-spacing: 0.16em;
|
| 302 |
+
text-transform: uppercase;
|
| 303 |
+
color: var(--accent-text);
|
| 304 |
+
margin-bottom: 24px;
|
| 305 |
+
margin-top: 0;
|
| 306 |
+
padding-top: 80px;
|
| 307 |
+
display: flex;
|
| 308 |
+
align-items: center;
|
| 309 |
+
gap: 12px;
|
| 310 |
+
}
|
| 311 |
+
section.lead .eyebrow::after {
|
| 312 |
+
content: "";
|
| 313 |
+
flex: 1;
|
| 314 |
+
height: 1px;
|
| 315 |
+
background: var(--rule-soft);
|
| 316 |
+
max-width: 280px;
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
section.lead h1 {
|
| 320 |
+
font-family: var(--font-sans);
|
| 321 |
+
font-weight: 700;
|
| 322 |
+
font-size: 104px;
|
| 323 |
+
line-height: 0.92;
|
| 324 |
+
letter-spacing: -0.035em;
|
| 325 |
+
margin: 0 0 16px;
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
section.lead h2 {
|
| 329 |
+
font-family: var(--font-sans);
|
| 330 |
+
font-weight: 400;
|
| 331 |
+
font-size: 26px;
|
| 332 |
+
letter-spacing: -0.005em;
|
| 333 |
+
text-transform: none;
|
| 334 |
+
color: var(--ink-2);
|
| 335 |
+
margin: 0 0 32px;
|
| 336 |
+
max-width: 30ch;
|
| 337 |
+
border: 0;
|
| 338 |
+
padding: 0;
|
| 339 |
+
display: block;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
section.lead .meta {
|
| 343 |
+
margin-top: 24px;
|
| 344 |
+
display: grid;
|
| 345 |
+
grid-template-columns: auto 1px auto auto;
|
| 346 |
+
gap: 16px;
|
| 347 |
+
align-items: center;
|
| 348 |
+
width: fit-content;
|
| 349 |
+
}
|
| 350 |
+
section.lead .meta-divider {
|
| 351 |
+
width: 1px;
|
| 352 |
+
height: 14px;
|
| 353 |
+
background: var(--rule-soft);
|
| 354 |
+
}
|
| 355 |
+
section.lead .meta-label {
|
| 356 |
+
font-family: var(--font-mono);
|
| 357 |
+
font-size: 11px;
|
| 358 |
+
font-weight: 500;
|
| 359 |
+
letter-spacing: 0.14em;
|
| 360 |
+
text-transform: uppercase;
|
| 361 |
+
color: var(--ink-3);
|
| 362 |
+
}
|
| 363 |
+
section.lead .meta-value {
|
| 364 |
+
font-family: var(--font-mono);
|
| 365 |
+
font-size: 11px;
|
| 366 |
+
font-weight: 600;
|
| 367 |
+
letter-spacing: 0.14em;
|
| 368 |
+
text-transform: uppercase;
|
| 369 |
+
color: var(--ink);
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
+
/* ββ CTA / closing slide β dark inverted βββββββββββββββββββββββββββββββ */
|
| 373 |
+
|
| 374 |
+
section.cta {
|
| 375 |
+
background: var(--paper-dark);
|
| 376 |
+
color: var(--paper);
|
| 377 |
+
padding-left: 88px;
|
| 378 |
+
padding-right: 88px;
|
| 379 |
+
justify-content: center;
|
| 380 |
+
}
|
| 381 |
+
section.cta::before {
|
| 382 |
+
/* Replaced by inline mark in deck.md (.cta-mark). */
|
| 383 |
+
content: none;
|
| 384 |
+
}
|
| 385 |
+
section.cta .cta-mark {
|
| 386 |
+
position: absolute;
|
| 387 |
+
left: 88px;
|
| 388 |
+
top: 56px;
|
| 389 |
+
width: 56px;
|
| 390 |
+
height: 56px;
|
| 391 |
+
display: block;
|
| 392 |
+
}
|
| 393 |
+
section.cta::after { color: rgba(244, 246, 249, 0.42); }
|
| 394 |
+
section.cta h1 {
|
| 395 |
+
font-family: var(--font-sans);
|
| 396 |
+
font-weight: 700;
|
| 397 |
+
font-size: 96px;
|
| 398 |
+
line-height: 0.95;
|
| 399 |
+
letter-spacing: -0.03em;
|
| 400 |
+
color: var(--paper);
|
| 401 |
+
margin: 80px 0 16px;
|
| 402 |
+
}
|
| 403 |
+
section.cta h2 {
|
| 404 |
+
font-family: var(--font-mono);
|
| 405 |
+
font-weight: 500;
|
| 406 |
+
font-size: 13px;
|
| 407 |
+
letter-spacing: 0.16em;
|
| 408 |
+
text-transform: uppercase;
|
| 409 |
+
color: var(--accent);
|
| 410 |
+
margin: 0 0 32px;
|
| 411 |
+
border: 0;
|
| 412 |
+
padding: 0;
|
| 413 |
+
display: block;
|
| 414 |
+
}
|
| 415 |
+
section.cta p {
|
| 416 |
+
font-size: 20px;
|
| 417 |
+
color: rgba(244, 246, 249, 0.85);
|
| 418 |
+
max-width: 70ch;
|
| 419 |
+
}
|
| 420 |
+
section.cta hr { border-top-color: rgba(203, 213, 225, 0.2); margin: 24px 0; }
|
| 421 |
+
section.cta .pill { background: rgba(244, 246, 249, 0.08); color: rgba(244, 246, 249, 0.85); border-color: rgba(203, 213, 225, 0.2); }
|
| 422 |
+
|
| 423 |
+
/* ββ Slide chrome: eyebrow + heading frame βββββββββββββββββββββββββββββ */
|
| 424 |
+
|
| 425 |
+
.eyebrow {
|
| 426 |
+
font-family: var(--font-mono);
|
| 427 |
+
font-size: 12px;
|
| 428 |
+
font-weight: 500;
|
| 429 |
+
letter-spacing: 0.18em;
|
| 430 |
+
text-transform: uppercase;
|
| 431 |
+
color: var(--accent-text);
|
| 432 |
+
margin: 0 0 8px;
|
| 433 |
+
display: flex;
|
| 434 |
+
align-items: center;
|
| 435 |
+
gap: 10px;
|
| 436 |
+
}
|
| 437 |
+
.eyebrow::before {
|
| 438 |
+
content: "β";
|
| 439 |
+
color: var(--accent);
|
| 440 |
+
font-family: var(--font-mono);
|
| 441 |
+
font-weight: 600;
|
| 442 |
+
font-size: 14px;
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
/* ββ Box / card primitives β the dashboard shape βββββββββββββββββββββββ */
|
| 446 |
+
|
| 447 |
+
.box {
|
| 448 |
+
border: 1px solid var(--rule-soft);
|
| 449 |
+
background: var(--paper);
|
| 450 |
+
padding: 18px 22px;
|
| 451 |
+
position: relative;
|
| 452 |
+
}
|
| 453 |
+
.box.tinted {
|
| 454 |
+
background: var(--paper-deep);
|
| 455 |
+
}
|
| 456 |
+
.box.dark {
|
| 457 |
+
background: var(--paper-dark);
|
| 458 |
+
color: var(--paper);
|
| 459 |
+
border: 0;
|
| 460 |
+
}
|
| 461 |
+
.box.dark .lbl,
|
| 462 |
+
.box.dark .smallcaps {
|
| 463 |
+
color: rgba(244, 246, 249, 0.6);
|
| 464 |
+
}
|
| 465 |
+
.box.dark .stat-value {
|
| 466 |
+
color: var(--paper);
|
| 467 |
+
}
|
| 468 |
+
.box.dark .body {
|
| 469 |
+
color: var(--paper);
|
| 470 |
+
font-size: 17px;
|
| 471 |
+
line-height: 1.4;
|
| 472 |
+
}
|
| 473 |
+
.box.dark .body strong {
|
| 474 |
+
color: var(--paper);
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
.box-grid {
|
| 478 |
+
display: grid;
|
| 479 |
+
gap: 12px;
|
| 480 |
+
margin-top: 12px;
|
| 481 |
+
}
|
| 482 |
+
.box-grid.cols-2 { grid-template-columns: 1fr 1fr; }
|
| 483 |
+
.box-grid.cols-3 { grid-template-columns: 1fr 1fr 1fr; }
|
| 484 |
+
.box-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
|
| 485 |
+
|
| 486 |
+
.box .lbl {
|
| 487 |
+
font-family: var(--font-mono);
|
| 488 |
+
font-size: 11px;
|
| 489 |
+
font-weight: 500;
|
| 490 |
+
letter-spacing: 0.12em;
|
| 491 |
+
text-transform: uppercase;
|
| 492 |
+
color: var(--ink-3);
|
| 493 |
+
margin-bottom: 6px;
|
| 494 |
+
}
|
| 495 |
+
.box .body {
|
| 496 |
+
font-size: 18px;
|
| 497 |
+
line-height: 1.4;
|
| 498 |
+
color: var(--ink);
|
| 499 |
+
}
|
| 500 |
+
.box .body strong { color: var(--ink); }
|
| 501 |
+
|
| 502 |
+
/* ββ Track-flag stack (slide 4) ββββββββββββββββββββββββββββββββββββββββ */
|
| 503 |
+
|
| 504 |
+
.track-row {
|
| 505 |
+
display: grid;
|
| 506 |
+
grid-template-columns: 28px 200px 1fr 90px;
|
| 507 |
+
gap: 16px;
|
| 508 |
+
align-items: center;
|
| 509 |
+
padding: 14px 18px;
|
| 510 |
+
border: 1px solid var(--rule-soft);
|
| 511 |
+
background: var(--paper);
|
| 512 |
+
margin-bottom: 8px;
|
| 513 |
+
}
|
| 514 |
+
.track-row.engaged {
|
| 515 |
+
background: var(--paper);
|
| 516 |
+
border-left: 3px solid var(--accent);
|
| 517 |
+
}
|
| 518 |
+
.track-row.unengaged {
|
| 519 |
+
opacity: 0.55;
|
| 520 |
+
border-left: 3px solid transparent;
|
| 521 |
+
}
|
| 522 |
+
.track-row .check {
|
| 523 |
+
font-family: var(--font-mono);
|
| 524 |
+
font-weight: 700;
|
| 525 |
+
font-size: 16px;
|
| 526 |
+
text-align: center;
|
| 527 |
+
color: var(--accent);
|
| 528 |
+
}
|
| 529 |
+
.track-row.unengaged .check { color: var(--ink-3); }
|
| 530 |
+
.track-row .name {
|
| 531 |
+
font-family: var(--font-sans);
|
| 532 |
+
font-weight: 600;
|
| 533 |
+
font-size: 17px;
|
| 534 |
+
color: var(--ink);
|
| 535 |
+
}
|
| 536 |
+
.track-row .detail {
|
| 537 |
+
font-size: 16px;
|
| 538 |
+
color: var(--ink-2);
|
| 539 |
+
line-height: 1.35;
|
| 540 |
+
}
|
| 541 |
+
.track-row .badge {
|
| 542 |
+
font-family: var(--font-mono);
|
| 543 |
+
font-size: 10px;
|
| 544 |
+
font-weight: 500;
|
| 545 |
+
letter-spacing: 0.1em;
|
| 546 |
+
text-transform: uppercase;
|
| 547 |
+
color: var(--ink-3);
|
| 548 |
+
text-align: right;
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
/* ββ Big stat / number card ββββββββββββββββββββββββββββββββββββββββββββ */
|
| 552 |
+
|
| 553 |
+
.stat {
|
| 554 |
+
display: flex;
|
| 555 |
+
flex-direction: column;
|
| 556 |
+
gap: 4px;
|
| 557 |
+
}
|
| 558 |
+
.stat-value {
|
| 559 |
+
font-family: var(--font-sans);
|
| 560 |
+
font-weight: 700;
|
| 561 |
+
font-size: 56px;
|
| 562 |
+
line-height: 1;
|
| 563 |
+
color: var(--ink);
|
| 564 |
+
letter-spacing: -0.025em;
|
| 565 |
+
}
|
| 566 |
+
.stat-label {
|
| 567 |
+
font-family: var(--font-mono);
|
| 568 |
+
font-size: 10px;
|
| 569 |
+
font-weight: 500;
|
| 570 |
+
letter-spacing: 0.14em;
|
| 571 |
+
text-transform: uppercase;
|
| 572 |
+
color: var(--ink-3);
|
| 573 |
+
margin-top: 6px;
|
| 574 |
+
}
|
| 575 |
+
|
| 576 |
+
/* ββ Codeblock (HTML-rendered, so inline spans keep color) βββββββββββββ */
|
| 577 |
+
|
| 578 |
+
.codeblock {
|
| 579 |
+
font-family: var(--font-mono);
|
| 580 |
+
font-size: 14px;
|
| 581 |
+
line-height: 1.55;
|
| 582 |
+
background: var(--paper-deep);
|
| 583 |
+
border: 1px solid var(--rule-soft);
|
| 584 |
+
border-left: 3px solid var(--accent);
|
| 585 |
+
padding: 16px 20px;
|
| 586 |
+
margin: 8px 0;
|
| 587 |
+
color: var(--ink);
|
| 588 |
+
}
|
| 589 |
+
.codeblock .cite {
|
| 590 |
+
color: var(--accent-text);
|
| 591 |
+
font-weight: 600;
|
| 592 |
+
}
|
| 593 |
+
.codeblock .label {
|
| 594 |
+
font-weight: 700;
|
| 595 |
+
color: var(--ink);
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
/* ββ Pill βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 599 |
+
|
| 600 |
+
.pill {
|
| 601 |
+
display: inline-block;
|
| 602 |
+
font-family: var(--font-mono);
|
| 603 |
+
font-size: 11px;
|
| 604 |
+
font-weight: 500;
|
| 605 |
+
letter-spacing: 0.1em;
|
| 606 |
+
text-transform: uppercase;
|
| 607 |
+
background: var(--paper-deep);
|
| 608 |
+
border: 1px solid var(--rule-soft);
|
| 609 |
+
padding: 4px 10px;
|
| 610 |
+
color: var(--ink-2);
|
| 611 |
+
margin-right: 6px;
|
| 612 |
+
}
|
| 613 |
+
.pill.accent {
|
| 614 |
+
background: var(--accent);
|
| 615 |
+
color: var(--paper);
|
| 616 |
+
border-color: var(--accent);
|
| 617 |
+
}
|
| 618 |
+
.pill.warn {
|
| 619 |
+
background: var(--accent-warn);
|
| 620 |
+
color: var(--paper);
|
| 621 |
+
border-color: var(--accent-warn);
|
| 622 |
+
}
|
| 623 |
+
.pill.dim {
|
| 624 |
+
opacity: 0.55;
|
| 625 |
+
}
|
| 626 |
+
|
| 627 |
+
/* ββ Small caps utility ββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 628 |
+
|
| 629 |
+
.smallcaps {
|
| 630 |
+
font-family: var(--font-mono);
|
| 631 |
+
font-size: 11px;
|
| 632 |
+
font-weight: 500;
|
| 633 |
+
letter-spacing: 0.12em;
|
| 634 |
+
text-transform: uppercase;
|
| 635 |
+
color: var(--ink-3);
|
| 636 |
+
}
|
| 637 |
+
|
| 638 |
+
/* ββ Two-column layout (slides 2, 6) βββββββββββββββββββββββββββββββββββ */
|
| 639 |
+
|
| 640 |
+
.two-col {
|
| 641 |
+
display: grid;
|
| 642 |
+
grid-template-columns: 1fr 1fr;
|
| 643 |
+
gap: 36px;
|
| 644 |
+
margin-top: 8px;
|
| 645 |
+
}
|
| 646 |
+
.two-col > div p,
|
| 647 |
+
.two-col > div li { font-size: 18px; line-height: 1.45; max-width: none; }
|
| 648 |
+
.two-col > div p:last-child,
|
| 649 |
+
.two-col > div li:last-child { margin-bottom: 0; }
|
| 650 |
+
|
| 651 |
+
/* ββ Stone-tinted heading rules ββββββββββββββββββββββββββββββββββββββββ */
|
| 652 |
+
|
| 653 |
+
section[data-stone="cornerstone"] h2 { color: var(--stone-cornerstone); border-bottom-color: var(--stone-cornerstone); }
|
| 654 |
+
section[data-stone="keystone"] h2 { color: var(--stone-keystone); border-bottom-color: var(--stone-keystone); }
|
| 655 |
+
section[data-stone="touchstone"] h2 { color: var(--stone-touchstone); border-bottom-color: var(--stone-touchstone); }
|
| 656 |
+
section[data-stone="lodestone"] h2 { color: var(--stone-lodestone); border-bottom-color: var(--stone-lodestone); }
|
| 657 |
+
section[data-stone="capstone"] h2 { color: var(--stone-capstone); border-bottom-color: var(--stone-capstone); }
|
slides/deck.md
CHANGED
|
@@ -56,148 +56,243 @@ description: AMD x lablab.ai Developer Hackathon, May 4β10 2026
|
|
| 56 |
</div>
|
| 57 |
|
| 58 |
<div class="box tinted">
|
| 59 |
-
<div class="lbl">
|
| 60 |
<div class="body" style="font-size: 19px; line-height: 1.4;">
|
| 61 |
-
|
| 62 |
</div>
|
| 63 |
</div>
|
| 64 |
|
| 65 |
</div>
|
| 66 |
|
| 67 |
-
<p style="margin-top:
|
| 68 |
|
| 69 |
-
---
|
| 70 |
|
| 71 |
-
|
| 72 |
|
| 73 |
-
|
| 74 |
|
| 75 |
-
|
| 76 |
|
| 77 |
-
<
|
| 78 |
|
| 79 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
-
<p style="
|
| 82 |
|
| 83 |
---
|
| 84 |
|
| 85 |
-
<div class="eyebrow">03 · The
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
<div class="
|
| 90 |
-
<div class="
|
| 91 |
-
<div class="
|
| 92 |
-
<div class="detail">Burr FSM · five-Stone evidence taxonomy · planner classifies intent and routes to the right roster · hallucination guard on every reconcile</div>
|
| 93 |
-
<div class="badge">Engaged</div>
|
| 94 |
</div>
|
| 95 |
|
| 96 |
-
<div class="
|
| 97 |
-
<div class="
|
| 98 |
-
<div class="
|
| 99 |
-
<div class="detail">3 Apache-2.0 NYC fine-tunes trained on AMD MI300X: Prithvi-EO-2.0-NYC-Pluvial · TerraMind-NYC-Adapters · Granite-TTM-r2-Battery-Surge</div>
|
| 100 |
-
<div class="badge">Engaged</div>
|
| 101 |
</div>
|
| 102 |
|
| 103 |
-
<div class="
|
| 104 |
-
<div class="
|
| 105 |
-
<div class="
|
| 106 |
-
<div class="detail">Sentinel-2 chip → Prithvi pluvial seg · TerraMind LULC + Buildings adapters · Granite Embedding 278M · GLiNER typed extraction</div>
|
| 107 |
-
<div class="badge">Engaged</div>
|
| 108 |
</div>
|
| 109 |
|
| 110 |
-
<div class="track-row unengaged">
|
| 111 |
-
<div class="check">Β·</div>
|
| 112 |
-
<div class="name">Build in Public</div>
|
| 113 |
-
<div class="detail">Documentation track · not the focus this round</div>
|
| 114 |
-
<div class="badge">Skipped</div>
|
| 115 |
</div>
|
| 116 |
|
| 117 |
---
|
| 118 |
|
| 119 |
-
<div class="eyebrow">04 ·
|
| 120 |
|
| 121 |
-
#
|
| 122 |
|
| 123 |
-
<
|
| 124 |
-
<thead>
|
| 125 |
-
<tr><th>address</th><th>intent</th><th>wall</th><th>steps</th><th>verified</th></tr>
|
| 126 |
-
</thead>
|
| 127 |
-
<tbody>
|
| 128 |
-
<tr><td>442 E Houston St · LES</td><td>address</td><td>7.6 s</td><td>19</td><td>4/4</td></tr>
|
| 129 |
-
<tr><td>80 Pioneer St · Red Hook</td><td>address</td><td>13.1 s</td><td>19</td><td>4/4</td></tr>
|
| 130 |
-
<tr><td>100 Gold St · Manhattan</td><td>address</td><td>11.2 s</td><td>19</td><td>4/4</td></tr>
|
| 131 |
-
<tr><td>Hollis · Queens</td><td>nbhd</td><td>5.8 s</td><td>9</td><td>4/4</td></tr>
|
| 132 |
-
<tr><td>Coney Island · Brooklyn</td><td>nbhd</td><td>9.9 s</td><td>9</td><td>4/4</td></tr>
|
| 133 |
-
</tbody>
|
| 134 |
-
</table>
|
| 135 |
|
| 136 |
-
<div
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
<div
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
</div>
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
<div
|
| 145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
</div>
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
<div
|
| 150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
</div>
|
| 153 |
|
| 154 |
---
|
| 155 |
|
| 156 |
-
<div class="eyebrow">05 ·
|
| 157 |
|
| 158 |
-
#
|
| 159 |
|
| 160 |
-
<div class="box-grid cols-
|
| 161 |
|
| 162 |
-
<div class="box">
|
| 163 |
-
<div class="lbl"
|
| 164 |
-
<div
|
|
|
|
|
|
|
|
|
|
| 165 |
</div>
|
| 166 |
|
| 167 |
-
<div class="box">
|
| 168 |
-
<div class="lbl"
|
| 169 |
-
<div
|
|
|
|
|
|
|
|
|
|
| 170 |
</div>
|
| 171 |
|
| 172 |
-
<div class="box">
|
| 173 |
-
<div class="lbl"
|
| 174 |
-
<div
|
|
|
|
|
|
|
|
|
|
| 175 |
</div>
|
| 176 |
|
| 177 |
-
<div class="box dark">
|
| 178 |
-
<div class="lbl">No commercial APIs</div>
|
| 179 |
-
<div class="body">Every dataset is public-record federal, state, or city. Every foundation model is Apache-2.0. <strong>Every claim cites its source.</strong></div>
|
| 180 |
</div>
|
| 181 |
|
| 182 |
-
</
|
| 183 |
|
| 184 |
---
|
| 185 |
|
| 186 |
-
<div class="eyebrow">06 ·
|
| 187 |
|
| 188 |
# Live demo.
|
| 189 |
|
| 190 |
-
<div
|
| 191 |
-
<
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
</div>
|
| 194 |
|
| 195 |
-
<div class="box
|
| 196 |
-
<div class="lbl">
|
| 197 |
-
<div class="body"
|
| 198 |
</div>
|
| 199 |
|
| 200 |
-
<
|
| 201 |
|
| 202 |
---
|
| 203 |
|
|
@@ -207,9 +302,7 @@ description: AMD x lablab.ai Developer Hackathon, May 4β10 2026
|
|
| 207 |
|
| 208 |
<div class="eyebrow" style="margin-top: 124px; color: var(--accent); border: 0; padding: 0;">Riprap · flood briefings on AMD</div>
|
| 209 |
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
## github.com/msradam/riprap-nyc
|
| 213 |
|
| 214 |
<hr>
|
| 215 |
|
|
@@ -224,3 +317,42 @@ AMD × lablab.ai · May 4–10 2026
|
|
| 224 |
<p style="font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; color: rgba(244,246,249,0.4); margin-top: 24px; text-transform: none;">
|
| 225 |
Dam mark: "Dam" by Chintuza via the Noun Project, CC-BY 3.0.
|
| 226 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
</div>
|
| 57 |
|
| 58 |
<div class="box tinted">
|
| 59 |
+
<div class="lbl">Nov 14·2025 · CNN / TechCrunch (paraphrase)</div>
|
| 60 |
<div class="body" style="font-size: 19px; line-height: 1.4;">
|
| 61 |
+
Zillow removed climate risk scores from listings under pressure from the real-estate industry. In their place: a link, far less visible.
|
| 62 |
</div>
|
| 63 |
</div>
|
| 64 |
|
| 65 |
</div>
|
| 66 |
|
| 67 |
+
<p style="margin-top: 20px; font-size: 22px;">When a number meets resistance, <strong>the only defense is the audit trail.</strong></p>
|
| 68 |
|
| 69 |
+
<p style="margin-top: 4px; font-size: 18px; color: var(--ink-3);">Riprap is not a property-risk score. It is the audit trail behind one.</p>
|
| 70 |
|
| 71 |
+
---
|
| 72 |
|
| 73 |
+
<div class="eyebrow">02 · SOLUTION</div>
|
| 74 |
|
| 75 |
+
# A flood-exposure briefing for any place in New York City.
|
| 76 |
|
| 77 |
+
<p style="margin-bottom: 14px; font-size: 20px; max-width: 72ch; color: var(--ink-2);">Type an address or neighborhood. Get a written briefing in 5–13 seconds, fusing four temporal modes — Sandy 2012 inundation, current 311 history, FloodNet sensor reads, NPCC4 projections — into one cited paragraph.</p>
|
| 78 |
|
| 79 |
+
<div style="border: 2px dashed #94A3B8; background: #E8ECF2; display: flex; align-items: center; justify-content: center; height: 280px; border-radius: 2px; margin-bottom: 10px;">
|
| 80 |
+
<p style="font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); text-align: center; margin: 0; padding: 24px;">
|
| 81 |
+
[ screenshot of riprap.nyc landing — to be added ]
|
| 82 |
+
</p>
|
| 83 |
+
</div>
|
| 84 |
|
| 85 |
+
<p style="font-size: 15px; color: var(--ink-3); margin: 0;">Behind the prose: every numeric claim links to its primary public-record source. Mellea rejection sampling refuses to publish what it can’t cite.</p>
|
| 86 |
|
| 87 |
---
|
| 88 |
|
| 89 |
+
<div class="eyebrow">03 · The civic-tech case</div>
|
| 90 |
+
|
| 91 |
+
# The civic-tech case.
|
| 92 |
+
|
| 93 |
+
<div class="box-grid cols-2">
|
| 94 |
|
| 95 |
+
<div class="box">
|
| 96 |
+
<div class="lbl">NY Property Disclosure Law</div>
|
| 97 |
+
<div class="body">March 2024. Sellers must disclose flood history. <strong>Riprap is the citable narrative.</strong></div>
|
| 98 |
+
</div>
|
| 99 |
|
| 100 |
+
<div class="box">
|
| 101 |
+
<div class="lbl">NYC DEP Stormwater Plan</div>
|
| 102 |
+
<div class="body">2024. $30B priority list, 86 sites. <strong>Riprap is the per-NTA evidence layer.</strong></div>
|
|
|
|
|
|
|
| 103 |
</div>
|
| 104 |
|
| 105 |
+
<div class="box">
|
| 106 |
+
<div class="lbl">EJNYC Flood Vulnerability Index</div>
|
| 107 |
+
<div class="body">2024. 35% of state climate spend goes to "disadvantaged communities." <strong>Riprap stays open-source so advocacy can audit.</strong></div>
|
|
|
|
|
|
|
| 108 |
</div>
|
| 109 |
|
| 110 |
+
<div class="box dark">
|
| 111 |
+
<div class="lbl">No commercial APIs</div>
|
| 112 |
+
<div class="body">Every dataset is public-record federal, state, or city. Every foundation model is Apache-2.0. <strong>Every claim cites its source.</strong></div>
|
|
|
|
|
|
|
| 113 |
</div>
|
| 114 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
</div>
|
| 116 |
|
| 117 |
---
|
| 118 |
|
| 119 |
+
<div class="eyebrow">04 · Architecture</div>
|
| 120 |
|
| 121 |
+
# Five Stones fan out. One cited briefing comes back.
|
| 122 |
|
| 123 |
+
<p style="margin: 4px 0 10px; font-size: 17px; color: var(--ink-3); font-family: var(--font-mono);">query → <strong style="color: var(--ink);">Planner</strong> (Granite 4.1 3B, intent classification) → Stone roster → <strong style="color: var(--ink);">Capstone</strong> (Granite 4.1 8B + Mellea) → briefing</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
+
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-top: 0;">
|
| 126 |
+
|
| 127 |
+
<div style="background: var(--paper-deep); border: 1px solid var(--rule-soft); border-top: 3px solid #475569; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;">
|
| 128 |
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
| 129 |
+
<span style="font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #475569;">Cornerstone Β· USGS 3DEP</span>
|
| 130 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3);">2020</span>
|
| 131 |
+
</div>
|
| 132 |
+
<div style="font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.2; margin-bottom: 4px;">Microtopography (HAND / TWI)</div>
|
| 133 |
+
<div style="display: grid; grid-template-columns: auto 1fr; gap: 2px 8px;">
|
| 134 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em;">HAND</span><span style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #475569;">0.82 m</span>
|
| 135 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em;">TWI</span><span style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #475569;">14.3</span>
|
| 136 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em;">Elev.</span><span style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #475569;">2.1 m MSL</span>
|
| 137 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em;">Pct. lower</span><span style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #475569;">78%</span>
|
| 138 |
+
</div>
|
| 139 |
+
<div style="margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: #475569; font-weight: 600;">[topo]</div>
|
| 140 |
</div>
|
| 141 |
+
|
| 142 |
+
<div style="background: var(--paper-deep); border: 1px solid var(--rule-soft); border-top: 3px solid #1A4480; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;">
|
| 143 |
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
| 144 |
+
<span style="font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #1A4480;">Keystone Β· TerraMind-NYC</span>
|
| 145 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3);">2024</span>
|
| 146 |
+
</div>
|
| 147 |
+
<div style="font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.2; margin-bottom: 4px;">Building footprint coverage</div>
|
| 148 |
+
<div style="margin: 6px 0;">
|
| 149 |
+
<div style="font-family: var(--font-mono); font-size: 30px; font-weight: 700; color: #1A4480; line-height: 1;">48.41<span style="font-size: 16px;">%</span></div>
|
| 150 |
+
<div style="font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); margin-top: 3px;">250 m radius · Buildings LoRA adapter</div>
|
| 151 |
+
</div>
|
| 152 |
+
<div style="margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: #1A4480; font-weight: 600;">[keystone_bldg]</div>
|
| 153 |
</div>
|
| 154 |
+
|
| 155 |
+
<div style="background: var(--paper-deep); border: 1px solid var(--rule-soft); border-top: 3px solid #0E7490; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;">
|
| 156 |
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
| 157 |
+
<span style="font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #0E7490;">Touchstone Β· NYC 311</span>
|
| 158 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3);">live</span>
|
| 159 |
+
</div>
|
| 160 |
+
<div style="font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.2; margin-bottom: 4px;">Flood complaints Β· 200 m buffer</div>
|
| 161 |
+
<div style="margin: 4px 0;">
|
| 162 |
+
<svg viewBox="0 0 220 60" style="width:100%; display:block;">
|
| 163 |
+
<rect x="8" y="52" width="212" height="1" fill="#CBD5E1"/>
|
| 164 |
+
<rect x="12" y="35" width="28" height="17" fill="#0E7490" rx="1"/>
|
| 165 |
+
<rect x="54" y="18" width="28" height="34" fill="#0E7490" rx="1"/>
|
| 166 |
+
<rect x="96" y="10" width="28" height="42" fill="#0E7490" rx="1"/>
|
| 167 |
+
<rect x="138" y="10" width="28" height="42" fill="#0E7490" rx="1"/>
|
| 168 |
+
<rect x="180" y="27" width="28" height="25" fill="#0E7490" rx="1"/>
|
| 169 |
+
<text x="26" y="32" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">2</text>
|
| 170 |
+
<text x="68" y="15" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">4</text>
|
| 171 |
+
<text x="110" y="7" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">5</text>
|
| 172 |
+
<text x="152" y="7" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">5</text>
|
| 173 |
+
<text x="194" y="24" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="8" fill="#0E7490">3</text>
|
| 174 |
+
<text x="26" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'19</text>
|
| 175 |
+
<text x="68" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'20</text>
|
| 176 |
+
<text x="110" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'21</text>
|
| 177 |
+
<text x="152" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'22</text>
|
| 178 |
+
<text x="194" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">'23</text>
|
| 179 |
+
</svg>
|
| 180 |
+
<div style="font-family: var(--font-mono); font-size: 10px; color: var(--ink-3);">19 requests · 5-yr lookback</div>
|
| 181 |
+
</div>
|
| 182 |
+
<div style="margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: #0E7490; font-weight: 600;">[nyc311]</div>
|
| 183 |
+
</div>
|
| 184 |
+
|
| 185 |
+
<div style="background: var(--paper-deep); border: 1px solid var(--rule-soft); border-top: 3px solid #92400E; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;">
|
| 186 |
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
| 187 |
+
<span style="font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #92400E;">Lodestone Β· Granite TTM r2</span>
|
| 188 |
+
<span style="font-family: var(--font-mono); font-size: 9px; color: var(--ink-3);">live</span>
|
| 189 |
+
</div>
|
| 190 |
+
<div style="font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.2; margin-bottom: 4px;">Surge residual nowcast</div>
|
| 191 |
+
<div style="margin: 4px 0;">
|
| 192 |
+
<svg viewBox="0 0 220 60" style="width:100%; display:block;">
|
| 193 |
+
<path d="M10,40 35,30 60,19 85,16 110,21 135,27 160,34 185,40 210,45 L210,52 L10,52 Z" fill="#92400E" opacity="0.12"/>
|
| 194 |
+
<rect x="8" y="52" width="212" height="1" fill="#CBD5E1"/>
|
| 195 |
+
<line x1="60" y1="19" x2="60" y2="52" stroke="#92400E" stroke-width="1" stroke-dasharray="3,2" opacity="0.6"/>
|
| 196 |
+
<polyline points="10,40 35,30 60,19 85,16 110,21 135,27 160,34 185,40 210,45" fill="none" stroke="#92400E" stroke-width="2" stroke-linejoin="round"/>
|
| 197 |
+
<circle cx="60" cy="19" r="3" fill="#92400E"/>
|
| 198 |
+
<text x="65" y="17" font-family="IBM Plex Mono,monospace" font-size="8" fill="#92400E" font-weight="700">0.22 ft</text>
|
| 199 |
+
<text x="10" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">0h</text>
|
| 200 |
+
<text x="60" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#92400E">NOW</text>
|
| 201 |
+
<text x="110" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">4.8h</text>
|
| 202 |
+
<text x="210" y="59" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="7" fill="#94A3B8">9.6h</text>
|
| 203 |
+
</svg>
|
| 204 |
+
<div style="font-family: var(--font-mono); font-size: 10px; color: var(--ink-3);">peak surge residual · 9.6 h horizon</div>
|
| 205 |
+
</div>
|
| 206 |
+
<div style="margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: #92400E; font-weight: 600;">[ttm_surge]</div>
|
| 207 |
</div>
|
| 208 |
+
|
| 209 |
+
</div>
|
| 210 |
+
|
| 211 |
+
<p style="margin-top: 8px; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);">Real evidence cards rendered by the live system · 442 East Houston Street, Manhattan.</p>
|
| 212 |
+
|
| 213 |
+
<div class="box" style="border-top: 3px solid #162E51; margin-top: 10px; padding: 12px 18px;">
|
| 214 |
+
<span style="font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: #162E51;">Capstone</span>
|
| 215 |
+
<span style="font-size: 16px; color: var(--ink-2); margin-left: 14px;">Granite 4.1 8B + Mellea rejection sampling · <code>numerics_grounded</code> · <code>no_placeholder_tokens</code> · <code>citations_dense</code> · <code>citations_resolve</code> · reroll until resolved → <strong>cited 4-section briefing</strong></span>
|
| 216 |
</div>
|
| 217 |
|
| 218 |
---
|
| 219 |
|
| 220 |
+
<div class="eyebrow">05 · Fine-Tuning on AMD MI300X</div>
|
| 221 |
|
| 222 |
+
# Three Apache-2.0 NYC fine-tunes on MI300X.
|
| 223 |
|
| 224 |
+
<div class="box-grid cols-3" style="margin-top: 12px; gap: 14px;">
|
| 225 |
|
| 226 |
+
<div class="box" style="border-top: 3px solid #0E7490; padding: 18px 18px 16px;">
|
| 227 |
+
<div class="lbl" style="color: #0E7490; margin-bottom: 6px;">Prithvi-EO-2.0-NYC-Pluvial</div>
|
| 228 |
+
<div style="font-size: 14px; color: var(--ink-2); margin-bottom: 12px;">Hurricane Ida pluvial flood detection from Sentinel-2</div>
|
| 229 |
+
<div style="font-family: var(--font-mono); font-size: 22px; font-weight: 700; color: var(--ink); letter-spacing: -0.02em;">0.5979</div>
|
| 230 |
+
<div style="font-family: var(--font-mono); font-size: 11px; color: var(--ink-3); margin-bottom: 10px;">test flood IoU Β· 6× lift over Sen1Floods11 baseline</div>
|
| 231 |
+
<div style="font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3);">MI300X · AMD Developer Cloud</div>
|
| 232 |
</div>
|
| 233 |
|
| 234 |
+
<div class="box" style="border-top: 3px solid #1A4480; padding: 18px 18px 16px;">
|
| 235 |
+
<div class="lbl" style="color: #1A4480; margin-bottom: 6px;">TerraMind-NYC-Adapters</div>
|
| 236 |
+
<div style="font-size: 14px; color: var(--ink-2); margin-bottom: 12px;">LULC + Buildings + TiM LoRA adapters for NYC</div>
|
| 237 |
+
<div style="font-family: var(--font-mono); font-size: 22px; font-weight: 700; color: var(--ink); letter-spacing: -0.02em;">+6.13<span style="font-size: 15px;">pp</span></div>
|
| 238 |
+
<div style="font-family: var(--font-mono); font-size: 11px; color: var(--ink-3); margin-bottom: 10px;">LULC mIoU over full-FT baseline Β· mIoU 0.5866</div>
|
| 239 |
+
<div style="font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3);">~18 min total · MI300X</div>
|
| 240 |
</div>
|
| 241 |
|
| 242 |
+
<div class="box" style="border-top: 3px solid #92400E; padding: 18px 18px 16px;">
|
| 243 |
+
<div class="lbl" style="color: #92400E; margin-bottom: 6px;">Granite-TTM-r2-Battery-Surge</div>
|
| 244 |
+
<div style="font-size: 14px; color: var(--ink-2); margin-bottom: 12px;">NOAA Battery tide gauge 96h surge residual nowcast</div>
|
| 245 |
+
<div style="font-family: var(--font-mono); font-size: 22px; font-weight: 700; color: var(--ink); letter-spacing: -0.02em;">0.157<span style="font-size: 15px;">m</span></div>
|
| 246 |
+
<div style="font-family: var(--font-mono); font-size: 11px; color: var(--ink-3); margin-bottom: 10px;">RMSE Β· −35% vs persistence baseline</div>
|
| 247 |
+
<div style="font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3);">MI300X · Apache-2.0 · HF Hub</div>
|
| 248 |
</div>
|
| 249 |
|
|
|
|
|
|
|
|
|
|
| 250 |
</div>
|
| 251 |
|
| 252 |
+
<p style="margin-top: 18px; font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-3);">Track submitted: Fine-Tuning on AMD GPUs · All three models Apache-2.0, published on HF Hub</p>
|
| 253 |
|
| 254 |
---
|
| 255 |
|
| 256 |
+
<div class="eyebrow">06 · DEMO</div>
|
| 257 |
|
| 258 |
# Live demo.
|
| 259 |
|
| 260 |
+
<div style="margin: 48px 0 32px; text-align: center;">
|
| 261 |
+
<p style="font-family: var(--font-mono); font-size: 28px; font-weight: 700; color: var(--ink); margin: 0 auto; max-width: 860px; line-height: 1.35;">“I’m thinking about renting an apartment at 80 Pioneer Street, Brooklyn. Should I worry?”</p>
|
| 262 |
+
</div>
|
| 263 |
+
|
| 264 |
+
<div style="text-align: center; margin-bottom: 40px;">
|
| 265 |
+
<span style="font-family: var(--font-mono); font-size: 20px; letter-spacing: 0.06em; color: var(--accent); font-weight: 700;">riprap.nyc</span>
|
| 266 |
+
</div>
|
| 267 |
+
|
| 268 |
+
<p style="text-align: center; font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); margin: 0 auto; max-width: none;">13 seconds end-to-end · 4/4 grounding checks · all sources public-record</p>
|
| 269 |
+
|
| 270 |
+
---
|
| 271 |
+
|
| 272 |
+
<div class="eyebrow">07 · What's next</div>
|
| 273 |
+
|
| 274 |
+
# What's next.
|
| 275 |
+
|
| 276 |
+
<p style="margin-bottom: 14px; font-size: 18px; color: var(--ink-3); font-family: var(--font-mono); letter-spacing: 0.02em;">The architecture is NYC-specific by data choice, not by code.</p>
|
| 277 |
+
|
| 278 |
+
<div class="box-grid cols-3" style="margin-top: 0;">
|
| 279 |
+
|
| 280 |
+
<div class="box">
|
| 281 |
+
<div class="lbl">Break out the Stones</div>
|
| 282 |
+
<div class="body">Each Stone is a coherent composition over data sources, models, and deterministic checks. Extract Cornerstone, Touchstone, Keystone, Lodestone as independent packages; any civic-tech project can pull one Stone without the full Riprap stack.</div>
|
| 283 |
+
</div>
|
| 284 |
+
|
| 285 |
+
<div class="box">
|
| 286 |
+
<div class="lbl">Other flood-impacted cities</div>
|
| 287 |
+
<div class="body">Houston (Harvey, Beryl), Miami (king tides), Boston (CSO floods), Jakarta, Manila, Dhaka — the same five-Stone pattern, different probe sets and RAG corpora per city.</div>
|
| 288 |
</div>
|
| 289 |
|
| 290 |
+
<div class="box tinted">
|
| 291 |
+
<div class="lbl">Historical-event mode</div>
|
| 292 |
+
<div class="body">Re-run the FSM with snapshot data from any past date. Validate the system against measured outcomes — what would Riprap have said before Sandy, before Ida, before the 2024 Beryl remnants. Calibration as a first-class feature.</div>
|
| 293 |
</div>
|
| 294 |
|
| 295 |
+
</div>
|
| 296 |
|
| 297 |
---
|
| 298 |
|
|
|
|
| 302 |
|
| 303 |
<div class="eyebrow" style="margin-top: 124px; color: var(--accent); border: 0; padding: 0;">Riprap · flood briefings on AMD</div>
|
| 304 |
|
| 305 |
+
<h1 style="white-space: nowrap; font-size: 72px;">github.com/msradam/riprap-nyc</h1>
|
|
|
|
|
|
|
| 306 |
|
| 307 |
<hr>
|
| 308 |
|
|
|
|
| 317 |
<p style="font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; color: rgba(244,246,249,0.4); margin-top: 24px; text-transform: none;">
|
| 318 |
Dam mark: "Dam" by Chintuza via the Noun Project, CC-BY 3.0.
|
| 319 |
</p>
|
| 320 |
+
|
| 321 |
+
---
|
| 322 |
+
|
| 323 |
+
<!-- _paginate: false -->
|
| 324 |
+
|
| 325 |
+
<div class="eyebrow">Appendix · The receipts</div>
|
| 326 |
+
|
| 327 |
+
# 5 of 5 NYC addresses. Every claim verified, every run.
|
| 328 |
+
|
| 329 |
+
<table>
|
| 330 |
+
<thead>
|
| 331 |
+
<tr><th>address</th><th>intent</th><th>wall</th><th>steps</th><th>verified</th></tr>
|
| 332 |
+
</thead>
|
| 333 |
+
<tbody>
|
| 334 |
+
<tr><td>442 E Houston St · LES</td><td>address</td><td>7.6 s</td><td>19</td><td>4/4</td></tr>
|
| 335 |
+
<tr><td>80 Pioneer St · Red Hook</td><td>address</td><td>13.1 s</td><td>19</td><td>4/4</td></tr>
|
| 336 |
+
<tr><td>100 Gold St · Manhattan</td><td>address</td><td>11.2 s</td><td>19</td><td>4/4</td></tr>
|
| 337 |
+
<tr><td>Hollis · Queens</td><td>nbhd</td><td>5.8 s</td><td>9</td><td>4/4</td></tr>
|
| 338 |
+
<tr><td>Coney Island · Brooklyn</td><td>nbhd</td><td>9.9 s</td><td>9</td><td>4/4</td></tr>
|
| 339 |
+
</tbody>
|
| 340 |
+
</table>
|
| 341 |
+
|
| 342 |
+
<div class="box-grid cols-3" style="margin-top: 16px;">
|
| 343 |
+
<div class="box">
|
| 344 |
+
<div class="lbl">Wall-clock</div>
|
| 345 |
+
<div class="stat-value">5.8–13.1<span style="font-size: 22px; color: var(--ink-3); font-weight: 400; letter-spacing: 0;"> s</span></div>
|
| 346 |
+
<div class="stat-label">vLLM on MI300X</div>
|
| 347 |
+
</div>
|
| 348 |
+
<div class="box">
|
| 349 |
+
<div class="lbl">Stones</div>
|
| 350 |
+
<div class="stat-value">5</div>
|
| 351 |
+
<div class="stat-label">evidence layers per briefing</div>
|
| 352 |
+
</div>
|
| 353 |
+
<div class="box">
|
| 354 |
+
<div class="lbl">Verified</div>
|
| 355 |
+
<div class="stat-value">4 / 4</div>
|
| 356 |
+
<div class="stat-label">source checks every run</div>
|
| 357 |
+
</div>
|
| 358 |
+
</div>
|
submission/COPY-DRAFTS.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Submission copy drafts β AMD x lablab.ai hackathon
|
| 2 |
+
|
| 3 |
+
Prepared 2026-05-07. Voice: civic-tech-clean, precise, understated.
|
| 4 |
+
Numbers from RESEARCH.md and probe_addresses.py. No invented statistics.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## Project title (max 50 characters)
|
| 9 |
+
|
| 10 |
+
Three options, each fits in 50 chars:
|
| 11 |
+
|
| 12 |
+
**Option A (recommended):** `Riprap β Cited NYC flood briefings on AMD`
|
| 13 |
+
(42 chars) β Names the project, names the output type, names the
|
| 14 |
+
platform. No adjectives. The word "cited" is load-bearing: it's the
|
| 15 |
+
differentiator in one word.
|
| 16 |
+
|
| 17 |
+
**Option B:** `Riprap: citation-grounded flood briefings`
|
| 18 |
+
(41 chars) β "Citation-grounded" is the project's defining
|
| 19 |
+
term; it front-loads the architectural commitment. Slightly more
|
| 20 |
+
technical than A.
|
| 21 |
+
|
| 22 |
+
**Option C:** `Riprap β NYC flood risk, every claim cited`
|
| 23 |
+
(41 chars) β Plain English, consumer-accessible. Weaker for a
|
| 24 |
+
technical hackathon audience.
|
| 25 |
+
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
## Short description (max 255 characters)
|
| 29 |
+
|
| 30 |
+
Three options:
|
| 31 |
+
|
| 32 |
+
**Option A (recommended):**
|
| 33 |
+
Riprap writes NYC flood-exposure briefings where every numeric claim cites its source β or doesn't appear. Granite 4.1 8B on AMD MI300X, three Apache-2.0 NYC fine-tunes, Mellea citation grounding. 5/5 addresses, 4/4 checks every run.
|
| 34 |
+
(237 chars)
|
| 35 |
+
|
| 36 |
+
Rationale: leads with the output, names the citation discipline in
|
| 37 |
+
plain English, names the GPU platform and the three fine-tune
|
| 38 |
+
artifacts, closes with the receipts. No adjectives. Ends on a
|
| 39 |
+
verifiable number.
|
| 40 |
+
|
| 41 |
+
**Option B:**
|
| 42 |
+
Three AMD MI300X fine-tuned models. Five evidence layers. One cited briefing. Riprap takes any NYC address, fans out across Sandy 2012 data, live FloodNet sensors, 311 history, and surge forecasts, then returns a 4-section paragraph with doc_id citations on every number.
|
| 43 |
+
(255 chars β exact limit)
|
| 44 |
+
|
| 45 |
+
Rationale: leads with the Fine-Tuning track evidence (the AMD
|
| 46 |
+
hardware claim), then explains the system. Denser; may be harder
|
| 47 |
+
to parse at a skim.
|
| 48 |
+
|
| 49 |
+
**Option C:**
|
| 50 |
+
Type a NYC address. Riprap runs five data probes β Sandy 2012 inundation, live sensors, 311 history, DEP scenarios, surge forecasts β and writes a cited flood briefing. IBM Granite 4.1 on AMD MI300X. Apache-2.0. Public data only.
|
| 51 |
+
(229 chars)
|
| 52 |
+
|
| 53 |
+
Rationale: most conversational, demo-first. Weakest on the
|
| 54 |
+
hackathon-track argument.
|
| 55 |
+
|
| 56 |
+
---
|
| 57 |
+
|
| 58 |
+
## Long description (~250β300 words)
|
| 59 |
+
|
| 60 |
+
Riprap is a citation-grounded flood-exposure briefing tool for NYC
|
| 61 |
+
addresses, built on IBM Granite 4.1 8B running on AMD MI300X via vLLM.
|
| 62 |
+
Type any address or neighborhood. Within 6β13 seconds, five evidence
|
| 63 |
+
layers fan out across NYC's public flood record and return a four-
|
| 64 |
+
section prose briefing β every numeric claim followed by a [doc_id]
|
| 65 |
+
citation that resolves to a named primary source.
|
| 66 |
+
|
| 67 |
+
The system refuses to publish a number it cannot cite. Mellea rejection
|
| 68 |
+
sampling enforces four invariants on every response: numeric claims
|
| 69 |
+
grounded in source documents, no placeholder tokens, citation density
|
| 70 |
+
per sentence, and all cited doc_ids resolve to inputs. If the briefing
|
| 71 |
+
fails any check, the model rerolls. The meta card shows which checks
|
| 72 |
+
passed and how many attempts it took.
|
| 73 |
+
|
| 74 |
+
Three Apache-2.0 NYC-specialized fine-tunes were trained on AMD MI300X
|
| 75 |
+
and published on HF Hub: `msradam/Prithvi-EO-2.0-NYC-Pluvial` (pluvial
|
| 76 |
+
flood segmentation from Sentinel-2), `msradam/TerraMind-NYC-Adapters`
|
| 77 |
+
(LULC and building-stock adapters), and `msradam/Granite-TTM-r2-Battery-
|
| 78 |
+
Surge` (96-hour surge residual nowcast, test MAE 0.1091 m vs 0.1467 m
|
| 79 |
+
zero-shot). All training code and reproduction recipes are in the repo.
|
| 80 |
+
|
| 81 |
+
The data pipeline is entirely public-record: Hurricane Sandy 2012
|
| 82 |
+
inundation zone, NYC DEP stormwater scenarios, USGS Ida 2021 high-water
|
| 83 |
+
marks, FloodNet ultrasonic sensors, NYC 311 complaint history, NOAA
|
| 84 |
+
tide gauge, NWS METAR, NPCC4 SLR projections, five NYC policy PDFs in
|
| 85 |
+
a Granite Embedding 278M RAG corpus. No commercial APIs are contacted
|
| 86 |
+
at runtime.
|
| 87 |
+
|
| 88 |
+
Five addresses across four NYC boroughs, verified with the address probe
|
| 89 |
+
suite: 5 of 5 pass, 4/4 Mellea grounding checks, 5.8β13.1 seconds
|
| 90 |
+
wall-clock on AMD MI300X.
|
| 91 |
+
|
| 92 |
+
The stack runs on both AMD MI300X (vLLM) and local Ollama with
|
| 93 |
+
auto-failover. Apache-2.0 end-to-end.
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
## Cover image β design brief
|
| 98 |
+
|
| 99 |
+
**Target artifact:** `submission/cover-16x9.png`
|
| 100 |
+
**Dimensions:** 1920Γ1080 px or 1280Γ720 px, 16:9 ratio, PNG
|
| 101 |
+
|
| 102 |
+
**Visual system (must match deck cover exactly):**
|
| 103 |
+
- Background: `#F4F6F9` (--paper, cool slate register)
|
| 104 |
+
- Dam mark (Noun Project "Dam" by Chintuza, CC-BY 3.0): positioned
|
| 105 |
+
top-left at approximately 72px from edges, colored `#005EA2`
|
| 106 |
+
(federal blue, --accent)
|
| 107 |
+
- Wordmark: "Riprap" in IBM Plex Sans 700, `#0F172A` (--ink), large
|
| 108 |
+
display size (~96β120px equivalent at 1920-wide)
|
| 109 |
+
- Tagline line 1: "Citation-grounded NYC flood-exposure briefings"
|
| 110 |
+
IBM Plex Sans 400, `#334155` (--ink-2)
|
| 111 |
+
- Tagline line 2: "AMD MI300X · Granite 4.1 · Apache-2.0"
|
| 112 |
+
IBM Plex Mono 500, `#64748B` (--ink-3), smaller (14β16px equivalent)
|
| 113 |
+
- Bottom strip (meta bar): thin rule at `#CBD5E1` (--rule-soft),
|
| 114 |
+
below the rule: "AMD Γ lablab.ai Developer Hackathon Β· May 4β10 2026"
|
| 115 |
+
in IBM Plex Mono, `#64748B`, 12px equivalent, uppercase
|
| 116 |
+
|
| 117 |
+
**What to avoid:**
|
| 118 |
+
- No bold color blocks in the background (the thumbnail reads fine
|
| 119 |
+
on paper register)
|
| 120 |
+
- No gradient, shadow, or decorative water imagery β the dam mark
|
| 121 |
+
carries the water metaphor
|
| 122 |
+
- No subtitle text beyond what's listed above
|
| 123 |
+
|
| 124 |
+
**Status:** The cover image requires an SVG/HTML renderer or design
|
| 125 |
+
tool. Automated generation is not feasible in this environment without
|
| 126 |
+
a headless browser for SVG-to-PNG export. Adam should generate this
|
| 127 |
+
from the brief above using:
|
| 128 |
+
- Figma (design file already has the tokens)
|
| 129 |
+
- The deck's cover slide exported at 1920Γ1080 via Marp
|
| 130 |
+
(`--allow-local-files --image png --output cover-16x9.png`)
|
| 131 |
+
- Or: `slides/logo.svg` + a simple HTML page exported via
|
| 132 |
+
headless Chrome / Puppeteer
|
| 133 |
+
|
| 134 |
+
**Quickest path:** re-export the cover slide from deck.pdf as a PNG at
|
| 135 |
+
1920Γ1080. The cover slide already has the correct design.
|
| 136 |
+
|
| 137 |
+
---
|
| 138 |
+
|
| 139 |
+
## Recommended submission combination
|
| 140 |
+
|
| 141 |
+
**Title (go with A):** `Riprap β Cited NYC flood briefings on AMD`
|
| 142 |
+
|
| 143 |
+
**Short description (go with A):**
|
| 144 |
+
Riprap writes NYC flood-exposure briefings where every numeric claim cites its source β or doesn't appear. Granite 4.1 8B on AMD MI300X, three Apache-2.0 NYC fine-tunes, Mellea citation grounding. 5/5 addresses, 4/4 checks every run.
|
| 145 |
+
|
| 146 |
+
**Long description:** the version above (no changes needed).
|
| 147 |
+
|
| 148 |
+
**Runner-up title:** `Riprap: citation-grounded flood briefings`
|
| 149 |
+
|
| 150 |
+
**Runner-up short (option B):**
|
| 151 |
+
Three AMD MI300X fine-tuned models. Five evidence layers. One cited briefing. Riprap takes any NYC address, fans out across Sandy 2012 data, live FloodNet sensors, 311 history, and surge forecasts, then returns a 4-section paragraph with doc_id citations on every number.
|