HardYards/web/world/data/sites/backyard_01.json
m3ultra b98dcad13a Sites are data: extract backyard_01.json; world.js builds from it
SPRINT10 gate 1a, carried since Sprint 8. Everything that was a module constant
— bed rect, house line, tree and post placements, shed, gnome, sun angle, fence
sides — is now data/sites/backyard_01.json, and world.js is a builder.

The extraction is byte-identical, and that's checkable rather than claimable:
E's branch-anchor tripwires pin five positions to 6 dp, a.test's quad-band
asserts pin the whole rigging geometry (>=3 quads in 18-45 m2, full coverage
still >45 m2), D's ladder field and B's balance lines all pass unchanged.
287/0/0.

It's byte-identical by construction, not by luck: the site-derived values keep
the builder's original local names, so ~500 lines of geometry that was already
correct is untouched and only its SOURCE changed.

Two things earned their place:

Anchors carry D's `work: "cloth" | "bracket"` — the MECHANISM, not the type. A
post is tensioned from a cleat at its base and a tree strop is thrown, but a
bracket bolted up a bare wall is the one you cannot fake. Keyed on mechanism
because `type` is a closed enum ('house'|'tree'|'post') that site_02's carport
doesn't fit, and needsLadder keyed on type FAILS OPEN there — the ladder
silently vanishes and you re-rig a 2.6 m bracket standing on the grass.

p4's swept position table moved INTO the JSON's `_why`, beside the coordinate it
justifies. That table is the only thing stopping someone nudging the post toward
the bed and collapsing DESIGN.md's central tension; it belongs with the number,
not in a file the number left.

validateSite() fails loud: sites are content, and a missing bed silently becomes
NaN coverage while a missing anchor list silently becomes an unriggable site.
createWorld now requires a site and says so.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:34:29 +10:00

87 lines
3.6 KiB
JSON

{
"id": "backyard_01",
"name": "The Backyard",
"blurb": "Your own place. You know where everything is, which is the only advantage you get.",
"yard": { "width": 30, "depth": 20 },
"sun": { "elevationDeg": 55, "azimuthDeg": -125 },
"gardenBed": { "x": 1, "z": 2, "w": 6, "d": 4 },
"house": {
"model": "house_yardside_v1",
"x": 0, "z": -10.5,
"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" }
]
},
"trees": [
{
"id": "t1", "model": "tree_gum_01_v1",
"x": -9, "z": 2, "phase": 0.7, "trunkH": 4.2, "anchorY": 3.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, "z": -2, "phase": 2.9, "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": -4.5, "z": 5.5, "h": 4.0, "type": "post", "work": "cloth" },
{ "id": "p2", "x": 4.0, "z": 6.0, "h": 4.0, "type": "post", "work": "cloth" },
{ "id": "p3", "x": 0, "z": 7.0, "h": 4.0, "type": "post", "work": "cloth" },
{
"id": "p4", "x": -3.2, "z": -1.2, "h": 4.0, "type": "post", "work": "cloth",
"_why": [
"DO NOT move this post toward the bed. Its position is swept, not chosen, and it is the",
"single number holding DESIGN.md's central tension open.",
"",
"Before p4 the wild night had NO winnable line, for a geometric reason: every anchor near",
"the bed (p1/p2/p3) is SOUTH of it and nothing stands north short of the house 10 m away,",
"so covering rigs had to span the yard and died while rigs small enough to survive sat",
"beside the bed rather than over it. p4 supplies the missing north-west corner.",
"",
"Swept against the smallest quad covering 90% of the bed:",
" (-2.2, -1.2) -> 44.4 m2 DO NOT: full coverage lands INSIDE the survivable band,",
" so covering the bed stops costing risk and the rigging",
" puzzle loses its wrong answers.",
" (-3.2, -1.2) -> 48.6 m2 <- here (with the 8 deg rake the top anchor lands at",
" -3.72,-1.40 and full coverage costs 51.6 m2)",
" (-3.2, -2.0) -> 51.7 m2",
" (-4.2, -3.0) -> 60.0 m2",
" beyond z=-4 -> 63.5 m2, i.e. too far out to matter at all",
"",
"Full coverage costs 63.5 m2 without it and 51.6 with: a real new option, still bigger",
"than the 23-38 m2 rigs that survive unaided. a.test.js asserts the >45 m2 floor, so",
"moving this inward goes red — that assert is the tension's only guard."
]
}
],
"shed": { "model": "shed_01_v1", "x": 11.8, "z": 6.2, "rotYDeg": -90 },
"shedTable": { "model": "shed_table_v1", "x": 9, "z": 6, "rotYDeg": -90, "pickupNode": "pickup_anchor" },
"gnome": { "model": "garden_gnome_01_v1", "x": 4.3, "z": 4.4, "rotYDeg": -126, "collateralValue": 25 },
"fence": {
"sides": ["south", "west", "east"]
},
"wind": {
"venturi": []
}
}