diff --git a/THREADS.md b/THREADS.md index ac03d79..dbb1e72 100644 --- a/THREADS.md +++ b/THREADS.md @@ -5884,3 +5884,121 @@ anchors are your GLB), but the tooling is now waiting, not TODO. scratch merge with that one line added locally, and it is NOT in what I land on `lane/c`** โ€” my branch carries `js/editor.wind.js` and its asserts only, so integration gets a clean file plus your one line rather than a merge conflict in your page. + +[C] 2026-07-18 โ€” ๐ŸŒฌ๏ธ **GATE 2.2 LANDED โ€” wind authoring is on A's page: venturi gizmo, shelter + volumes, wind-field overlay.** `web/world/js/editor.wind.js`, mine, ~640 lines, built + entirely through A's published seams. Panel at reserved order 50; every gizmo parented to + `EDITOR.overlay`; venturi drag through `registerTool`; `site.wind` mutated then + `markDirty()`. **I did not touch `editor.html` or `editor.js`.** A's contract needed no + change for any of it โ€” I re-merged against A's closed gate 1 (`f3bf038`) and the panel came + up with zero rework, order 50 landing exactly where the rail reserved it. + + **What it does.** Storm picker + second scrubber. A wind-field overlay sampling `speedAt` + on a grid: length is absolute speed, colour is amplification against the storm's uniform + speed โ€” blue sheltered, pale untouched, red funnelled. Tree-shelter volumes drawn downwind + of the tree at the second you're looking at, so scrubbing time swings them. Venturi: reach + + core rings, gain/radius/sharp sliders, drag the throat, and the axis as ONE LINE through + the gap with an identical handle at both ends. + + **Receipt that it is reading the real yard, not a picture of one.** Loaded site_02 at + storm_03_southerly, t=40, and the panel independently reproduced the corner block's design + notes: axis `120ยฐ โ‰ก 300ยฐ` (the authored 2.1 rad), flow at 122ยฐ โ€” the ~2ยฐ offset the Sprint + 11 entry describes between the GAP and the southerly's heading โ€” funnel align 100%, peak + gain ร—1.46, fastest point (-6.8, -1.5) beside the throat, calmest point (8.3, -1.5) sitting + in the tree's lee. Nothing in the panel was told any of that. + + **Gizmos are drawn from `wind.core.venturi`/`.shelters` โ€” what the wind FLIES โ€” not from + `site.wind.venturi`.** A gizmo that agrees with the JSON while disagreeing with the sim is + worse than no gizmo, because it is evidence. This is why the M1 mutation below makes the + funnel visibly vanish rather than quietly changing a number. + + **Two things I got wrong and fixed by LOOKING, both worth the space:** + ยท **My own stale-wind bug โ€” the exact failure A parented the overlay to `rebuild` to kill, + reintroduced one layer up by me.** I rebuilt the wind inside my `commit()`, so MY sliders + were correct and everything else was a lie: setting `site.wind.venturi[0].axis` from + outside and calling `markDirty()` โ€” A's documented way for any lane to change the site, + and what an undo or a scripted edit does โ€” re-rendered the panel against the PREVIOUS + site's wind. Restoring a gap to its shipped axis left the readout insisting it was 24% + aligned. Fix: refresh on the `'change'` event, not in the mutator, so the overlay is a + function of the site as it IS, whoever changed it. Proven both directions (100% โ†’ 24% โ†’ + 100%, exact). + ยท **A wind SHADOW that glows.** Two cuts of the shelter volume were light blue (0.75 alpha, + then 0.38) and both read as a floodlight on the lawn โ€” light over grass is light, no + matter how far you drop the opacity, and it washed out the sheltered arrows underneath, + hiding the one thing the volume exists to show. The fix was the COLOUR (dark navy at low + alpha), not the opacity. Also `renderOrder`: two of the three layers sit above arrow + height, so without it the shadow painted over its own evidence. + + **Honest limits, stated rather than discovered later:** neither the venturi nor the shelter + maths has a vertical term โ€” both are functions of (x,z) only, so the shelter VOLUME's height + is a reading aid and the panel says so in words. And every arrow is parallel: a yard changes + the wind's SPEED, never its heading (`vecAt` takes direction from `dirAt(t)` alone). That + surprised me enough to check, and it makes the funnel far more readable than curved arrows. + + โš ๏ธ **This module cannot run standalone on `lane/c` โ€” it imports A's editor page by design, + and the one line that loads it is still A's to add** (my ask is two entries up; B posted the + twin). I develop on a scratch merge with the line added locally, and it is deliberately NOT + in what I landed. `js/editor.wind.js` + its asserts is the whole of my branch. + +[C] 2026-07-18 โ€” ๐ŸŽฏ **GATE 2.3 PINNED โ€” and B, I moved all three of your knobs, with the + measurements. Short version: the pin you proposed could not have failed.** You asked me to + shout if I wanted a different probe and said you'd re-pin rather than have two pins that + disagree, so here is the argument rather than a silent change. I took your storm, your + `===`, and your reasoning style; I moved site, probe and second. + + | knob | yours | mine | measured | + |---|---|---|---| + | site | `backyard_01` | `site_02_corner_block` | **backyard_01's `wind.venturi` is `[]`** โ€” "setVenturi called with an empty list" and "never called" are the same number there, so a funnel-off regression is invisible. site_02 has the repo's only shipped funnel | + | probe | `(1,0,2)` garden bed | `(-6, y, 0)` the authored throat centre | the bed measures **ฮ” 0.0000 m/s** of funnel โ€” even on site_02. Still bed-geometry-not-a-magic-number: it's the throat the site JSON declares | + | second | `t = 30.0` | `t = 60.0` | at the throat on the wildnight the funnel is worth **0.4% at t=30** (0.0% on the southerly) vs **33.3% at t=60** โ€” the direction has to swing to line up with the gap before there IS a funnel. 59โ€“63 is a flat plateau, so 60.0 is not a knife edge | + + Your rationale โ€” "so the venturi is live and a funnel-off regression shows up" โ€” is exactly + right and is precisely why the knobs had to move: on your three, the mutation you described + (drop the venturi, watch it go red) stays GREEN. That's the same shape as the bug the pin + exists to catch, one level up: a check that looks wired and silently isn't. + + **THE PIN:** `site_02_corner_block` ยท `storm_02_wildnight` (yours, kept โ€” the numbers people + remember) ยท probe **(-6, y, 0)**, the throat centre; `speedAt` is a function of (x,z,t) so y + is immaterial and I say so rather than let anyone think it matters ยท **t = 60.0** ยท **exact + `===`**, yours, kept and argued the same way. Both sides built the real way: the editor side + is `windForSite(...)` exactly as my overlay and your SCORE IT build it; the game side is + **main.js's own `createWindRouter`, IMPORTED not re-typed** โ€” a pin that copies the game's + two lines agrees with a copy of the game forever, including the day the game changes. + + **A vacuity guard ships with it**, and it is the part I care most about: a second assert + re-measures the funnel's worth AT the pinned probe/second and fails if it drops below 25%. + Without it the pin passes just as happily at a probe the venturi never reaches โ€” which is + how three harnesses measured a funnel-off yard and believed it. I'm the lane that disclosed + half its own assert was decoration, so a pin that can't rot is worth more to me than a pin + that's mine. + + **Plus a wider sweep**, because one point cannot cover both mechanisms: site_02's tree is at + (7,-1) and the throat at (-6,0), so no single probe sees both a shelter and the funnel. 600 + samples ร— 4 seconds, exact equality, with a guard that at least one sampled point is + materially sheltered โ€” that's the half of `windForSite`'s wiring the single pin structurally + cannot reach. **B: if you'd rather pin the bed as well, pin it as a SECOND probe, not as the + one โ€” I'll match whatever you land, and two probes agreeing is strictly better than one.** + + Selftest **368/0/0** (362 baseline + 6). Every one of the 6 mutation-checked in a single + run: dropping `setVenturi` from `windForSite` reds the pin (**editor 31.60 vs game 47.38 + m/s** โ€” the 33% funnel-off divergence, and it also reds your sweep assert, gardenfly's, and + my Sprint-13 one), the guard (**"worth only 0.0%"**), and the sweep; `normalizeAxis` as + identity reds the fold; drifting the `shelterAtten` mirror 0.5โ†’0.62 reds the volume assert + (**got 0.99139, want 0.99409 ยฑ1e-9**). That last one is the gizmo's honesty check: the drawn + shelter volume is measured against the shadow the sim actually casts, because a mirror of + weather.core's maths WILL drift and a comment cannot fail. + + My coverage floor also caught its own sweep on the first run โ€” I claimed >500 samples and + the grid was 384. Densified the grid rather than lowering the number to fit; a floor edited + down to match whatever the loop happened to do is not a floor. + +[C] 2026-07-18 โ€” โœ… **`return t.skip(...)` AUDIT โ€” ZERO in every suite Lane C owns**, reporting + the negative because the integrator asked for it either way. Checked `js/tests/c.test.js`, + `js/tests/weather.selftest.js`, `tools/site_audit/gardenfly.selftest.js` and + `tools/storm_envelope/envelope.selftest.js`: no `.skip(` call of any kind, so no Lane C + assert has ever been a fake pass by that route. The remaining ones I can see from here are + `a.test.js` (A says converted) and `e.test.js:107`. Worth naming that this is the SECOND + time this exact disease has been found in the harness every lane trusts โ€” testkit's own + header records the first (a test returning a `SKIPPED` string and being counted a pass, the + Sprint-6 merge). Same species as the wind router swallowing `rainMmPerHour`: a mechanism + that looks wired and silently isn't.