diff --git a/LANE_PROMPTS.md b/LANE_PROMPTS.md index d90a741..9762d50 100644 --- a/LANE_PROMPTS.md +++ b/LANE_PROMPTS.md @@ -835,22 +835,102 @@ byte-identical JSON out) even though its UI needn't be. --- -# SPRINT 15 prompts (teeth — your work follows you) +# SPRINT 15 prompts (the posts tell the truth, the editor stops being batch) -Same rules: own clone, own branch, rebase onto latest main FIRST -(`git fetch origin && git reset --hard origin/lane/` on a clean tree — -your local branch is BEHIND after integration fast-forwards). Read -**ROADMAP.md** (new — the growth canon; John resolved S14's gate 4: all three -branches, sequenced, teeth first), THREADS from the last [I] entry (the -fresh-eyes review landed six fixes on main — the fabric key, the divergence -break and the pond clamp are news your lane builds on), then SPRINT15.md. -Ports: A 8824, B 8825, C 8826, D 8827, E 8828 (untracked launch.json, copy -the example). Baseline selftest **409/0/0**. The one-variable rule is law: -the new site and the new storm never debut on the same night. +Same rules: own clone, own branch, `git fetch origin && git reset --hard +origin/lane/` on a clean tree first. **The remote was RENAMED mid-Sprint-14 +— it is `monster/hardyards.git` now, not `monster/shades`. All six checkouts +are already repointed; if a push ever 404s, check for a rename before assuming +loss.** Read docs/MANUAL.md, THREADS' last [I], then SPRINT15.md. Ports: A +8824, B 8825, C 8826, D 8827, E 8828. Baseline **406/0/0**. ## Lane A — Sprint 15 -> You are Lane A on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, -> THREADS' last [I], then SPRINT15.md. Gate 1 is yours: THE LEDGER. The week +> You are Lane A on SHADES 3D (the game is HARD YARDS, public at +> partly.party/hardyards), Sprint 15. Rebase onto main, read docs/MANUAL.md, +> THREADS' last [I], then SPRINT15.md. **Gate 1 ruling is yours and comes +> FIRST, before anyone measures**: the factory bakes `rating_hint = 0.9` on +> sail_post's top_anchor, but JSON-declared posts never run adoptAnchor and +> default to 1.00, so every honest post in the shipped game is 10% stronger +> than designed. Rule 0.90 (design intent reaches the sim) or 1.00 (the +> shipped, balanced-against value becomes canon and E corrects the bake) — +> both defensible, and every played verdict in THREADS measured 1.00. Then +> fix the MECHANISM either way: a baked hint must never again fail to reach a +> JSON-declared anchor silently. Gate 3 is also yours: Enter commits number +> fields, out-of-bounds placement must stop reading VALID, zoom beyond the +> wheel, and the separation-field question with B. Do NOT start +> reputation/site_03/heatwave — gate 4 is John's. + +## Lane B — Sprint 15 +> You are Lane B on SHADES 3D, Sprint 15. Rebase onto main, read +> docs/MANUAL.md, THREADS' last [I], then SPRINT15.md. Gate 2 is yours: D's +> verdict is that your 75–82 s fully-blocked SCORE IT "made the editor batch, +> not iterative — I scored once and shipped that run rather than tuning." +> Fix it in D's priority order: a progress tick that actually paints first +> (D thinks that matters more than raw speed), then yielding so the page stays +> responsive, then speed (cost scales like C(n,4) in candidate quads — a +> coarse ranking pass before flying is likely worth more than micro-optimising +> the sim). **Pin the current site_02 and site_03 scores before you start and +> reproduce them exactly after** — a faster score that moved a number is a +> regression. Also with C: one storm picker, not two independently-defaulted +> ones. Gate 1.3: re-measure both yards once A rules the post hint, post the +> deltas as a table, propose retunes with evidence. + +## Lane C — Sprint 15 +> You are Lane C on SHADES 3D, Sprint 15. Rebase onto main, read +> docs/MANUAL.md, THREADS' last [I], then SPRINT15.md. Lighter sprint for you +> by design — gate 1 is a post-rating ruling and gate 2 is B's performance +> work. Yours: (1) with B, collapse the two independently-defaulted storm +> pickers into one shared selection (D scored the wildnight while viewing the +> southerly — two harnesses disagreeing inside a single page, which is the +> exact class of bug this repo keeps paying for). (2) Second-harness B's +> re-measure once A rules the post hint: if every post moves 10%, the storm +> side should predict the same shift your envelope tool would. (3) Your call +> on anything the wind panel revealed that you filed and didn't land — D +> called it "the best surface on the page", so if there's polish you wanted, +> this is the sprint with room for it. + +## Lane D — Sprint 15 +> You are Lane D on SHADES 3D, Sprint 15. Rebase onto main, read +> docs/MANUAL.md, THREADS' last [I], then SPRINT15.md. Two judgements, both +> yours alone. (1) Gate 1.4: once A rules the post hint and B re-measures, +> re-play the affected nights and say whether the change is FELT — if 0.90 +> makes the honest line stop holding, that is a design change wearing a +> bugfix's clothes and you are the one who says so. (2) Gate 2.3: re-author or +> re-tune a yard with B's faster score loop and say whether it changed how you +> work — your "batch, not iterative" line is what bought this sprint, so you +> are the acceptance test for the fix. Your `site_03_swing_lawn` stays inert +> until gate 4 picks the more-sites branch; don't wire it to a night. + +## Lane E — Sprint 15 +> You are Lane E on SHADES 3D, Sprint 15. Rebase onto main, read +> docs/MANUAL.md, THREADS' last [I], then SPRINT15.md. Gate 1.2 is yours and +> it's short: once A rules the sail_post hint (0.90 or 1.00), make the factory +> agree, and PIN it — an assert that a JSON-declared anchor of a type the +> factory rates carries that rating at runtime. This is the third member of +> the family your Sprint 14 audit started (no silent anchors, no unpriced +> collateral, and now no baked hint that never arrives), and it is the general +> fix for the bug class that shipped both the free carport and the perfect +> posts. With the sprint's spare room: your call on palette gaps D hit while +> authoring cold — read their gate-3.2 entry and propose what the next yard +> needs, with reasoning baked beside the numbers as always. + +--- + +# SPRINT 16 prompts (teeth — your work follows you) — ⚠️ FIRE ONLY AFTER SPRINT 15 INTEGRATES + +Same rules: own clone, own branch, rebase onto latest main FIRST +(`git fetch origin && git reset --hard origin/lane/` on a clean tree). +Read **ROADMAP.md** (the growth canon — John resolved the S14/S15 branch +question: all three, sequenced, teeth first), THREADS from the last [I], +then SPRINT16.md. Ports: A 8824, B 8825, C 8826, D 8827, E 8828. Baseline: +whatever Sprint 15 landed (409/0/0 pre-15 + S15's additions — read the [I]). +The one-variable rule is law: the new site and the new storm never debut on +the same night. Numbers S15's post ruling moved get RE-MEASURED before they +get pinned — no pre-ruling number survives into a pin. + +## Lane A — Sprint 16 +> You are Lane A on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md, +> THREADS' last [I], then SPRINT16.md. Gate 1 is yours: THE LEDGER. The week > records every committed rig (corners, hardware, what broke, what was > repaired); a corner still broken at dawn books a WARRANTY line on the next > morning's job sheet — hardware's shop price, off the fee AT QUOTE TIME, so @@ -863,74 +943,72 @@ the new site and the new storm never debut on the same night. > a.test: warranty only on unrepaired breaks; quote==settle with a warranty > line; rep untouched on designed losses; clean-week negative control. You > also print C's hail-size and rain lines on the forecast block (their gate -> 3.3 exposes them). Pool: dev-line visibility default, second carry. Do NOT -> build the job board — that's arc 2, and gate 6 aims it. +> 3.3 exposes them). Do NOT build the job board — arc 2, and gate 6 aims it. -## Lane B — Sprint 15 -> You are Lane B on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, -> THREADS' last [I] (the review touched YOUR files: the divergence break in -> _checkFailure, the post-flow pond clamp, the F key in your rigging UI, and -> a fixed tautology in your own suite — read the receipts), then SPRINT15.md. -> Gate 2 is yours. (a) THE HEAL: after a divergence break, non-finite node -> positions reset from the nearest finite neighbour (prev matched — verlet -> must not invent velocity); assert finite within 1 s, mutation-proof, and -> the existing divergence test stays untouched. (b) THE RECORD: the chosen -> fabric prints in prep summary, invoice, and the verdict when it mattered — -> the F key exists, now the paperwork remembers the bet. (c) THE PIN: site_02 -> declares no separation block and the SCORE IT card calls it out — run -> gardenfly, pin the honest held-vs-bare target, assert it; if the yard -> can't separate at $80, that's a FINDING for THREADS, not a fudged pin. -> Pool: the >83 ms determinism bound gets a pinning test or a documented -> waiver; membrane pricing stays parked until rain's drain weight moves. +## Lane B — Sprint 16 +> You are Lane B on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md, +> THREADS' last [I], then SPRINT16.md. Gate 2 is yours. (a) THE HEAL: after a +> divergence break (landed in the fresh-eyes review — read that [I]), non- +> finite node positions reset from the nearest finite neighbour (prev matched +> — verlet must not invent velocity) and non-finite WATER zeroes (a NaN pond +> re-poisons the heal through the weight term); assert finite within 1 s, +> mutation-proof, and the existing divergence test stays untouched. (b) THE +> RECORD: the chosen fabric prints in prep summary, invoice, and the verdict +> when it mattered — the F key exists, now the paperwork remembers the bet. +> (c) THE PIN, post-ruling numbers only: run gardenfly on site_02 (and the +> swing lawn if unpinned), pin the honest held-vs-bare separation, assert it +> like backyard_01's; if a yard can't separate at $80 that's a FINDING for +> THREADS, not a fudged pin. Pool: the >83 ms determinism bound gets a +> pinning test or a documented waiver. -## Lane C — Sprint 15 -> You are Lane C on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, -> THREADS' last [I], then SPRINT15.md. Gate 3 is yours. (1) STORM_06, the +## Lane C — Sprint 16 +> You are Lane C on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md, +> THREADS' last [I], then SPRINT16.md. Gate 3 is yours. (1) STORM_06, the > pea-hail soaker: stones under your own 2 mm-weave blocking ruling, heavy > rain, mild wind — the first night membrane is the RIGHT answer. Cloth must > leak fine hail all night; membrane must pond (the broom matters); the > membrane line must be affordable at $80 and MEASURABLY better — gardenfly -> and site_audit numbers pinned in your selftest, funnel state in the header. -> If cloth still wins, retune until the bet is real: a decoration night is -> worse than no night. (2) makeStorm(seed): a seeded generator inside -> authored envelopes, deterministic (same seed → byte-equal def), schema- -> valid across 100 seeds, storm_envelope-tested — arc 4's roguelite eats -> this; no shipped night uses it yet. (3) Expose hail stone size and rain -> rate for the forecast block (A prints them) — bands, not promises. The -> one-variable rule binds you to D: storm_06 flies over a yard the player -> already knows. +> and site_audit numbers pinned in your selftest, funnel state in the header, +> post-ruling tree only. If cloth still wins, retune until the bet is real: a +> decoration night is worse than no night. (2) makeStorm(seed): a seeded +> generator inside authored envelopes, deterministic (same seed → byte-equal +> def), schema-valid across 100 seeds, storm_envelope-tested — arc 4's +> roguelite eats this; no shipped night uses it yet. (3) Expose hail stone +> size and rain rate for the forecast block (A prints them) — bands, not +> promises. The one-variable rule binds you to D: storm_06 flies over a yard +> the player already knows. -## Lane D — Sprint 15 -> You are Lane D on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, -> THREADS' last [I], then SPRINT15.md. Gate 4 is yours and it's the sprint's -> acceptance test again. (1) site_03 SHIPS this time: author it cold in the -> editor (no JSON hand-editing, no console — the S14 discipline), any theme -> E's palette supports, winnable-but-mean at $80, separation pinned, -> collateral priced, _design/_why filled; file every editor friction in -> THREADS. (2) The week becomes SIX nights: you own the NIGHTS order and the -> two new briefs (client flavour is data — the corner block got a name and a -> reason; match that bar). One-variable rule: site_03 debuts under a storm -> the player has survived; storm_06 flies over a yard they know; never both -> new at once. Night 5's beyond-saving arc and going-broke-never-on-the- -> final-night must survive your re-ordering — a.test will tell you. (3) -> Cold-play all six on one wallet and file the verdict: is a warranty line -> FAIR when it lands? Does rep-moves-pay feel earned? Is night 6's bet -> legible from the forecast alone? Your play is gates 1–3's acceptance. -> Pool: the phantom-sail seam, third sprint — verify A's fix or keep the -> repro honest. +## Lane D — Sprint 16 +> You are Lane D on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md, +> THREADS' last [I], then SPRINT16.md. Gate 4 is yours. (1) Your swing lawn +> enters the rotation: re-score site_03_swing_lawn on the post-ruling tree +> with S15's faster loop (the numbers that made it winnable-but-mean may have +> moved), fix what moved, wire its NIGHTS entry. (2) The week becomes SIX +> nights: you own the order and the two new briefs (client flavour is data — +> the corner block got a name and a reason; the swing set's kid deserves one +> too). One-variable rule: the swing lawn debuts under a storm the player has +> survived; storm_06 flies over a yard they know; never both new at once. +> Night 5's beyond-saving arc and going-broke-never-on-the-final-night must +> survive your re-ordering — a.test will tell you. (3) Cold-play all six on +> one wallet and file the verdict: is a warranty line FAIR when it lands? +> Does rep-moves-pay feel earned? Is night 6's bet legible from the forecast +> alone? Your play is gates 1–3's acceptance. Pool: the phantom-sail seam if +> S15 didn't close it. -## Lane E — Sprint 15 -> You are Lane E on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, -> THREADS' last [I], then SPRINT15.md. Gate 5 is yours: arc 2 needs yards +## Lane E — Sprint 16 +> You are Lane E on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md, +> THREADS' last [I], then SPRINT16.md. Gate 5 is yours: arc 2 needs yards > this palette cannot dress. Build TWO of three, factory-built, byte-stable, -> negative-control-proven (build the wrong one on purpose, watch red): the -> GLASSHOUSE (collateral apex — priced, wrecked variant, collateral_key -> wired so a SECOND one placed in the editor still bills; nothing about it -> adopts as an anchor), the POOL KIT (pool + compliant fence ring where -> every fence post is honest tie_off:false — adoptAnchor's `?? 1` makes a -> silent node the best steel in the game, and the pool yard's whole puzzle -> is that the legal tie-offs are in the wrong places), the TRAMPOLINE + tie- -> down kit (if S14's pick didn't land it — typed honestly in the checked -> enum). Flag D in THREADS as each lands; site_03's theme may want one. -> Axis trap stands: measure exported GLBs in three.js coords, look at the -> render in assetcheck, never trust a docstring about orientation. +> negative-control-proven, and every anchor-bearing node passes S15's gate-1 +> mechanism (a baked hint that can't reach a JSON-declared anchor is the bug +> class that cost the posts their truth): the GLASSHOUSE (collateral apex — +> priced, wrecked variant, collateral_key wired so a SECOND one placed in +> the editor still bills; nothing about it adopts as an anchor), the POOL +> KIT (pool + compliant fence ring where every fence post is honest +> tie_off:false — adoptAnchor's `?? 1` makes a silent node the best steel in +> the game, and the pool yard's whole puzzle is that the legal tie-offs are +> in the wrong places), the TRAMPOLINE + tie-down kit (typed honestly in the +> checked enum; the tie-down is prep's counter to the storm's opening +> projectile, priced like it matters). Flag D in THREADS as each lands. Axis +> trap stands: measure exported GLBs in three.js coords, look at the render +> in assetcheck, never trust a docstring about orientation. diff --git a/README.md b/README.md index 9db0217..b28648a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ No pip, no npm, no build. Python 3.8+ and a browser. · `ROADMAP.md` — the growth canon (the arcs from the week to the business) 3. `THREADS.md` — the append-only lane log. **The project's memory.** Read the last few `[I]` integrator entries to know the current state. -4. `SPRINT15.md` — the current sprint (highest-numbered SPRINTn.md wins) +4. `SPRINT15.md` — the current sprint (highest-numbered *fired* sprint wins: + SPRINT16 is pre-speced TEETH and fires only when 15 integrates — its own + header says so) 5. `LANE_PROMPTS.md` — copy-paste prompts per lane, per sprint (bottom = newest) ## The lane model (how this repo is built) @@ -67,9 +69,9 @@ Host gitea.partly.party Then: ``` -git clone git@gitea.partly.party:monster/shades.git ~/Documents/shades +git clone git@gitea.partly.party:monster/hardyards.git ~/Documents/shades for L in A B C D E; do - git clone git@gitea.partly.party:monster/shades.git ~/Documents/shades-lane$L + git clone git@gitea.partly.party:monster/hardyards.git ~/Documents/shades-lane$L git -C ~/Documents/shades-lane$L checkout -B lane/$(echo $L | tr 'A-Z' 'a-z') origin/lane/$(echo $L | tr 'A-Z' 'a-z') done ``` diff --git a/ROADMAP.md b/ROADMAP.md index 0d679fd..f9dd1fa 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -18,8 +18,8 @@ now." ## Where we are (the honest baseline) -Five 90-second nights, two yards, five storms, one wallet, ~15 minutes of -campaign. Selftest 409/0/0. An editor that makes new yards cheap and scores them +Five 90-second nights, two shipped yards (a third — the swing lawn — authored +and inert), five storms, one wallet, ~15 minutes of campaign. Selftest 409/0/0. An editor that makes new yards cheap and scores them through the real engines. An economy whose own comments say the balance is unmeasured, and a scorecard that says a bare bed reads 82.6 where the best clean rig reads 91.3 — **doing nothing scores 90% of doing everything, and that is the @@ -51,7 +51,16 @@ in the repo; the thinnest thing is consequence. --- -## ARC 1 — TEETH (SPRINT15, speced in full in SPRINT15.md) +## ARC 0 — THE TRUE BASELINE (SPRINT15, already speced — the fork's gift) + +*This roadmap and SPRINT15 were written in parallel forks the same day and +reconciled: S15's gates (the post rating_hint ruling, the iterative editor +score, the honest edges) re-measure the very numbers arc 1 pins, so S15 runs +FIRST and TEETH fires as SPRINT16. The fork was the process working — D's +cold-authoring pass found a shipped balance bug nobody reasoning from the +code would have caught.* + +## ARC 1 — TEETH (SPRINT16, speced in full in SPRINT16.md) *The week gets consequences. Your work follows you.* @@ -59,11 +68,11 @@ Reputation and warranty hang off the invoice exactly as DESIGN.md drew it: a corner of yours that blew and stayed blown books an unpaid warranty line on the next morning's job sheet; a clean night builds the number on the letterhead; the number moves what clients pay. The fabric bet gets its paper trail (forecast -argues hail size and rain, invoice records the cloth you chose). site_03 finally -ships, the week becomes six nights, and one of them — C's pea-hail soaker — is -the first night where the default fabric is the wrong answer. The bare-beds -separation fight starts here and does not end until held-vs-bare is a pinned, -teeth-bearing number on every shipped site. +argues hail size and rain, invoice records the cloth you chose). D's swing lawn +enters the rotation, the week becomes six nights, and one of them — C's pea-hail +soaker — is the first night where the default fabric is the wrong answer. The +bare-beds separation fight starts here and does not end until held-vs-bare is a +pinned, teeth-bearing number on every shipped site. ## ARC 2 — THE BOOK OF CLIENTS (≈ two sprints) @@ -161,6 +170,6 @@ surcharge and the mudslide entering the debris system waits here, intact. named here so nobody half-builds it sooner. - **Deploy**: tools/deploy_hardyards.sh after every integration; the live site is currently one sprint behind main and that gap should never exceed one. -- **The sim's debt, tracked**: node-position heal after divergence (SPRINT15 +- **The sim's debt, tracked**: node-position heal after divergence (SPRINT16 gate 2), the >83 ms frame determinism bound (documented, un-tested), membrane pricing the day rain's drain weight moves (rigging.js's comment names it). diff --git a/SPRINT15.md b/SPRINT15.md index b942c2f..c585416 100644 --- a/SPRINT15.md +++ b/SPRINT15.md @@ -1,164 +1,108 @@ -# SPRINT 15 — TEETH: YOUR WORK FOLLOWS YOU +# SPRINT 15 — THE POSTS TELL THE TRUTH, AND THE EDITOR STOPS BEING BATCH -*Sprint 14 built the editor and the editor built the argument: SCORE IT put the -bare-beds number on a card (bare 82.6 vs best clean 91.3) and nobody can unsee -it. John played, ruled on gate 4 (ROADMAP.md, 2026-07-18): all three branches, -sequenced, teeth first. This sprint the week grows consequences — reputation on -the letterhead, warranty on the job sheet, a sixth night, a third yard, and the -first night where the default fabric is the wrong answer. Baseline selftest -**409/0/0** (the fresh-eyes review landed six fixes on main — read THREADS [I] -2026-07-18 before you start; the fabric key, the divergence break and the pond -clamp are all news your lane may be building on). Keep it green; mutation-check -anything you'd call coverage.* +*Sprint 14 built the yard editor and it immediately earned itself: nine bugs +found before anyone authored a yard with it, three of them live on the public +URL. D then authored a complete third yard cold — no JSON, no console — and +it scored winnable-but-mean and played that way. 406/0/0, live at +partly.party/hardyards, editor included.* -**The one-variable rule is LAW this sprint (ROADMAP principle 5):** six nights, -exactly one flies the new storm, exactly one is the new site, and they are NOT -the same night. A player who loses night 6 must be able to say whether the yard -beat them or the weather did. +*What it also found is a shipped balance bug we deliberately did not patch, +because half the repo's measured numbers are standing on it. That's gate 1.* -## Gate 1 — THE LEDGER (A owns: week.js, hud.js surfaces, main.js wiring) +Read THREADS from the last [I] entry. Baseline **406/0/0**; keep it green, +mutation-check anything you'd call coverage. -Reputation and warranty, hung off the paperwork that already exists. No new -phase, no new sim — this is week-state plus three surfaces telling the truth. +## Gate 1 — THE POSTS TELL THE TRUTH (A rules · E supplies · B re-measures · D re-plays) -1. **The work history.** Every committed rig is recorded on the week: corners, - hardware, effective ratings, what broke, what was repaired by dawn. This is - data the night already computes — the ledger just keeps it. -2. **Warranty.** A corner of YOURS that broke and was still broken at dawn books - a warranty item: the NEXT morning's job sheet carries it as a line — - "warranty — q3, shackle, no charge to the client" — and its cost (the - hardware's shop price, unpaid labour is the point) comes off that night's - fee AT QUOTE TIME. week.js's own invariant is non-negotiable: the sheet and - the invoice never diverge — the quote already carries the deduction, so - settle pays exactly what was quoted. A repair you made mid-storm is NOT a - warranty item; riding it out broken is. -3. **Reputation.** One number on the letterhead (pick the unit — stars read as - client-speak; whatever it is, it appears under the ABN on job sheet AND - invoice, E's letterhead contract). Clean night (no breaks, no collateral): - up. Warranty item: down. Collateral event: down harder. It MOVES PAY at - quote time — a trusted outfit's callout fee multiplier, printed on the - sheet, so the player watches their number become money. Start neutral; - night 5's beyond-saving garden must NOT cost rep (the flag exists — read it; - a designed loss the ledger punishes is the verdict lying again). -4. **Aftermath teaches it.** The invoice's verdict line and the end-of-week - card both speak rep: a week that ends broke-but-clean and a week that ends - rich-with-warranties are different endings and should read as such. -5. Tests, a.test: warranty fires only on unrepaired breaks (repaired = no - item); quote==settle holds with a warranty line on the sheet; rep never - moves on gardenBeyondSaving nights for garden reasons; the multiplier is on - the sheet the night it applies. Negative control: a clean five-night week - books zero warranty items and ends rep-positive. +The factory bakes `rating_hint = 0.9` on `sail_post`'s `top_anchor`. Every +post in every shipped site is a plain JSON entry with **no `node`**, so +`adoptAnchor` never runs and they all default to **1.00**. Every honest post +in the game is 10% stronger than E designed it, and always has been. -## Gate 2 — THE HEAL + THE PAPER TRAIL + THE PIN (B owns) +This is a gate and not a patch because A's `separation` block pins measured hp +through the real chain, B's audit tables predict against it, and D's played +verdicts sit on it. Move the number and all three move together, in order: -1. **Divergence heal.** The review landed the break (a NaN corner now lets go, - reason:'divergence', production path) — but the freed node then integrates - from NaN forces and the cloth renders ugly after one. Sanitize on the break: - any non-finite node position resets from its nearest finite neighbour (or - the corner's anchor), prev matched so verlet doesn't invent velocity. - Assert: poison → break → every node finite within 1 s of sim, render-safe. - Mutation: heal off, assert red. The existing divergence test must keep - passing untouched — the heal is IN ADDITION to the break, not instead. -2. **Fabric on the paperwork.** The chosen fabric prints in the prep summary, - on the invoice, and in the aftermath verdict when it mattered (a membrane - night survived in cloth should say what the leak cost). The FORECAST half is - C's (gate 3.3) — yours is that the choice the player made is on the record. -3. **Pin site_02's separation.** The SCORE IT card says NONE PINNED and calls - it out — a shipping site should promise held-vs-bare and be held to it. Run - gardenfly, pick the honest target, pin the block in site_02's JSON, assert - it like backyard_01's. If the honest answer is "this yard cannot separate - held from bare at $80", that is a FINDING about the yard — post it in - THREADS and gate 4's balance fight starts there, not at a fudged pin. +1. **A rules FIRST, before anyone measures:** is the honest post 0.90 (E's + design intent reaches the sim) or 1.00 (the shipped value becomes canon and + E's baked hint is corrected to match)? Both are defensible — 1.00 is what + every played verdict in THREADS actually measured, and "the value we shipped + and balanced against is the real one" is a legitimate ruling. What is NOT + defensible is leaving the factory and the sim disagreeing. **Whichever way + it goes, the mechanism must be fixed so a baked hint can never again fail to + reach a JSON-declared anchor silently** — that's the actual bug, and it will + bite the next asset the same way (`tie_off`, `collateral`, anything). +2. **E:** make the factory agree with the ruling and pin it — an assert that a + JSON-declared anchor of a type the factory rates carries that rating at + runtime. Your S14 audit rules (no silent anchors, no unpriced collateral) + are the model; this is the third of that family. +3. **B:** re-measure everything that moved. If the ruling is 0.90 every post + line in both shipped yards gets weaker — expect the separation pin, the + winnable-lines tables, and the night-4/5 affordability story to shift, and + post the deltas as a table. Propose retunes with evidence; A rules. +4. **D:** re-play the affected nights and say whether the change is FELT. If + 0.90 makes the honest line stop holding, that's a design change wearing a + bugfix's clothes and you're the one who says so. -## Gate 3 — THE MEMBRANE NIGHT + THE SEEDED STORM (C owns) +## Gate 2 — THE EDITOR STOPS BEING BATCH (B owns; D judges) -1. **storm_06 — the pea-hail soaker.** Small stones (under the 2 mm-weave - ruling's blocking threshold), heavy rain, mild wind. The night the bet - flips: cloth leaks fine hail onto the bed all night; membrane stops - everything and the mild wind means its doubled load is affordable — but it - PONDS, so the broom matters. Author it so both lines exist and the membrane - line is genuinely better: measure with gardenfly/site_audit (funnel state in - the header, per the discipline), pin membrane-vs-cloth garden separation in - the storm's own selftest, and make sure a $80 wallet affords the membrane - line on the night's site. If the measured numbers say cloth still wins, - retune the hail spectrum until the bet is real — the storm exists to make - the F key matter, and a decoration night is worse than no night. -2. **makeStorm(seed) — groundwork, not a shipped night.** A seeded generator - inside authored envelopes (base curve, gust budget, direction change window, - rain/hail ranges as data), producing storm defs that pass the schema - validator and the storm_envelope harness. Deterministic from seed — - ROADMAP arc 4's roguelite eats this. Selftest: same seed → byte-equal def; - 100 seeds → all schema-valid, all inside the envelope; a seed that violates - the envelope is a red test, not a shrug. -3. **The forecast argues the bet.** Hail stone size and rain rate join wind on - the job sheet's forecast block (the data is in the storm JSON; A prints - what you expose). The band-not-a-promise rule holds — forecast bands, real - values inside them. +D's verdict: the 75–82 s fully-blocked score "made the editor batch, not +iterative — I scored once and shipped that run rather than tuning." That is +the difference between a tool you author WITH and a tool you check work AGAINST. +1. **B:** make the score interruptible and legible. In priority order — a + progress tick that actually paints (D says this may matter more than raw + speed), then yielding so the page stays responsive, then speed itself + (the cost scales like C(n,4) in candidate quads — a coarse pass that ranks + before it flies is likely worth more than micro-optimising the sim). + Whatever you do, the numbers must not change: pin the current site_02 and + site_03 scores before you start and reproduce them after. +2. **B + C:** one storm picker, not two independently-defaulted ones (D scored + the wildnight while viewing the southerly — the exact class of + two-harnesses-disagreeing this repo keeps paying for, now inside one page). +3. **D:** re-author or re-tune a yard with the faster loop and say whether it + changed how you work. That's the acceptance test, same as gate 3.2 was. -## Gate 4 — SITE_03 AND THE SIX-NIGHT WEEK (D owns) +## Gate 3 — THE EDITOR'S HONEST EDGES (A owns, mostly) -1. **site_03 ships.** Your S14 cold-authoring run never landed a yard — the - editor is a sprint older now and SCORE IT prices collateral. Author it - cold (editor only, the S14 discipline), any theme E's palette supports, - winnable-but-mean at $80, separation pinned, collateral priced, _design/ - _why filled. File the friction in THREADS — every place the editor fights - you is still gate-1/2 feedback one sprint late. -2. **The week becomes six nights.** Wire site_03 and storm_06 into NIGHTS under - the one-variable rule: site_03 debuts on a storm the player has already - survived; storm_06 flies over a yard they already know. You own the order - and the two briefs (client flavour is data — write clients worth meeting; - the corner block got a name and a reason, match that bar). Night 5's - beyond-saving flag and the went-broke arc must both survive re-ordering — - the a.test week asserts will tell you. -3. **Cold-play the whole six**, wallet from night 1, and file the verdict: - does a warranty callout read as FAIR when it lands on your sheet? Does rep - moving pay feel earned or arbitrary? Is night 6's bet legible from the - forecast alone? Your play IS the acceptance test for gates 1–3. -4. Pool (yours): the phantom-sail seam re-test, third sprint running — if A's - view fix landed, verify and close it in THREADS; if not, your repro keeps - it honest. +D's friction list, by owner, all seen in anger: +1. **A:** number fields commit on blur only — Enter does nothing, and a house + sat on the garden bed while the panel read the new value. Commit on Enter. +2. **A:** objects place outside the fence silently and the yard still reads + VALID. Either validate the yard bounds or draw the boundary — a yard that + lies about being valid is the one thing this editor must never do. +3. **A:** zoom is wheel-only (no keyboard, no pinch). +4. **A+B:** no `separation` field exists in the editor, while B's score card + says a shipping site should pin one. Either add it to the SITE panel or + rule that separation is an integration-time concern and say so on the card. +5. **A:** honour `tie_off: false` at the palette layer if you offer anchors by + name from GLB nodes (E's flag; the runtime half is already honoured). -## Gate 5 — THE NEXT TEMPTATIONS (E owns) +## Gate 4 — JOHN PLAYS, THEN CHOOSES (nine sprints) -Arc 2 needs yards this palette cannot dress yet. Two of these three, factory- -built, honest extras, negative-control asserts (your discipline — build the -wrong one on purpose and watch red): -1. **The glasshouse** — the collateral apex. Priced high, wreck variant, - collateral_key wired so a second one placed in the editor bills (the - carport's five-sprint lesson is one sprint old). Panes that read as fragile - at a glance; nothing about it may adopt as an anchor. -2. **The pool kit** — pool + compliant fence ring. The fence posts are the - temptation: honest `tie_off:false` (silence is not neutral — adoptAnchor's - `?? 1` makes an unrated node the best steel in the game), because the whole - point of the pool yard is that the legal tie-offs are all in the wrong - places. -3. **The trampoline** — if S14's pick didn't land it: typed honestly in the - checked enum, plus a tie-down kit prop (the prep-phase counter to the - storm's opening projectile, priced like it matters). -Flag D in THREADS as each lands — site_03's theme may want one. +**⚡ THE BRANCH HALF OF THIS GATE IS RESOLVED (2026-07-18, integration checkout):** +this sprint plan and a TEETH sprint plan were written in parallel forks the +same day, and John ruled while they were being reconciled — **all three +branches, sequenced: teeth → sites/clients → heatwaves.** ROADMAP.md (new, +the growth canon) carries the arcs; SPRINT16.md carries TEETH pre-speced, +firing when this sprint integrates. The gates above stand exactly as written +— they re-measure the numbers SPRINT16 pins, which is why 15 runs first. -## Gate 6 — JOHN: PLAY, THEN AIM ARC 2 - -Six nights at partly.party/hardyards (deploy after integration — the live gap -is one sprint and ROADMAP says it never exceeds that). Three sentences in -THREADS. Then one ruling for arc 2's first sprint: **emergency mid-storm -callout or the cowboy audit** — which job type leads THE BOOK OF CLIENTS? - -Pool, spread by ownership: dev-line visibility default (A, second carry); -bench-vs-UI residual cause (open since S13; the margin rule stands in); -membrane pricing the day rain's drain weight moves (B, rigging.js's comment -names the start); the >83 ms determinism bound gets a pinning test or a -documented waiver (B). +**The PLAY half stands:** partly.party/hardyards — five nights, three +sentences in THREADS. The fabric key (F) is newly live; night 3's yard has a +sibling now (`site_03_swing_lawn`, inert until SPRINT16 wires it). +- **More sites** — D's `site_03_swing_lawn` is authored, scored and played, + sitting inert waiting for a NIGHTS entry. This branch is now nearly free. +- **Reputation / warranty** — DESIGN.md's consequence loop; the invoice exists + to hang it off. Unchanged in cost. +- **Heatwave days** — the slow-pressure half of the design, sun-arc coverage, + a genuinely different puzzle. Unchanged in cost. +Nobody builds any of the three this sprint. ``` -gate 1: ledger — warranty on the sheet, rep on the letterhead, quote==settle - holds, a designed loss costs nothing -gate 2: divergence heal (finite in 1s, mutation-proof); fabric on the record; - site_02 separation pinned or honestly refused -gate 3: storm_06 makes membrane the right answer, measured not asserted; - makeStorm(seed) deterministic inside envelopes; forecast argues the bet -gate 4: site_03 cold-authored and shipped; six nights, one-variable rule; - the whole week cold-played and judged -gate 5: two temptation assets, honest extras, negative controls -gate 6: John's three sentences + the arc-2 job-type ruling +gate 1: the post hint ruled, the mechanism fixed so a baked hint cannot + silently fail to reach a JSON anchor, both yards re-measured, D re-plays +gate 2: the score paints progress and stays responsive; numbers reproduce + exactly; one storm picker; D says the loop changed +gate 3: Enter commits, out-of-bounds stops reading VALID, the friction list empty +gate 4: John's three sentences + the branch, in writing ``` diff --git a/SPRINT16.md b/SPRINT16.md new file mode 100644 index 0000000..243e0ec --- /dev/null +++ b/SPRINT16.md @@ -0,0 +1,168 @@ +# SPRINT 16 — TEETH: YOUR WORK FOLLOWS YOU + +*⚠️ PRE-SPECED, FIRES AFTER SPRINT 15 INTEGRATES. Sprint 15 (the post ruling, +the iterative editor, the honest edges) re-measures numbers this sprint pins — +do not start these gates on a pre-15 tree, and treat every number below that +S15's gate-1 re-measure moves as "re-measure first". Written 2026-07-18 at +John's direction (ROADMAP.md — gate 4 of S14 resolved: all three branches, +sequenced, teeth first).* + +*The week grows consequences — reputation on the letterhead, warranty on the +job sheet, a sixth night, the swing lawn in the rotation, and the first night +where the default fabric is the wrong answer. Baseline selftest: whatever +Sprint 15 lands (409/0/0 pre-15 plus S15's additions). Keep it green; +mutation-check anything you'd call coverage.* + +**The one-variable rule is LAW this sprint (ROADMAP principle 5):** six nights, +exactly one flies the new storm, exactly one is the new site, and they are NOT +the same night. A player who loses night 6 must be able to say whether the yard +beat them or the weather did. + +## Gate 1 — THE LEDGER (A owns: week.js, hud.js surfaces, main.js wiring) + +Reputation and warranty, hung off the paperwork that already exists. No new +phase, no new sim — this is week-state plus three surfaces telling the truth. + +1. **The work history.** Every committed rig is recorded on the week: corners, + hardware, effective ratings, what broke, what was repaired by dawn. This is + data the night already computes — the ledger just keeps it. +2. **Warranty.** A corner of YOURS that broke and was still broken at dawn books + a warranty item: the NEXT morning's job sheet carries it as a line — + "warranty — q3, shackle, no charge to the client" — and its cost (the + hardware's shop price, unpaid labour is the point) comes off that night's + fee AT QUOTE TIME. week.js's own invariant is non-negotiable: the sheet and + the invoice never diverge — the quote already carries the deduction, so + settle pays exactly what was quoted. A repair you made mid-storm is NOT a + warranty item; riding it out broken is. +3. **Reputation.** One number on the letterhead (pick the unit — stars read as + client-speak; whatever it is, it appears under the ABN on job sheet AND + invoice, E's letterhead contract). Clean night (no breaks, no collateral): + up. Warranty item: down. Collateral event: down harder. It MOVES PAY at + quote time — a trusted outfit's callout fee multiplier, printed on the + sheet, so the player watches their number become money. Start neutral; + night 5's beyond-saving garden must NOT cost rep (the flag exists — read it; + a designed loss the ledger punishes is the verdict lying again). +4. **Aftermath teaches it.** The invoice's verdict line and the end-of-week + card both speak rep: a week that ends broke-but-clean and a week that ends + rich-with-warranties are different endings and should read as such. +5. Tests, a.test: warranty fires only on unrepaired breaks (repaired = no + item); quote==settle holds with a warranty line on the sheet; rep never + moves on gardenBeyondSaving nights for garden reasons; the multiplier is on + the sheet the night it applies. Negative control: a clean week books zero + warranty items and ends rep-positive. + +## Gate 2 — THE HEAL + THE PAPER TRAIL + THE PIN (B owns) + +1. **Divergence heal.** The review landed the break (a NaN corner now lets go, + reason:'divergence', production path) — but the freed node then integrates + from NaN forces and the cloth renders ugly after one. Sanitize on the break: + any non-finite node position resets from its nearest finite neighbour (or + the corner's anchor), prev matched so verlet doesn't invent velocity, and + non-finite water zeroed (a NaN pond re-poisons the heal through the weight + term). Assert: poison → break → every node finite within 1 s of sim. + Mutation: heal off, assert red. The existing divergence test must keep + passing untouched — the heal is IN ADDITION to the break, not instead. +2. **Fabric on the paperwork.** The chosen fabric prints in the prep summary, + on the invoice, and in the aftermath verdict when it mattered (a membrane + night survived in cloth should say what the leak cost). The FORECAST half is + C's (gate 3.3) — yours is that the choice the player made is on the record. +3. **Pin site_02's separation — AFTER S15's re-measure.** The SCORE IT card + says NONE PINNED and calls it out. Once the post ruling has settled the + numbers, run gardenfly, pick the honest target, pin the block in site_02's + JSON, assert it like backyard_01's. If the honest answer is "this yard + cannot separate held from bare at $80", that is a FINDING about the yard — + post it in THREADS; the balance fight starts there, not at a fudged pin. + Same pass, same rule for `site_03_swing_lawn` if S15 left it unpinned. + +## Gate 3 — THE MEMBRANE NIGHT + THE SEEDED STORM (C owns) + +1. **storm_06 — the pea-hail soaker.** Small stones (under the 2 mm-weave + ruling's blocking threshold), heavy rain, mild wind. The night the bet + flips: cloth leaks fine hail onto the bed all night; membrane stops + everything and the mild wind means its doubled load is affordable — but it + PONDS, so the broom matters. Author it so both lines exist and the membrane + line is genuinely better: measure with gardenfly/site_audit (funnel state in + the header, per the discipline; post-ruling numbers only), pin + membrane-vs-cloth garden separation in the storm's own selftest, and make + sure a $80 wallet affords the membrane line on the night's site. If the + measured numbers say cloth still wins, retune the hail spectrum until the + bet is real — the storm exists to make the F key matter, and a decoration + night is worse than no night. +2. **makeStorm(seed) — groundwork, not a shipped night.** A seeded generator + inside authored envelopes (base curve, gust budget, direction change window, + rain/hail ranges as data), producing storm defs that pass the schema + validator and the storm_envelope harness. Deterministic from seed — + ROADMAP arc 4's roguelite eats this. Selftest: same seed → byte-equal def; + 100 seeds → all schema-valid, all inside the envelope; a seed that violates + the envelope is a red test, not a shrug. +3. **The forecast argues the bet.** Hail stone size and rain rate join wind on + the job sheet's forecast block (the data is in the storm JSON; A prints + what you expose). The band-not-a-promise rule holds — forecast bands, real + values inside them. + +## Gate 4 — THE SIX-NIGHT WEEK (D owns) + +1. **The swing lawn enters the rotation.** Your site_03_swing_lawn landed in + S14 — authored cold, scored, played, inert. Re-score it on the post-ruling + tree with S15's faster loop (the numbers that made it winnable-but-mean may + have moved), fix what moved, then wire it into NIGHTS. If S15's edges work + added a separation field or B pinned one for it, carry it; otherwise gate + 2.3 covers it. +2. **The week becomes six nights.** Wire the swing lawn and storm_06 into + NIGHTS under the one-variable rule: the new site debuts on a storm the + player has already survived; storm_06 flies over a yard they already know. + You own the order and the two new briefs (client flavour is data — the + corner block got a name and a reason; the swing set's kid deserves one + too). Night 5's beyond-saving flag and going-broke-never-on-the-final-night + must both survive re-ordering — the a.test week asserts will tell you. +3. **Cold-play the whole six**, wallet from night 1, and file the verdict: + does a warranty callout read as FAIR when it lands on your sheet? Does rep + moving pay feel earned or arbitrary? Is night 6's bet legible from the + forecast alone? Your play IS the acceptance test for gates 1–3. +4. Pool (yours): the phantom-sail seam if S15 didn't close it. + +## Gate 5 — THE NEXT TEMPTATIONS (E owns) + +Arc 2 needs yards this palette cannot dress yet. Two of these three, factory- +built, byte-stable, negative-control-proven (build the wrong one on purpose, +watch red) — and every anchor-bearing node passes the S15 gate-1 mechanism +(a baked hint that cannot reach a JSON-declared anchor is the bug class that +cost the posts their truth): +1. **The glasshouse** — the collateral apex. Priced high, wreck variant, + collateral_key wired so a SECOND one placed in the editor still bills (the + carport's five-sprint lesson). Panes that read as fragile at a glance; + nothing about it may adopt as an anchor. +2. **The pool kit** — pool + compliant fence ring. The fence posts are the + temptation: honest `tie_off:false` on every one (silence is not neutral — + adoptAnchor's `?? 1` makes an unrated node the best steel in the game), + because the pool yard's whole puzzle is that the legal tie-offs are all in + the wrong places. +3. **The trampoline + tie-down kit** — typed honestly in the checked enum; + the tie-down is the prep-phase counter to the storm's opening projectile, + priced like it matters. +Flag D in THREADS as each lands. + +## Gate 6 — JOHN: PLAY, THEN AIM ARC 2 + +Six nights at partly.party/hardyards (deploy after integration — ROADMAP: the +live gap never exceeds one sprint). Three sentences in THREADS. Then one +ruling for arc 2's first sprint: **emergency mid-storm callout or the cowboy +audit** — which job type leads THE BOOK OF CLIENTS? + +Pool, spread by ownership: whatever S15's pool leaves standing, plus: +membrane pricing the day rain's drain weight moves (B, rigging.js's comment +names the start); the >83 ms determinism bound gets a pinning test or a +documented waiver (B). + +``` +gate 1: ledger — warranty on the sheet, rep on the letterhead, quote==settle + holds, a designed loss costs nothing +gate 2: divergence heal (finite in 1s, mutation-proof); fabric on the record; + separations pinned post-ruling or honestly refused +gate 3: storm_06 makes membrane the right answer, measured not asserted; + makeStorm(seed) deterministic inside envelopes; forecast argues the bet +gate 4: swing lawn re-scored and wired; six nights, one-variable rule; + the whole week cold-played and judged +gate 5: two temptation assets, honest extras, negative controls, S15-clean +gate 6: John's three sentences + the arc-2 job-type ruling +``` diff --git a/THREADS.md b/THREADS.md index b7fb916..ac6044f 100644 --- a/THREADS.md +++ b/THREADS.md @@ -6530,6 +6530,280 @@ anchors are your GLB), but the tooling is now waiting, not TODO. real wait. If it bites, say so — the `FLY_CAP` (12) and the sweep are both knobs I can turn, and I'd rather hear it from someone using it in anger than guess. +[D] 2026-07-18 — 🏡 **GATE 3.2 — I AUTHORED A THIRD YARD COLD IN THE EDITOR, AND THE EDITOR IS + SHIPPING BOTH OF E'S NEW PROPS INERT.** Site JSON landed on lane/d as the site_03 CANDIDATE, + awaiting John's gate-4 branch call. Selftest **406/0/0** on my tree with it landed. No JSON + hand-editing and no console for any of the authoring — one exception, declared at the bottom. + + **🔴 THE HEADLINE, AND IT IS A's FILE: the palette's two newest entries omit the `_v1` suffix + every other entry carries, so `swing_set_01` and `tree_jacaranda_01` 404, never draw, and + adopt at ratingHint 1.00.** Found in the first ten minutes of placing. + + · **Receipt, from the network log:** `GET /web/world/models/swing_set_01.glb → 404 File not + found`. The shipped asset is `swing_set_01_v1.glb`, and it preloads **200 OK** under its + real name three lines earlier in the same log. Same for `tree_jacaranda_01`. + · **Source:** `editor.js` 239/243 (`model: 'swing_set_01'`, `wreckedModel: + 'swing_set_01_wrecked'`) and 257/260 (`model: 'tree_jacaranda_01'`). The other nine palette + entries all carry `_v1`. (`nodeNamesFor` at 385-398 also omits `_v1` — that one is CORRECT, + those are scene-node names, not files. I checked before filing.) + · **Consequence, read straight off A's own SELECTED panel:** + + | prop | E designed | editor adopted | + |---|---|---| + | swing_set_01 `s1_f1` / `s1_f2` | 0.45 / 0.45 | **1.00 / 1.00** | + | tree_jacaranda_01 `t1` / `t1b` / `t1c` | 0.95 / 0.52 / 0.40 | **1.00 / 1.00 / 1.00** | + + Neither prop renders — you get a selection ring on empty grass. And the yard still reads + **VALID — the game can boot this**. + · **This is E's GAP 1 exactly, one layer up.** `adoptAnchor` does `rating_hint ?? 1`, so a + model that 404s doesn't fail — it becomes the best steel in the game. A's own ruling says + the 0.45 "is the prop's entire reason to exist" and that softening it "would delete the + honest middle the palette was missing". The 404 deletes it. The jacaranda's whole design — + **"gum: climb freely, pay 24%. Jacaranda: climb at all, pay 58%"** — becomes three perfect + anchors, i.e. a better tree than the gum at every rung. Both props gate 3.1 built so that + gate 3.2 could ask a real question, and neither was asking it. + · **It propagates into the DATA.** The palette's `make()` writes the model string into the + site JSON, so the first editor-authored yard using either prop ships a permanently broken + model reference. This is not just an editor-preview bug. + · **Gate 1's promise holds and is not enough.** A wrote that the palette "structurally cannot + offer a prop that would produce a site `validateSite` rejects" — true, and irrelevant here, + because `validateSite` cannot open a GLB (A's own words when they took the `tie_off` + runtime half). Nothing in 406 tests that a palette model name resolves to a file. + · **Negative-controlled in both directions.** With the four names corrected locally: + `swing_set_01_v1` → `s1_f1`/`s1_f2` **hint 0.45**; `tree_jacaranda_01_v1` → **0.95 / 0.52 / + 0.40**; both props draw. Reverted before landing — **the fix is A's, and `editor.js` is + untouched on lane/d.** I authored the yard on that local patch, the same way B and C + developed against A's import line without landing it. Everything below was measured on the + corrected palette, so the numbers describe the yard A's one-line fix will produce. + · **The assert that would have caught it** is one line in the round-trip test A already + wrote, since it dresses the world: every palette item's model must resolve. A's adoption + check keys on `collateral`, which the swing set HAS — so the existing check passes while + the mesh is missing and the hint is a lie. + + **🏡 THE YARD — `site_03_swing_lawn`, "The Delaneys' back lawn". 24×16, 11 anchors.** + The thesis: **the good steel is all LOW and the only height is bad.** Three temptations, + three different lies, and the blurb sells all three — *"Swing set, big jacaranda, house right + there — you'll have no trouble finding something to tie to."* + + | anchor | rating | the lie | + |---|---|---| + | `s1_f1` `s1_f2` swing apexes | 0.45 | the crossbar between them is a dead-level 2.05 m rail spanning 2.3 m and is NOT an anchor. Blowing an apex bills **$140** | + | `t1` jacaranda low fork | 0.95 | genuinely excellent — but it is 2.5 m up and cuts the sail flat | + | `t1b` / `t1c` jacaranda height | 0.52 / 0.40 | where the sail wants its high corner. Height costs 58% here | + | `h1` `h2` `h3` fascia | 0.35 | the familiar lie, and takes the **$90** gutter | + | `t2` / `t2b` gum | 1.00 / 0.88 | the best steel in the yard, parked 9 m away on the WRONG SIDE on purpose | + | `p1` post | 1.00 | one honest corner. You need four | + + **The wind is the yard's personality, not decoration.** The venturi is the real gap between + the shed and the west end of the house — I placed the throat by clicking that gap and set the + axis to the slot's true orientation (90°, running north-south), *not* to whatever maximised + alignment. C's panel then told me what that geometry was worth, live: **align 62%, peak + ×1.28, fastest 14.96 m/s at (-8.3,-4.4)**. Throat sits **3.0 m from the swing set's apexes**, + inside r=5 — so the tempting middle option is also the windy one. Deliberately gentler than + the corner block (100%, ×1.46): **that yard keeps the crown for worst weather and worst bill.** + + **SCORE IT** — storm_03_southerly, FUNNEL ON, 11 anchors dressed, **82.5 s**: + ✓ **WINNABLE at $80** · 81 quads in band · **11 clean** · 16 marginal · + cheapest honest `h3,t2,t2b,p1` $65 (holds at $55), 22 m², 85.3 FULL · + bare bed **83.7** → best clean `h3,t1c,t2,p1` **90.0**, separation **+6.3 HP** · + margin flags: t2b 0%, t1/t1b/p1/s1_f1 all 1% · collateral exposure **$230 on an $80 job**. + The card's own warning is the yard's design in one line: **the best-READING line + `t1c,t2,t2b,s1_f2` is MARGINAL at 91.8 FULL.** The prettiest number in the yard is the one + that breaks. + + **🎮 THEN I PLAYED IT** (scratch branch `scratch/laneD-play-site03`, night 2 → my yard; A's + `week.js` is untouched on lane/d, exactly as in Sprint 12). + · **The job sheet reads.** JOB 1042, the Delaneys, 31 Ferndale Ave, NIGHT 2 OF 5, SOUTHERLY + BUSTER, and my blurb landing on the letterhead as the cover story. $122 the night, $80 bank. + · **The yard reads from the spawn.** All three temptations visible without moving: the three + fascia rings, the jacaranda's ladder of rings climbing away, and the swing set's two apex + rings **with the crossbar bare between them**. The rail is obviously level, obviously + spanning, and has no ring on it. E's prop does exactly what it was built to do. The HUD + types it `swing_frame`, so the player's pre-rig read is honest — and it does NOT distinguish + the two tree species, so "which tree" is answered by looking at the yard (lilac vs sage), + not by reading a list. That is the right place for it. + · **The trap bites, and it bites honestly.** Flew the marginal best-reading line at + affordable hardware ($60): **`t1c` broke** — the 0.40 limb, the exact corner the card + flagged — and the garden finished **83.9 FULL against a bare bed of 83.7**. Rigging the + prettiest line in the yard was worth **+0.2 HP over doing nothing.** Flew the cheapest + honest line at the same money: **`h3` broke**, 85.2 FULL, and h3 blowing bills the $90 + gutter. At full-strength hardware ($120 — over budget) all three lines are clean. + **That is the yard: you cannot afford top steel on four corners, and it punishes which one + you skimp on.** + · **B's card and the real game agree on my yard**: card 90.0 → played **90.7**; card 91.8 → + played **93.6**. Two chains, one number, on a yard neither was tuned against. + · **My own lane, on editor-authored geometry.** `needsLadder` keys on `work === 'bracket'`, + and A's palette emits `work` per anchor — so **the ladder followed the site data with zero + code change**, which closes the handoff I asked A for in Sprint 10 ("emit `work` per anchor + and the ladder follows the site"). On this yard the three fascia corners are both the 0.35 + lie AND the only ladder job: the cheapest-looking corners are the slowest to repair. The + shed renders at (-9,-5) and `shed_table` at (3.32,7.75) — **A's NaN-quaternion fix confirmed + on a yard built after it.** + + **👻 PHANTOM SAIL — DEAD, verified in play.** Rigged `p1,p2,p3,p4` on backyard_01, then + `loadSiteInto('site_03_swing_lawn')`: + + | | before | after | + |---|---|---| + | `rig.rigged` | true | **false** | + | `sailView` | present | **null** | + | in scene | true | **false** | + + A's fix holds across a live site transition, which is my Sprint-13 sighting reproduced and + closed. Visually confirmed too: no cloth behind the splash card on the new yard. + + **🧰 FRICTION, BY OWNER.** + · **A — number fields commit on BLUR only; Enter does nothing.** Typed `-6` into the house's + z, pressed Enter, and the field read `-6` while the site JSON still read `1.501` — the house + was standing on the garden bed and the bed had vanished under it. Only clicking elsewhere + committed. It mostly self-heals for a human (clicking the next thing blurs), but for the + duration the panel and the world disagree with no visual tell, and I lost real time + believing the bed had been deleted. Enter should commit. + · **A — the editor places objects outside the yard without a word.** My first bike landed at + **z = 8.699 in a 16 m yard** (z ∈ [-8, 8]), outside the fence, and VALIDATION still said + "VALID — the game can boot this". Which is true. But nobody means to put the bike in the + neighbour's yard. + · **A + B — there is no `separation` field anywhere in the editor, and B's own card asks for + one.** The card says, correctly, that "a shipping site should pin one: it is the difference + between 'a sail helps here' and 'a sail is the point of this night'." The editor cannot + comply. I shipped without one and said so in `_why` rather than hand-editing it in. Either + the SITE panel grows the field or the card stops asking. + · **A — zoom is mouse-wheel only.** No keyboard, no buttons. Minor, but it is the one camera + control with no alternative on a page that ships publicly. + · **B — the 82.5 s blocking score converted the editor from an iterative tool into a batch + one, and that is the honest answer to "what did it cost you".** Mine ran **82.5 s against + the integrator's 75 s on site_02** — 11 anchors / 81 candidate quads vs 10 / 66, and it + scales like C(n,4), so this gets worse with exactly the yards an editor makes easy to build. + **Everything I know about my yard came from ONE run.** I did not tune the yard against the + score; I designed it, scored it once, and shipped what that run said, because a second + opinion cost 82 s of a page that looks crashed. I would have moved the bed and re-scored if + it had been 8 s. **If gate 4 picks "more sites", a responsive score is the difference + between authoring and guessing** — and I'd take a progress tick and a yielded loop over raw + speed, because the worst part is not the wait, it's not knowing whether it died. + · **B + C — two storm pickers, independently defaulted.** SCORE IT opened on `02_wildnight` + while WIND opened on `03_southerly`. Scoring against one storm while reading the other's + wind field is a quiet way to be wrong, and I only caught it because I went looking. One + storm selection, shared. + · **E — the palette prose is doing real work.** Every button's tooltip is the design argument + ("the crossbar LOOKS like the answer and is not an anchor"). I chose props by reading the + palette. That is what it is for and it worked. + · **E + A — `sail_post`'s audited `top_anchor 0.90` never reaches the runtime.** Every post on + my yard **and on shipped backyard_01** adopts at `ratingHint 1.00`, and posts carry no + `tieOff` field at all — they are constructed anchors, never GLB-adopted, so the extra is + never read. E's audit table row is a document and nothing asserts it. Either the 0.90 is + meant to bite (and every post in the game has been 11% stronger than its asset says) or the + row should say "constructed at 1.00". I corrected my own `_design` after measuring it. + + **✅ WHAT SANG, because the investment is the story too.** + · **The SELECTED panel's live anchor readout is why the headline bug took ten minutes instead + of a sprint.** It lists the anchors as ADOPTED FROM THE DRESSED WORLD with their hints, not + as declared — so `hint 1.00` sat there in a warning-coloured badge next to a prop I knew was + 0.45. Showing the adopted value is the entire reason that bug was findable. + · **The editor renders the real dressed world**, so a missing prop is visible by absence. A + diagram would have drawn me a swing set that isn't there. + · **`mountPanel` ordering held**: five panels from four lanes on one rail, no collisions, and + C's wind panel is the best surface on the page — I placed a venturi by clicking a gap and + never once wanted a private harness. + · **The EXPORT ≠ SHIPPED note**, which lists four steps including "a cold playthrough by + someone who did not author it". A tool that tells you it isn't finished. + · Text fields survive `markDirty` re-render with focus and caret intact — typed 23 characters + into `name` and all 23 landed. + + **🔍 THREE THINGS I CHECKED AND DID NOT FILE**, because measure-don't-reason cuts both ways: + the side rail DOES scroll (`overflow-y:auto`, scrollHeight 3237) — my wheel events were a + tooling artifact, not a bug; the HUD's "anchors 1" was a stale text read and the HUD is + correct (it showed 11); and the template's 5×3.5 m bed is NOT below an audit floor — I went to + `sweep.js` and `AUDIT.BAND {lo:18,hi:45}` is the SAIL QUAD band, not the bed. I had that one + written up as a finding before I checked. + + **DECLARED, since gate 3.2 is a discipline test:** the yard's geometry, every placement, the + wind block, the blurb and both note blocks came out of the editor and were transcribed from + its own canonical export. Two hand edits after the fact, both prose, both disclosed: the local + `_v1` palette patch described above (not landed), and correcting my `_design`'s claim that p1 + is 0.90 once play measured it at 1.00 — I would rather hand-edit a note than ship a false + number in a level's design doc. + + **site_03_swing_lawn is landed on lane/d as DATA ONLY and is inert** — nothing reaches it + until a `week.js` NIGHTS entry names it, and that entry lives only on my scratch branch. It is + the **site_03 CANDIDATE awaiting John's gate-4 branch decision**. If the call is "more sites", + it is a night that already reads, already scores winnable-but-mean, and has been played. It + would take night 2 (southerly) and relieve backyard_01 of one of its four nights. + +[I] 2026-07-18 — **SPRINT 14 INTEGRATION (main).** All five lanes merged; selftest **406/0/0** on the + merged tree, the exact per-lane sum (362 + A 11 + B 13 + C 6 + E 14), verified in the browser. + The yard editor exists, it works, and **three lanes built into one page with ZERO code conflicts** + — only THREADS. A's seam contract, published on day one before the implementation was finished, + is the reason; it is the `.letterhead` pattern proven at a much larger scale and it should be the + default whenever two lanes must share a surface. + + **Integrator edits, both because the owning lane had finished (the S13 precedent):** + · Mounted B's and C's panels in `editor.html` — one import line each, which is all either lane + ever asked A for. Inside the try/catch on purpose: a panel that throws should say so on the + glass, not leave a hole in the rail. + · **Fixed D's headline bug in `editor.js`** — the palette shipped E's two new props with the + `_v1` suffix missing, so both GLBs 404'd, never drew, and — because `adoptAnchor` does + `rating_hint ?? 1` — **a missing model silently became the best steel in the game** (swing + apexes 1.00 instead of E's 0.45; jacaranda 1.00/1.00/1.00 instead of 0.95/0.52/0.40), while the + yard still validated VALID. D negative-controlled it both directions on a local patch they + deliberately did not land. Verified my fix through the editor's REAL placement path (click the + palette, click the ground): `s1_f1=0.45 s1_f2=0.45`. Note the shape for the manual: this is + E's own `tie_off` lesson wearing a filename — **silence is not neutral wherever the default is + generous.** + + **⚠️ CARRIED TO SPRINT 15, AND IT IS A SHIPPED BALANCE BUG, NOT AN EDITOR ONE.** D found it and + I confirmed it end to end: the factory bakes `rating_hint = 0.9` on `sail_post`'s `top_anchor`, + but every post in `backyard_01.json` (and site_02) is a plain data entry with **no `node`**, so + `adoptAnchor` never runs for them and they all default to **1.00**. Every honest post in the + shipped game is 10% stronger than E designed it, and has been for as long as posts have been + data. **I did not fix this at integration and nobody should hot-fix it**: it moves balance across + both shipped yards, and A's `separation` block pins measured hp numbers through the real chain + while B's audit tables and D's played numbers all sit on the current value. It is a RULING (A) + + a re-measure (B) + a re-play (D), which is a gate, not a patch. + + 🔧 **THE REMOTE WAS RENAMED MID-SPRINT — `monster/shades` → `monster/hardyards`**, via the Gitea + web UI at 09:02 (matching the repo to the game's name). Nothing was lost; every ref was intact + under the new name. D hit it as `Cannot find repository` and correctly REFUSED to create a repo + or rewrite the remote to force a push — they committed locally and reported instead, which is + exactly right and is why their work survived. All six checkouts are repointed and D's two commits + are pushed. **If a push ever fails with "cannot find repository", check for a rename before + assuming loss: `git ls-remote ssh://git@100.71.119.27:222/monster/.git`.** + + **The sprint's real argument, in one line: the editor found NINE bugs before anyone authored a + yard with it**, and three were live on the public URL — a graybox house slab across site_02's + north horizon, the Hendersons' shed which has NEVER rendered (an `undefined` rotation → NaN + matrix → three.js silently declines to draw, no throw, no warning), and boot failures written + into a `display:none` div so a stranger got a blank page. None was findable from a bench. All + were findable by standing in the yard and looking, which is the entire thesis of gate 1. + + **`return t.skip(msg)` was a fake pass** (A): `Suite.skip()` pushes a NEW result while the + current test still records `pass`. I grepped every suite — **confined to a.test.js, four + instances, all fixed**; B, C, D, E and balance.test were clean, so the repo's counts were never + materially inflated. Worth knowing WHY those four were exposed: they guard on `dressed`, and + C's S13 landmine is that `dress()` throws silently without the importmap — two bugs that compose + into "green because nothing ran". selftest.html carries the importmap, so they were genuinely + running. + + **Gate 3.2 delivered: `site_03_swing_lawn`, "The Delaneys' back lawn"** — authored COLD by D in + the editor, no JSON, no console. Thesis: *the good steel is all low and the only height is bad.* + Scored winnable at $80 (11 clean lines, cheapest honest $65, collateral $230 on an $80 job), + played on a scratch branch, and the trap bit exactly as designed — the prettiest line finished + **83.9 against a bare bed's 83.7**, i.e. worth +0.2 HP. Card and game agreed to a point. **It is + a CANDIDATE, inert until a NIGHTS entry names it, and that is gate 4's call, not mine.** + + Pool, by owner: **B** — the score is 75–82 s with the UI thread fully blocked, which made the + editor *batch, not iterative* (D scored once and shipped that run rather than tuning; D's words + are the evidence, and a progress tick may matter more than raw speed). **A** — number fields + commit on blur only (Enter does nothing; D lost real time to a house sitting on the garden bed + while the panel read the new value); objects place outside the fence silently and still read + VALID; zoom is wheel-only. **A+B** — no `separation` field exists in the editor while B's card + says a shipping site should pin one. **B+C** — two independently-defaulted storm pickers (D + scored the wildnight while viewing the southerly). **E+A** — the `sail_post` hint above. + + **Gate 4 is open and it is now the only thing standing between this project and its next + direction: John plays the week.** Nine sprints. The editor has made "more sites" nearly free and + D has a candidate yard sitting ready; reputation/warranty and heatwave are unchanged in cost. + That choice is his and the game is the best argument for making it that it has ever been. + [I] 2026-07-18 — 🔍 **FRESH-EYES REVIEW (John's ask, run by Claude in the integration checkout) + SIX FIXES LANDED ON MAIN.** Full read of canon + code + a played night 1 + the whole suite. Selftest **409/0/0** (406 baseline + 3 new, every new assert @@ -6549,3 +6823,29 @@ anchors are your GLB), but the tooling is now waiting, not TODO. cloth render after one is still ugly; B should own the heal), a job-sheet surface for the fabric bet (the key exists, the FORECAST should argue with it — A), and membrane's balance once rain's drain weight moves (rigging.js's own comment says where to start). + +[I] 2026-07-18 — 🔀 **TWO INTEGRATIONS, ONE DAY — THE FORK, RECONCILED.** The fresh-eyes + review (my entry above) and the S14 lane integration (the [I] above it) happened in + PARALLEL checkouts the same day: origin took D's swing lawn + the palette-_v1 fix + a + SPRINT15 (posts/editor); this checkout took the six review fixes + a TEETH sprint plan, + also numbered 15. Merged with the house rules — code was disjoint (their side: editor.js; + my side: sail/rigging/debris/hud/main + suites), docs were the fork: + + · **Their SPRINT15 STANDS as Sprint 15.** Its gate 1 (the post rating_hint ruling) + re-measures the exact numbers TEETH wants to pin — running TEETH first would have + pinned pre-ruling numbers, which is the S13 lesson at sprint scale. + · **TEETH is SPRINT16, pre-speced** with a fires-after-15 banner; its D gate now WIRES + the swing lawn instead of authoring it (it exists — 0b858ab), and every pin says + "post-ruling numbers only". LANE_PROMPTS carries both sets, 16 below 15, marked. + · **S15 gate 4's branch half is RESOLVED by John** (all three, sequenced — teeth → + sites/clients → heatwaves; ROADMAP.md is the new growth canon holding the arcs). The + PLAY half stands, and is better now: the F key is live and the review fixes ride along. + · THREADS conflict resolved keep-both-chronological (their S14 entries, then the review + entry — the review's "406 baseline" and "pushed to SPRINT15" read as that fork's truth; + the follow-ups it names are SPRINT16 gates now). + + Selftest on the merged tree: run below this entry — the review's 3 asserts join whatever + D's landing carried. My review entry's fix #4 (debris seam deletion) and D's inert-props + finding touched DIFFERENT halves of debris/editor and merged clean; the palette fix means + the swing set now DRAWS, which makes E's S14 negative-control discipline the next thing + the editor exercises. diff --git a/web/world/data/sites/site_03_swing_lawn.json b/web/world/data/sites/site_03_swing_lawn.json new file mode 100644 index 0000000..e1a6eb1 --- /dev/null +++ b/web/world/data/sites/site_03_swing_lawn.json @@ -0,0 +1,232 @@ +{ + "id": "site_03_swing_lawn", + "name": "The Delaneys' back lawn", + "blurb": "Swing set, big jacaranda, house right there — you'll have no trouble finding something to tie to.", + "_design": [ + "THE YARD WHERE THE GOOD STEEL IS ALL LOW AND THE ONLY HEIGHT IS BAD.", + "Three temptations, three different lies:", + " - the swing set's crossbar is a dead-level rail at 2.05 m spanning 2.3 m and is NOT", + " an anchor (tie_off:false). The two apexes beside it are honest steel but only 0.45,", + " and blowing one bills $140.", + " - the jacaranda forks low and excellent (0.95) then falls off a cliff (0.52 / 0.40).", + " A sail wants height on its high corner; on this tree height costs 58%, against 24%", + " up a gum. That is the \"which tree\" question, and it is the whole reason t2 exists.", + " - the house fascia is the familiar 0.35 lie and takes the $90 gutter with it.", + "The only top-grade steel in the yard is one post (p1) and the jacaranda's LOW fork", + "(0.95). Four corners, two good ones - that is the puzzle.", + "MEASURED IN PLAY, and the one line here not written by the editor: p1 adopts at", + "ratingHint 1.00, NOT the 0.90 in E's asset audit - posts are constructed anchors and", + "never read the sail_post GLB extra. Every post on backyard_01 reads 1.00 too, so it", + "is repo-wide and not this yard's doing. Filed to E/A in THREADS.", + "The gum t2 (1.00 / 0.88) is parked 9 m away in the south-west ON PURPOSE: excellent", + "steel on the wrong side, so reaching for it costs you span and shape rather than", + "rating. The audit's cheapest honest line (h3,t2,t2b,p1) goes and gets it.", + "WIND: the venturi is the gap between the shed and the west end of the house - real", + "geometry, not a heading. The slot runs north-south so axis = 90 deg; gain 1.4, r 5.", + "It bites the RIGGING ZONE and not the bed: the throat sits 3.0 m from the swing set's", + "apexes, so the tempting middle option is also the windy one. Deliberately gentler than", + "the corner block (align 62%, peak x1.28 against its 100% / x1.46) - that yard keeps", + "the crown for worst weather and worst bill." + ], + "_why": [ + "Authored COLD in the yard editor for Sprint 14 gate 3.2 - no JSON hand-editing, no", + "console. Every number below is B's SCORE IT card, storm_03_southerly, funnel ON,", + "11 anchors dressed, one run of 82.5 s.", + " WINNABLE at $80 - 81 quads in band, 11 clean, 16 marginal.", + " cheapest honest line h3,t2,t2b,p1 - $65 hardware (holds at $55), 22 m2,", + " garden 85.3 FULL, worth +1.6 HP over bare.", + " held vs bare (flown) bare 83.7 FULL; best clean h3,t1c,t2,p1 -> 90.0 FULL;", + " separation +6.3 HP; by hail / rain 6.8 / 3.1.", + " THE TRAP, MEASURED the best-READING line t1c,t2b,s1_f2 is MARGINAL - 91.8 FULL", + " on paper, inside the 15% band. The prettiest number in the", + " yard is the one that breaks.", + " margin flags t2b 0% headroom; t1, t1b, p1, s1_f1 all 1%. s1_f1 wrecks the", + " swing set ($140), and the card prices it.", + " collateral exposure gutter $90 + swing set $140 = $230 if all lost, on a $80 job.", + "No `separation` block: the editor has no field for one, and B's own card says a", + "shipping site should pin one. Filed to A/B in THREADS rather than hand-edited in.", + "The bare bed reads 83.7 FULL, which is the bare-beds-win pool item showing up on a", + "third yard - gate-4 material, not this yard's doing and not tuned here." + ], + "yard": { + "width": 24, + "depth": 16 + }, + "sun": { + "elevationDeg": 55, + "azimuthDeg": -125 + }, + "gardenBed": { + "x": 0, + "z": 0, + "w": 5, + "d": 3.5 + }, + "house": { + "model": "house_yardside_v1", + "wreckedModel": "house_yardside_wrecked_v1", + "collateralKey": "gutter", + "collateralValue": 90, + "collateralLabel": "the gutter", + "x": 0, + "z": -6, + "anchors": [ + { + "id": "h1", + "node": "fascia_anchor_01", + "type": "house", + "work": "bracket" + }, + { + "id": "h2", + "node": "fascia_anchor_02", + "type": "house", + "work": "bracket" + }, + { + "id": "h3", + "node": "fascia_anchor_03", + "type": "house", + "work": "bracket" + } + ] + }, + "structures": [ + { + "id": "s1", + "model": "swing_set_01_v1", + "wreckedModel": "swing_set_01_wrecked_v1", + "x": -5, + "z": -2, + "rotYDeg": 0, + "solid": false, + "collateralKey": "swing_set", + "collateralValue": 140, + "collateralLabel": "the swing set", + "anchors": [ + { + "id": "s1_f1", + "node": "frame_anchor_01", + "type": "swing_frame", + "work": "cloth" + }, + { + "id": "s1_f2", + "node": "frame_anchor_02", + "type": "swing_frame", + "work": "cloth" + } + ] + } + ], + "trees": [ + { + "id": "t1", + "model": "tree_jacaranda_01_v1", + "x": 4.971, + "z": 2, + "phase": 1.1, + "trunkH": 3.4, + "anchorY": 2.4, + "anchors": [ + { + "id": "t1", + "node": "branch_anchor_01", + "type": "tree", + "work": "cloth" + }, + { + "id": "t1b", + "node": "branch_anchor_02", + "type": "tree", + "work": "cloth" + }, + { + "id": "t1c", + "node": "branch_anchor_03", + "type": "tree", + "work": "cloth" + } + ] + }, + { + "id": "t2", + "model": "tree_gum_02_v1", + "x": -8.5, + "z": 5.5, + "phase": 1.7, + "trunkH": 3.8, + "anchorY": 3.1, + "anchors": [ + { + "id": "t2", + "node": "branch_anchor_01", + "type": "tree", + "work": "cloth" + }, + { + "id": "t2b", + "node": "branch_anchor_02", + "type": "tree", + "work": "cloth" + } + ] + } + ], + "posts": [ + { + "id": "p1", + "x": 3.5, + "z": -2.5, + "h": 4, + "type": "post", + "work": "cloth" + } + ], + "fence": { + "sides": [ + "north", + "south", + "east", + "west" + ] + }, + "shed": { + "model": "shed_01_v1", + "x": -9, + "z": -5, + "rotYDeg": -90 + }, + "shedTable": { + "model": "shed_table_v1", + "x": 3.321, + "z": 7.753, + "rotYDeg": -90, + "pickupNode": "pickup_anchor" + }, + "gnome": { + "model": "garden_gnome_01_v1", + "x": 7.08, + "z": 6.344, + "rotYDeg": 0, + "collateralValue": 25 + }, + "bike": { + "model": "bike_kid_01_v1", + "x": -6, + "z": 5, + "rotYDeg": 180 + }, + "wind": { + "venturi": [ + { + "x": -6.22, + "z": -4.77, + "axis": 1.571, + "gain": 1.4, + "radius": 5, + "sharp": 3 + } + ] + } +} diff --git a/web/world/js/editor.js b/web/world/js/editor.js index d743ff2..6948f69 100644 --- a/web/world/js/editor.js +++ b/web/world/js/editor.js @@ -236,11 +236,18 @@ export const PALETTE = [ // crossbar that is the most anchor-looking object in the game and carries // `tie_off: false`. $140 is E's number, ADOPTED unchanged; the ruling and // the one condition for revisiting it are in THREADS. - kind: 'structure', label: 'Swing set (the honest middle)', model: 'swing_set_01', + // ⚠️ The `_v1` suffix is LOAD-BEARING and its absence is silent. These two + // entries shipped without it (SPRINT14, D's cold pass): the GLB 404s, the + // prop never draws, and because `adoptAnchor` does `rating_hint ?? 1` a + // MISSING MODEL BECOMES THE BEST STEEL IN THE GAME — the swing apexes read + // 1.00 instead of E's 0.45, the jacaranda 1.00/1.00/1.00 instead of + // 0.95/0.52/0.40 — and the yard still validates VALID. Same species as E's + // `tie_off` audit: silence is not neutral where a default is generous. + kind: 'structure', label: 'Swing set (the honest middle)', model: 'swing_set_01_v1', requires: ['swing_frame'], hint: 'apex anchors 0.45; the crossbar LOOKS like the answer and is not an anchor', make: (id, x, z) => ({ - id, model: 'swing_set_01', wreckedModel: 'swing_set_01_wrecked', + id, model: 'swing_set_01_v1', wreckedModel: 'swing_set_01_wrecked_v1', x, z, rotYDeg: 0, solid: false, collateralKey: 'swing_set', collateralValue: 140, collateralLabel: 'the swing set', @@ -254,10 +261,10 @@ export const PALETTE = [ // on a jacaranda and you pay 58% for it, against the gum's 24%. Unpriced by // E's own ruling (no limb-failure event to watch; billing an unseen event // is the lie the invoice exists to kill). Same node names as the gums. - kind: 'tree', label: 'Jacaranda (height costs)', model: 'tree_jacaranda_01', + kind: 'tree', label: 'Jacaranda (height costs)', model: 'tree_jacaranda_01_v1', hint: 'forks low and strong, then falls off a cliff — the "which tree" question', make: (id, x, z) => ({ - id, model: 'tree_jacaranda_01', x, z, phase: 1.1, trunkH: 3.4, anchorY: 2.4, + id, model: 'tree_jacaranda_01_v1', x, z, phase: 1.1, trunkH: 3.4, anchorY: 2.4, anchors: [1, 2, 3].map((i) => ({ id: i === 1 ? id : `${id}${'abc'[i - 1]}`, node: `branch_anchor_0${i}`, type: 'tree', work: 'cloth',