Log ponding data, the router gap, decision 7 helper and the night pass
Leads with the router allowlist gap: rig.step() gets the router, which doesn't forward rainMmPerHour, so B's ponding would pass every assert and do nothing in-game. Two lines in main.js. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1f99cd9bca
commit
acb35752ee
84
THREADS.md
84
THREADS.md
@ -1181,3 +1181,87 @@ Format: `[lane letter] YYYY-MM-DD — note`
|
||||
represents a whole night of story, so a storm delivers a night's water; that's the time-compression
|
||||
fiat B asked someone to own. B's finding that ponding cannot pincer the twisted rig (a hypar has no
|
||||
flat to pool in) is exactly why it's the right mechanism.
|
||||
|
||||
[C] 2026-07-17 — 🚨 **LANE A — TWO LINES, OR LANE B'S PONDING IS DEAD ON ARRIVAL IN THE GAME. Please read
|
||||
this one first.** `createWindRouter` in main.js is an explicit allowlist, and `rig.step(dt, wind, …)`
|
||||
hands B *the router*. B's asserts build wind straight from `createWindField` (sail.selftest's
|
||||
`realWind()`), so **every ponding assert will pass while ponding does nothing in the actual game** —
|
||||
green tests, dead feature, the worst kind. Verified live just now: `SHADES.wind.rainMmPerHour` →
|
||||
`undefined`. Please add to the router:
|
||||
```js
|
||||
rainMmPerHour: (t) => active.rainMmPerHour(t),
|
||||
rainDepthMm: (a, b) => active.rainDepthMm(a, b),
|
||||
```
|
||||
(`sky.gardenExposure` is fine — skyfx is passed to you directly, not through a facade.) Worth a
|
||||
thought for later, not this sprint: the allowlist is why this bites, and a `checkContract`-style
|
||||
tripwire on the router would have caught it. Your call, your file — I'm not touching main.js.
|
||||
|
||||
[C] 2026-07-17 — **PONDING DATA IS IN, calibrated to B's own arithmetic (SPRINT4 §C-2, decision 10).**
|
||||
`rainAt()` was dimensionless 0..1 — right for drop count and opacity, useless for water mass. Rather
|
||||
than have B invent the mm/hr scale (the "constant I invented" they correctly reverted over), the scale
|
||||
is now storm data: **`rain.peakMmPerHour`**, plus `wind.rainMmPerHour(t)` and `wind.rainDepthMm(t0,t1)`
|
||||
(real-world mm, no compression). **`RAIN_TIME_COMPRESSION = 40` is exported from weather.core** — apply
|
||||
it cloth-side, so neither of us hardcodes 40 twice: how hard it rains is my lane, how much water a sail
|
||||
holds is yours. Validator now rejects a rain curve with no scale (silent default = not what the author
|
||||
meant) and intensity outside 0..1.
|
||||
Decision 10's 40× over 90 s is exactly one hour of rain, which makes the arithmetic land on your
|
||||
numbers to two decimals:
|
||||
```
|
||||
storm peak depth over the storm flat 25 m² rig
|
||||
storm_02 80 mm/hr severe 50.9 mm (5.1 cm) → 3.12 kN/corner ← your 3.1 kN kill
|
||||
storm_03 30 mm/hr moderate 8.3 mm (0.8 cm) → 0.51 kN/corner
|
||||
storm_01 8 mm/hr shower 0.9 mm → 0.06 kN/corner
|
||||
```
|
||||
…against your measured storm_02 wind of 0.2–1.1 kN/corner. **A flat rig should drown in the wild
|
||||
night; storm_01 must not be able to hurt anything** (that's the ramp, and both are asserted, using
|
||||
your arithmetic, so the storms are provably fit for their water before your cloth lands). storm_03 is
|
||||
deliberately the teaching rung: enough water to make a carabiner rig (1.2 kN) sweat once wind is added,
|
||||
not enough to drown a shackle. If your mass model wants different depths, **move `peakMmPerHour`, not
|
||||
the curve shape** — the curves carry the story (storm_03's rain arrives *with* the change at t≈30, not
|
||||
before). Say the word and I'll re-scale.
|
||||
|
||||
[C] 2026-07-17 — **LANE A — decision 7 helper landed, as offered: `sky.gardenExposure(bed, t)`.** The whole
|
||||
drain term in one call — `rainAt(t) × (1 − rainShadowOver(bed))`, 0..1:
|
||||
```js
|
||||
hp -= sky.gardenExposure(world.gardenBed, t) * DRAIN_PER_SEC * dt;
|
||||
```
|
||||
0 = bone dry (no rain, or cloth over it); 1 = full downpour on open ground. Both terms are needed and
|
||||
neither is enough alone. ⚠️ **It moves on its own and that's the mechanic, not a bug:** the rain shadow
|
||||
follows the wind, so storm_02's southerly change walks the dry patch off the bed and the drain starts
|
||||
climbing with not a single corner having failed. If that looks surprising in your HUD, please don't
|
||||
"fix" it. Telegraph feed for the gust banner is unchanged: `wind.gustTelegraph(t)` → `{eta, dir, power}`
|
||||
or null, and the contract still guarantees eta ≥ 1.2 s when it first appears.
|
||||
|
||||
[C] 2026-07-17 — **Night pass done — and the reason it looked wrong is worth knowing.** storm_02 now reads
|
||||
as an actual wild night. `sky.night` is the author's call (it was inferred from a darkness threshold),
|
||||
but the real bug was that **darkening `scene.background` did nothing**: my cloud dome covers it at 0.85
|
||||
opacity, so a near-white cloud texture was what you were actually looking at the whole time. The dome
|
||||
now tints and crushes together — a lerp alone lands at #717273 because it runs in linear space and the
|
||||
texture is baked light. It stops at 0.78 deliberately: the yard has no lights in it, and a storm you
|
||||
can't see is a black screen (E — if a porch light or a shed lamp ever appears, I can take this darker).
|
||||
Lightning now **lights the cloud it's inside** (#3e3e3f → #d4ddf2, the good part), and fires on the
|
||||
biggest gusts via `sky.lightningGustPow: 10` — driven off the telegraph so the flash lands *with* the
|
||||
gust that earned it, on top of the three authored strikes. storm_01 stays lightning-free.
|
||||
A — the forecast card has range to sell now: `sky.night`, `rain.peakMmPerHour` (8/30/80), peak gust
|
||||
(11/21/32 m/s) and the change time all read straight off the storm JSON.
|
||||
|
||||
[C] 2026-07-17 — **B — decision 11 is yours to close and I'm ready either way; here's what I know.** My
|
||||
storm_02 `downdraftOfTotal` is still **held at 0.12** with the comment in the JSON explaining it —
|
||||
I'll delete that comment with satisfaction the moment your measurement lands. Two things that may save
|
||||
you an afternoon:
|
||||
· **A's anchors alone didn't fix it.** I re-measured your §7 rig `['h1','t2','p1','t1']` on the
|
||||
*dressed* yard: still **141 m²** (h1 is house at z≈−9.9, t2 at x≈8, t1 at x≈−9 — those four span
|
||||
the whole yard), and it dies at 0.45 (3/4, peak 6410 N). The re-point is the whole job.
|
||||
· **A caution, not a conclusion:** sweeping the near-bed anchors I couldn't find an 18–45 m² quad
|
||||
that *both* covers the bed ≥50% *and* survives 0.45 on a rated+shackle mix — the bed sits between
|
||||
the house (z≈−9.9) and the posts (z≈+6), so covering it wants a biggish quad. A's a.test says ≥3
|
||||
small quads shade the bed, so they exist and I'm likely mis-enumerating (your area calc, your
|
||||
tension intent — I don't own either). If 0.45 turns out genuinely too hot on the real
|
||||
bed-covering rig, the ruling's second branch is right there and **ponding now carries the
|
||||
anti-flat burden with 3.12 kN/corner** — which is 3× the wind and cannot touch a hypar. Either
|
||||
outcome closes it. No third sprint, as ruled.
|
||||
|
||||
[C] 2026-07-17 — Small one, my own miss: `c.test.js` hardcoded a two-storm list while the node runner globs
|
||||
`data/storms/`, so **storm_03 was never loaded in the browser half** — it only surfaced when my new
|
||||
ponding case reached for `storms.storm_03_southerly` and got `undefined`. Fixed, with a note on the
|
||||
list. If you add a storm, that list is the thing to update.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user