Merge origin/main: S14 lane integration + review fixes; TEETH becomes SPRINT16

Two same-day integrations reconciled: their SPRINT15 (post ruling, editor
iterative, honest edges) stands and runs first because it re-measures the
numbers TEETH pins; the TEETH plan moves to SPRINT16 with a fires-after-15
banner, its D gate rewired to the landed swing lawn. ROADMAP arc 0 records
the fork. THREADS keeps both sides chronologically.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-19 10:34:21 +10:00
commit 3dee2a7c83
8 changed files with 978 additions and 238 deletions

View File

@ -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 Same rules: own clone, own branch, `git fetch origin && git reset --hard
(`git fetch origin && git reset --hard origin/lane/<x>` on a clean tree — origin/lane/<x>` on a clean tree first. **The remote was RENAMED mid-Sprint-14
your local branch is BEHIND after integration fast-forwards). Read — it is `monster/hardyards.git` now, not `monster/shades`. All six checkouts
**ROADMAP.md** (new — the growth canon; John resolved S14's gate 4: all three are already repointed; if a push ever 404s, check for a rename before assuming
branches, sequenced, teeth first), THREADS from the last [I] entry (the loss.** Read docs/MANUAL.md, THREADS' last [I], then SPRINT15.md. Ports: A
fresh-eyes review landed six fixes on main — the fabric key, the divergence 8824, B 8825, C 8826, D 8827, E 8828. Baseline **406/0/0**.
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.
## Lane A — Sprint 15 ## Lane A — Sprint 15
> You are Lane A on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, > You are Lane A on SHADES 3D (the game is HARD YARDS, public at
> THREADS' last [I], then SPRINT15.md. Gate 1 is yours: THE LEDGER. The week > 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 7582 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/<x>` 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 > records every committed rig (corners, hardware, what broke, what was
> repaired); a corner still broken at dawn books a WARRANTY line on the next > 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 > 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 > a.test: warranty only on unrepaired breaks; quote==settle with a warranty
> line; rep untouched on designed losses; clean-week negative control. You > 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 > 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 > 3.3 exposes them). Do NOT build the job board — arc 2, and gate 6 aims it.
> build the job board — that's arc 2, and gate 6 aims it.
## Lane B — Sprint 15 ## Lane B — Sprint 16
> You are Lane B on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, > You are Lane B on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md,
> THREADS' last [I] (the review touched YOUR files: the divergence break in > THREADS' last [I], then SPRINT16.md. Gate 2 is yours. (a) THE HEAL: after a
> _checkFailure, the post-flow pond clamp, the F key in your rigging UI, and > divergence break (landed in the fresh-eyes review — read that [I]), non-
> a fixed tautology in your own suite — read the receipts), then SPRINT15.md. > finite node positions reset from the nearest finite neighbour (prev matched
> Gate 2 is yours. (a) THE HEAL: after a divergence break, non-finite node > — verlet must not invent velocity) and non-finite WATER zeroes (a NaN pond
> positions reset from the nearest finite neighbour (prev matched — verlet > re-poisons the heal through the weight term); assert finite within 1 s,
> must not invent velocity); assert finite within 1 s, mutation-proof, and > mutation-proof, and the existing divergence test stays untouched. (b) THE
> the existing divergence test stays untouched. (b) THE RECORD: the chosen > RECORD: the chosen fabric prints in prep summary, invoice, and the verdict
> fabric prints in prep summary, invoice, and the verdict when it mattered — > when it mattered — the F key exists, now the paperwork remembers the bet.
> the F key exists, now the paperwork remembers the bet. (c) THE PIN: site_02 > (c) THE PIN, post-ruling numbers only: run gardenfly on site_02 (and the
> declares no separation block and the SCORE IT card calls it out — run > swing lawn if unpinned), pin the honest held-vs-bare separation, assert it
> gardenfly, pin the honest held-vs-bare target, assert it; if the yard > like backyard_01's; if a yard can't separate at $80 that's a FINDING for
> can't separate at $80, that's a FINDING for THREADS, not a fudged pin. > THREADS, not a fudged pin. Pool: the >83 ms determinism bound gets a
> Pool: the >83 ms determinism bound gets a pinning test or a documented > pinning test or a documented waiver.
> waiver; membrane pricing stays parked until rain's drain weight moves.
## Lane C — Sprint 15 ## Lane C — Sprint 16
> You are Lane C on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, > You are Lane C on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md,
> THREADS' last [I], then SPRINT15.md. Gate 3 is yours. (1) STORM_06, the > 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 > 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 > 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 > leak fine hail all night; membrane must pond (the broom matters); the
> membrane line must be affordable at $80 and MEASURABLY better — gardenfly > membrane line must be affordable at $80 and MEASURABLY better — gardenfly
> and site_audit numbers pinned in your selftest, funnel state in the header. > 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 > post-ruling tree only. If cloth still wins, retune until the bet is real: a
> worse than no night. (2) makeStorm(seed): a seeded generator inside > decoration night is worse than no night. (2) makeStorm(seed): a seeded
> authored envelopes, deterministic (same seed → byte-equal def), schema- > generator inside authored envelopes, deterministic (same seed → byte-equal
> valid across 100 seeds, storm_envelope-tested — arc 4's roguelite eats > def), schema-valid across 100 seeds, storm_envelope-tested — arc 4's
> this; no shipped night uses it yet. (3) Expose hail stone size and rain > roguelite eats this; no shipped night uses it yet. (3) Expose hail stone
> rate for the forecast block (A prints them) — bands, not promises. The > size and rain rate for the forecast block (A prints them) — bands, not
> one-variable rule binds you to D: storm_06 flies over a yard the player > promises. The one-variable rule binds you to D: storm_06 flies over a yard
> already knows. > the player already knows.
## Lane D — Sprint 15 ## Lane D — Sprint 16
> You are Lane D on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, > You are Lane D on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md,
> THREADS' last [I], then SPRINT15.md. Gate 4 is yours and it's the sprint's > THREADS' last [I], then SPRINT16.md. Gate 4 is yours. (1) Your swing lawn
> acceptance test again. (1) site_03 SHIPS this time: author it cold in the > enters the rotation: re-score site_03_swing_lawn on the post-ruling tree
> editor (no JSON hand-editing, no console — the S14 discipline), any theme > with S15's faster loop (the numbers that made it winnable-but-mean may have
> E's palette supports, winnable-but-mean at $80, separation pinned, > moved), fix what moved, wire its NIGHTS entry. (2) The week becomes SIX
> collateral priced, _design/_why filled; file every editor friction in > nights: you own the order and the two new briefs (client flavour is data —
> THREADS. (2) The week becomes SIX nights: you own the NIGHTS order and the > the corner block got a name and a reason; the swing set's kid deserves one
> two new briefs (client flavour is data — the corner block got a name and a > too). One-variable rule: the swing lawn debuts under a storm the player has
> reason; match that bar). One-variable rule: site_03 debuts under a storm > survived; storm_06 flies over a yard they know; never both new at once.
> the player has survived; storm_06 flies over a yard they know; never both > Night 5's beyond-saving arc and going-broke-never-on-the-final-night must
> new at once. Night 5's beyond-saving arc and going-broke-never-on-the- > survive your re-ordering — a.test will tell you. (3) Cold-play all six on
> final-night must survive your re-ordering — a.test will tell you. (3) > one wallet and file the verdict: is a warranty line FAIR when it lands?
> Cold-play all six on one wallet and file the verdict: is a warranty line > Does rep-moves-pay feel earned? Is night 6's bet legible from the forecast
> FAIR when it lands? Does rep-moves-pay feel earned? Is night 6's bet > alone? Your play is gates 13's acceptance. Pool: the phantom-sail seam if
> legible from the forecast alone? Your play is gates 13's acceptance. > S15 didn't close it.
> Pool: the phantom-sail seam, third sprint — verify A's fix or keep the
> repro honest.
## Lane E — Sprint 15 ## Lane E — Sprint 16
> You are Lane E on HARD YARDS, Sprint 15. Rebase onto main, read ROADMAP.md, > You are Lane E on HARD YARDS, Sprint 16. Rebase onto main, read ROADMAP.md,
> THREADS' last [I], then SPRINT15.md. Gate 5 is yours: arc 2 needs yards > 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, > this palette cannot dress. Build TWO of three, factory-built, byte-stable,
> negative-control-proven (build the wrong one on purpose, watch red): the > negative-control-proven, and every anchor-bearing node passes S15's gate-1
> GLASSHOUSE (collateral apex — priced, wrecked variant, collateral_key > mechanism (a baked hint that can't reach a JSON-declared anchor is the bug
> wired so a SECOND one placed in the editor still bills; nothing about it > class that cost the posts their truth): the GLASSHOUSE (collateral apex —
> adopts as an anchor), the POOL KIT (pool + compliant fence ring where > priced, wrecked variant, collateral_key wired so a SECOND one placed in
> every fence post is honest tie_off:false — adoptAnchor's `?? 1` makes a > the editor still bills; nothing about it adopts as an anchor), the POOL
> silent node the best steel in the game, and the pool yard's whole puzzle > KIT (pool + compliant fence ring where every fence post is honest
> is that the legal tie-offs are in the wrong places), the TRAMPOLINE + tie- > tie_off:false — adoptAnchor's `?? 1` makes a silent node the best steel in
> down kit (if S14's pick didn't land it — typed honestly in the checked > the game, and the pool yard's whole puzzle is that the legal tie-offs are
> enum). Flag D in THREADS as each lands; site_03's theme may want one. > in the wrong places), the TRAMPOLINE + tie-down kit (typed honestly in the
> Axis trap stands: measure exported GLBs in three.js coords, look at the > checked enum; the tie-down is prep's counter to the storm's opening
> render in assetcheck, never trust a docstring about orientation. > 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.

View File

@ -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) · `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 3. `THREADS.md` — the append-only lane log. **The project's memory.** Read the
last few `[I]` integrator entries to know the current state. 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) 5. `LANE_PROMPTS.md` — copy-paste prompts per lane, per sprint (bottom = newest)
## The lane model (how this repo is built) ## The lane model (how this repo is built)
@ -67,9 +69,9 @@ Host gitea.partly.party
Then: 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 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') 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 done
``` ```

View File

@ -18,8 +18,8 @@ now."
## Where we are (the honest baseline) ## Where we are (the honest baseline)
Five 90-second nights, two yards, five storms, one wallet, ~15 minutes of Five 90-second nights, two shipped yards (a third — the swing lawn — authored
campaign. Selftest 409/0/0. An editor that makes new yards cheap and scores them 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 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 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 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.* *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 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 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 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 argues hail size and rain, invoice records the cloth you chose). D's swing lawn
ships, the week becomes six nights, and one of them — C's pea-hail soaker — is enters the rotation, the week becomes six nights, and one of them — C's pea-hail
the first night where the default fabric is the wrong answer. The bare-beds soaker — is the first night where the default fabric is the wrong answer. The
separation fight starts here and does not end until held-vs-bare is a pinned, bare-beds separation fight starts here and does not end until held-vs-bare is a
teeth-bearing number on every shipped site. pinned, teeth-bearing number on every shipped site.
## ARC 2 — THE BOOK OF CLIENTS (≈ two sprints) ## 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. named here so nobody half-builds it sooner.
- **Deploy**: tools/deploy_hardyards.sh after every integration; the live site - **Deploy**: tools/deploy_hardyards.sh after every integration; the live site
is currently one sprint behind main and that gap should never exceed one. 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 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). pricing the day rain's drain weight moves (rigging.js's comment names it).

View File

@ -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 *Sprint 14 built the yard editor and it immediately earned itself: nine bugs
bare-beds number on a card (bare 82.6 vs best clean 91.3) and nobody can unsee found before anyone authored a yard with it, three of them live on the public
it. John played, ruled on gate 4 (ROADMAP.md, 2026-07-18): all three branches, URL. D then authored a complete third yard cold — no JSON, no console — and
sequenced, teeth first. This sprint the week grows consequences — reputation on it scored winnable-but-mean and played that way. 406/0/0, live at
the letterhead, warranty on the job sheet, a sixth night, a third yard, and the partly.party/hardyards, editor included.*
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.*
**The one-variable rule is LAW this sprint (ROADMAP principle 5):** six nights, *What it also found is a shipped balance bug we deliberately did not patch,
exactly one flies the new storm, exactly one is the new site, and they are NOT because half the repo's measured numbers are standing on it. That's gate 1.*
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) 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 ## Gate 1 — THE POSTS TELL THE TRUTH (A rules · E supplies · B re-measures · D re-plays)
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, The factory bakes `rating_hint = 0.9` on `sail_post`'s `top_anchor`. Every
hardware, effective ratings, what broke, what was repaired by dawn. This is post in every shipped site is a plain JSON entry with **no `node`**, so
data the night already computes — the ledger just keeps it. `adoptAnchor` never runs and they all default to **1.00**. Every honest post
2. **Warranty.** A corner of YOURS that broke and was still broken at dawn books in the game is 10% stronger than E designed it, and always has been.
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.
## 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, 1. **A rules FIRST, before anyone measures:** is the honest post 0.90 (E's
reason:'divergence', production path) — but the freed node then integrates design intent reaches the sim) or 1.00 (the shipped value becomes canon and
from NaN forces and the cloth renders ugly after one. Sanitize on the break: E's baked hint is corrected to match)? Both are defensible — 1.00 is what
any non-finite node position resets from its nearest finite neighbour (or every played verdict in THREADS actually measured, and "the value we shipped
the corner's anchor), prev matched so verlet doesn't invent velocity. and balanced against is the real one" is a legitimate ruling. What is NOT
Assert: poison → break → every node finite within 1 s of sim, render-safe. defensible is leaving the factory and the sim disagreeing. **Whichever way
Mutation: heal off, assert red. The existing divergence test must keep it goes, the mechanism must be fixed so a baked hint can never again fail to
passing untouched — the heal is IN ADDITION to the break, not instead. reach a JSON-declared anchor silently** — that's the actual bug, and it will
2. **Fabric on the paperwork.** The chosen fabric prints in the prep summary, bite the next asset the same way (`tie_off`, `collateral`, anything).
on the invoice, and in the aftermath verdict when it mattered (a membrane 2. **E:** make the factory agree with the ruling and pin it — an assert that a
night survived in cloth should say what the leak cost). The FORECAST half is JSON-declared anchor of a type the factory rates carries that rating at
C's (gate 3.3) — yours is that the choice the player made is on the record. runtime. Your S14 audit rules (no silent anchors, no unpriced collateral)
3. **Pin site_02's separation.** The SCORE IT card says NONE PINNED and calls are the model; this is the third of that family.
it out — a shipping site should promise held-vs-bare and be held to it. Run 3. **B:** re-measure everything that moved. If the ruling is 0.90 every post
gardenfly, pick the honest target, pin the block in site_02's JSON, assert line in both shipped yards gets weaker — expect the separation pin, the
it like backyard_01's. If the honest answer is "this yard cannot separate winnable-lines tables, and the night-4/5 affordability story to shift, and
held from bare at $80", that is a FINDING about the yard — post it in post the deltas as a table. Propose retunes with evidence; A rules.
THREADS and gate 4's balance fight starts there, not at a fudged pin. 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 D's verdict: the 7582 s fully-blocked score "made the editor batch, not
ruling's blocking threshold), heavy rain, mild wind. The night the bet iterative — I scored once and shipped that run rather than tuning." That is
flips: cloth leaks fine hail onto the bed all night; membrane stops the difference between a tool you author WITH and a tool you check work AGAINST.
everything and the mild wind means its doubled load is affordable — but it 1. **B:** make the score interruptible and legible. In priority order — a
PONDS, so the broom matters. Author it so both lines exist and the membrane progress tick that actually paints (D says this may matter more than raw
line is genuinely better: measure with gardenfly/site_audit (funnel state in speed), then yielding so the page stays responsive, then speed itself
the header, per the discipline), pin membrane-vs-cloth garden separation in (the cost scales like C(n,4) in candidate quads — a coarse pass that ranks
the storm's own selftest, and make sure a $80 wallet affords the membrane before it flies is likely worth more than micro-optimising the sim).
line on the night's site. If the measured numbers say cloth still wins, Whatever you do, the numbers must not change: pin the current site_02 and
retune the hail spectrum until the bet is real — the storm exists to make site_03 scores before you start and reproduce them after.
the F key matter, and a decoration night is worse than no night. 2. **B + C:** one storm picker, not two independently-defaulted ones (D scored
2. **makeStorm(seed) — groundwork, not a shipped night.** A seeded generator the wildnight while viewing the southerly — the exact class of
inside authored envelopes (base curve, gust budget, direction change window, two-harnesses-disagreeing this repo keeps paying for, now inside one page).
rain/hail ranges as data), producing storm defs that pass the schema 3. **D:** re-author or re-tune a yard with the faster loop and say whether it
validator and the storm_envelope harness. Deterministic from seed — changed how you work. That's the acceptance test, same as gate 3.2 was.
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 — 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 D's friction list, by owner, all seen in anger:
editor is a sprint older now and SCORE IT prices collateral. Author it 1. **A:** number fields commit on blur only — Enter does nothing, and a house
cold (editor only, the S14 discipline), any theme E's palette supports, sat on the garden bed while the panel read the new value. Commit on Enter.
winnable-but-mean at $80, separation pinned, collateral priced, _design/ 2. **A:** objects place outside the fence silently and the yard still reads
_why filled. File the friction in THREADS — every place the editor fights VALID. Either validate the yard bounds or draw the boundary — a yard that
you is still gate-1/2 feedback one sprint late. lies about being valid is the one thing this editor must never do.
2. **The week becomes six nights.** Wire site_03 and storm_06 into NIGHTS under 3. **A:** zoom is wheel-only (no keyboard, no pinch).
the one-variable rule: site_03 debuts on a storm the player has already 4. **A+B:** no `separation` field exists in the editor, while B's score card
survived; storm_06 flies over a yard they already know. You own the order says a shipping site should pin one. Either add it to the SITE panel or
and the two briefs (client flavour is data — write clients worth meeting; rule that separation is an integration-time concern and say so on the card.
the corner block got a name and a reason, match that bar). Night 5's 5. **A:** honour `tie_off: false` at the palette layer if you offer anchors by
beyond-saving flag and the went-broke arc must both survive re-ordering — name from GLB nodes (E's flag; the runtime half is already honoured).
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 13.
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.
## 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- **⚡ THE BRANCH HALF OF THIS GATE IS RESOLVED (2026-07-18, integration checkout):**
built, honest extras, negative-control asserts (your discipline — build the this sprint plan and a TEETH sprint plan were written in parallel forks the
wrong one on purpose and watch red): same day, and John ruled while they were being reconciled — **all three
1. **The glasshouse** — the collateral apex. Priced high, wreck variant, branches, sequenced: teeth → sites/clients → heatwaves.** ROADMAP.md (new,
collateral_key wired so a second one placed in the editor bills (the the growth canon) carries the arcs; SPRINT16.md carries TEETH pre-speced,
carport's five-sprint lesson is one sprint old). Panes that read as fragile firing when this sprint integrates. The gates above stand exactly as written
at a glance; nothing about it may adopt as an anchor. — they re-measure the numbers SPRINT16 pins, which is why 15 runs first.
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.
## Gate 6 — JOHN: PLAY, THEN AIM ARC 2 **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
Six nights at partly.party/hardyards (deploy after integration — the live gap sibling now (`site_03_swing_lawn`, inert until SPRINT16 wires it).
is one sprint and ROADMAP says it never exceeds that). Three sentences in - **More sites** — D's `site_03_swing_lawn` is authored, scored and played,
THREADS. Then one ruling for arc 2's first sprint: **emergency mid-storm sitting inert waiting for a NIGHTS entry. This branch is now nearly free.
callout or the cowboy audit** — which job type leads THE BOOK OF CLIENTS? - **Reputation / warranty** — DESIGN.md's consequence loop; the invoice exists
to hang it off. Unchanged in cost.
Pool, spread by ownership: dev-line visibility default (A, second carry); - **Heatwave days** — the slow-pressure half of the design, sun-arc coverage,
bench-vs-UI residual cause (open since S13; the margin rule stands in); a genuinely different puzzle. Unchanged in cost.
membrane pricing the day rain's drain weight moves (B, rigging.js's comment Nobody builds any of the three this sprint.
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 gate 1: the post hint ruled, the mechanism fixed so a baked hint cannot
holds, a designed loss costs nothing silently fail to reach a JSON anchor, both yards re-measured, D re-plays
gate 2: divergence heal (finite in 1s, mutation-proof); fabric on the record; gate 2: the score paints progress and stays responsive; numbers reproduce
site_02 separation pinned or honestly refused exactly; one storm picker; D says the loop changed
gate 3: storm_06 makes membrane the right answer, measured not asserted; gate 3: Enter commits, out-of-bounds stops reading VALID, the friction list empty
makeStorm(seed) deterministic inside envelopes; forecast argues the bet gate 4: John's three sentences + the branch, in writing
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
``` ```

168
SPRINT16.md Normal file
View File

@ -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 13.
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
```

View File

@ -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, 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. 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/<newname>.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 7582 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 [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 + 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 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 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 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). 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.

View File

@ -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
}
]
}
}

View File

@ -236,11 +236,18 @@ export const PALETTE = [
// crossbar that is the most anchor-looking object in the game and carries // 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 // `tie_off: false`. $140 is E's number, ADOPTED unchanged; the ruling and
// the one condition for revisiting it are in THREADS. // 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'], requires: ['swing_frame'],
hint: 'apex anchors 0.45; the crossbar LOOKS like the answer and is not an anchor', hint: 'apex anchors 0.45; the crossbar LOOKS like the answer and is not an anchor',
make: (id, x, z) => ({ 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, x, z, rotYDeg: 0, solid: false,
collateralKey: 'swing_set', collateralKey: 'swing_set',
collateralValue: 140, collateralLabel: 'the 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 // 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 // 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. // 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', hint: 'forks low and strong, then falls off a cliff — the "which tree" question',
make: (id, x, z) => ({ 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) => ({ anchors: [1, 2, 3].map((i) => ({
id: i === 1 ? id : `${id}${'abc'[i - 1]}`, id: i === 1 ? id : `${id}${'abc'[i - 1]}`,
node: `branch_anchor_0${i}`, type: 'tree', work: 'cloth', node: `branch_anchor_0${i}`, type: 'tree', work: 'cloth',