The scoring quantity is the FLOWN garden state now, not static cover%. New gardenfly.js: windForSite (C's shared builder — venturi + shelters, one copy in the repo) → real attach (throws on D's skipped-attach trap) → skyfx exposure → garden.js, per candidate line; cover% demoted to a labelled geometry diagnostic. audit.html flies every affordable line + bare bed and judges the site's pinned separation block on the block's own storm; audit.mjs (node) keeps fast winnability, gains p5 in its verified dump, and points at the browser for garden truth. On C's correction, three structural adoptions: - windForSite everywhere (three harnesses independently mis-built site wind; no fourth copy); - LIVE anchors via the re-pointable wind proxy — the audit's own frozen-sway remap was C's landmine 2 wearing my file's name (audit.html:69); - the MARGIN rule (AUDIT.MARGIN = 0.15, one copy): every row prices twice — $hw holds vs $cleanHw with >=15% headroom — and only clean lines are winners; verdict code 'marginal-only' when the budget can only buy the knife edge (D's 39.8-TATTERED wild night, C's dead 91.9 headline). Also game-true flight: the phantom 12s calm settle is GONE from the sweep (commit->attach->storm is one keypress; the settle skewed every storm sample 12 s off the authored curve — SailRig samples wind at its INTERNAL clock). That skew is not academic: it is a.test's separation-flight harness too, and gardenfly.selftest now pins BOTH chains (game-true 63.8 tattered vs skewed 68.4 'full' on the pinned p5 line — the disagreement is flagged to A in THREADS, not overruled here). The pinned recipe also sweeps regardless of the 18-45 band (it is 45.9 m² — the band predates p5; flagged to A). Selftest 356/0/0 on the scratch merge (b+a+c@45bdc2d). Mutation-checked: MARGIN=0 reddens the margin test (node); dropping siteDef from gardenfly's windForSite call reddens the venturi test (browser, exact test named). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
46 lines
2.6 KiB
JavaScript
46 lines
2.6 KiB
JavaScript
/**
|
|
* Lane B selftests — cloth, corner loads, failure cascade, prep economy.
|
|
*
|
|
* The asserts themselves live next to the code they test, in
|
|
* `js/sail.selftest.js` and `js/rigging.selftest.js`, exported as [name, fn]
|
|
* pairs. This file is only the adapter that hands them to Lane A's Suite.
|
|
*
|
|
* The reason for the indirection: those two modules also run under plain
|
|
* `node web/world/js/sail.selftest.js` — no browser, no server, no renderer,
|
|
* ~6 s — which is how the cloth got proven before M0 landed. Keeping the
|
|
* asserts there means the browser suite and the headless suite can never drift,
|
|
* because they are literally the same array.
|
|
*
|
|
* PLAN3D §5-B asked for three asserts. All three are in there, plus a statics
|
|
* balance that pins the load meter to real newtons:
|
|
* 1. hypar sheds load — scored on WORST CASE over eight wind directions
|
|
* rather than one, because Lane C's storms veer and the player never gets
|
|
* to pick the wind. Per-direction would be a false assert: a flat sail
|
|
* sitting edge-on to the wind genuinely has low drag and beats the hypar
|
|
* from that one angle. Worst-case is what the hardware has to survive.
|
|
* 2. cascade — break a corner at fixed t, a neighbour's load spikes >= 2x.
|
|
* 3. determinism — byte-equal load traces, plus ragged frame dt converging on
|
|
* the fixed-dt trace (Lane A's render loop delivers ragged dt, so the
|
|
* accumulator has to absorb it or none of this applies to the real game).
|
|
*/
|
|
|
|
import { SAIL_TESTS } from '../sail.selftest.js';
|
|
import { RIGGING_TESTS } from '../rigging.selftest.js';
|
|
import { SWEEP_TESTS } from '../../../../tools/site_audit/sweep.selftest.js';
|
|
import { buildGardenflyTests } from '../../../../tools/site_audit/gardenfly.selftest.js';
|
|
|
|
/** @param {import('../testkit.js').Suite} t */
|
|
export default async function run(t) {
|
|
for (const [name, fn] of SAIL_TESTS) t.test(name, fn);
|
|
for (const [name, fn] of RIGGING_TESTS) t.test(`rigging: ${name}`, fn);
|
|
// site_audit's own sweep. SPRINT11: the tool was flying site_02 with the
|
|
// venturi switched off — the auditor needed an auditor. See sweep.selftest.js.
|
|
for (const [name, fn] of SWEEP_TESTS) t.test(`site_audit: ${name}`, fn);
|
|
// SPRINT13: the audit's garden engine (gardenfly.js) — browser-only, so the
|
|
// flights run here in the async prelude and the tests assert on the results
|
|
// (Suite.test cannot await; a.test's pinned-separation flight is the
|
|
// precedent). run() is async now — runAll awaits it.
|
|
const gardenflyTests = await buildGardenflyTests();
|
|
for (const [name, fn] of gardenflyTests) t.test(`site_audit: ${name}`, fn);
|
|
}
|