diff --git a/.claude/launch.json b/.claude/launch.example.json similarity index 100% rename from .claude/launch.json rename to .claude/launch.example.json diff --git a/.gitignore b/.gitignore index 8598e15..915c4de 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ tools/blender/thumbs/ .DS_Store __pycache__/ *.pyc +.claude/launch.json diff --git a/LANE_PROMPTS.md b/LANE_PROMPTS.md index 30ad49a..8d7251c 100644 --- a/LANE_PROMPTS.md +++ b/LANE_PROMPTS.md @@ -612,3 +612,59 @@ THREADS' last [I] entry then SPRINT11.md. > Client juice: job-sheet letterhead + invoice styling for A's cards, one > per-client prop if cheap. Stand by on the tree-move if B's audit demands > it (C's gain drop goes first). + +--- + +# SPRINT 12 prompts (the steel tells the truth, the paperwork looks the part) + +Same rules: own clone, own branch, rebase onto latest main FIRST. Read +THREADS' last [I] entry then SPRINT12.md. New this sprint: launch.json is +untracked — `cp .claude/launch.example.json .claude/launch.json` once per +clone and pick your own port (suggested: A 8824, B 8825, C 8826, D 8827, +E 8828; integration holds 8823). + +## Lane A — Sprint 12 +> You are Lane A on SHADES 3D, Sprint 12. Rebase onto main, read SPRINT12.md. +> Gate 3 is yours: paste E's letterhead/invoice kit from tools/jobsheet +> (between the CUT markers; keep .void), rule on the HARD YARDS masthead and +> keep the ABN sequential, and wire the kid's bike (site-JSON key + loader +> line — read E's −Z lean note FIRST) with a ruling on its $60. Do NOT start +> reputation, site_03, or heatwave — gate 4 decides the branch at sprint end. +> Shepherd as always; B's re-audit may route retune proposals through you. + +## Lane B — Sprint 12 +> You are Lane B on SHADES 3D, Sprint 12. Rebase onto main, read SPRINT12.md. +> Gate 2 is yours: wire ratingHint into the failure threshold (A's ruling, +> THREADS sprint 11 — `load > c.hw.rating * (anchor.ratingHint ?? 1)`), with +> an assert on the CONSEQUENCE (cb blows before q at max tension through the +> funnel). Then re-audit BOTH yards — E's baked hints move everything with a +> GLB anchor — and post winnable lines per night. Numbers are E's: propose +> retunes in THREADS, don't nudge. Also fix the weak-link tie-break D flagged +> (strict < keeps first click; flag lowest rating × hint). + +## Lane C — Sprint 12 +> You are Lane C on SHADES 3D, Sprint 12. Rebase onto main, read SPRINT12.md. +> Two jobs: (1) be the second harness on B's post-wiring failure envelope — +> measure it through the storm while B measures it through the audit; if the +> two disagree, find the variable before anyone retunes. (2) Give the +> southerly buster's early change an in-world tell (skyfx): sky darkening +> from the south, the lull before the shift — deterministic (dt, t), readable +> without spoiling. D judges it. + +## Lane D — Sprint 12 +> You are Lane D on SHADES 3D, Sprint 12. Rebase onto main, read SPRINT12.md. +> Replay both yards AFTER B's ratingHint wiring lands: the trap should fire +> at default tension now (your measured fork), the ladder trip should fire by +> itself (your #6), the weak-link label should point honestly (your #7). +> Judge C's change-tell — does it telegraph without spoiling? — and re-read +> the backyard blurb A changed under you ("worked it for years" vs "your own +> place"). Feel notes in THREADS, receipts as always. + +## Lane E — Sprint 12 +> You are Lane E on SHADES 3D, Sprint 12. Rebase onto main, read SPRINT12.md. +> The gutter, the carport pattern, second time around: propose its collateral +> price with the reasoning baked beside it, and build the torn-gutter wreck +> that swaps on the same event. Expect retune proposals against your +> 0.22/0.30/0.35 hints from B's re-audit once the sim reads them — the +> numbers are yours to defend or move, with B's audit as the evidence. The +> tree-move stays cancelled. diff --git a/SPRINT12.md b/SPRINT12.md new file mode 100644 index 0000000..f461b8d --- /dev/null +++ b/SPRINT12.md @@ -0,0 +1,93 @@ +# SPRINT 12 — THE STEEL TELLS THE TRUTH, AND THE PAPERWORK LOOKS THE PART + +*Sprint 11 delivered everything it named: the venturi reconciled (twice — both +directions of the same lesson), the enum checked, the carport priced and +billing, the job sheet quoting before you spend, the invoice with C's +resolving forecast on it, and — the door in front of all of it — B's +`setWorld`, so night 3 is playable by mouse. 315/0/0, verified live at +integration. What's left is one ruled-but-unwired mechanic (the ratings), one +unpriced failure (the gutter), one unpasted kit (E's letterhead), and the +five-sprint debt at the top of this file.* + +Read THREADS from the last [I] entry. + +## Gate 1 — JOHN PLAYS THE WEEK (fifth sprint standing — the excuse is gone) + +The week is reachable end to end for the first time. `cp +.claude/launch.example.json .claude/launch.json`, `python3 server.py --port +8823`, play all five nights with the mouse, three sentences in THREADS. +- Sprint 13's branch — **more sites vs reputation/warranty vs heatwave days** + — is decided by those sentences and by nothing else. Everyone else's gate-2 + work below is deliberately branch-independent. +- Watch the bank. A competent clean week now banks $485 (§BROKE_BELOW). If it + reads as money losing its meaning, A's declared first cut is the $20 clean + bonus — newest number, least load-bearing, per-job data. + +## Gate 2 — WIRE THE RATINGS (B owns; A's ruling is in THREADS, sprint 11) + +The trap is priced but not weak: `sail.js` fails a corner on `c.hw.rating` +alone, so "the worst steel in the game" is currently just the worst bill, and +the lever that decides whether the trap fires is tension, not the anchor (D, +measured). +1. **B: land the shape** `load > c.hw.rating * (anchor.ratingHint ?? 1)`. + world.js now guarantees the field (DEFAULT_RATING_HINT = 1); the `?? 1` is + belt-and-braces. Assert the consequence, not the formula: at max tension + through the funnel, a **cb** corner must blow before a **q** corner. +2. **B: re-audit BOTH yards the moment it lands.** E's baked hints (beam 0.22, + post 0.30, fascia 0.35, tree 1.0) will move the balance of everything with + a GLB anchor — the backyard's fascia and posts included. Post the winnable + lines per night like the sprint-11 table. **The numbers are E's**: propose + retunes in THREADS with the audit as evidence; don't quietly nudge. +3. **D: replay both yards after the wiring.** Your measured fork (tension 1.0 + → q3 blows, carport free; 1.4 → cb1 blows, −$205) should now bite at + default tension. The ladder trip should fire by itself (your #6). And your + #7: **B fixes the weak-link tie-break** (rigging.js reduces with strict `<` + — flag the lowest `rating × hint`, not the first click). +4. **C: second harness on the envelope.** B measures failure thresholds + through the audit; you measure the same envelope through the storm + (`speedAt` × time, both yards). Two harnesses, one number — the repo rule. + If they disagree, find the variable before anyone retunes. + +## Gate 3 — THE PAPERWORK AND THE GUTTER (A + E; the invoice earns its ABN) + +1. **A: paste E's kit** — `tools/jobsheet/index.html`, between the CUT + markers, purely additive by construction. Keep `.void` (the struck-through + dead bonus is the invoice teaching the player what to protect). Two rulings + while you're in there: the **HARD YARDS** masthead (E's reasoning is in the + tool; a surname mark waits for a named player) and the **sequential ABN + stays sequential** — it reads as "example" the way 555 numbers do. Don't + fix it into a real business's tax identity. +2. **A: wire the bike** — site-JSON key + loader line, the gnome pattern. + Read E's lean note first: it leans **−Z in three.js**, toward the fence; + bars ~0.10 m off the palings. Rule on E's $60: a bike in a 32 m/s funnelled + southerly that ignores the gust is a worse lie than no bike. +3. **E: price the gutter and build its wreck** — the carport pattern, your + second time around: propose the number with reasoning baked beside it, ship + the torn-gutter wreckage that swaps on the same event. With fascia 0.35 + wired (gate 2) and the gutter priced, backyard_01 grows its own honest + trap — DESIGN.md's fascia lie ("holds until the first real gust, then rips + off taking the gutter with it") finally true in the sim. **A rules the + number; `collateralFor('gutter')` stops returning null.** +4. **C: the change announces itself.** The corner block's thesis is "looks + like night 2 and isn't" — the 18 s early change. Give the southerly buster + its in-world tell (skyfx is yours): the sky darkening from the south, the + lull before the shift — readable by a player watching the yard, not the + data. Deterministic off `(dt, t)` like everything else. D judges whether + it telegraphs without spoiling. + +## Gate 4 — SPRINT 13'S BRANCH (integrator + John, end of sprint) + +Named early so nobody half-builds it: reputation/warranty (DESIGN.md's +consequence loop — the invoice now exists to hang it off), site_03 (a third +yard is cheap now that sites are data), or the heatwave day (slow-pressure +levels, the other half of the design). Decided on John's sentences, not +before. Do not start any of the three this sprint. + +``` +gate 1: John's three sentences in THREADS +gate 2: ratings wired · both yards re-audited · retunes proposed not nudged · + D confirms the trap fires at default tension · weak-link honest +gate 3: letterhead + invoice pasted (.void survives) · HARD YARDS ruled · + bike wired and ruled · gutter priced, wrecked, and billing +gate 4: Sprint 13 branch chosen, in writing, in THREADS +``` diff --git a/THREADS.md b/THREADS.md index c113a90..a17fec3 100644 --- a/THREADS.md +++ b/THREADS.md @@ -3923,3 +3923,46 @@ anchors are your GLB), but the tooling is now waiting, not TODO. through a rewritten NIGHTS[0] to get a coherent boot.** Everything A landed tonight — the enum, the bill, the wreck, that verdict line — is currently unreachable in the shipped game. B's one method is the door in front of all of it. + +[I] 2026-07-17 — **SPRINT 11 INTEGRATION (main).** All five lanes merged; only THREADS conflicted + (keep-both). selftest **315/0/0** — the exact sum (296 + A's 10 + B's 4 + C's 2 + E's 3), so no + assert was lost or double-counted in the merge. (Housekeeping: lane entries above are dated + 07-19/07-20; this machine's clock says 07-17 — the lanes' dates drifted, the ORDER is right.) + + **The sprint's headline, verified live, not just green:** the night-3 soft-lock is DEAD. B's + `setWorld` + A's honoured `boot({site})` merged together; driven in the browser, + `loadSiteInto('site_02_corner_block')` repoints the markers from the backyard's twelve to the + corner block's ten, byte-identical to `world.anchors`, zero console errors. D's cold-pass bug 1 — + "you cannot rig, cannot start, cannot leave" — no longer reproduces. **The full five-night week is + reachable by mouse for the first time.** + + **Two cross-lane integration edits, both pre-agreed in THREADS, both done here:** + · hud.js — A's ⚠️ INTEGRATION marker resolved: the `TOMORROW_LEAD = 0.6` placeholder is gone, + the call site is C's verbatim `forecastLines(tomorrowDef, leadFor(1, wk.nights))`. Verified on + the rendered card: *"forecast confidence 75%"* — leadFor(1,5) = 0.25, exactly C's number. + · main.js — A's one private touch into B's session (`_startBudget` + `reset()`) replaced with + B's `setBudget(week.bank)`. The fake is deleted, as B asked. Exercised at boot: shop banks $80. + + **Ruling on the launch.json port fight (A + E both filed it):** `.claude/launch.json` is now + UNTRACKED — `.claude/launch.example.json` ships in its place and `.gitignore` covers the real one. + Each clone copies the example once and picks its own port; suggested spread: integration 8823, + lanes A–E 8824–8828. A shared tracked file where every clone needs a different value was a merge + conflict with a fuse on it, and on JING5 (one machine, six checkouts) the fuse was lit. + + **C's "half my assert is decoration" disclosure:** keeping it, exactly as labelled — a contract + guard for a future `band()` rewrite, not coverage. The honesty is the useful part and it's in the + test's own comment now. Nothing to delete. + + Carried to SPRINT12, none blocking boot: + · **ratingHint is ruled REAL and still read by nothing** — A's ruling stands, the sequencing + blocker (setWorld) is gone. B wires `load > c.hw.rating * (anchor.ratingHint ?? 1)`; the + re-audit that follows is the sprint's real work, because E's baked 0.22/0.30/0.35 will move + the balance of BOTH yards the moment the sim reads them. + · **the gutter is still free** — `collateralFor('gutter')` returns null and backyard_01's house + is a free failure. E proposes a price + builds the wreck (the carport pattern), A rules. + · **E's invoice/letterhead kit is unpasted** (tools/jobsheet, CUT markers), the HARD YARDS + masthead and the sequential ABN await A's ruling, and the kid's bike is dead weight until A + wires its site-JSON key. The lean is baked toward −Z; read E's entry before placing it. + · **gate 3 — John plays the week. FIVE sprints standing, and for the first time the excuse is + gone: the week is playable end to end.** §BROKE_BELOW watch: if the bank runs away, A's + declared first cut is the $20 clean bonus, not feeFor/gardenBonusMax. diff --git a/web/world/js/hud.js b/web/world/js/hud.js index ff7fc0e..2bbb6e7 100644 --- a/web/world/js/hud.js +++ b/web/world/js/hud.js @@ -15,7 +15,7 @@ import * as THREE from '../vendor/three.module.js'; import { STORM_LEN } from './contracts.js'; -import { forecastLines } from './weather.js'; +import { forecastLines, leadFor } from './weather.js'; const clamp01 = (v) => (v < 0 ? 0 : v > 1 ? 1 : v); const kmh = (ms) => ms * 3.6; @@ -435,20 +435,12 @@ showForecast({ key, def, site, tomorrowDef }, wk, onGo) { // read it as nights first, passed 1, and the card advertised "forecast // confidence 0%": a forecast admitting it knows nothing, printed as news. // - // ⚠️ INTEGRATION, SPRINT11: this constant is a placeholder and C has - // already landed its replacement on lane/c — `leadFor(nightsOut, weekNights)` - // in weather.js, which maps the week onto forecastFor's 0..1 domain and - // gives 0.25 for tomorrow of five (75% confidence), not my 0.6. Theirs is - // better than a guess: they verified the band RESOLVES — 4020 samples, 0 - // violations, tomorrow's band always contains tonight's, so the number - // tightens toward the truth and never rules out what it previously - // allowed. That's the property that makes it safe to print on a card. - // At merge this becomes C's own call site, verbatim from their THREADS: - // forecastLines(tomorrowDef, leadFor(1, wk.nights)) - // Not imported here yet only because leadFor is on lane/c and this branch - // would go red reaching for it. - const TOMORROW_LEAD = 0.6; - const tf = tomorrowDef ? forecastLines(tomorrowDef, TOMORROW_LEAD) : null; + // C's `leadFor` maps "N nights out" onto that dial — 0.25 for tomorrow of + // five (75% confidence). Safe to print because the band RESOLVES: verified + // 4020 samples, 0 violations — tomorrow's band always contains tonight's, + // so the number tightens toward the truth and never rules out what it + // previously allowed. (Integrated at SPRINT11 merge; was a 0.6 placeholder.) + const tf = tomorrowDef ? forecastLines(tomorrowDef, leadFor(1, wk.nights)) : null; const tomorrow = tf ? `
tomorrow diff --git a/web/world/js/main.js b/web/world/js/main.js index c3dce55..b2052a3 100644 --- a/web/world/js/main.js +++ b/web/world/js/main.js @@ -771,15 +771,10 @@ export async function boot(opts = {}) { // loadSiteInto, keyed on the rebuild itself, so it can't desync from it. await loadSiteInto(week.site); - // Reaching into Lane B's session to re-bank the shop. `RiggingSession` takes - // a budget at construction but exposes no setter, and the UI owns the - // instance — so this is the one private touch in main.js, done loudly rather - // than by forking their file. I've asked for `session.setBudget(n)` in - // THREADS; when it lands these two lines become one. (Same route - // `session.reset()` took: I faked it, asked, they landed it, I deleted the - // fake.) - rigging.session._startBudget = week.bank; - rigging.session.reset(); + // B's setBudget (SPRINT11): re-bank + reset in one named call. This was + // main.js's one private touch into their session (`_startBudget` + reset(), + // asked in THREADS, landed, fake deleted — same route reset() took). + rigging.session.setBudget(week.bank); spentThisNight = 0; // SPRINT11 — the job sheet reads three new things off the week: tonight's // JOB (client + brief), the QUOTE (what it pays, before you rig it, which is