HardYards/web/world/js
type-two 4718548c38 Lane B S18 gates 2+3+1: ⚠️ THE VARIABLE IS POROSITY; the clean boundary is DWELL; the knife
One landing, three gates and Lane C's clock finding. (Not split: sail.js,
rigging.js and balance.test.js each carry work for more than one gate and a
per-hunk split would have been fiction.)

GATE 2 — THE VARIABLE IS `porosity`, AND THE LIAR WAS THE GAME.
`rig.setFabric()` has exactly one call site, `RiggingSession.commit(rig)`, and
the shipped commit path never reaches it: the rigging UI's commit() calls
onCommit and main.js's rigSail goes straight to rig.attach(). So
session.commit(rig) runs in TESTS AND BENCHES ONLY, and the sim's rig (built
once at boot as `new SailRig({anchors})`, porosity defaulting to 0) has flown
the waterproof membrane every night of every playtest while the panel, the
SCORE IT card, the HUD row, the invoice and every audit number said shade
cloth. The F key moved the paperwork and never the physics.

    shade cloth 0.30 (what every card promised)  0/4 lost  p2 3.07  p3 1.78
    membrane 0      (what the sim actually flew)  2/4 lost  p2 3.48  p3 3.90

Row two is D's cold play AND the pin's own _playedReference (hp 55.7, p2 then
p3) to the decimal, reproduced in the shipped game. It also retires C's
"benches under-read the real UI by 5-15%, cause unfound": with the fabric held
equal, the bench and the FULL game chain agree to three decimals, two sites,
two storms. Debris hits on the sail: 0 — D's candidate ruled out by
measurement. Fixed on my side: commit() carries the fabric as onCommit's 4th
argument; dev_rigging.html picks it up, which is the shape of the one-line
pickup filed for A's rigSail.

GATE 3 — THE CLEAN BOUNDARY IS THE FUSE, NOT A PERCENTAGE. Same flight, two
corners, identical $15 shackles, same 3.2 kN rating:
    p1  3.528 kN = 10.3% OVER -> HELD  (over for 0.150 s)
    p2  3.493 kN =  9.2% OVER -> BROKE (over for 0.400 s)
p1 peaks HIGHER and survives, so "peak within X% of rating" is wrong at every
X. sail.js never used peak — it burns a 0.4 s overload fuse. OVERLOAD_SECS /
OVERLOAD_RECOVER are exported and priceCandidate replays them per corner per
tier, so the audit and the sim now answer "will it hold" with one arithmetic.
AUDIT.MARGIN stops deciding anything and says why in its own comment.

GATE 1 — THE RIG YOU DIDN'T BUILD, AND THE KNIFE. `_releaseCorner` is now the
one definition of a corner letting go (it was open-coded twice); on top of it:
`failCorner` for a corner that arrived down, `cutAway` for DESIGN.md line 165
emitting A's requested `{t, corners, reason}`, and `session.inherit()` which
adopts somebody else's rig for $0 and keeps it out of the refund — because
week.js pays back half of every intact corner, so counting a stranger's steel
as yours would have the ledger PUNISHING the player for every corner they cut.
Neither a cut nor an inherited corner emits `break`: warranty must not chase a
corner you chose to let go or one you never hung.

LANE C's CLOCK FINDING, fixed. `_substep` samples the wind at `this.t`, which
attach() zeroes — safe only while the caller's clock starts with it, exactly as
attach()'s own comment says. An emergency opens at t=30, so the cloth would fly
the storm's first 60 s under a sky flying its last 60. attach() now takes
{at, wind} (seeded, or genuinely pre-flown so the drape and the pond are real),
seedClock() is the late door, and `clockSkew` makes any future drift readable
instead of silent — measured 90 N vs 655 N for the same rig entering at t=0 vs
t=40, which is the size of what was hiding.

