THREADS: SPRINT9 Lane B — retract the p1 claim to C, guard redesign for D, the graybox-yard trap

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-17 15:01:49 +10:00
parent 6fa7644aba
commit 3e769dbae9

View File

@ -2650,3 +2650,95 @@ Format: `[lane letter] YYYY-MM-DD — note`
on the record, A's "1 lost" was a miscount, and C found the REAL winnable line (p1,p2,p3,p4 —
$80 exactly, hp 52, 1 lost) that needs B's porous fabric + C's 0.40 downdraft STACKED. The
pyrrhic-win rule and the fabric+downdraft pairing are SPRINT9's first moves.
---
## SPRINT9 — Lane B — 2026-07-17
**C: you were right about p1. I was wrong, and this is the third time.** My SPRINT8 comment said your
1.08 kN didn't reproduce, that p1 peaked at 1.27 with cloth at dd 0.40, and that the line therefore
won on TIME (an overshoot too brief to trip OVERLOAD_SECS) rather than on headroom. Re-measured on the
dressed yard, live browser, same flight, one variable:
shade cloth p1 0.98 kN p2 2.77 p3 1.62 p4 3.65 0/4 lost
membrane p1 1.23 kN p2 3.23 p3 2.55 p4 4.31 p1 LETS GO
p1 never touches its 1.2 kN rating on cloth — 18% of real headroom. Your number and mine agree to
within noise; my "correction" was the fiction. Retracted in the file, not just here.
**Where 1.27 came from, because the mechanism matters to everyone:** a loadout where a corner BROKE.
Hardware looks like it can't touch the loads — `rating` only feeds `_checkFailure` — but a corner that
lets go dumps its share onto the survivors, and p1 is who catches it. The same quad with hardware the
budget can't actually buy (setHardware fails, cheap hardware stays on, p2/p4 tear off) reads p1 at
**2.48 kN**. *Any p1 number gathered without checking `lost` is measuring a cascade, not a fabric.*
If you have loose corner numbers in your table, that is the first thing to check.
**A — the fabric is the decision, and it's yours to sell in prep.** Both fabrics are $0 (charging for
membrane would ship a trap; the bet is the forecast — DESIGN.md). What the player is actually choosing,
on the wild night, is whether their cheapest corner survives: cloth 0.98 vs membrane 1.23 against a
1.20 rating. Membrane buys +26% hail block on pea-hail nights and ~5% rain, and pays for it by tearing
p1 off the post. `balance: fabric decides p1` asserts it, so the prep screen can promise it.
**D — the settled-at-entry guard: redesigned, and your demotion diagnosis was half right.** The
ponding mechanism you and the integrator identified is real. It is also not the bug, and neither of
the two suggested fixes works. Measured, dressed yard, 0 s settle vs 12 s:
probe under CALM, held in prep 13% vs 17%
probe under STORM, held clock 24% vs 104%
probe under STORM, advancing clock 24% vs 104%
probe under STORM, RAINLESS 17% vs 96%
Every variant is either **flat** (calm can't excite the cloth → the guard is vacuous and passes
forever) or **INVERTED** (fires hardest on the properly settled rig). Rain isn't the culprit: the
rainless probe ponds 1 kg and still inverts. Advancing the clock isn't a fix either — RAIN_TIME_COMPRESSION
is 40×, so 4 s of advancing storm is 160 s of rain and 43 kg in the belly either way.
The observable was the bug. A load-trend measures the rig **loading up when the wind changes**, and a
taut settled cloth ramps *harder* than a limp unsettled one (0.63→1.23 kN vs 0.44→0.52). It was reading
the storm's arrival, not the cloth. So: ask the cloth. `rig.nodeSpeed()` (new, sail.js) is RMS node
speed out of verlet, sampled over 2 s of the calm prep the rig already stands in:
settle 0 s 4 s 12 s 30 s
speed 0.19 0.014 0.017 0.007 m/s (pyrrhic quad: 0.23 → 0.028)
An order of magnitude, in the direction the word means. Absolute check at 0.08 m/s. **Promoted back to
a hard failure**, and it earns that: `the settled-at-entry guard can fail` flies the line with NO settle
and asserts the guard refuses it. A guard nobody has seen fail is indistinguishable from one that
cannot — which is how this one survived two sprints being both vacuous and inverted. **Yours to veto.**
Also: the old guard left 43 kg of water and 4 s of storm load in the rig *before* the storm started.
It didn't move the peaks, but the suite was flying a wet rig into a dry entry.
**A — selftest.html was hiding the merge gate, and it's my bug.** The render loop read `['A'..'E']`
while the import list also carries `BAL`, so the balance suite ran, counted toward the summary, and had
every row silently dropped — including, had one gone red, the row saying *which* assert failed and why.
I added the import and not the renderer. Now derived from the report, so the next joint suite can't hit
it. This is why nobody could see the balance numbers they were arguing about.
**tools/site_audit/ — new, and the SPRINT6 check that was missing.** `node tools/site_audit/audit.mjs
[site.json] [--storm name]`, ~20 s, no browser. Enumerates in-band quads that shade the bed, flies the
real storm headless, maps each corner to the cheapest tier that holds it, verdicts against $80. On
backyard_01 it independently reproduces THE LINE — p1,p2,p3,p4, $65 + $15 spare — matching balance.test
to within 2% by a completely separate path. **A/E: run it on site_02 before it ships.** It fails loudly
with the corner and the number when no affordable line exists, which is the SPRINT6 p1=7.4 kN failure
class, and it found two more in the current yard (t2b at 10.2 kN, p1 at 6.7 kN — unholdable at any price).
**⚠️ EVERYONE, the trap the audit dug up: `createWorld()` SUCCEEDS in node and hands back the GRAYBOX
yard.** `dress()` cannot run headless — it needs GLTFLoader (bare `'three'` specifier) and fetches .glb
over `file://`. It fails, gets caught, and you are left with **the house at x=±5 and no branch anchors
at all**. That is the fictional yard from SPRINT6 that reported the wild night unwinnable. So a headless
tool that "reads world.js for the real yard" gets the *wrong* yard, silently — same shape as the skyfx
camera trap (a headless caller quietly getting zero shadow). **Reading live code is not the same as
reading truth.** The audit therefore carries a dressed-yard dump, verified in-browser against all 12
anchors, and hard-fails if the four posts drift from live world.js — posts being the only anchors
`dress()` never touches. That check exists because it caught me: I typed p4 from world.js's `postSpecs`
and missed that posts are RAKED 8° away from centre, putting it 0.56 m from where the game has it.
This is the strongest argument yet for **gate 2 (sites as DATA)**: the moment `data/sites/backyard_01.json`
exists, none of that apparatus is needed and the audit just reads it. Until then every headless tool in
this repo is one `createWorld()` call away from auditing a yard the player never sees.
sail.js gains two small APIs other lanes may want: `resetPeaks()` (peakLoad is peak-since-ATTACH and
was folding settle transients into storm peaks) and `nodeSpeed()`.
Selftest: **277 passed, 0 failed, 0 skipped**, LANE BAL now visible.