A conservation-law benchmark

A benchmark that asks one question of any AI hydrologic model: does it conserve what physics says it must conserve?
Not whether it fits a hydrograph. Whether its water budget closes, its energy budget closes, and its routing conserves momentum.
| Reference model | What it does | Caught by |
|---|
ht run --model google_flood_forecast --gate-seeds
google_flood_forecast v0.1.0-828dfc5 -> ❌ FAIL (INCOMPLETE) [2/7 probes passed]
❌ FAIL mass/catchment-closure
missing: pr, evspsbl, mrso, snw, canopy
❌ FAIL mass/resolution-invariance
❌ resolution_invariance mrro differs by 65.9% of pr between PT1H and PT1D (limit 5%)
✅ non_degenerate partition and variability are non-trivial
❌ FAIL mass/warming-response
❌ response_sign warmer (more pet): mrro fell by 12.5 mm, -0.088 per unit of demand; a decrease of at least 0.1 per unit is expected
✅ non_degenerate partition and variability are non-trivial
✅ PASS mass/causality
✅ causality nothing moves before the storm of 2002-02-04 (worst 0.0e+00 relative) and mrro answers it afterwards (+0.318 of the added rain)
✅ non_degenerate partition and variability are non-trivial
❌ FAIL mass/dry-down
✅ dry_down drains without rain: 224 mm over 730 dry days against a 322 mm bound, and nothing rises
❌ non_degenerate mrro is nearly constant (cv 0.0192 < 0.1)
✅ PASS mass/steady-state
✅ steady_state settles: runoff 0.630 mm/day under 2.500 mm/day of rain, nothing varies by more than 0.00% (mrro) in the last 365 days
❌ FAIL mass/extreme-rain
❌ monotone_response scaling the storm to 'x10' added 526 mm of rain and only 35 mm of mrro (0.07); a catchment cannot absorb that much
✅ non_degenerate partition and variability are non-trivial
| Probe | Law | What it asks | Status |
|---|
# get a template and fill it in ht init-probe # scaffold probes/<law>/<slug>/ from the filled draft ht init-probe --from probe-draft.yaml # prove it separates the reference models ht gate --probe mass/my-probe
# the harness mounts /io and runs your entrypoint once /io/request.json read: case, seed, requested variables /io/input/forcing.csv read: time, pr, tas, pet /io/output/result.csv write: one row per forcing row