+15 asserts, all node-verified; the two gate-2 pins mutation-checked both ways.
2026-07-25 19:53:06 +10:00
..
tests Lane B S18 gates 2+3+1: ⚠️ THE VARIABLE IS POROSITY; the clean boundary is DWELL; the knife 2026-07-25 19:53:06 +10:00
anchor_ratings.gen.js WIP Lane E S17 gate 3.1: corroded tier (PREDECESSOR STATE — negative control M1 still applied) 2026-07-21 01:18:44 +10:00
board.js Lane D S17 gate 3.2: THE POOL YARD — authored cold, in the game only by being chosen 2026-07-21 03:53:20 +10:00
broom.js Lane D: calibrate the douse to B's real ponds; fix the sagging-belly refusal 2026-07-17 09:23:17 +10:00
camera.js Sprint 13 gate 2.5: two camera fixes — spawn framing, aftermath cloth-swallow 2026-07-18 01:18:29 +10:00
contracts.js WIP Lane E S17 gate 3.1: corroded tier (PREDECESSOR STATE — negative control M1 still applied) 2026-07-21 01:18:44 +10:00
debris.js debris: delete the dead sail.nodes seam — sail._applyDebris is the one contact model 2026-07-18 19:03:36 +10:00
editor_score.js Lane B S17 gates 0.3 + 2: the card names its cloth, the session speaks for the client 2026-07-21 02:45:49 +10:00
editor_storm.js Lane C S16: gate 3 lands — the fabric bet pinned, forecast argues it, storm in the lists; selftest 438/0/0 2026-07-20 20:33:10 +10:00
editor.js Lane A S17 second shift: separation-block fabric field RULED (B's S16 ask), E's corroded-post palette hunk applied verbatim, enum widening endorsed 2026-07-21 02:22:27 +10:00
editor.wind.js Lane C S15: converge gate 2.2 onto B's THREADS proposal — B's filename/global/source/default taken, storm+time API kept; THREADS: seam receipt + gate-1.3 storm-side zero-delta confirmation 2026-07-20 18:23:31 +10:00
garden.js Sprint 13: garden model exported as garden.js (B's gate-1.2 blocker) + applyMute(sky,on) as a value 2026-07-18 12:44:37 +10:00
hud.js S17 integration: apply C's offerBand hunk — the board stops lying about night 7 (hail on every offer card) 2026-07-21 06:25:14 +10:00
interact.js Lane D: calibrate the douse to B's real ponds; fix the sagging-belly refusal 2026-07-17 09:23:17 +10:00
ladder.js Lane D: ladder survives a mid-carry knockdown, and the prompt names the right bracket 2026-07-18 01:00:01 +10:00
main.js S17 integration: apply C's offerBand hunk — the board stops lying about night 7 (hail on every offer card) 2026-07-21 06:25:14 +10:00
player.js Lane D: the player leans into the wind (gate 2.4 — as a root pose, not a clip) 2026-07-18 01:33:47 +10:00
player.sim.js Lane D: the player leans into the wind (gate 2.4 — as a root pose, not a clip) 2026-07-18 01:33:47 +10:00
ratings.js Lane E S15 gate 1.2: ratings.js — factoryExtras(type), the runtime end of the manifest; refuses to guess for ambiguous types, warns once 2026-07-20 17:49:38 +10:00
rigging.js Lane B S18 gates 2+3+1: ⚠️ THE VARIABLE IS POROSITY; the clean boundary is DWELL; the knife 2026-07-25 19:53:06 +10:00
rigging.selftest.js Lane B S18 gates 2+3+1: ⚠️ THE VARIABLE IS POROSITY; the clean boundary is DWELL; the knife 2026-07-25 19:53:06 +10:00
sail.js Lane B S18 gates 2+3+1: ⚠️ THE VARIABLE IS POROSITY; the clean boundary is DWELL; the knife 2026-07-25 19:53:06 +10:00
sail.selftest.js Lane B S18 gates 2+3+1: ⚠️ THE VARIABLE IS POROSITY; the clean boundary is DWELL; the knife 2026-07-25 19:53:06 +10:00
skyfx.js Lane C S13 gate 2.3 + D's sliver: ambient leaves from 30 km/h; hail pool hides at zero 2026-07-18 11:51:26 +10:00
stormgen.js Lane C S16: makeStorm(seed) — seeded storms inside authored envelopes (gate 3.2) 2026-07-20 20:11:34 +10:00
testkit.js Gate 0: name the harness dispute's cause; make skips real (testkit) 2026-07-17 10:21:01 +10:00
weather.core.js Lane C S16: gate 3 lands — the fabric bet pinned, forecast argues it, storm in the lists; selftest 438/0/0 2026-07-20 20:33:10 +10:00
weather.js Lane C S17 gate 1 (cont.): the OFFER BAND and the PAIRING law — night 7 stops lying 2026-07-21 03:07:02 +10:00
week.js Lane A S17 gates 0+1+2: THE JOB BOARD — two offers a morning, the chosen job IS the night 2026-07-21 02:11:16 +10:00
world.js Sprint 15 integration: merge all lanes (418/0/0), apply E's world.js manifest patch, [I] entry 2026-07-20 19:38:11 +10:00