diff --git a/THREADS.md b/THREADS.md index da7c55f..3ec5a86 100644 --- a/THREADS.md +++ b/THREADS.md @@ -505,3 +505,82 @@ Format: `[lane letter] YYYY-MM-DD — note` a storm from a headless browser — which is what this sprint's "90 s storm_02 run captured" acceptance needs. Same code path the rAF loop uses; no test-only branch that can drift. Everything I reported above was measured through them. + +[B] 2026-07-17 — **SPRINT 2 LANDED on `lane/b`: decisions 4 & 5, the picking UI, the real-wind §7 gate.** + 39 asserts green (26 sail + 13 rigging), `checkContract('sailRig')` still conforms. + · **Decision 4** — conformed to Lane D's spelling, not the reverse: `repair(i)`, `trim(i, delta)`, + `cornerPos(i)`. All three are contract entries now rather than PROPOSED comments, so the tripwire + enforces the seam. D: `repair(i)` takes no hardware arg because prep sells exactly one kind of + spare, so it re-rigs at SHACKLE grade — an upgrade on a blown carabiner, a downgrade on a blown + rated shackle. `cornerPos(i)` is a fresh vector on the live node: measured 13 m off the anchor on + a flogging corner, so your prompt chases it. + · **Decision 5** — `sail.step(dt, wind, t, debris)` now applies sphere-vs-cloth impulses. Symmetric: + every newton-second the cloth takes out of a crate, the crate loses. Conserves to 0.000% on an + interior hit (asserted). Pinned corners are the deliberate exception — a crate off a corner dumps + its momentum into the house, which is correct, it's bolted to a wall. **Lane A: this needs the + 4th arg — `rig.step(dt, wind, windT, debris)` in main.js, or the crates fly through the sail.** + · **§7 gate now runs on the real storm JSON**, not my stub. Flat drum-tight carabiner rig cascades + 4/4; twisted mixed rig holds 4/4; twisted rig with one dodgy corner blows it and finishes 4/4 + after a single `repair()` — the sprint's DoD scenario, in an assert. + +[B] 2026-07-17 — **⚠️ LANE C — decision 3 does NOT clear its own bar yet. Numbers, before you merge.** + Your ask was: flat-horizontal peak ≥ 60% of flat-pitched over 8 directions. Measured against your + branch, 8 headings, **full 90 s**: **flat-horizontal 0.56 kN vs flat-pitched 1.66 kN = 34%.** Still a + free lunch. Why: `downdraft: 0.3` is 0.3 of the **gust component only**, and storm_02's strongest + downdraft is **−4.5 m/s** against a **32.6 m/s** horizontal peak (t=75.3 s). Pressure goes as v², so + 4.5² / (32.6·sin 16.7°)² ≈ ⅓ — which is the 34% almost exactly. To reach 60% the downdraft needs to + hit ~7.3 m/s, i.e. **downdraft ≈ 0.55–0.6 of gust power**, or make it a fraction of TOTAL speed rather + than gust-only (I'd prefer total: a gust front descends whether or not it's also the peak). + Your +116% A/B is real and I reproduced it (twisted rig 1.16 → 2.73 kN) — but it measured absolute + load on one pitched 192 m² quad, which is a different question from the horizontal-vs-pitched RATIO, + and I don't think a direction sweep was ever in it. Also worth knowing: the ratio is sensitive to what + I call "flat-pitched" (mine is 16.7°), so if you'd rather move the bar than the data, say so and I'll + make the geometry explicit in the assert. + **My assert is written and SKIPS while main has no downdraft field, so main stays green — but it goes + RED the moment your branch merges unless the downdraft rises.** You offered "a one-line data edit"; + this is me taking you up on it. Ping when it's in and I'll re-measure the same sweep. + Two other things from your entries, both confirmed: `debris.pieces` matches what I built against + (sphere r at (x,y,z), read fresh, mesh untouched — I never hold a piece past its step), and I'm now + passing your `out` vector to `wind.sample`, which I'd been ignoring — that was ~9.7k throwaway + Vector3s a second. My answer on your rain-vs-sun HP question is with Lane A, but for the record I + agree with you: wire garden HP to `rainShadowOver`, keep `coverageOver` for the daytime readout. At + night the sun shadow is a number about nothing. + +[B] 2026-07-17 — **⚠️ LANE A — the §7 cheap-rig cascade currently fires at t=0.4 s, and it's the yard.** + A flat drum-tight carabiner rig on the obvious quad `h1/h3/p2/p1` loses its first corner 0.4 s after + the storm starts — not from the storm, from PRE-TENSION alone. 192 m² at tension 1.3 is ~6 kN per + corner before any wind blows (measured per-corner peaks: h1 6.10 / h3 6.00 / p2 7.25 / p1 6.01 kN). + It's physically right — you cannot drum-tighten 192 m² on $5 carabiners — but it reads as "the rig + exploded before the storm did anything", which is a worse lesson than "the gust got it". **Decision 2 + fixes this**: once 18–45 m² quads exist, pre-tension drops off the cliff and the cascade lands + mid-storm where it belongs. Not blocking; flagging so it isn't mistaken for a cloth bug when you play + it. Related: the twisted quad `h1/t2/p1/t1` is 145 m² and survives comfortably (peak 2.73 kN with C's + downdraft), so the yard is *playable* today, just not *teaching* today. + Also: prep can't show live corner loads, because nothing is attached until commit. DESIGN.md wants + "live force arrows during planning" — that needs a preview rig stepped during prep. Cheap to do from + my side if you want it in the HUD; say the word. + +[B] 2026-07-17 — **Lane A — wiring the prep phase (this is your step 8).** `createRiggingUI({scene, + camera, domElement, world, onCommit, onMessage})` → `ui.setActive(phase === 'prep')` on phaseChange, + `ui.update(dt, t)` each frame, `ui.commit()` when Enter leaves prep — it calls back through your + `rigSail()` door exactly as you asked, so the single-door invariant holds. `ui.summary` gives the HUD + `{budget, spent, tension, spares, canStart, corners:[{anchorId,hw,rating,cost}], weakest, area}`. + It ships its own DOM panel; pass `panel:false` and render `summary` yourself if hud.js wants it. + It renders its own anchor markers because the yard has none to raycast against — world.js builds + posts and trunks, not pick targets. If you'd rather own them, take `world.anchorMarkers` and I'll + consume it; otherwise leave it with me, marker styling is prep-phase UI. + LMB rig / cycle, shift-LMB remove, `[`/`]` tension, S spare — RMB stays yours (camera orbit). + Verified by hand in `dev_rigging.html` (new, follows C's weather_demo / D's dev_player pattern): + clicked h2, cycled carabiner→shackle, budget $80→$65, weak link flagged, dashed quad preview, Enter → + sail in scene (100 verts / 162 tris, casting a real shadow across the bed) → storm → corner loads + reading 1.0–1.2 kN. **One thing worth stealing: the panel shows live sail AREA.** Picking the obvious + quad says "191 m2" *before* you commit — which is the only way the 70–192 m² problem is visible to a + player. Retire dev_rigging.html once index.html hosts prep. + +[B] 2026-07-17 — a bug worth passing on, since it's the kind every lane can have: `_checkFailure` marked a + corner broken but never gave its node its mass back, so a "blown" corner stayed pinned in mid-air and + the sail quietly went dead instead of flogging. **The cascade test missed it completely because it + forced the break by hand and called `_repin()` itself** — it set up the state the code was supposed to + produce, and so it never executed the path that was broken. The replacement drives a real overload + failure and asserts the corner tears free and keeps moving. If your suite hand-builds state before + asserting on it, it may be green over a dead code path.