SPRINT4 §Lane C 2/3/4.
PONDING DATA (decision 10). rainAt() was a dimensionless 0..1 — fine for drop
count and opacity, useless for water mass. Lane B would have had to invent the
mm/hr scale, which is exactly the "default-off code tuned by a constant I
invented" they rightly reverted. So the scale is storm data now:
rain.peakMmPerHour (validated 0..300; a rain curve without one is a hard error,
since ponding would silently use the default instead of what the author meant),
plus wind.rainMmPerHour(t) and rainDepthMm(t0,t1). RAIN_TIME_COMPRESSION=40 is
exported from weather.core so B applies it cloth-side rather than either of us
hardcoding 40 twice: how hard it rains is mine, how much water a sail holds is
theirs.
Calibrated to B's own arithmetic, and the numbers land on it:
storm_02 80 mm/hr severe -> 50.9 mm = 3.12 kN/corner (B predicted 3.1)
storm_03 30 mm/hr moderate -> 8.3 mm = 0.51 kN/corner (teases a carabiner)
storm_01 8 mm/hr shower -> 0.9 mm = 0.06 kN/corner (harmless, as designed)
against a storm_02 wind load of 0.2-1.1 kN. A flat rig should drown in the wild
night; a hypar pools nothing and won't notice. Asserted with B's arithmetic so
the storms are provably fit for their water before their cloth lands.
DECISION 7 helper for Lane A: sky.gardenExposure(bed, t) = rainAt x (1-shadow),
the whole drain term in one call. Note it moves on its own — the rain shadow
follows the wind, so the southerly change walks the dry patch off the bed and
the drain climbs with no corner having failed.
NIGHT PASS. sky.night is now the author's call (was: inferred from a darkness
threshold), and darkening scene.background did nothing anyway — the cloud dome
covers it at 0.85 opacity, so an overcast-grey texture was what you actually saw.
The dome now tints AND crushes (a lerp alone lands #717273: it runs in linear
space and the texture is baked near-white). Stops at 0.78 because the yard has no
lights and a storm you can't see is a black screen. Lightning now lights the
cloud it's inside (#3e3e3f -> #d4ddf2), and fires on the biggest gusts via
sky.lightningGustPow, not just the three authored strikes — driven off the
telegraph so the flash lands with the gust that earned it.
Also: c.test's storm list was hardcoded to two storms while the node runner globs
the directory, so storm_03 was untested in the browser half. Its own ponding
assert caught it.
Selftest 195/0/0 (was 184). Verified live: night reads as night, flash lights the
cloud, exposure responds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>