GATE 0.3 — THE CHARTER, SAID OUT LOUD (D's soaker finding, A consulted on
wording and their every-card rule adopted). The audit always flew shade cloth
and never said so; on the soaker that silence read 'WINNABLE over an all-DEAD
garden — the night is broken'. Now: AUDIT.POROSITY is the one charter knob
(sweep.js + gardenfly.js both read it), scorecard.fabricCharter() names the
fabric and whether tonight's stones pass its weave (same hailBlockFor the sim
charges), and the SCORE IT header prints 'fabric · shade cloth (sweep
charter)' on EVERY card — plus, on leaky-storm nights, A's wording: the
fabric is the bet, and the audit does not place it.
GATE 2 — CLIENT CONSTRAINTS: A's shapes (seam contract, built against as
posted), this lane's teeth.
· rigging.js: validateNightConstraint (checked enum, both ends of the seam),
setConstraints (survives reset like _startBudget; [] is the only clear).
The house ban refuses the pick with the CLIENT'S WORDS as the ticker
reason; the cap refuses at SPEND time on every path (rig / cycle / set /
spares / fabric), exact at the boundary, refunds always free — and commit
is belt-and-braces: a caller who walked around the doors gets a THROW.
The cap covers the whole invoice, spares included — the client queries the
invoice, not the hardware line.
· sweep.js: applyNightConstraints, ONE function both drivers share — bans
shrink the candidate list (a card that recommends a forbidden line is the
card lying), the cap re-prices affordable/clean without touching a load.
· scoreSite carries constraints/budget/constrainedOut; the card prints the
client's terms next to funnel and fabric, and tells 'no geometry' from
'client forbids all of it'.
· spent getter now measures against _startBudget (the re-banked session was
measuring spend against the default bank).
Selftest 485/0/0 in the browser (474 + exactly the 11 added: 1 sail, 4
session, 2 charter, 4 audit-constraints). Wiring line for A filed in THREADS:
session.setConstraints(week.job?.constraints ?? []) at the night boundary.
findCandidates/priceCandidate/judgeSweep extracted so the sync auditSweep and
the new auditSweepAsync are ONE copy of the math; scoreSite drives the async
path with onProgress ticks (sweep/fly/separation phases). Yield is a
MessageChannel task, not setTimeout — Chrome's intensive timer throttling
turned an occluded run into one flight per minute, measured. Three new
asserts, mutation-checked red-then-green in one sitting (impure chunk + a
swallowed tick = all three red with the intended diagnostics).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
C measured my proposed pin and it could not have failed. Converging rather than
landing a second pin that disagrees. Knobs are now C's:
site_02_corner_block / storm_02_wildnight / throat (-6,y,0) / t=60.0 / exact ===
Three wrong answers before this, all the same failure:
1. backyard_01 has venturi: [] — nothing to detect. (I found this one.)
2. site_02's BED is funnel-blind, Δ 0.0000 m/s. (Both of us, independently.)
3. t=30 on the wildnight is worth 0.44%. I MISSED THIS ONE: my throat
measurement read a healthy Δ +4.948 m/s and I trusted it, but that was on
storm_03b_earlybuster, not the wildnight the pin flies. A sensitivity
measured on one storm says nothing about another. The mutation check I was
proud of would have passed on the wrong storm.
Adopted C's vacuity guard and its 25% threshold — one number, not two. It is
the check that would have caught my proposal on its own: mutation asks 'did it
move', the guard asks 'is the funnel worth enough here that a wiring bug could
not hide in the rounding'.
Game side now imports main.js's createWindRouter instead of retyping its two
wiring lines (C's correction). My old version built BOTH sides with
windForSite, so it could only have caught a bad input, never a router that
routed wrong.
CONVERGENCE RECEIPT — my chain reads C's numbers to the decimal:
t=60 throat editor 47.38 game 47.38 exact === true funnel-off 31.60 = 33.3%
t=30 throat on 18.55 off 18.46 = 0.44% (C's finding, reproduced)
What my half adds over C's: C pins windForSite (the builder) against the game;
this pins buildScoringWorld — the path SCORE IT actually runs — against the
same router. Three chains, one number.
Kept the garden bed as a SECOND probe on C's explicit offer: what the sail
shades is what the audit is about, and equality there is a real claim even
though the funnel is not what decides it. Labelled as carrying no funnel
tripwire, because it cannot.
Mutation-checked: setting t back to 30 reds the guard and both tripwires with
the exact diagnostic (0.44%, 18.46 -> 18.55) while the EQUALITY asserts stay
green — which is precisely C's argument, reproduced on my own chain.
Selftest 375 passed / 0 failed / 0 skipped.
Two chains built independently — GAME (loadSite -> createWorld -> dress ->
windForSite) and EDITOR (siteClone-shaped object -> buildScoringWorld ->
windForSite) — asserted EXACTLY equal at t=30, speed and vector, at every
probe on both shipped yards. Exactly, not nearly: a tolerance is where the
funnel-off bug hid, and it moved the S13 headline 91.5 -> 39.8.
The mutation checks caught my own probe TWICE before this went green, which is
the only reason it is worth anything:
1. I proposed backyard_01 in THREADS. It declares venturi: [] — no funnel to
lose, so a funnel-off regression could never turn it red.
2. So I moved to site_02 — and its BED is 6.08 m from a radius-5 throat.
Starving that yard of its venturi moved the bed probe by 0.000 m/s.
Both versions passed their equality assert and both were worthless.
Measured sensitivity on site_02/earlybuster at t=30 (funnel on/off, trees
present/hidden):
bed ( 0, 1) 12.452 dV +0.000 dT +0.000 <- blind, not a pin site
throat (-6, 0) 15.910 dV +4.948 dT +0.000 <- funnel tripwire
near tr1 ( 7,-1) 8.593 dV +0.000 dT -4.891 <- shelter tripwire
Mutation-checked: starving the editor clone of its venturi turns the @throat
equality red (plus both funnel asserts), while bed/tree stay green exactly as
the table predicts.
Also recorded as a pin, because it is load-bearing for anyone reading the
score card: the corner block's funnel does NOT reach its garden bed. It is
aimed at the rigging zone (the site JSON says so: q1 is 3.5 m inside the
radius), so on that yard the funnel is a hardware-load story, not a
garden-exposure one.
Selftest 374 passed / 0 failed / 0 skipped (362 baseline + 12).