Sprint 11 integration: merge all lanes, wire leadFor + setBudget, untrack launch.json; Sprint 12 plan + prompts

This commit is contained in:
type-two 2026-07-17 18:08:32 +10:00
parent 7791299ef0
commit e99f029206
7 changed files with 204 additions and 24 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ tools/blender/thumbs/
.DS_Store .DS_Store
__pycache__/ __pycache__/
*.pyc *.pyc
.claude/launch.json

View File

@ -612,3 +612,59 @@ THREADS' last [I] entry then SPRINT11.md.
> Client juice: job-sheet letterhead + invoice styling for A's cards, one > 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 > per-client prop if cheap. Stand by on the tree-move if B's audit demands
> it (C's gain drop goes first). > 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.

93
SPRINT12.md Normal file
View File

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

View File

@ -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 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 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. 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 AE 88248828. 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.

View File

@ -15,7 +15,7 @@
import * as THREE from '../vendor/three.module.js'; import * as THREE from '../vendor/three.module.js';
import { STORM_LEN } from './contracts.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 clamp01 = (v) => (v < 0 ? 0 : v > 1 ? 1 : v);
const kmh = (ms) => ms * 3.6; 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 // read it as nights first, passed 1, and the card advertised "forecast
// confidence 0%": a forecast admitting it knows nothing, printed as news. // confidence 0%": a forecast admitting it knows nothing, printed as news.
// //
// ⚠️ INTEGRATION, SPRINT11: this constant is a placeholder and C has // C's `leadFor` maps "N nights out" onto that dial — 0.25 for tomorrow of
// already landed its replacement on lane/c — `leadFor(nightsOut, weekNights)` // five (75% confidence). Safe to print because the band RESOLVES: verified
// in weather.js, which maps the week onto forecastFor's 0..1 domain and // 4020 samples, 0 violations — tomorrow's band always contains tonight's,
// gives 0.25 for tomorrow of five (75% confidence), not my 0.6. Theirs is // so the number tightens toward the truth and never rules out what it
// better than a guess: they verified the band RESOLVES — 4020 samples, 0 // previously allowed. (Integrated at SPRINT11 merge; was a 0.6 placeholder.)
// violations, tomorrow's band always contains tonight's, so the number const tf = tomorrowDef ? forecastLines(tomorrowDef, leadFor(1, wk.nights)) : null;
// 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;
const tomorrow = tf ? ` const tomorrow = tf ? `
<div class="tomorrow"> <div class="tomorrow">
<span class="tlabel">tomorrow</span> <span class="tlabel">tomorrow</span>

View File

@ -771,15 +771,10 @@ export async function boot(opts = {}) {
// loadSiteInto, keyed on the rebuild itself, so it can't desync from it. // loadSiteInto, keyed on the rebuild itself, so it can't desync from it.
await loadSiteInto(week.site); await loadSiteInto(week.site);
// Reaching into Lane B's session to re-bank the shop. `RiggingSession` takes // B's setBudget (SPRINT11): re-bank + reset in one named call. This was
// a budget at construction but exposes no setter, and the UI owns the // main.js's one private touch into their session (`_startBudget` + reset(),
// instance — so this is the one private touch in main.js, done loudly rather // asked in THREADS, landed, fake deleted — same route reset() took).
// than by forking their file. I've asked for `session.setBudget(n)` in rigging.session.setBudget(week.bank);
// 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();
spentThisNight = 0; spentThisNight = 0;
// SPRINT11 — the job sheet reads three new things off the week: tonight's // 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 // JOB (client + brief), the QUOTE (what it pays, before you rig it, which is