Compare commits

..

4 Commits

Author SHA1 Message Date
type-two
76ff175c03 Sprint 17 integration: the board, the terms, the pool yard — 515/0/0, [I] entry 2026-07-21 04:37:09 +10:00
type-two
d8466355e8 Lane D S17: [D] THREADS — the seven-night cold play through the board, verdicts per night
Two alternatives taken (the pool yard N1, the $45 cap N4), the marquee, the
designed loss and the fabric bet on the spine. THE WEEK HELD 7/7 · 6/7 · $401
· star 0.0 · 5 warranty jobs. Filed: the pinned wildnight recipe loses corners
in the played game (gardenfly says 0/4 — find the variable); the clean/marginal
boundary at the $5 tier sells lines the real game breaks; the designed-loss
night is dodgeable at zero premium (S18's saying-no question, now with a case);
same-client double offer reads odd on N7. Final tree 515/0/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 04:30:20 +10:00
type-two
64fb25529f Lane D S17: [D] THREADS — gate 3.2 landing (the pool yard, receipts, the second-rust-post filing)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 03:53:46 +10:00
type-two
39701884e3 Lane D S17 gate 3.2: THE POOL YARD — authored cold, in the game only by being chosen
site_04_pool_yard: E's ring (fourteen honest noes) east, the bed poolside, ONE
corroded post in the venturi channel where the NE corner wants steel. Authored
in the editor (palette shapes, iterative SCORE IT x3, canonical export).
Gauntlet: WINNABLE at $80 — 13 quads, 6 clean, 1 marginal; cheapest hold $40
(h1,t1b,p2,p3, 92.7 FULL, no rust); every 92%-cover line routes through c1 —
clean $65 rated on t1,p1,p2,c1, UNHOLDABLE on the two shapes that pull the eye
harder. audit.html over the shipped file reproduces the card to the digit.
Separation REFUSED with receipts (bare 83.7 FULL — mild-night canon, A's 0.4).

board.js (A's granted edit): pool_yard_southerly POOL entry with the audit
receipt. d.test: +5 pins (chosen-never-scripted, ring/rust facts, judged-XOR,
wreck apron, exposure $160). gardenfly.selftest (B's file, flagged): judged
walk 3 -> 4 in the same commit that ships the yard.

Selftest 515/0/0 (510 + 5, the count moved by exactly what was added).
Mutations: 1 full-suite (finding stripped -> DOUBLE red: B's walk 'UNJUDGED'
+ my XOR pin, 513/2), 10 probed (entry deleted/re-paired/receipt stripped/
scripted-slot, gnome unpriced/rust zeroed/fence priced, apron x3) — every one
red with its own words, controls green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 03:53:20 +10:00
5 changed files with 553 additions and 2 deletions

View File

@ -8985,3 +8985,190 @@ anchors are your GLB), but the tooling is now waiting, not TODO.
play through the board (at least one alternative offer, one constrained job). Gate 4
(John plays the seven at partly.party) closes the sprint; deploy fires after D's pass
integrates.
[D] 2026-07-21 — 🌊 **GATE 3.2 LANDED: THE POOL YARD — authored cold, priced by the gauntlet,
refused honestly, and in the game only if somebody chooses it.** Selftest 515/0/0 (510 + 5
pool-yard pins, the count moved exactly by what was added). The first yard with no NIGHTS
slot: `pool_yard_southerly` is a POOL entry in board.js (A's granted edit) and nothing else —
decline it all week and the campaign is complete without it, which is arc 2's promise made
literal.
**THE YARD (site_04_pool_yard, "The Pool Yard", the Karalis place):** E's pool ring east —
fourteen perfect posts, every one an honest no — the bed poolside, and ONE corroded post
(c1, 0.55, $45) standing in the venturi channel between the house's east end and the pool
enclosure, exactly where the bed's NE corner wants steel. Honest steel is scarce and west
(gum t1/t1b, posts p1-p3, the familiar 0.35 fascia). Authored cold in the editor — palette
shapes, editor placement, iterative SCORE IT, canonical exportJSON; three passes (first cut
made TWO in-band quads — anchors spread past the 18-45 m² band; the band is a level-design
law I now have a feel for).
**THE NUMBERS (SCORE IT card = audit.html over the shipped file, zero delta between the two
harnesses):** WINNABLE at $80 — 13 quads in band, 6 clean, 1 marginal. Cheapest hold $40
(h1,t1b,p2,p3 → 92.7 FULL, 58% cover, no rust in it). **Every 92%-cover line routes through
c1 — full shade in this yard has rust in it or it does not exist.** And the tier does what E
built it for: the SAME eye flies 1.9 kN (clean, $30 rated) on t1,p1,p2,c1 and 3.6-4.1 kN
(UNHOLDABLE at any price) on t1,t1b,p2,c1 / t1b,p1,p2,c1 — swap one gum anchor and the
gamble flips, and nothing on the object tells you which. **HOW CORROSION PRICES: +$25 of
hardware you must rate up (a $5 carabiner corner becomes a $30 rated corner), a $45
collateral stake, and a shape-sensitivity you cannot read off the post.** Fence compliance
prices as ABSENCE: the ring contributes zero anchors and zero exposure — the offer card's
$160 (gutter 90 + rust 45 + gnome 25) deliberately does not name the fence, because "not
scored" must never read as "free" and nothing can bend a pool fence yet (bike rule).
**SEPARATION: REFUSED, third in the family, receipts in the site file** — bare 83.7 FULL
vs best flown 92.7 (+9.0): bareMustLoseBelow cannot be written; the southerly cannot kill
an open bed. First refusal authored AFTER A's 0.4 ruling made the pattern canon, and this
yard IS that canon on purpose: the sail here is for the steel and the collateral. Flagged
for B: when the garden-BONUS separation shape lands, this yard's +9.0-at-$40 is a
bonus-stake candidate — re-measure then.
**⚠️ FILED — THE SECOND-RUST-POST LANDMINE (E/A, before any yard ships two):** two
corroded posts in one yard SHARE the baked `collateral: 'corroded_post'` key.
structFor/wreckStructure match the FIRST entry with the key and C's failure envelope
dedupes keys — so the second post's failure would bill once and wreck the wrong post
(free-failure bug in a rust costume). The pool yard ships ONE post on purpose and pins it
(d.test). A second rung in one yard needs per-instance key plumbing first.
**PINS (+5, d.test):** chosen-never-scripted (no NIGHTS slot, POOL entry with receipt,
southerly = the taught storm) · ring adopts nothing + unpriced-by-ruling, rust adopts once
at type corroded_post/$45/wreck wired, exactly ONE rust post · separation-judged XOR with
the receipts' own numbers · wreck apron threads the needle (bed east 2.5 ≤ shaft west
2.84; shaft east 3.36 ≤ ring west 3.7 — E's +Z 3.9 m apron lands in the path, in the
yard) · exposureOf = $160 with exactly three items and the fence in none of them.
Cross-lane, flagged (revert-and-tell-me): gardenfly.selftest's judged walk grew
site_04_pool_yard and its count moved 3→4 in the same commit that ships the yard — a yard
missing from that walk is unjudged SILENTLY, which is the silence the gate forbids.
**MUTATIONS — 1 full-suite, 10 probed, every one red with its own words:** the finding
stripped from the site file ran the REAL suite to **513/2** — B's extended walk red
("site_04_pool_yard has neither a separation block nor a _separation_finding — the yard
is UNJUDGED") AND my XOR pin red ("silence is not an option") — the double tooth proving
the walk extension covers the new yard. Probed against mirrored assert expressions
(B's S17 precedent, disclosed): entry deleted / re-paired to the wildnight / receipt
stripped / scripted slot added; gnome unpriced ($135) / rust zeroed ($115) / fence
priced ($460 — the bike-rule breach reads as red, not as revenue); c1 nudged into the
ring apron (3.86 > 3.70) / bed widened under the shaft / c1 falling over the south
fence. Controls green on the shipped data.
[D] 2026-07-21 — 🎮 **GATE 3.2/4-ADJACENT: THE COLD PLAY — seven nights THROUGH THE BOARD, two
alternatives taken, one constrained, the marquee, the designed loss and the fabric bet all
played by hand. Final tree selftest 515/0/0. The week ends THE WEEK HELD · 7/7 nights ·
6/7 gardens · $401 · ★ 0.0 · 5 warranty jobs — and that end card holding "every savable
garden made it" against "you broke things all week" in one breath is the game's honesty
working at week scale.** Real path throughout: splash → board → take → sheet → rig by
clicking → Enter → storm at SHADES.step(1/60) (the repo's own fast-forward) → invoice.
Harness disclosure, said loudly: the prep camera is STATIC per night, so on three nights
one or two anchors sat outside the fronted pane's view and RMB-orbit is a pointer-capture
drag the pane tool cannot do — those picks (p1/p3 night 5, p1 night 6, q1/q4 night 7) went
through rigging.session.rig()/setHardware(), the exact calls the click handler makes.
Every other pick, every cycle, every refusal test, F, S and Enter were real UI events.
**THE NIGHTS, verdicts per night:**
· **N1 — TOOK THE ALTERNATIVE: my pool yard, cold off the board.** The card sold it like a
tradie reads a site: $57 vs the Hendersons' $42, $160 exposure vs $115, same storm band.
Flew the gamble line t1,p1,p2,c1 at $65+spare. The rust corner CARRIED it — c1 peaked
2.23 of its derated 3.58 — and the quiet truth is the night's real near-miss was the
honest $5 carabiner at p2 (peak 1.12/1.2). Every corner held, garden 93 (audit said 92.0
flown — played number 1.3 over, same ballpark), +$79, ★3.0→3.5. **Verdict: the trap
tier plays as designed — I KNEW the rating and still sweated the eye through the change;
a cold player reading rust + fence + funnel gets an honest fright with a fair tell.**
· **N2 — spine, southerly × backyard.** Shackles on the post quad, held 4/4, 89, +$81.
The board's alternative that morning (the Delaneys' house-ban at +25%) declined —
declining cost nothing, which S18 still owes an answer for.
· **N3 — spine, earlybuster × corner block.** Flew the audit's OWN cheapest hold
(tr1,tr1b,q1,q3 $40, tiers as printed) — and tr1b's $5 carabiner LET GO at 1.17 of 1.2,
inside the bench-vs-game under-read MANUAL carries as policy. Garden 88, +$88, but
★4.0→3.5. ⚠️ **Filed for B/A: the card called this line CLEAN. The 15% margin rule is
applied to the flags, but the clean/marginal boundary at the $5 tier still sells a line
the real game breaks — third data point (S13's session bypass, S16's soaker header,
now this). Maybe cleanHw should carry the margin policy the flags already know.**
· **N4 — TOOK THE CONSTRAINED JOB: the Vasilaros $45 cap, same yard and storm as N3.**
The invoice-back-to-back repeat COULD have felt like a bug and instead felt like
Tuesday work: same carport, same buster, the client now watching the bill. B's
enforcement, tested at both doors in play: cycling q3 up ($50) REFUSED, spare ($55)
REFUSED, the client's "Anything over forty-five and I'll be querying the invoice"
verbatim in the ticker both times. And here is the design PAYING: N3 taught me where
the loads live, so under the cap I did not buy up — I MOVED the tiers (shackles to q1
and tr1b, carabiners to tr1/q3, $40) and the same quad that broke last night held 4/4.
Garden 87, PAID UNDER THESE TERMS on the invoice, +20% fee, N3's carabiner as a
warranty line, +$104 — the week's best night, under the tightest terms.
**Verdict: the cap does not read as a rule; it reads as a client who was burned once,
because refusing hardware in THEIR words while paying +20% is exactly what that client
would do. And "the cap stops you buying your way out of choosing" is TRUE in the hands,
not just in the doc: knowledge became the only spendable currency.**
· **N5 — spine, THE WILDNIGHT (my gate-0.1 restoration, first full play at seven).** Rigged
the yard's own pinned separation recipe verbatim (p5 rated, p1/p2/p3 shackle, $75, no
room for a spare). The marquee delivered the canon: p2 blown at the change (3.48/3.2),
p3 followed (3.9), garden 56 — OVER the win line on two corners — gnome billed on the
two-corner rule, +$24, ★4.0→2.0. "THE GARDEN MADE IT. The sail didn't. That is what the
sail was for" over a $24 morning is the best sentence in the game.
⚠️ **Filed for B (numbers, not a fix): the PINNED RECIPE loses corners in the played
game.** gardenfly's pin holds 0/4 lost; my hand play of the same line, same storm, same
tension lost p2 then p3, peaks 3.48/3.90 vs shackle 3.2. Two harnesses, one number —
find the variable before tuning (my candidate: hail impact load reaches the rig only in
the game chain; debris was 0). Garden-side the pin's INTENT held (56 > 50, bare loses).
· **N6 — spine, the icenight, the designed loss.** Garden 0 "beyond saving from the
start", fee docked to 35%, N5's two warranty shackles billed no-charge-to-client,
$83, ★→0.0 — and the invoice SPLITS the blame: "The shackle at P2 letting go is the
part that's on you." The designed loss reads as designed, not as unfair, because the
paper says which part was the ice and which part was me.
· **N7 — spine, the soaker, the F key.** $75 ring rated-on-q1, membrane. q2 — the pond
corner, the broom's job — let go LATE at 3.32/3.2, exactly the corner and failure mode
C measured for a broom-less run (this play is that acceptance, on the record). Garden
96 FULL, "held 391 kg of what it stopped. That is the membrane's price", +$28.
**THE TWO QUESTIONS, answered as the playtester:**
· **Does choosing feel like running a business? YES — at the morning table, and it is the
strongest new feeling in the game.** Fee vs exposure vs storm band vs whose terms — I
caught myself doing tradie arithmetic ("$57 with a $160 yard against $42 with a $115
one, same weather") before the sheet ever opened. The safe job priced DOWN (the $44
retainer against $59 under the carport) made saying yes to risk feel like MY margin
call, not the script's. What does NOT yet feel like a business: saying no. Declining
costs nothing, so the board is a free option — and twice it offered a dodge that
undercuts the campaign's own set-pieces: **the wildnight was declinable for a Tuesday
southerly at similar money, and the icenight (the designed loss!) sat next to a
winnable constrained job at the SAME $68.** A fee-maximizer skips the week's two
hardest lessons and never knows. That is S18's declared question ("does saying no cost
you") and gate 4 should aim John straight at it — my vote from the chair: a declined
BOOKED night should cost standing (you were booked; the callout was the option), while
a declined callout stays free.
· **Does a constraint read as a client or a rule? A CLIENT, cleanly.** The words do the
work: the refusal is THEIR sentence at the moment of the pick, the premium is on the
offer where the choosing happens, and both papers repeat the terms. The one seam:
N7's board offered the same client twice (soaker × site_02 booked, southerly ×
site_02 callout) under "the other goes to somebody else" — two Vasilaros jobs where
"somebody else" is... the Vasilaros place. Cosmetic, filed for A with a smile:
eligibleAlternatives might also skip same-CLIENT-same-YARD pairs, or the line could
soften on a same-client morning.
**Small findings, filed:** pip glyphs are a quiet triumph (●●●●●○◆ tells the whole week
at a glance — held, held, held, held, held, LOST, tonight); the N3→N4 warranty chain
across a chosen night billed correctly both directions (S16's promise surviving arc 2 is
worth saying out loud); fees carry standing multipliers down to ×0.88 at ★0.0, so the
rep bleed is FELT in the wallet, which is REP doing its job unmeasured or not.
[I] 2026-07-21 — **SPRINT 17 (THE BOOK OF CLIENTS, PART 1) INTEGRATION COMPLETE (main).** D's
second pass merged fast-forward; selftest **515/0/0** on the final tree (510 part-1 + D's
5 pins, the exact sum), verified in the browser. Deployed to partly.party/hardyards.
The week is seven nights offered two at a time: THE BOARD every morning, client terms
with teeth (the cap refuses in the client's own voice), the charter line on every SCORE
IT card, the heal's second wire, the honesty gate over NIGHTS POOL, and THE POOL YARD
— the first yard a player can only reach by choosing it — carrying E's corroded post as
exactly the trap-with-a-tell the repo ships.
**D's cold-play verdicts, the short form:** choosing feels like running a business (the
fee-vs-exposure-vs-terms arithmetic at the morning table is the strongest new feeling
in the game); a constraint reads as a client, not a rule. The week ran 7/7 stood up,
6/7 gardens, $401, ★0.0 with 5 warranty jobs — the ledger keeps every receipt.
**Filed forward (Sprint 18 seeds, all with live cases attached):** ⚖️ the sharpest —
**should declining a BOOKED night cost standing?** The board twice let a fee-maximizer
dodge the campaign's own set-pieces (the wildnight for a Tuesday southerly; the
designed-loss icenight against a winnable $68) — "does saying no cost you" is no longer
hypothetical. For B: the wildnight played-vs-gardenfly recipe disagreement (find the
variable; candidate is hail impact load reaching the rig only in-game), and the $5-tier
clean-boundary optimism (N3 broke the audit's own "clean" line at 1.17/1.2). For E/A:
the second-rust-post landmine (shared collateral key; the yard ships ONE and pins it).
For A: N7's same-client double offer reads odd under "the other goes to somebody else".
Gate 4 is JOHN'S: partly.party/hardyards — seven nights, the board, the Vasilaros cap,
and the Karalis pool yard in the pool. Your three sentences steer Sprint 18.

View File

@ -119,7 +119,12 @@ export async function buildGardenflyTests() {
let sepStates = null, sepStatesErr = null;
try {
sepStates = [];
for (const name of ['backyard_01', 'site_02_corner_block', 'site_03_swing_lawn']) {
// SPRINT17 gate 3.2 [D]: site_04_pool_yard added to the judged walk in the
// same commit that ships it (B's file, flagged in THREADS, revert-and-tell-
// me standard) — a yard missing from this list is UNJUDGED silently, which
// is the exact silence the gate-2.3 rule forbids. It carries a refusal
// finding (bare 83.7 FULL under its southerly; receipts in the site file).
for (const name of ['backyard_01', 'site_02_corner_block', 'site_03_swing_lawn', 'site_04_pool_yard']) {
const sd = await loadSite(name);
sepStates.push({ name, pinned: !!sd.separation, finding: !!sd._separation_finding });
}
@ -204,7 +209,7 @@ export async function buildGardenflyTests() {
// but silence is not an option, and holding both means the finding went
// stale the day the pin landed and must be deleted (its own text says so).
if (sepStatesErr) throw new Error(`site read died: ${sepStatesErr}`);
assert(sepStates.length === 3, `expected 3 shipped yards, read ${sepStates.length}`);
assert(sepStates.length === 4, `expected 4 shipped yards, read ${sepStates.length}`);
for (const s of sepStates) {
assert(s.pinned || s.finding,
`${s.name} has neither a separation block nor a _separation_finding — the yard is UNJUDGED; ` +

View File

@ -0,0 +1,237 @@
{
"id": "site_04_pool_yard",
"name": "The Pool Yard",
"blurb": "Fourteen perfect fence posts and not one of them is yours to use. The only steel standing where the shade wants a corner went in before the pool did.",
"_design": [
"THE YARD WHERE EVERYTHING SAYS NO EXCEPT THE THING THAT SHOULD NOT.",
"ROADMAP arc-2 thesis: fence compliance + corroded cheap hardware. The pool ring is",
"FOURTEEN honest noes (every post tie_off:false in the GLB — a certified pool barrier",
"is not an anchor, at any rating, ever); the corroded post c1 is the one thing that",
"DOES adopt on the east side, at 0.55, and it stands exactly where the bed's NE",
"corner wants steel. That is not an accident, it is the whole level:",
" - the bed is poolside; every 92%-cover quad in the band routes through c1.",
" Full shade in this yard has rust in it, or it does not exist (measured, SCORE IT:",
" the three 92% lines are t1,p1,p2,c1 / t1,t1b,p2,c1 / t1b,p1,p2,c1 — no others).",
" - the honest steel (gum t1/t1b, posts p1,p2,p3, the familiar 0.35 fascia) tops out",
" at 58% cover for $40 — the cheap safe rig leaves the bed's pool corner open.",
" - c1 is SHAPE-SENSITIVE, and that is the corroded tier working: the same drooped",
" eye is clean at $65 rated on t1,p1,p2,c1, and UNHOLDABLE AT ANY PRICE on the two",
" quads that pull it harder (t1,t1b,p2,c1 and t1b,p1,p2,c1). You cannot read which",
" off the object — E built the rung so you gamble rather than compute (their 0.55",
" reasoning, THREADS S17), and this yard is that gamble with a fence around it.",
"WIND: the venturi is the channel between the house's east end and the pool enclosure",
"— real geometry, gate-side, running N-S (axis 1.571, gain 1.4, r 5, throat (3.1,-3.5)).",
"It bites c1 and h3: the two lie-tiers are the two corners in the wind. The wreck",
"apron is PAID FOR (E's placement facts): c1 falls +Z ~3.9 m and the shaft lands in",
"the path between bed (x<=2.5) and ring (x>=3.7) — clear of both by ~0.3 m each side.",
"One corroded post ON PURPOSE: two would share the baked collateral key",
"(structFor/wreckStructure match the first entry; C's envelope dedupes keys), so the",
"second failure would bill and wreck the wrong post. Filed in THREADS [D] S17 —",
"a second rust rung in one yard needs its own key plumbing first."
],
"_why": [
"Authored COLD in the yard editor, SPRINT17 gate 3.2 — palette shapes, editor",
"placement, iterative SCORE IT (three passes: 2 quads -> tightened to band -> 13).",
"Every number below is the SCORE IT card, storm_03_southerly, FUNNEL ON, 9 dressed",
"anchors, export-clone world, 136.5 s flight. Confirmed by audit.html over the saved",
"file (same engine, second route) before shipping.",
" WINNABLE at $80 — 13 quads in band, 6 clean, 1 marginal.",
" cheapest honest line h1,t1b,p2,p3 — $40 hardware (+$15 spare), 45 m2, 58% cover,",
" garden 92.7 FULL, +9.0 HP over bare. No rust in it.",
" the rust gate full cover exists ONLY through c1 (92% x3, above); cheapest",
" clean rust line t1,p1,p2,c1 $65 — corrosion prices as +$25 of",
" hardware you must rate UP, plus a $45 stake on a 0.55 eye.",
" margin flags c1 4% headroom on h1,h2,p2,c1 (wrecks the corroded post $45);",
" p1 2% / t1b 2% / t1 4% — the 15% rule, marginal is not PASS.",
" collateral exposure gutter $90 (h1-h3) + corroded post $45 (c1) = $135 from",
" anchors; board exposure $160 with the gnome (exposureOf).",
" bare bed 83.7 FULL — the southerly cannot kill an open bed (its pea",
" hail takes 10.7 HP). The sail here is for the STEEL and the",
" collateral, per A's S17 gate-0.4 ruling — which is this",
" yard's thesis said as canon."
],
"yard": {
"width": 24,
"depth": 16
},
"sun": {
"elevationDeg": 55,
"azimuthDeg": -125
},
"gardenBed": {
"x": 0,
"z": 1.5,
"w": 5,
"d": 3.5
},
"house": {
"model": "house_yardside_v1",
"wreckedModel": "house_yardside_wrecked_v1",
"collateralKey": "gutter",
"collateralValue": 90,
"collateralLabel": "the gutter",
"x": -3,
"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": "c1",
"model": "sail_post_corroded_v1",
"wreckedModel": "sail_post_corroded_wrecked_v1",
"x": 3.1,
"z": -2,
"rotYDeg": 0,
"solid": true,
"collateralKey": "corroded_post",
"collateralValue": 45,
"collateralLabel": "the corroded post",
"anchors": [
{
"id": "c1_a1",
"node": "top_anchor",
"type": "corroded_post",
"work": "cloth"
}
]
},
{
"id": "pool",
"model": "pool_kit_01_v1",
"x": 6.5,
"z": 1.5,
"rotYDeg": 0,
"solid": true,
"anchors": []
}
],
"trees": [
{
"id": "t1",
"model": "tree_gum_02_v1",
"x": -6.8,
"z": 2,
"phase": 1.7,
"trunkH": 3.8,
"anchorY": 3.1,
"anchors": [
{
"id": "t1",
"node": "branch_anchor_01",
"type": "tree",
"work": "cloth"
},
{
"id": "t1b",
"node": "branch_anchor_02",
"type": "tree",
"work": "cloth"
}
]
}
],
"posts": [
{
"id": "p1",
"x": -3.4,
"z": 4.6,
"h": 4,
"type": "post",
"work": "cloth"
},
{
"id": "p2",
"x": 3.4,
"z": 4.6,
"h": 4,
"type": "post",
"work": "cloth"
},
{
"id": "p3",
"x": -5.8,
"z": -1.5,
"h": 4,
"type": "post",
"work": "cloth"
}
],
"fence": {
"sides": [
"north",
"south",
"east",
"west"
]
},
"shed": {
"model": "shed_01_v1",
"x": -9.5,
"z": -5.5,
"rotYDeg": -90
},
"shedTable": {
"model": "shed_table_v1",
"x": -6.5,
"z": 7,
"rotYDeg": -90,
"pickupNode": "pickup_anchor"
},
"gnome": {
"model": "garden_gnome_01_v1",
"x": 4.6,
"z": 4.4,
"rotYDeg": 0,
"collateralValue": 25
},
"wind": {
"_venturi": [
"The pool-terrace channel: house east end to pool enclosure, gate side, running N-S.",
"Axis 1.571 is the channel's GEOMETRY (a line, mod pi — site_02's reconciled lesson),",
"not a storm heading. Gain 1.4 / r 5 puts c1 (1.4 m off-throat) and h3 (3.7 m) in the",
"wind: the corroded post is the venturi post, which is the temptation priced."
],
"venturi": [
{
"x": 3.1,
"z": -3.5,
"axis": 1.571,
"gain": 1.4,
"radius": 5,
"sharp": 3
}
]
},
"_separation_finding": [
"SPRINT17 gate 3.2 [D] — a separation pin was ATTEMPTED and honestly REFUSED, the",
"third refusal in the family (site_02, site_03, now here) and the first authored",
"AFTER A's gate-0.4 ruling made the pattern canon. Measured (SCORE IT, funnel ON,",
"storm_03_southerly, 2026-07-21): bare bed 83.7 FULL (hail 10.7 + rain 5.6 HP);",
"best flown 92.7 FULL (h1,t1b,p2,p3, $40); best gain +9.0 HP. bareMustLoseBelow",
"cannot be written at any threshold — bare WINS the night by 33.7 over the win",
"line. On this night the sail is for the steel and the collateral (A, S17 0.4,",
"ruled canon, taught by the board's exposure line): what c1 separates is not the",
"bed from the hail but $45 and your margin from your greed. When B's garden-BONUS",
"separation shape lands (A ruled FOR it, deferred to its own gate), re-measure —",
"this yard's +9.0 over bare at $40 is a bonus-stake candidate, not a win/lose one."
]
}

View File

@ -240,6 +240,29 @@ export const POOL = [
+ 'working, it stops you buying your way out of choosing. The carport at $180 is the '
+ 'tension — the rig this cap pushes you toward is the one with the least margin over it.',
},
{
// SPRINT17 gate 3.2 [D] — THE POOL YARD. The first yard whose ONLY route
// into the game is being chosen: it is in no NIGHTS slot, so declining it
// forever is a complete campaign. That is the arc-2 promise made literal,
// and it is why the entry lives here and not in week.js.
id: 'pool_yard_southerly',
storm: 'storm_03_southerly', site: 'site_04_pool_yard',
client: 'the Karalis place', addr: '5 Clearwater Ct — the pool yard',
brief: 'New pool, new fence, certified in March — and the certifier is back Thursday, so '
+ 'nothing touches that fence. Not a rope, not a clamp. The old sail post from before '
+ 'the pool went in is still standing, they say, and they say it like a selling point. '
+ 'It has been breathing pool air for years. Tuesday\'s southerly, the bed\'s by the water.',
_why: 'AUTHORED COLD + AUDITED (D, S17 gate 3.2 — SCORE IT card, funnel ON, confirmed by '
+ 'audit.html over the shipped file): 13 quads in band, 6 clean, 1 marginal; cheapest '
+ 'hold $40 (h1,t1b,p2,p3 → garden 92.7 FULL, 58% cover, no rust in it). The trap is '
+ 'priced, not padded: every 92%-cover line routes through the corroded post c1 (0.55, '
+ '$45 collateral, IN the venturi) — clean at $65 rated on t1,p1,p2,c1, UNHOLDABLE on '
+ 'the two shapes that pull the eye harder. Southerly = the storm nights 2 and 4 teach; '
+ 'the yard is the only new variable (one-variable law). Separation REFUSED with '
+ 'receipts in the site file (bare 83.7 FULL — mild-night canon per A\'s 0.4 ruling); '
+ 'exposure $160 (gutter 90 + corroded post 45 + gnome 25), the fence\'s fourteen '
+ 'posts deliberately absent from that number because none of them can be tied to.',
},
];
/**

View File

@ -24,6 +24,13 @@ import { loadStorm, createWind } from '../weather.js';
// file read the ladder. week.js is contracts-only underneath, so this stays
// headless-safe.
import { NIGHTS, nightAt } from '../week.js';
// SPRINT17 gate 3.2: the pool-yard pins at the bottom read the board's POOL
// (pure data, zero THREE) and loadSite (fetch + validateSite — the teeth; a
// site file this suite merely fetch()ed raw could rot invalid and stay green).
// loadSite pulls world.js which pulls THREE — browser-only, same as the GLB
// facts everywhere else in this repo; the node runner never imports this file.
import { POOL, exposureOf } from '../board.js';
import { loadSite } from '../world.js';
const DT = FIXED_DT;
@ -1229,4 +1236,96 @@ export default async function run(t) {
"the wildnight sits directly before the icenight — the icenight's brief says 'less wind than "
+ "last night', and the paper never lies: re-order these and re-word that brief in the same commit");
});
// ── SPRINT17 gate 3.2: THE POOL YARD — authored cold, entered through the ──
// board, pinned by its author. These pins carry the yard's three claims:
// it is CHOSEN or absent (never scripted), its fence is fourteen honest noes
// and its rust is one priced yes, and its refusal + wreck apron are facts
// with numbers, not vibes. The site file itself goes through loadSite here,
// so a hand-edit that breaks validateSite goes red in THIS suite too, not
// only at boot.
let poolSite = null, poolSiteErr = null;
try { poolSite = await loadSite('site_04_pool_yard'); }
catch (err) { poolSiteErr = String((err && err.stack) || err); }
t.test('pool yard (S17 3.2): in the game ONLY through the board — chosen, never scripted', () => {
const entry = POOL.find((p) => p.site === 'site_04_pool_yard');
assert(entry, 'the pool yard has a POOL entry — its one door into the game');
assertEq(entry.storm, 'storm_03_southerly',
'it flies the southerly — the storm nights 2 and 4 teach, so the yard is the only new variable; '
+ 'any other pairing needs its own gauntlet run FIRST (the _why receipt describes this one)');
assert(entry._why && entry._why.includes('audit'),
'the entry carries its audit receipt — an unmeasured pairing is a night the board is guessing about');
const scripted = NIGHTS.map((_, i) => nightAt(i));
assert(!scripted.some((n) => n.site === 'site_04_pool_yard'),
'NO scripted night flies the pool yard — "the first yard whose only route in is being chosen" '
+ 'is the arc-2 promise, and a week.js slot would quietly break it');
});
t.test('pool yard (S17 3.2): the ring says no fourteen times, the rust says yes once', () => {
if (poolSiteErr) throw new Error(`site load died: ${poolSiteErr}`);
const ring = poolSite.structures.find((s) => s.model === 'pool_kit_01_v1');
assert(ring, 'the pool ring is in the yard — it IS the thesis');
assertEq((ring.anchors ?? []).length, 0,
'the ring adopts NOTHING in site data — its fourteen noes are baked tie_off:false in the GLB, '
+ 'and a site-level anchor here would override a certified fence into steel');
assert(ring.collateralValue == null,
'the ring is unpriced BY RULING (the bike rule) — nothing can bend a pool fence yet');
const rust = poolSite.structures.find((s) => s.model === 'sail_post_corroded_v1');
assert(rust, 'the corroded post stands');
assertEq((rust.anchors ?? []).length, 1, 'exactly one adoptable eye');
assertEq(rust.anchors[0].type, 'corroded_post', 'typed to E\'s tier so it rates 0.55, not 1.00');
assertEq(rust.collateralValue, 45, 'the make-safe bill, E\'s number, adopted with its reasoning');
assert(rust.wreckedModel === 'sail_post_corroded_wrecked_v1',
'the wreck variant is wired — a trap that vanishes instead of falling is a free failure');
const rusts = poolSite.structures.filter((s) => s.model === 'sail_post_corroded_v1');
assertEq(rusts.length, 1,
'ONE corroded post on purpose: two would share the baked collateral key and the second failure '
+ 'would bill/wreck the wrong one (structFor matches first; filed in THREADS [D] S17)');
});
t.test('pool yard (S17 3.2): separation-JUDGED — refused with receipts, XOR the pin', () => {
if (poolSiteErr) throw new Error(`site load died: ${poolSiteErr}`);
const pinned = !!poolSite.separation, finding = !!poolSite._separation_finding;
assert(pinned || finding, 'the yard is JUDGED — silence is not an option (S16 gate 2.3)');
assert(!(pinned && finding), 'both at once means the finding went stale in the pinning commit');
assert(finding, 'today it is a REFUSAL: bare wins the southerly (mild-night canon, A\'s 0.4 ruling)');
const text = poolSite._separation_finding.join(' ');
assert(text.includes('83.7') && text.includes('92.7'),
'the refusal carries its measured receipts (bare 83.7 / best flown 92.7) — a finding without '
+ 'numbers is a shrug wearing a ruling\'s clothes');
});
t.test('pool yard (S17 3.2): the wreck apron threads the needle between bed and ring', () => {
if (poolSiteErr) throw new Error(`site load died: ${poolSiteErr}`);
// E's placement facts (THREADS S17 gate 3.1): the corroded post falls +Z
// ~3.9 m, shaft half-width ~0.26 m. The fallen shaft must land in the path
// — clear of the bed's east edge AND the ring's west run — or the wreck
// interpenetrates client property the moment the trap fires.
const rust = poolSite.structures.find((s) => s.model === 'sail_post_corroded_v1');
const ring = poolSite.structures.find((s) => s.model === 'pool_kit_01_v1');
const bedEast = poolSite.gardenBed.x + poolSite.gardenBed.w / 2;
const shaftWest = rust.x - 0.26, shaftEast = rust.x + 0.26;
const ringWest = ring.x - 2.8; // ring proper 5.6 wide (E's measured bbox)
assert(bedEast <= shaftWest,
`bed east edge ${bedEast} overlaps the fallen shaft (west ${shaftWest.toFixed(2)}) — move c1 or the bed`);
assert(shaftEast <= ringWest,
`fallen shaft east ${shaftEast.toFixed(2)} reaches the ring (west ${ringWest.toFixed(2)}) — move c1 or the pool`);
const apronTip = rust.z + 3.9;
assert(apronTip <= poolSite.yard.depth / 2,
`the shaft falls past the south fence (tip z ${apronTip}) — it must land IN the yard, that is the point`);
});
t.test('pool yard (S17 3.2): the offer card prices the rust and never the fence', () => {
if (poolSiteErr) throw new Error(`site load died: ${poolSiteErr}`);
const { total, items } = exposureOf(poolSite);
assertEq(total, 160, 'exposure $160 = gutter 90 + corroded post 45 + gnome 25');
const labels = items.map((i) => i.what).sort().join(' · ');
assertEq(labels, 'garden gnome · the corroded post · the gutter',
'three priced items exactly — the fourteen fence posts are deliberately NOT exposure: '
+ 'nothing can bend a pool fence yet (the bike rule), and a $0 line item would teach "free"');
const rustItem = items.find((i) => i.what === 'the corroded post');
assertEq(rustItem.cost, 45,
'the rust is the cheapest structure you can break — the stake E sized the gamble in');
});
}