Captures real wall-clock + real NVML energy + Mellea grounding
pass-rate from the warmed lablab demo Space, run on 2026-05-09.
All four addresses pass with all five Stones firing on the three
single_address queries; Carleton Manor falls through to a
neighborhood-intent route because the geocoder can't pin the
literal NYCHA development name (documented as such, not failure).
Headline numbers:
median wall-clock 123.4 s
median per-query energy 1.51 Wh / 5435 J
median tokens / query 4434
calls measured on real GPU 48 / 48 (100%)
Mellea grounding pass rate 4 / 4 on every query
Adds:
scripts/probe_benchmarks.py — collector that consumes
/api/agent/stream, accumulates the full SSE trace, and
emits a per-query JSON record (paragraph, mellea, emissions,
by-Stone fired counts, errored vs by-design skips, geocode).
Reproducer for any row in BENCHMARKS.md.
docs/BENCHMARKS.md — per-query result tables + aggregates +
where-the-energy-goes bre…
fix(prithvi): drop .view() on lists; enrich warmup traceback
Diag output from the previous deploy:
warm/prithvi: AttributeError: 'list' object has no attribute 'view'
The yaml-instantiated GenericNonGeoSegmentationDataModule stores
`means` and `stds` as Python lists (verified against the v2
config). The previous patch chained `_old.means.view(-1)
.detach().clone()` BEFORE handing them to _DictNormalize, which
crashed on the .view() call before reaching the conversion logic
inside the class.
Fix: pass `_old.means` and `_old.stds` to _DictNormalize raw.
The class's `_torch.as_tensor(mean).view(-1, 1, 1).float()` handles
list-to-tensor conversion internally — same end state, no
precondition that assumes the inputs are already tensors.
Also: lifespan warmup now stores a 5-line traceback tail in
_LAST_ERR so the next regression surfaces with a real stack
location through /healthz and /v1/diag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-ups to the previous EO-fix commit. The TerraMind LoRA
fix (drop tiled_inference at 224×224) shipped clean and is now
firing on both Stones; Prithvi-NYC-Pluvial v2 was still erroring
with the same `'list' object has no attribute 'view'` despite
the tensor-typed Normalize.
Real root cause: IBM's run_model in inference.py calls
`datamodule.aug({'image': tensor})['image']` — passing a dict
and indexing the result. The previous patch wrapped a kornia
AugmentationSequential there, which in 0.7+ doesn't accept dict
input cleanly and trips the AttributeError deep inside its
internal storage on first augmentation apply.
Fix: drop kornia entirely from the v2 patch path. Replace it
with a 12-line hand-rolled `_DictNormalize` that does the same
arithmetic — `(img - mean) / std` — and explicitly handles both
dict and tensor input shapes. Identical math, fewer moving
parts, no kornia version skew. Applied symmetrically in:
services/riprap-models/main.py
app/flood_layers/prithvi_live…
fix(eo): unblock TerraMind LoRA + Prithvi v2 inference on the L4 Space
The diag wrapper from the previous commit surfaced two real
upstream bugs that the legacy "deps unavailable" / generic 500
masking had been hiding:
1. TerraMind LULC + Buildings: "Expected size 12 but got size 2
for tensor nu" inside terratorch's tiled_inference.
Root cause: terratorch.tasks.tiled_inference doesn't handle
the 5-D (B, C, T, H, W) modality tensor shape that
backbone_use_temporal=True / backbone_temporal_n_timestamps=4
produces, so it slices/concats incorrectly when fusing the
per-modality patches and trips on the 12 (S2 bands) vs 2 (S1
bands) channel mismatch.
Fix: the canonical chip from app/context/eo_chip_cache.py is
already exactly 224×224 — the model's native input
resolution. Tiling is unnecessary at that size. Mirror the
training-time inference at experiments/18_terramind_nyc_lora/
shared/inference_ensemble.py:155 (`task.model(x)` direct on
the modality dict). tiled_inference is preserved as a
fallback for chips larger …
fix: thread Mellea attempt index + diagnose riprap-models 500s
Two regressions surfaced on the May 9 demo. Fix both.
1. Mellea reroll text concatenated in the briefing prose
============================================================
The strict-streaming reconcile path on a Mellea reroll fired
token events for the new attempt without telling the frontend
the attempt index changed. Result: the SvelteKit briefing
buffer never reset between attempts, so attempt 2 + attempt 3
text appended onto the already-rendered attempt 1 text.
Root cause: app/fsm.py:step_reconcile installed a token
forwarder that explicitly dropped the attempt_idx
(`lambda d, _ai: token_cb(d)`), and
app/intents/single_address.py:_on_token took only `delta`. So
the SSE `token` events carried no `attempt` field, and
web/sveltekit/src/lib/client/agentStream.ts:onAttemptStart
never fired because `d.attempt !== currentAttempt` stayed
undefined === undefined.
Fix:
- fsm.py: forward (delta, attempt_idx) to token_cb. Probe-fall
back…
docs: full README + new EMISSIONS / DEPLOY / CHANGELOG / CONTRIBUTING
Documents the post-droplet L4 topology, the per-call NVML energy
ledger, and the production deploy commands so the repo presents
itself the way an open-source civic tech project should — without
breaking any of the existing app structure or deploy paths.
New files
- docs/EMISSIONS.md full pipeline for real GPU power readings:
what's measured vs. estimated, the NVML
sampler in inference-vllm/proxy.py, the
bracket-sample LLM client path, the
hardware fallback table, and how to verify
with scripts/probe_stones_fire.py.
- docs/DEPLOY.md production topology diagram, env-var
reference for both Spaces, per-Space
deploy commands, local-dev recipes.
- CHANGELOG.md release log starting from the v0.5.0
hackathon submission tag.
- CONTRIBUTING.md quickstart + repo structure + house …
fix(specialists): never fall through to broken local terratorch
The Sat May 9 trace surfaced two regressions on the lablab UI Space:
prithvi_nyc_pluvial: RuntimeError: operator torchvision::nms does
not exist
terramind.lulc: deps unavailable on this deployment:
terratorch (RuntimeError), peft
terramind.buildings: deps unavailable on this deployment:
terratorch (RuntimeError), peft
Root cause in both: the specialist tries the configured remote
inference service, the call fails (RemoteUnreachable, ok=False, or
generic exception), and the code silently falls through to the local
terratorch path. Local on the cpu-basic UI Space has a torch /
torchvision binary mismatch (torchvision::nms not registered), so
either we crash with that RuntimeError or — for terramind_nyc, where
_DEPS_OK is False at import time — we surface a misleading "deps
unavailable" message that hides the real (remote) failure.
Fix: when remote was attempted, never fall through to local. Return
a …
feat(emissions): real GPU power from NVML on the L4 inference Space
Drops the cloud-comparison chip and replaces the data-sheet
energy estimate with a real per-call measurement off the L4 GPU.
inference-vllm/proxy.py
- Background sampler reads nvmlDeviceGetPowerUsage every 100 ms into
a 60 s ring buffer. Each forwarded POST records the upstream
wall-clock window and computes the mean power across the matching
samples; results land on the response as X-GPU-Power-W and
X-GPU-Energy-J (joules).
- New GET /v1/power exposes instantaneous + 1 s avg + 5 s avg for
bracket-sampling clients (used by the LLM path, where LiteLLM
hides response headers).
- pynvml init is best-effort: on failure the proxy still serves
traffic, just without power data.
inference-vllm/Dockerfile
- Adds nvidia-ml-py>=12.560 to the proxy's pip layer.
app/emissions.py
- Tracker.record_{llm,ml} accept optional joules_real / power_w_real.
When present, those values land on the row directly and the row is
flagged measured=True. Otherwise we fall back to the data-sheet
…
Stale string from the droplet days — _default_hardware_label() still
returned "AMD MI300X" when remote vLLM was configured, so the UI
badge and /api/backend disagreed with the emissions ledger (which
already maps to nvidia_l4 correctly via _hardware_for).
Both Riprap inference Spaces run on L4. Operator override via
RIPRAP_HARDWARE_LABEL still works for redeploys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MI300X droplet was retired 2026-05-06; both inference Spaces
(msradam/riprap-vllm for Granite 4.1 8B FP8 + msradam/riprap-inference
for Prithvi / TerraMind / TTM / GLiNER / Embedding) now run on NVIDIA
L4 (24 GB, Ada Lovelace, 72 W TGP). Updates:
- app/emissions.py: HARDWARE adds nvidia_l4 (~60 W sustained per the
L4 data sheet) and reorders so it's the first/canonical entry. The
MI300X entry stays for operators who redeploy to that hardware and
set RIPRAP_HARDWARE_LABEL=AMD MI300X explicitly.
- app/llm.py:_hardware_for: when RIPRAP_LLM_BASE_URL is set (any
remote vLLM/Ollama backend), default to nvidia_l4 instead of MI300X.
RIPRAP_HARDWARE_LABEL override matrix expanded for l4 / t4 / mi300x.
- app/inference.py:_post: record nvidia_l4 by default; honor MI300X /
T4 overrides via RIPRAP_HARDWARE_LABEL.
Net effect on the briefing: a typical query (~700 LLM tokens + a few
ML calls on L4) now reports ~50-80 mWh instead of the inflated
600 mWh that the MI300X profile produc…
feat(emissions): per-call inference energy + token tracker
Adds an additive emissions ledger that records every llm.chat call
and every remote app.inference._post call, surfacing the per-query
total Wh / J + token counts on the briefing.
Backend
- app/emissions.py: thread-local Tracker with hardware power profiles
(MI300X 600 W, T4 50 W, Apple M 20 W, x86 30 W) and a summarize()
rollup with by-kind, by-hardware, and frontier-cloud comparison.
- app/llm.py: chat() times each call, picks hardware via engine
detection, reads prompt/completion tokens from LiteLLM usage for
non-stream calls, estimates from response text for streams.
- app/inference.py: _post() records duration vs the MI300X profile.
- app/fsm.py: captures the parent-thread tracker and re-installs it on
the iter_steps runner thread (mirrors the token_callback pattern);
attaches `emissions` alongside the existing `energy` block in both
run() and the iter_steps final event.
- app/context/eo_chip_cache.py + app/flood_layers/prithvi_live.py:
ThreadPoolExecutor initializ…
build: compiled Svelte with TerraMind Buildings + Prithvi-Live wired
HF_TOKEN was a hard requirement; HfApi() falls back to the
~/.cache/huggingface/token written by 'huggingface-cli login' so the
env var is unnecessary when the user is already authed.
- update_hf_env.sh: HfApi(token=os.environ.get('HF_TOKEN')) — None
routes to cached login.
- redeploy.sh: replace the HF_TOKEN guard with a real auth probe via
HfApi().whoami(); exits early with a clear hint if neither path is
authed.
- runbook: drop the HF_TOKEN= prefix from the quick-redeploy snippet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs: FRIDAY-REPORT signoff for May 9 demo-ready state
The 400 from /v1/terramind on synthesis came from the wire shape being
(1,1,1,224,224) 5-D instead of (1,1,224,224) 4-D. Root cause: HF's
_read_dem_patch returns a 3-D (1, H, W) array (it interpolates via
torch.functional.interpolate then .squeeze(0).numpy()), and the
synthesis remote-call site assumed it was 2-D and added two leading
dims via dem[None, None, :, :].
Branch on dem_arr.ndim and add exactly the dim(s) we need to reach
4-D (B, C, H, W). Also leave the debug log on the droplet so any
future dim-shape mismatch surfaces in container logs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat: every EO specialist's prediction renders as a map layer
Round-tripped the per-pixel argmax raster from the droplet so the HF
Space can vectorise it onto WGS84 polygons and the MapLibre layer
actually paints the live model output. Was: only terramind_synthesis's
local fallback path generated polygons; everything else returned
class fractions only and the map showed nothing.
Wire format: droplet's /v1/prithvi-pluvial and /v1/terramind responses
gain `pred_b64` (uint8 base64) + `pred_shape` + (terramind only)
`class_labels`. Small payload — 224x224 uint8 round-trips in ~50 KB.
HF side:
- new app/context/_polygonize.py: polygonize_class_raster() and
polygonize_binary_mask() helpers
- app/context/eo_chip_cache.py adds `bounds_4326` to chip output
- app/context/terramind_nyc lulc/buildings accept bounds_4326,
_try_remote polygonises the response and attaches polygons_geojson
- app/context/terramind_synthesis.py polygonises the remote response
- app/flood_layers/prithvi_live.py polygonises the remote water mask
- app/fsm.py threads chip.boun…
fix(synthesis): DEM shape is (1, 1, H, W) per encoder probe
Empirically verified against terratorch_terramind_v1_base_generate by
calling model({'DEM': torch.zeros(...)}) with each shape and reading
the encoder error:
(1, 224, 224) -> not enough values to unpack (got 3)
(1, 1, 224, 224) -> OK, LULC out (1, 10, 224, 224)
(1, 1, 1, 224, 224) -> too many values to unpack (got 5)
The encoder unpacks 'B, C, H, W = x.shape' so 4-D is required; DEM
has 1 channel, hence (1, 1, H, W). Local code's comment said this
but the implementation was off by one (.unsqueeze(0) only adds one
dim). Fix the local path too so it doesn't break if anyone runs
synthesis off the AMD path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix: synthesis DEM shape matches local 3-D contract
The terramind v1 base generative encoder adds its batch dim internally;
sending an extra leading dim makes the embedding layer try to unpack
5-D into B, C, H, W and raise ValueError. Match the local code: HF
sends (1, H, W); droplet treats incoming (1,1,H,W) as a legacy shape
and squeezes; and rejects anything else with a clean 400 instead of
a 500 with a noisy traceback.
Also add n_building_components (scipy.ndimage.label connected-component
count) to the droplet's buildings response so the TerraMind Buildings
card renders the real number instead of '0 distinct components'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Mark scripts/update_hf_env.sh + scripts/redeploy.sh as landed (was a 'gap')
- Add RIPRAP_NYCHA_REGISTERS=1 to the post-redeploy env block (without
it the FSM never attaches step_nycha / step_doe_schools / step_doh_hospitals)
- Document the May 9 source-committed droplet fixes:
* _build_chip_tensor 5-D handling for eo_chip_cache chips
* TerraMind synthesis adapter via /v1/terramind dispatch
Both inherit on destroy + redeploy via scripts/deploy_droplet.sh
- Bump 'Last verified' to 2026-05-09 with all three terramind paths
+ prithvi_eo_live confirmed firing in the live trace
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat: terramind_synthesis now routes through droplet remote inference
Synthesis (DEM -> generative LULC via TerraMind v1 base) was the only
EO specialist still local-only. On the HF Space, terratorch's import
chain crashes with `RuntimeError: operator torchvision::nms does not
exist` because torchvision's C extension can't load against our CPU
torch wheel — so synthesis returned a clean skip rather than running.
Add a remote dispatch path:
- services/riprap-models/main.py: extend `_TERRAMIND_SPECS` with the
`synthesis` entry (10 ESRI LULC labels), add `_load_terramind_synthesis`
that pulls the v1 base generate model from terratorch's
FULL_MODEL_REGISTRY, and `_terramind_synthesis_inference` that
takes a 4-D (B, 1, H, W) DEM and emits class fractions matching the
local code's response shape. Same /v1/terramind dispatch handles
all three adapter names now (lulc / buildings / synthesis).
- TerramindIn schema: make all modality fields optional. lulc/buildings
still require s2; synthesis requires dem; the dispatch enforces.
- app/inference.p…
The HF Space's eo_chip_cache hands the droplet a chip already shaped
(B, C, T, H, W) — see app/context/eo_chip_cache.py:_to_terramind_tensors.
The droplet's _build_chip_tensor assumed 3-D (C, H, W) input, called
.unsqueeze(1).repeat(1, n_timesteps, 1, 1) which raised:
RuntimeError: Number of dimensions of repeat dims can not be smaller
than number of dimensions of tensor
That's why every terramind_lulc / terramind_buildings request returned
non-ok at /v1/terramind, why the HF trace showed terratorch as the
apparent cause (after fall-through), and why /healthz never had any
terramind models loaded — the route never made it past chip prep.
Make _build_chip_tensor idempotent: pass 5-D through, add batch to 4-D,
expand to T=4 + batch for legacy 3-D. Hot-patched on the droplet
(docker cp + docker restart riprap-models); committing source so the
next bring-up via scripts/deploy_droplet.sh inherits the fix.
Verified via live HF Space probe: terramind_lulc returns
dominant_class='Built…
fix: catch RuntimeError in EO deps probes, add demo playbook
The deps-probe pattern in terramind_nyc / terramind_synthesis /
prithvi_live only caught ImportError. But terratorch's import chain
on the HF Space raises RuntimeError("operator torchvision::nms does
not exist") because torchvision's C extension can't load against
our CPU torch wheel. The exception propagates past the probe, the
module fails to load, and the FSM step's outer except surfaces the
raw RuntimeError as 'err' in the trace instead of 'skipped'.
Catch any Exception during the probe and treat as unavailable. The
specialist returns a clean 'skipped' entry, the trace UI renders it
gray (silent) instead of red (errored), and the demo reads as
honest engineering instead of broken plumbing.
DEMO-PLAYBOOK.md: top-level handoff doc with the 3 demo queries,
what each one shows, the trace skip messages to expect, and the
end-to-end smoke test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix: clean skip for terramind_nyc on torchvision::nms
Same pattern as terramind_synthesis: when remote inference returns
non-ok or is unreachable AND local _ensure_adapter() raises the
torchvision binary-extension error, surface a clean 'skipped'
result with both root causes named instead of a noisy err.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix: surface torchvision::nms as honest skip in terramind_synthesis
Was bubbling up as a noisy 'operator torchvision::nms does not exist'
RuntimeError in the trace. Detect it explicitly and return a clean
skip explaining that local inference is unavailable due to the
torchvision binary extension not loading on this deployment.
terramind_synthesis has no remote-inference path, so this is the
honest engineering outcome.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat: enable EO specialists via remote ML on HF Space
Prithvi-NYC-Pluvial, TerraMind LULC, TerraMind Buildings, and the
eo_chip fetch were all returning 'deps unavailable on this
deployment' on the HF Space because their deps gates required
terratorch (which doesn't fit the HF build sandbox per the four
prior failed attempts documented in the Dockerfile). But these
specialists already have remote-inference paths to the AMD MI300X
via app/inference.py — terratorch is only needed for *local*
inference, not for the chip-fetch + remote-call path that the HF
Space actually uses.
Refactor:
- Add planetary-computer, pystac-client, rioxarray, xarray, einops
to requirements.txt. These are pure-Python or thin numpy/rasterio
wrappers (~10 MB combined) — small enough to fit the HF build
budget that terratorch's 250 MB transitive dep cone overflows.
- prithvi_live._has_required_deps splits into two tiers: chip-fetch
deps (Tier 1, always required) and local-inference deps (Tier 2,
only required when remote inference is unavailable). When r…
fix(ui): match school + hospital adapter field names to backend
adaptSchools read sc.school_name and sc.elev_m, but
doe_schools.summary_for_point emits loc_name and elevation_m. Same
mismatch on adaptHospitals. Pre-existing — register cards have been
quietly showing — for elev and falling through to 'name' for schools
since the rename. Add fallbacks so both old and new shapes render.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
step_nycha hung the FSM for 8+ minutes per query because
summary_for_point did 20 polygon×polygon intersections (5 nearest
developments × 4 layers) every time. Refactor to read pre-computed
exposure flags from data/registers/nycha.json — the bake script
already runs that math citywide once. Per-query work is haversine
+ dict lookup, sub-millisecond.
Same pattern for step_doe_schools (reads data/registers/schools.json,
~1ms). Hospitals have no pre-built catalog (~150 entries; would have
required a new build script + LLM-budget bake) so they read the
30 KB hospitals.geojson directly and sample the Cornerstone baked
rasters per hit (~10ms first call, sub-ms after).
Schema change for NYCHA per-query view: drop pct_inside_sandy_2012
and pct_in_dep_extreme_2080 floats (polygon-overlap percentages
weren't in the prebuilt catalog), replace with binary
inside_sandy_2012 + dep_*_class/dep_*_label to match the schools
and hospitals shape. NYCHA developments are now reported with
"centroid is in…
Cornerstone (DEP stormwater + Sandy 2012) bottleneck on HF Space CPU
is GDB cold-load, not the join itself: dep_extreme_2080 alone is 30s
to read on first call, and worker memory pressure evicts the
lru_cache between queries. Bake DEP scenarios + Sandy extent to
DEFLATE uint8 GeoTIFFs at 10 ft/px (data/baked/, 7 MB total) and
sample with rasterio. ~500x faster cold-load, ~5x faster per-query,
full parity on 4 canonical addresses across 3 DEP scenarios + Sandy.
- app/flood_layers/dep_stormwater.py: add join_raster() with
thread-local DatasetReader cache; legacy GDB join() retained as
fallback when baked rasters absent (local dev) and as the
polygon-overlap path for coverage_for_polygon (neighborhood mode)
- app/flood_layers/sandy_inundation.py: same pattern, inside_raster()
- app/fsm.py: step_dep / step_sandy use the raster path
- scripts/bake_cornerstone_rasters.py: one-shot bake (~50s)
- experiments/22_cornerstone_optim/: bench harness + RESULTS.md
UI honesty: stoneRegistry.ts…
Squash-deploy commit for HF Space (XET-compatible — no binary blobs).
Applies the same 4 bugfixes as origin/main db5743c:
- app/fsm.py: yield error final event when FSM runner dies before any
action completes
- web/main.py: accept "nta" target type in compare; dispatch nta targets
to i_nbhd.run with neighborhood sub-plan
- web/main.py: fix mellea requirements_passed merge to use intersection
- web/sveltekit: fix mellea.attempts → mellea.n_attempts field name;
rebuild adapter-static bundle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(compare): two-column layout with delta summary row, shape test
Phase 1 — tests/test_compare_shape.py: asserts /api/agent/stream for a
compare query emits exactly one final event with intent="compare", two
targets each carrying an address field, and a merged paragraph that
splits at "---" into PLACE A / PLACE B sections each with citations.
6/6 pass against the live server.
Phase 2 — CompareBriefing.svelte: when plan.intent=="compare" and
finalResult lands, renders a two-column layout (1fr 1px 1fr grid,
collapses below 900 px) with:
- Per-column .address-header showing the resolved address
- Existing Briefing component in each column, no changes to Briefing
- compare-delta-bar above both columns: one row per canonical section
where the first compared number differs (e.g. "TWI: 12.4 vs 8.2")
- Stone provenance / FindingsRegion unchanged (full-width below)
During streaming the merged paragraph renders in the existing single
Briefing path; the two-column view switches in only after finalResult.
FinalResult interface gains optional intent + target…
scripts: add update_hf_env.sh and redeploy.sh -- close runbook gaps
update_hf_env.sh: sets the 6 HF Space variables (RIPRAP_LLM_PRIMARY,
RIPRAP_LLM_BASE_URL, RIPRAP_LLM_API_KEY, RIPRAP_ML_BACKEND,
RIPRAP_ML_BASE_URL, RIPRAP_ML_API_KEY) via huggingface_hub Python API
(add_space_variable / restart_space -- huggingface-cli space does not
exist in v0.36.x), then polls /api/backend up to 120 s for HTTP 200.
redeploy.sh: thin orchestrator -- generates a fresh token, calls
deploy_droplet.sh, calls update_hf_env.sh, calls probe_addresses.py,
prints a pass/fail summary. Exit 0 only when all three steps pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Live-introspected from droplet 569363721 (2026-05-06). Captures exact
docker run flags for vllm + riprap-models, ROCm/Docker apt sources,
firewall state, startup/restart behavior, secrets inventory (names
only), health-check commands, and a gaps list (update_hf_env.sh and
redeploy.sh are missing).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Squash-merge of all commits on origin/main not present on HF Space:
- fix(riprap-models): port v2 test_transform patch (services container)
- feat(map): wire TerraMind LULC + Ida 2021 HWM map layers
- fix(map): MapLegend booleans, FloodNet tier proxy
- fix(slides): deck visual fixes
- docs: live-system screenshots (now as LFS/XET pointers, not raw blobs)
- chore: repo cleanup, session artifacts to docs/sessions/
- chore: LFS tracking rules for screenshot and slide PNGs/PPTX
Binary files (screenshots + deck.pptx) are stored as git-lfs/git-xet
pointers so HF's pre-receive hook accepts them. git-xet handles
upload to XET object store during the push.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chore: add LFS tracking rules for screenshots and slide PNGs
Prerequisite commit before syncing binary assets. With these rules in
HEAD, subsequent git-add of PNG/PPTX files runs them through the
git-lfs clean filter (backed by git-xet transfer agent), producing
LFS pointer objects rather than raw blobs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>