6bcc2540cf
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6bcc2540cf |
Sprint 11 gate 2: the job sheet — five nights become five jobs
DESIGN.md has opened with this since line 3 — "every callout is a different site, a different client" — and the game had the site and not the client. The forecast card is a job sheet now and the aftermath is an invoice. THE WEEK, once it was asked to name a client, mostly wrote itself: four nights are the same yard, so they're one client on retainer while they're away (the Hendersons, in Cairns from Friday — SPRINT11's own line). Night 3 is a different SITE, so it is necessarily a different CLIENT: a short-notice callout to a corner block you've never rigged, on the night the change comes early. That isn't flavour bolted on, it's what `site: site_02_corner_block` already meant, finally said out loud. BUDGET Y IS THE FEATURE, not the letterhead. DESIGN.md's brief is "client wants X, budget Y, forecast Z" and this card had Z alone. The fee has existed since Sprint 8 and the player has never seen it until the money was already spent — you chose what to rig without knowing what the job was worth, which is a reveal, not a decision. The sheet quotes base + garden + clean up front, and a test pins that a perfect night pays exactly what the sheet advertised: a job sheet that over-promises is the game lying on paper. THE CLEAN BONUS is the new money, and it's the one number in week.js that isn't measured. It gives the invoice three axes that mean three different things — you turned up, their garden lived, you didn't wreck the place — and it's NOT gated on winning, because a lost night where you broke nothing of theirs is a different fact and deserves its own row. It also makes the carport bite twice: 180 + 20 = 200, inside E's own "over ~250 is a silent game over" ceiling. Measured: it adds ~$100 to a competent week (385 -> 485), which makes BROKE_BELOW's known runaway worse. I did NOT pay for it by quietly trimming feeFor or gardenBonusMax — those carry measured evidence, and re-tuning someone else's evidence to fund my own feature is the move this repo keeps catching. If gate 3 says the bank runs away, this is the lever to cut first and it's data. TWO BUGS, BOTH CAUGHT BY LOOKING AT THE CARD, neither by a test: · "THE HENDERSONS" over "The Backyard — Your own place". The blurb and the letterhead called each other liars on screen. The blurb gave way: "your own backyard" is a leftover from when the game had one yard, and DESIGN.md never wanted it. The line's job survives — night 1 is the tutorial because you know this yard, now from having worked it for years rather than owning it. · C's `lead` is a 0..1 haze dial (confidence = 1 − lead), not "nights out". I read it as nights, passed 1, and the sheet advertised "forecast confidence 0%". Now 0.6, because C's own hail rule calls L > 0.55 too distant to promise ice, and tomorrow is exactly that. C: the slot is mine, the number is yours. Lane C's forecastLines(def, lead) finally has a caller above 0 — two sprints after they built it. Lane E: .letterhead / .brief / .jobsheet / .tomorrow are structure only and yours to style. selftest 304/0/0 (was 300). |
||
|
|
4aee055f2d |
Sprint 11 gate 1: three rulings — enum widened, carport priced, venturi reconciled
The corner block's seams, all of them A's to close. ENUM (D's flag). Anchor.type was a closed JSDoc enum 'house'|'tree'|'post', so site_02 typed its carport 'post' to fit — quietly enrolling it in the sail-post family that C's venturi and B's audit both read. Widened to E's own asset strings (carport, carport_post) and, more to the point, CHECKED: it was documentation for ten sprints, and documentation cannot fail. validateSite now rejects an unknown type by name. The ladder is untouched either way — D keyed it on `work`, the mechanism, which is exactly why that was right. CARPORT (E's proposal, adopted). $180, with E's reasoning intact: gnome 25, a night's budget 80, so 180 is 2.25 nights — a good week turned broke and felt for the rest of the run, without ending a strong one outright. Under ~120 it's a shrug, over ~250 a silent game over that teaches nothing. The number lives in site JSON, not the GLB: sites are data, and what a carport COSTS is this site's economy, not a property of the mesh. E's baked value stays as the fallback and the proposal. And it BILLS now. E shipped the trap and said it plainly — the anchors said collateral "carport" and nothing said what one cost, so nothing scored it. You could lose the worst steel in the game and pay for a $15 shackle. One broken corner is enough (the gnome needs two: it needs the sail to land on it; the carport doesn't need the sail at all), priced per structure so two beams is one carport gone, not $360. The wreck swaps on the same event — E built it to the same origin for exactly this. VENTURI (C's question). There was no disagreement to settle. weather.core aligns on |dot(wind, axis)| because a gap funnels either way through it, so an axis is a LINE: -1.08 + PI = 2.0616, and my shipped 2.1 was the same gap read from the other end. Took C's number because C MEASURED it off storm_03b and I eyeballed mine at authoring time; the 2.2 deg of my eyeballing was worth 0.44 m/s. gain left at C's 1.35 — that's a balance lever, theirs with B's audit. Also wired validateSiteWind at site load, C's ask from Sprint 10. selftest 300/0/0 (was 296). |
||
|
|
0f5d69b32d |
The second site: corner block loads from JSON; night 3 moves to it
SPRINT10 gate 1b. A night is now {storm, site}; the week hands over a yard per
night and night three is the corner block. Nights 1/2/4/5 are the backyard; the
plain-string night form still works and defaults to backyard_01.
site_02_corner_block.json: a smaller 24x16 yard with E's carport as the trap it
was built to be — beam anchors rating_hint 0.22, posts 0.30, both below the
house fascia's 0.35, collateral "carport". The site IS winnable off the honest
anchors (one tree east, four ground posts): I enumerated the honest quads at
authoring time and there are four in-band ones covering the bed, so the carport
is temptation not necessity. Full load audit is Lane B's tools/site_audit.
The carport's beams are keyed work:"bracket" — D's field, the MECHANISM not the
type. A bracket up a bare beam is the one anchor you can't fake from the ground,
so it needs the ladder; keyed on type it would fail open (a carport isn't a
house) and the ladder mechanic would silently vanish, which is exactly the audit
D ran. C's venturi list is in the JSON with two gap coordinates they asked for.
The world is rebuilt on a site change, not re-pointed: createPlayer captures the
world into its ground clamp, collider, ladder and broom (Lane D's modules, no
setters), so a clean rebuild is honest — and a site change only happens at a
forecast, never mid-storm. world.dispose() frees the old yard so nothing leaks.
The re-point of rig + session lives inside loadSiteInto, keyed on the rebuild
itself, after a caller-side version proved fragile.
Verified end to end through real phase transitions: play night 1, hit the
aftermath, and the forecast into night 3 rebuilds into the corner block —
carport present, 10 anchors (cb/cp/q/tr, not h/t/p), smaller bed, player rebuilt,
no scene leak. Then back to the backyard, 12 anchors restored. Selftest 289/0/0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
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>
|