Compare commits
2 Commits
1073ac97e8
...
f53a0d5ee9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f53a0d5ee9 | ||
|
|
d8e7087c2c |
147
THREADS.md
147
THREADS.md
@ -8021,3 +8021,150 @@ anchors are your GLB), but the tooling is now waiting, not TODO.
|
|||||||
before the sep-judged assert landed (its green is in the 445 merged run; its red was proven
|
before the sep-judged assert landed (its green is in the 445 merged run; its red was proven
|
||||||
by hiding site_03's finding — "site_03_swing_lawn has neither a separation block nor a
|
by hiding site_03's finding — "site_03_swing_lawn has neither a separation block nor a
|
||||||
_separation_finding", restored, green). Pushed lane/b.
|
_separation_finding", restored, green). Pushed lane/b.
|
||||||
|
[D] 2026-07-20 — 🗓 **GATE 4.1 + 4.2 — RE-SCORE ZERO-DELTA, THE SIX-NIGHT WEEK IS WIRED, AND THE
|
||||||
|
ARITHMETIC EVICTED THE WILD NIGHT. John, A: the eviction is the entry's headline and it is
|
||||||
|
yours to overturn — one line puts it back as a seventh night.** Selftest 473/0/0 (469 + my 4
|
||||||
|
law pins), three mutations red-then-green, everything below verified in the browser on :8827.
|
||||||
|
|
||||||
|
**4.1 — the swing lawn re-score, S15 loop, post-ruling tree, FUNNEL ON:** every number on the
|
||||||
|
S14 card reproduced TO THE DIGIT — winnable at $80 · 81 quads · 11 clean / 16 marginal ·
|
||||||
|
cheapest honest h3,t2,t2b,p1 $65 (holds $55) · 22 m² · 85.3 FULL · bare 83.7 → best clean
|
||||||
|
h3,t1c,t2,p1 90.0, sep +6.3 · best-reading t1c,t2,t2b,s1_f2 still MARGINAL at 91.8 (the trap
|
||||||
|
line, still flagged as such). B predicted zero movement from their gate-2.3 probe and the
|
||||||
|
prediction held — nothing to fix, nothing retuned. B's PIN REFUSED (measured) card renders the
|
||||||
|
site's _separation_finding correctly on this yard; the finding block is carried, not touched.
|
||||||
|
|
||||||
|
**4.2 — the ladder as wired (week.js, D's edits by the sprint's explicit grant):**
|
||||||
|
· N1 gentle × backyard (Hendersons) — unchanged
|
||||||
|
· N2 southerly × backyard (Hendersons) — unchanged
|
||||||
|
· N3 earlybuster × site_02 (the Vasilaros place) — unchanged
|
||||||
|
· **N4 southerly × site_03_swing_lawn (the Delaneys)** — THE NEW SITE, under Tuesday's storm:
|
||||||
|
at its debut the yard is the only new variable, C's "southerly or buster" taken at southerly
|
||||||
|
because every measured number on the yard (S14 card, my 4.1 re-score, B's probe) is a
|
||||||
|
southerly number.
|
||||||
|
· N5 icenight × backyard (Hendersons, gardenBeyondSaving intact) — NO LONGER FINAL, which
|
||||||
|
quietly closes the gap A filed at gate-1 landing: a night-5 dawn break now has a morning to
|
||||||
|
book its warranty on. Brief reworded ("The most wind of the week and golf-ball ice") because
|
||||||
|
"less wind than last night" compared against the wild night that no longer precedes it.
|
||||||
|
· **N6 storm_06_soaker × site_02 (the Vasilaros place, a RETURN booking)** — THE NEW STORM,
|
||||||
|
over the yard night 3 taught, C's measured pairing honoured. Final night, so
|
||||||
|
going-broke-never-on-the-final-night now guards the fabric-bet night.
|
||||||
|
**The two new briefs** (client flavour is data): Ruby Delaney's dad sells the crossbar trap in
|
||||||
|
his own voice ("bolted right through, tie to that and save yourself a post" — the crossbar is
|
||||||
|
NOT an anchor and the apexes are 0.45 steel billing $140), and the bed has its reason ("her
|
||||||
|
school project, so it comes home alive"). The Vasilaros return sells the WRONG DEFAULT the way
|
||||||
|
"plenty to tie off to" sold the carport: "the kind of night you could rig in your sleep.
|
||||||
|
Whatever you flew last time will do, they said." — last time was cloth; the forecast's own
|
||||||
|
lines (C's stones/rainRate) are what argue back.
|
||||||
|
|
||||||
|
**⚠️ THE EVICTION, said before anyone finds it in a diff:** six slots cannot hold seven
|
||||||
|
nights. Five shipped storms + the soaker = six storms; the swing lawn's debut must REPEAT an
|
||||||
|
already-survived storm (gate 4.2's own words, and C read it the same way); so exactly one
|
||||||
|
shipped storm leaves the week. Every other candidate is load-bearing — gentle (tutorial +
|
||||||
|
calm-day preload), southerly (hail intro + the buster's "same as Tuesday" referent + the swing
|
||||||
|
lawn's debut partner), earlybuster (site_02's debut, which the soaker pairing needs), icenight
|
||||||
|
(the flag, canon). **The WILD NIGHT is out of the rotation.** Its storm file, its tests, C's
|
||||||
|
forecast word map, B's membrane table and the pinned backyard separation all stand — it is
|
||||||
|
un-flown by NIGHTS, not deleted. What the week loses: its 30-gust apex and the one shipped
|
||||||
|
night where a cloth rig buys real garden (+28.1 HP, B's table). What it gains: the soaker as
|
||||||
|
the new teeth night (bare 17.5 DEAD → membrane 96.5 FULL — a Δ79 the wild night never
|
||||||
|
offered). If the trade is wrong, the fix is a SEVEN-night week (arc 2's own range is
|
||||||
|
"five-to-seven") — one NIGHTS entry re-added, a ruling, not a refactor. **Gate 6 material.**
|
||||||
|
· The week asserts moved exactly as the sprint predicted (a.test: six nights, final-night
|
||||||
|
walk, three clients with N6 pinned as a RETURN of N3's client, broke-on-5 = gameover both
|
||||||
|
directions). Four new SIX-NIGHT LAW pins live in d.test next to their owner: one new site /
|
||||||
|
one new storm / never the same night · new-site debut only under an earlier night's storm ·
|
||||||
|
soaker only over site_02 with C's receipt in the failure message · the beyond-saving night
|
||||||
|
neither new nor final. Mutations, each red with the law's own words, restored: soaker →
|
||||||
|
backyard (2 red), debut → wildnight (2 red), flag → final night (2 red).
|
||||||
|
· **A, your files, my edits, flagged:** hud.js now reads NIGHTS.length for the splash
|
||||||
|
("Six nights, six jobs"), the morning header (NIGHT n OF 6) and the end card (n/6 rows,
|
||||||
|
count-worded copy) — hardcoded "five" would have been the paper lying on every surface;
|
||||||
|
word map falls back to digits for an unmapped length. a.test's week suite updated as above.
|
||||||
|
Revert-and-tell-me standard applies.
|
||||||
|
· **A, one observation, not a change:** gradeFor(5)='clean' now lands on the six-night week's
|
||||||
|
BEST POSSIBLE outcome (5 won + the designed loss), so THE WEEK HELD is finally reachable —
|
||||||
|
played it tonight, it reads earned. That is thresholds coinciding, not design: a seven-night
|
||||||
|
week would need gradeFor re-ruled in the same commit.
|
||||||
|
|
||||||
|
[D] 2026-07-20 — 🎮 **GATE 4.3 — THE COLD-PLAY: SIX NIGHTS, ONE WALLET, REAL UI, AND A VERDICT
|
||||||
|
FOR EVERY GATE. Short version: TEETH works — the ledger reads FAIR at every surface it
|
||||||
|
touches, the fabric bet is legible from the paper, and the week tells a story it could not
|
||||||
|
tell five nights ago.** Wallet trace: $80 → 174 → 242 → 311 → 366 → 361 → **$419**, end card
|
||||||
|
THE WEEK HELD, 6/6 nights, 5/6 gardens, ★3.0, 2 warranty jobs booked. Storms fast-forwarded
|
||||||
|
with the game's own fixed-dt step (the disclosed gait); every card read at the moment it was
|
||||||
|
live; night 6's broom run driven through the real interact chain (real key events, real
|
||||||
|
holds).
|
||||||
|
|
||||||
|
**GATE 1 (A) — the ledger: FAIR, and twice it was fair about ME.**
|
||||||
|
· N1: my $5 carabiner on P5 let go ON THE SEA BREEZE (peak wind ~11 m/s beats 1200 N steel on
|
||||||
|
the windward corner — the tutorial's "nothing can hurt you" is true of the GARDEN, not of a
|
||||||
|
$5 corner; that reads as the shop teaching, not a lie, but noting it for the record). I rode
|
||||||
|
it out broken: ★3.0 → ★2.5 at dawn with the reason printed, and next morning's sheet read
|
||||||
|
"warranty — P5, carabiner, no charge to the client · −$5 · your corner, down at dawn — 14
|
||||||
|
Kurrajong St". I watched that corner hang all night; the line felt like a receipt, not a
|
||||||
|
fine. **Warranty callout on your sheet: FAIR.**
|
||||||
|
· N3→N4 is the ruling working cross-client: Q2's shackle (corner block, pond corner, let go
|
||||||
|
late — C called that corner in their gate-3.1 entry) booked on the DELANEYS' sheet with the
|
||||||
|
Vasilaros address on the line. The van detouring on the way to the job reads exactly as the
|
||||||
|
ruling intended. quote==settle held every morning it was tested, deduction on both papers.
|
||||||
|
· **Rep moving pay: EARNED.** ×0.98 on the fee row the mornings after my two dawn breaks,
|
||||||
|
×1.02 on night 6 after the icenight's clean +0.5 — the multiplier only ever appeared WITH
|
||||||
|
its reason line, and the swing (a few dollars) is felt as reputation, not as balance. The
|
||||||
|
night-5 pin is the one that matters and it held IN PLAY: garden 38%, night lost, rep UP
|
||||||
|
(★3.0 → ★3.5, "clean night") with the verdict crediting the steel — "Yours held anyway.
|
||||||
|
That was the whole job tonight, and you did it." A designed loss that pays respect instead
|
||||||
|
of docking it. **Gate 1: PASS, no retune asked.**
|
||||||
|
**GATE 2 (B) — the heal, the paper trail, the pin refusals.**
|
||||||
|
· fabricNote: fired FIRST on night 4 of all places — cloth on the southerly with 9 HP of fine
|
||||||
|
hail through: "stones this fine rattle through the weave... The membrane stops every stone."
|
||||||
|
Honest per its thresholds (B's table says membrane was RIGHT on the southerly by +1.7), and
|
||||||
|
pedagogically it is the whole reason night 6 was legible — see gate 3. N6's membrane note
|
||||||
|
("held 391 kg of what it stopped. That is the membrane's price") lands the trade. Silent on
|
||||||
|
the nights it would have been a guess. **The line fires when the bet mattered: PASS.**
|
||||||
|
· The heal: six honest nights produced ZERO divergences — that datum first. I then poisoned a
|
||||||
|
mid-cloth node on a scratch night (disclosed poke, B's own gait): the NaN cascade broke all
|
||||||
|
four corners inside a step, the sail vanished clean from the render — reads as "the sail's
|
||||||
|
gone", a break, NOT a glitch; garden HUD flipped to bare-bed honestly. **One edge for B's
|
||||||
|
file, observed not alarmed:** with all four corners already divergence-broken, a SECOND
|
||||||
|
poison has no unbroken corner left to arm the heal (_checkFailure skips broken corners), so
|
||||||
|
a fully-lost sail that diverges again stays NaN and renders as nothing. Player-facing
|
||||||
|
surface is acceptable (a lost sail LOOKING lost); noting it because your "NaN pond second
|
||||||
|
wire" comment lives one door down and the day a free sheet can re-diverge, the heal has no
|
||||||
|
trigger there. Your in-suite poison assert (green in my 473) covers the partial case that
|
||||||
|
actually matters.
|
||||||
|
· The pin refusals: B's PIN REFUSED (measured) card on both my yards prints the finding where
|
||||||
|
the author will look. Correct surface, nothing to add.
|
||||||
|
**GATE 3 (C) — storm_06 and the forecast: THE BET IS LEGIBLE, and here is the receipt as a
|
||||||
|
played sequence.** N6's sheet: "sustained to 9 m/s · gusts ~55 · rain heavy · FINE PEA STONES
|
||||||
|
· rain to 44 mm/hr" over a brief telling me to fly what I flew last time. Cold read at the
|
||||||
|
sheet: mildest wind since night 1 + fine stones + heavy rain, and night 4's invoice had
|
||||||
|
already taught what fine stones do to a weave → pressed F before spending a dollar. Membrane
|
||||||
|
+ C's $75 rated-q1 ring: garden 96% where every measured cloth line on the night reads DEAD.
|
||||||
|
**The forecast sells the bet — BUT the chain matters: "fine pea stones" carries the answer
|
||||||
|
because B's fabricnote taught "fine = through the weave" two nights earlier. Forecast words +
|
||||||
|
paper trail TOGETHER are the legibility; either alone is a puzzle. In the shipped order the
|
||||||
|
chain works. PASS.**
|
||||||
|
· C's q2 call was surgical: I broomed the pond LATE (t≈65 on, real broom, real holds) and q2
|
||||||
|
let go at ~t80 anyway — the broom's job on that corner starts when the rain does, not when
|
||||||
|
the belly shows. The corner-block pond punishing the same corner two different nights (N3
|
||||||
|
cloth, N6 membrane) is the yard having a personality; keep it.
|
||||||
|
· Final-night dawn breaks: q2 down at dawn on N6 cost the rep (★3.5 → ★3.0, printed) and
|
||||||
|
booked NOTHING — no morning left. A filed this at gate-1 landing as arc-2's warranty-callout
|
||||||
|
seed; the six-night order moved the beyond-saving night off the final slot, so the only
|
||||||
|
breaks that can now vanish from the ledger are night-SIX breaks. Same filing, smaller hole,
|
||||||
|
arc 2 closes it.
|
||||||
|
**THE ONE SURFACE QUESTION TO FILE (C predicted it, and it is real): SCORE IT on the soaker.**
|
||||||
|
Flew it over site_02 post-play: the card says **"✓ WINNABLE — at $80"** and then shows a
|
||||||
|
garden table where EVERY line is DEAD (cheapest honest 19.1, best clean 32, bare 17.5) — and
|
||||||
|
NOTHING on the card says the sweep is cloth-fixed. The numbers are honest and C's charter is
|
||||||
|
sound; the SILENCE is the problem: on the one night where fabric is the whole answer, a
|
||||||
|
reader without the charter in their head concludes "winnable rig, unwinnable garden — the
|
||||||
|
night is broken." **Ask for A/B (one line, not a re-architecture): the score header should
|
||||||
|
name its fabric the way it names its funnel state — "fabric · shade cloth (sweep charter)" —
|
||||||
|
and on a storm whose stones pass the weave ruling, one footnote line: "membrane not swept —
|
||||||
|
the F key is not in this card." Who owns the line is yours to settle (B's card, A's page).**
|
||||||
|
Filed as a surface question, not a bug, exactly per C's coda.
|
||||||
|
|
||||||
|
Pushed lane/d: d8e7087 (the wiring), this entry and its sibling above. Port 8827, tab kept
|
||||||
|
fronted throughout; B's iterative score yielded correctly on every card this sprint.
|
||||||
|
|||||||
@ -19,7 +19,15 @@ import { forecastLines, leadFor } from './weather.js';
|
|||||||
// SPRINT16 — the end card compares the final rep against neutral. A constant,
|
// SPRINT16 — the end card compares the final rep against neutral. A constant,
|
||||||
// not state: the HUD still only READS. week.js has no DOM and no THREE, so
|
// not state: the HUD still only READS. week.js has no DOM and no THREE, so
|
||||||
// this import costs nothing it didn't already cost main.js.
|
// this import costs nothing it didn't already cost main.js.
|
||||||
import { REP } from './week.js';
|
import { NIGHTS, REP } from './week.js';
|
||||||
|
|
||||||
|
// SPRINT16 gate 4 (Lane D, flagged for A): the week is data — NIGHTS.length —
|
||||||
|
// and these surfaces used to hardcode "five". A six-night week whose splash,
|
||||||
|
// morning header and end card still said 5 would be the paper lying, so every
|
||||||
|
// count below reads the ladder. The word map keeps the splash's prose voice;
|
||||||
|
// an unmapped length falls back to the digit, which is honest if inelegant.
|
||||||
|
const NIGHT_COUNT = NIGHTS.length;
|
||||||
|
const NIGHT_WORD = { 5: 'Five', 6: 'Six', 7: 'Seven' }[NIGHT_COUNT] ?? String(NIGHT_COUNT);
|
||||||
|
|
||||||
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;
|
||||||
@ -533,7 +541,7 @@ export function createHud(d) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="premise">Rig shade sails, then keep the client's garden alive
|
<p class="premise">Rig shade sails, then keep the client's garden alive
|
||||||
through the storm. Five nights, five jobs, one wallet.</p>
|
through the storm. ${NIGHT_WORD} nights, ${NIGHT_WORD.toLowerCase()} jobs, one wallet.</p>
|
||||||
<div class="sect">THE CONTROLS</div>
|
<div class="sect">THE CONTROLS</div>
|
||||||
<div class="keys">
|
<div class="keys">
|
||||||
${[
|
${[
|
||||||
@ -921,9 +929,10 @@ showForecast({ key, def, site, tomorrowDef }, wk, onGo) {
|
|||||||
// scraped one, and a third for the ending nobody knew existed until the
|
// scraped one, and a third for the ending nobody knew existed until the
|
||||||
// economy was measured: solvent, with the gardens dead behind you.
|
// economy was measured: solvent, with the gardens dead behind you.
|
||||||
const COPY = {
|
const COPY = {
|
||||||
clean: ['THE WEEK HELD', "Five nights. Everything's still where you put it.",
|
clean: ['THE WEEK HELD', `${NIGHT_WORD} nights. Everything's still where you put it.`,
|
||||||
"Nobody thanks you for the storm that did nothing. That's the job."],
|
"Nobody thanks you for the storm that did nothing. That's the job."],
|
||||||
scraped: ['FIVE NIGHTS, FIVE MORNINGS', 'You made the least wrong call five times running.',
|
scraped: [`${NIGHT_WORD.toUpperCase()} NIGHTS, ${NIGHT_WORD.toUpperCase()} MORNINGS`,
|
||||||
|
`You made the least wrong call ${NIGHT_WORD.toLowerCase()} times running.`,
|
||||||
'Some of it held. You know which bits didn\'t.'],
|
'Some of it held. You know which bits didn\'t.'],
|
||||||
solvent: ['STILL TRADING', 'The books balanced. The garden didn\'t.',
|
solvent: ['STILL TRADING', 'The books balanced. The garden didn\'t.',
|
||||||
'You can be paid all week and still have nothing to point at.'],
|
'You can be paid all week and still have nothing to point at.'],
|
||||||
@ -953,8 +962,8 @@ showForecast({ key, def, site, tomorrowDef }, wk, onGo) {
|
|||||||
<div class="endtext">
|
<div class="endtext">
|
||||||
<h1>${head}</h1>
|
<h1>${head}</h1>
|
||||||
<h2>${sub}</h2>
|
<h2>${sub}</h2>
|
||||||
<div class="row"><span>nights survived</span><b>${nights}/5</b></div>
|
<div class="row"><span>nights survived</span><b>${nights}/${NIGHT_COUNT}</b></div>
|
||||||
<div class="row"><span>gardens held</span><b>${s.held ?? 0}/5</b></div>
|
<div class="row"><span>gardens held</span><b>${s.held ?? 0}/${NIGHT_COUNT}</b></div>
|
||||||
<div class="row"><span>in the bank</span><b>$${s.bankAfter}</b></div>
|
<div class="row"><span>in the bank</span><b>$${s.bankAfter}</b></div>
|
||||||
${s.rep != null ? `<div class="row"><span>reputation</span><b>${repFmt(s.rep)}</b></div>` : ''}
|
${s.rep != null ? `<div class="row"><span>reputation</span><b>${repFmt(s.rep)}</b></div>` : ''}
|
||||||
${s.warrantiesToDate ? `<div class="row"><span>warranty jobs booked</span><b>${s.warrantiesToDate}</b></div>` : ''}
|
${s.warrantiesToDate ? `<div class="row"><span>warranty jobs booked</span><b>${s.warrantiesToDate}</b></div>` : ''}
|
||||||
@ -1071,7 +1080,7 @@ showForecast({ key, def, site, tomorrowDef }, wk, onGo) {
|
|||||||
|
|
||||||
card.innerHTML = `<div class="card jobcard">
|
card.innerHTML = `<div class="card jobcard">
|
||||||
${letterhead}
|
${letterhead}
|
||||||
<h1>MORNING${w ? ` · NIGHT ${w.night} OF 5` : ''}</h1>
|
<h1>MORNING${w ? ` · NIGHT ${w.night} OF ${w.nights ?? NIGHT_COUNT}` : ''}</h1>
|
||||||
<h2>${r.subtitle}</h2>
|
<h2>${r.subtitle}</h2>
|
||||||
${rows}
|
${rows}
|
||||||
${money}
|
${money}
|
||||||
|
|||||||
@ -215,17 +215,25 @@ export default async function run(t) {
|
|||||||
|
|
||||||
// --- the week (SPRINT8 gate 1) -------------------------------------------
|
// --- the week (SPRINT8 gate 1) -------------------------------------------
|
||||||
|
|
||||||
t.test('the week is five escalating nights, each a storm and a site', () => {
|
t.test('the week is six escalating nights, each a storm and a site', () => {
|
||||||
assertEq(NIGHTS.length, 5);
|
// SPRINT16 gate 4 (D's wiring): five → six. The swing lawn debuts night 4
|
||||||
|
// under Tuesday's southerly; the soaker closes the week over the corner
|
||||||
|
// block. The deeper pins — one-variable law, survived-storm debut, the
|
||||||
|
// measured soaker pairing — live in d.test.js next to the wiring's owner.
|
||||||
|
assertEq(NIGHTS.length, 6);
|
||||||
// SPRINT10: a night is {storm, site} now. nightAt() normalises either shape.
|
// SPRINT10: a night is {storm, site} now. nightAt() normalises either shape.
|
||||||
assertEq(nightAt(0).storm, 'storm_01_gentle', 'night one must be the one that cannot hurt you');
|
assertEq(nightAt(0).storm, 'storm_01_gentle', 'night one must be the one that cannot hurt you');
|
||||||
assertEq(nightAt(4).storm, 'storm_02b_icenight', 'night five is the ice night');
|
assertEq(nightAt(3).storm, 'storm_03_southerly', 'night four repeats the southerly — the yard is the only new thing');
|
||||||
|
assertEq(nightAt(3).site, 'site_03_swing_lawn', 'night four is the swing lawn');
|
||||||
|
assertEq(nightAt(4).storm, 'storm_02b_icenight', 'night five is still the ice night');
|
||||||
|
assertEq(nightAt(5).storm, 'storm_06_soaker', 'night six is the fabric bet');
|
||||||
|
assertEq(nightAt(5).site, 'site_02_corner_block', "and it flies over the corner block — C's measured pairing");
|
||||||
assertEq(nightAt(2).site, 'site_02_corner_block', 'night three moves to the corner block');
|
assertEq(nightAt(2).site, 'site_02_corner_block', 'night three moves to the corner block');
|
||||||
assertEq(nightAt(0).site, 'backyard_01', 'the rest are the backyard');
|
assertEq(nightAt(0).site, 'backyard_01', 'the retainer nights are the backyard');
|
||||||
// SPRINT13 gate 1.4: the unsavable-garden flag is night 5's alone, and a
|
// SPRINT13 gate 1.4: the unsavable-garden flag is night 5's alone, and a
|
||||||
// night without it is presumed savable — new nights can't inherit the excuse.
|
// night without it is presumed savable — new nights can't inherit the excuse.
|
||||||
assertEq(nightAt(4).gardenBeyondSaving, true, 'night 5 is beyond saving BY DESIGN — canon, ruled');
|
assertEq(nightAt(4).gardenBeyondSaving, true, 'night 5 is beyond saving BY DESIGN — canon, ruled');
|
||||||
for (let i = 0; i < 4; i++) assertEq(nightAt(i).gardenBeyondSaving, false, `night ${i + 1} is savable`);
|
for (const i of [0, 1, 2, 3, 5]) assertEq(nightAt(i).gardenBeyondSaving, false, `night ${i + 1} is savable`);
|
||||||
const w = createWeek();
|
const w = createWeek();
|
||||||
assertEq(w.night, 1); assertEq(w.bank, 80);
|
assertEq(w.night, 1); assertEq(w.bank, 80);
|
||||||
assertEq(w.site, 'backyard_01', 'week.site tracks the ladder');
|
assertEq(w.site, 'backyard_01', 'week.site tracks the ladder');
|
||||||
@ -234,9 +242,12 @@ export default async function run(t) {
|
|||||||
assertEq(w.site, 'site_02_corner_block', 'and reaches the corner block on night three');
|
assertEq(w.site, 'site_02_corner_block', 'and reaches the corner block on night three');
|
||||||
for (let i = 0; i < 2; i++) w.advance();
|
for (let i = 0; i < 2; i++) w.advance();
|
||||||
assertEq(w.night, 5);
|
assertEq(w.night, 5);
|
||||||
assert(w.isFinalNight, 'night five is the last');
|
assert(!w.isFinalNight, 'the ice night is no longer the last — its dawn breaks have a morning to book on now');
|
||||||
w.advance();
|
w.advance();
|
||||||
assertEq(w.night, 5, 'the ladder does not walk off its own end');
|
assertEq(w.night, 6);
|
||||||
|
assert(w.isFinalNight, 'night six is the last');
|
||||||
|
w.advance();
|
||||||
|
assertEq(w.night, 6, 'the ladder does not walk off its own end');
|
||||||
});
|
});
|
||||||
|
|
||||||
t.test('money persists across nights, and the bank is next night\'s shop', () => {
|
t.test('money persists across nights, and the bank is next night\'s shop', () => {
|
||||||
@ -292,14 +303,15 @@ export default async function run(t) {
|
|||||||
assert(j.brief && j.brief.length > 20, `night ${i + 1} has a brief worth reading`);
|
assert(j.brief && j.brief.length > 20, `night ${i + 1} has a brief worth reading`);
|
||||||
assert(j.site, `night ${i + 1} has a yard`);
|
assert(j.site, `night ${i + 1} has a yard`);
|
||||||
}
|
}
|
||||||
// The week's shape, which is DATA and not decoration: four nights on
|
// The week's shape, which is DATA and not decoration: the Hendersons'
|
||||||
// retainer at one yard while they're away, and ONE short-notice callout to
|
// retainer, one short-notice callout to a stranger's corner block, the
|
||||||
// a stranger's corner block. Night 3 landing differently is the whole point
|
// Delaneys' swing lawn — and the corner block calling BACK, which is
|
||||||
// of the ladder — a different site MEANS a different client.
|
// SPRINT16's whole thesis (your work follows you) written into the ladder.
|
||||||
const clients = NIGHTS.map((_, i) => nightAt(i).client);
|
const clients = NIGHTS.map((_, i) => nightAt(i).client);
|
||||||
assertEq(new Set(clients).size, 2, 'two clients: the retainer and the one-off');
|
assertEq(new Set(clients).size, 3, 'three clients: the retainer, the one-off, and the swing lawn');
|
||||||
assert(clients[2] !== clients[0], 'night 3 is somebody else');
|
assert(clients[2] !== clients[0], 'night 3 is somebody else');
|
||||||
assertEq(nightAt(2).site, 'site_02_corner_block', "and it's their corner block");
|
assertEq(nightAt(2).site, 'site_02_corner_block', "and it's their corner block");
|
||||||
|
assertEq(clients[5], clients[2], 'night six is the corner block client again — a return booking, not a stranger');
|
||||||
});
|
});
|
||||||
|
|
||||||
t.test('an unbriefed night is a job with no letterhead, not a crash', () => {
|
t.test('an unbriefed night is a job with no letterhead, not a crash', () => {
|
||||||
@ -386,10 +398,17 @@ export default async function run(t) {
|
|||||||
assertEq(s.outcome, 'gameover', 'you cannot rig four corners, so the week is over');
|
assertEq(s.outcome, 'gameover', 'you cannot rig four corners, so the week is over');
|
||||||
|
|
||||||
// On the last night there is no next shop to be unable to afford, so being
|
// On the last night there is no next shop to be unable to afford, so being
|
||||||
// broke is just being broke — the week still finished.
|
// broke is just being broke — the week still finished. SPRINT16: the last
|
||||||
|
// night is SIX now, and night five going broke is a real game over again —
|
||||||
|
// both directions pinned so the re-ordering can't quietly move the rule.
|
||||||
const w2 = createWeek();
|
const w2 = createWeek();
|
||||||
for (let i = 0; i < 4; i++) w2.advance();
|
for (let i = 0; i < NIGHTS.length - 1; i++) w2.advance();
|
||||||
assertEq(w2.settle(ruin, def, 80).outcome, 'win', 'night five always ends the week, not the run');
|
assert(w2.isFinalNight, 'precondition: the walk reached the final night');
|
||||||
|
assertEq(w2.settle(ruin, def, 80).outcome, 'win', 'the final night always ends the week, not the run');
|
||||||
|
const w3 = createWeek();
|
||||||
|
for (let i = 0; i < 4; i++) w3.advance();
|
||||||
|
assertEq(w3.settle(ruin, def, 80).outcome, 'gameover',
|
||||||
|
'night five is not the final night any more — going broke there ends the run');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Gate 2 acceptance: both sites load from data, and the corner block is not
|
// Gate 2 acceptance: both sites load from data, and the corner block is not
|
||||||
@ -711,15 +730,15 @@ export default async function run(t) {
|
|||||||
t.test('SPRINT16 negative control: a clean week books ZERO warranty items and ends rep-positive', () => {
|
t.test('SPRINT16 negative control: a clean week books ZERO warranty items and ends rep-positive', () => {
|
||||||
const w = createWeek();
|
const w = createWeek();
|
||||||
let items = 0;
|
let items = 0;
|
||||||
for (let n = 0; n < 5; n++) {
|
for (let n = 0; n < NIGHTS.length; n++) {
|
||||||
items += w.quote(s16def).warranty.length; // counted off the sheet…
|
items += w.quote(s16def).warranty.length; // counted off the sheet…
|
||||||
items += w.settle(cleanRun(), s16def, 0).warranty.length; // …and the invoice
|
items += w.settle(cleanRun(), s16def, 0).warranty.length; // …and the invoice
|
||||||
w.advance();
|
w.advance();
|
||||||
}
|
}
|
||||||
assertEq(items, 0, 'no sheet and no invoice ever carried a warranty line');
|
assertEq(items, 0, 'no sheet and no invoice ever carried a warranty line');
|
||||||
assert(w.rep > REP.START, `the week ends rep-positive (★${w.rep})`);
|
assert(w.rep > REP.START, `the week ends rep-positive (★${w.rep})`);
|
||||||
assertEq(w.rep, REP.MAX, 'five clean nights ride the clamp to the top of the scale');
|
assertEq(w.rep, REP.MAX, 'six clean nights ride the clamp to the top of the scale');
|
||||||
assertEq(w.log[4].warrantiesToDate, 0, 'and the end card has no receipts to show');
|
assertEq(w.log[NIGHTS.length - 1].warrantiesToDate, 0, 'and the end card has no receipts to show');
|
||||||
w.reset();
|
w.reset();
|
||||||
assertEq(w.rep, REP.START, 'reset() is a new outfit — rep goes back to neutral');
|
assertEq(w.rep, REP.START, 'reset() is a new outfit — rep goes back to neutral');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -20,6 +20,9 @@ import { needsLadder as realNeedsLadder, createLadder } from '../ladder.js';
|
|||||||
import { assert, assertEq, assertClose, assertLess, fixedLoop } from '../testkit.js';
|
import { assert, assertEq, assertClose, assertLess, fixedLoop } from '../testkit.js';
|
||||||
import { FIXED_DT } from '../contracts.js';
|
import { FIXED_DT } from '../contracts.js';
|
||||||
import { loadStorm, createWind } from '../weather.js';
|
import { loadStorm, createWind } from '../weather.js';
|
||||||
|
// SPRINT16 gate 4: the six-night law pins at the bottom of this file read the
|
||||||
|
// ladder. week.js is contracts-only underneath, so this stays headless-safe.
|
||||||
|
import { NIGHTS, nightAt } from '../week.js';
|
||||||
|
|
||||||
const DT = FIXED_DT;
|
const DT = FIXED_DT;
|
||||||
|
|
||||||
@ -1135,4 +1138,61 @@ export default async function run(t) {
|
|||||||
assertEq(repaired, 0, 're-rig fires with a spare');
|
assertEq(repaired, 0, 're-rig fires with a spare');
|
||||||
assertEq(p.carrying, null, 'and consumes it');
|
assertEq(p.carrying, null, 'and consumes it');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ================================================================ SPRINT16 gate 4
|
||||||
|
// THE SIX-NIGHT LAW, pinned next to its owner. a.test pins the ladder's
|
||||||
|
// shape (which storm, which site, which slot); these pin the RULES the
|
||||||
|
// ordering must obey, so a future re-order that keeps six nights but breaks
|
||||||
|
// the law goes red with the law's own words. Every threshold here is
|
||||||
|
// ROADMAP principle 5 or a measured THREADS receipt, not taste.
|
||||||
|
|
||||||
|
t.test('SPRINT16 six-night law: exactly one new site, exactly one new storm, never the same night', () => {
|
||||||
|
const nights = NIGHTS.map((_, i) => nightAt(i));
|
||||||
|
const PRE_S16_SITES = ['backyard_01', 'site_02_corner_block'];
|
||||||
|
const PRE_S16_STORMS = ['storm_01_gentle', 'storm_02_wildnight', 'storm_02b_icenight',
|
||||||
|
'storm_03_southerly', 'storm_03b_earlybuster'];
|
||||||
|
const newSiteNights = nights.filter((n) => !PRE_S16_SITES.includes(n.site));
|
||||||
|
const newStormNights = nights.filter((n) => !PRE_S16_STORMS.includes(n.storm));
|
||||||
|
assertEq(newSiteNights.length, 1, 'exactly ONE night is the new site');
|
||||||
|
assertEq(newSiteNights[0].site, 'site_03_swing_lawn');
|
||||||
|
assertEq(newStormNights.length, 1, 'exactly ONE night flies the new storm');
|
||||||
|
assertEq(newStormNights[0].storm, 'storm_06_soaker');
|
||||||
|
assert(newSiteNights[0] !== newStormNights[0],
|
||||||
|
'and they are NOT the same night — a player who loses must be able to name what beat them');
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('SPRINT16 six-night law: the new site debuts under a storm already survived', () => {
|
||||||
|
const idx = NIGHTS.findIndex((_, i) => nightAt(i).site === 'site_03_swing_lawn');
|
||||||
|
assert(idx > 0, 'the swing lawn is in the rotation and is not night one');
|
||||||
|
const debut = nightAt(idx);
|
||||||
|
const earlier = NIGHTS.slice(0, idx).map((_, i) => nightAt(i).storm);
|
||||||
|
assert(earlier.includes(debut.storm),
|
||||||
|
`night ${idx + 1} flies ${debut.storm}, which must have flown on an earlier night — `
|
||||||
|
+ 'at the swing lawn\'s debut the yard is the ONLY new variable');
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test("SPRINT16 six-night law: the soaker flies the corner block — C's measured pairing", () => {
|
||||||
|
// THREADS [C] 2026-07-20: the backyard's buyable geometry caps hail cover
|
||||||
|
// over the bed at ~31% — best membrane line there reads 37.5, a bet with
|
||||||
|
// no win in it. The soaker over anything but site_02 needs a re-measure
|
||||||
|
// FIRST; this pin is where a re-pairing finds that out.
|
||||||
|
const soaker = NIGHTS.map((_, i) => nightAt(i)).find((n) => n.storm === 'storm_06_soaker');
|
||||||
|
assert(soaker, 'the soaker is in the week');
|
||||||
|
assertEq(soaker.site, 'site_02_corner_block',
|
||||||
|
'storm_06 over any other yard is unmeasured — fly gardenfly over the new pairing before moving this');
|
||||||
|
const debutIdx = NIGHTS.findIndex((_, i) => nightAt(i).site === soaker.site);
|
||||||
|
const soakerIdx = NIGHTS.findIndex((_, i) => nightAt(i).storm === 'storm_06_soaker');
|
||||||
|
assert(debutIdx < soakerIdx, 'and the yard was taught on an earlier night — the storm is the only new variable');
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('SPRINT16 six-night law: the beyond-saving night is neither new nor final', () => {
|
||||||
|
const flagged = NIGHTS.map((_, i) => nightAt(i)).filter((n) => n.gardenBeyondSaving);
|
||||||
|
assertEq(flagged.length, 1, 'exactly one designed loss in the week');
|
||||||
|
assertEq(flagged[0].storm, 'storm_02b_icenight', 'and it is the ice night — the flag is a measured fact about');
|
||||||
|
assertEq(flagged[0].site, 'backyard_01', 'THIS yard under THIS storm (bare 0.0, best buyable 27.7, win at 50)');
|
||||||
|
const idx = NIGHTS.findIndex((_, i) => nightAt(i).gardenBeyondSaving);
|
||||||
|
assert(idx !== NIGHTS.length - 1,
|
||||||
|
'the designed loss must not be the final night: its dawn breaks need a morning to book warranty on '
|
||||||
|
+ '(A filed the night-5-final gap in THREADS — the six-night order is what closes it)');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* SHADES — the week. Lane A owns this file.
|
* SHADES — the week. Lane A owns this file.
|
||||||
|
* (SPRINT16 gate 4: the NIGHTS ladder and the two new briefs are Lane D's
|
||||||
|
* edits, by the sprint's explicit grant — A owns everything else here.)
|
||||||
*
|
*
|
||||||
* Five nights, one yard, one wallet. This is deliberately a thin wrapper around
|
* Six nights, one wallet. This is deliberately a thin wrapper around
|
||||||
* the phase machine rather than a new system: main.js already knows how to run
|
* the phase machine rather than a new system: main.js already knows how to run
|
||||||
* forecast → prep → storm → aftermath, and the week's whole job is to answer
|
* forecast → prep → storm → aftermath, and the week's whole job is to answer
|
||||||
* three questions between rounds — which storm is tonight, how much money do you
|
* three questions between rounds — which storm is tonight, how much money do you
|
||||||
@ -20,30 +22,51 @@ import { START_BUDGET, HARDWARE } from './contracts.js';
|
|||||||
* 1 Sea Breeze gust 10.5, no hail — the tutorial: nothing can hurt you
|
* 1 Sea Breeze gust 10.5, no hail — the tutorial: nothing can hurt you
|
||||||
* 2 Southerly Buster gust 20.0, hail — the change, and hail, arrive
|
* 2 Southerly Buster gust 20.0, hail — the change, and hail, arrive
|
||||||
* 3 Early Buster gust 20.0, hail — same teeth, change comes EARLY
|
* 3 Early Buster gust 20.0, hail — same teeth, change comes EARLY
|
||||||
* 4 Wild Night gust 30.0, hail — the one the whole repo tuned for
|
* 4 Southerly again gust 20.0, hail — the storm you know, the yard you don't
|
||||||
* 5 Ice Night gust 28.5, hail — less wind, more ice
|
* 5 Ice Night gust 28.5, hail — the most wind of the week, and worse ice
|
||||||
|
* 6 Pea-Hail Soaker gust 15.2, fine hail — the night the default fabric is wrong
|
||||||
* Night 3 is the trap: it looks like night 2 on the forecast card and isn't.
|
* Night 3 is the trap: it looks like night 2 on the forecast card and isn't.
|
||||||
*
|
*
|
||||||
* SPRINT10: a night is now a STORM and a SITE. Nights 1/2/4/5 are the backyard;
|
* SPRINT10: a night is now a STORM and a SITE. Night 3 moves to the corner block
|
||||||
* night 3 moves to the corner block — the same "looks like night 2 and isn't"
|
* — the same "looks like night 2 and isn't" beat, sharpened: it's a new yard
|
||||||
* beat, sharpened: it's a new yard you've never rigged, its carport is a trap,
|
* you've never rigged, its carport is a trap, and its storm carries the
|
||||||
* and its storm carries the southerly change that makes the venturi scream. The
|
* southerly change that makes the venturi scream. The plain-string form is
|
||||||
* plain-string form is still accepted (defaults to backyard_01) so nothing that
|
* still accepted (defaults to backyard_01) so nothing that reads NIGHTS[i] as
|
||||||
* reads NIGHTS[i] as a storm key breaks.
|
* a storm key breaks.
|
||||||
*
|
*
|
||||||
* SPRINT11: a night is now a JOB. Same ladder, same yards — what's new is the
|
* SPRINT11: a night is now a JOB. What's new is the frame DESIGN.md has had
|
||||||
* frame DESIGN.md has had from line one: *"work arrives as callouts — each one a
|
* from line one: *"work arrives as callouts — each one a site with existing
|
||||||
* site with existing conditions, a client brief, a budget, and a forecast
|
* conditions, a client brief, a budget, and a forecast window."* You are not
|
||||||
* window."* You are not surviving five nights; you are working five jobs.
|
* surviving the week; you are working its jobs.
|
||||||
*
|
*
|
||||||
* The week that fell out of the data, once it was asked to name a client: the
|
* The week that fell out of the data, once it was asked to name a client: the
|
||||||
* Hendersons are away (SPRINT11's own line — they leave for Cairns), so four of
|
* Hendersons are away (SPRINT11's own line — they leave for Cairns), so the
|
||||||
* the five nights are the same yard, on retainer, through a bad week. Night 3 is
|
* backyard nights are the same yard, on retainer, through a bad week. A
|
||||||
* a different site, so it is necessarily a different client — a short-notice
|
* different site is necessarily a different client.
|
||||||
* callout to a yard you have never rigged, on the night the change comes early.
|
*
|
||||||
* That is not flavour bolted on: it is what `site: site_02_corner_block` already
|
* SPRINT16 gate 4 (Lane D, by the sprint's explicit grant): SIX nights. The
|
||||||
* MEANT, finally said out loud. The corner block lands harder because it's a
|
* swing lawn enters the rotation at night 4 under the southerly — the ONE
|
||||||
* stranger's place and you're there at short notice.
|
* variable rule (ROADMAP principle 5), applied literally: at its debut the only
|
||||||
|
* new thing is the yard, because the storm is Tuesday's, already survived. The
|
||||||
|
* soaker flies night 6 over the corner block — a yard night 3 taught — because
|
||||||
|
* C MEASURED the pairing (THREADS [C] 2026-07-20): the backyard's buyable
|
||||||
|
* geometry caps hail cover over the bed at ~31%, so over there the membrane
|
||||||
|
* bet has no win in it. Exactly one night is the new site (4), exactly one
|
||||||
|
* flies the new storm (6), and they are not the same night.
|
||||||
|
*
|
||||||
|
* ⚠️ THE STRUCTURAL CONSEQUENCE, SAID OUT LOUD (D, filed in THREADS for John's
|
||||||
|
* gate 6): six slots cannot hold seven nights. Five shipped storms + the
|
||||||
|
* soaker is six distinct storms; the swing lawn's debut must REPEAT a storm
|
||||||
|
* the player already survived; so exactly one shipped storm leaves the week —
|
||||||
|
* and every other candidate is load-bearing (gentle = the tutorial and the
|
||||||
|
* calm-day preload; southerly = the hail intro and the early buster's
|
||||||
|
* referent; early buster = site_02's debut, which the soaker's pairing needs;
|
||||||
|
* icenight = the beyond-saving flag, canon). THE WILD NIGHT (storm_02) is out
|
||||||
|
* of the rotation. Its storm file, its tests, and the pinned backyard
|
||||||
|
* separation all stand — it is un-flown by the week, not deleted. If that
|
||||||
|
* eviction is wrong, the honest fix is a SEVEN-night week (arc 2's own range
|
||||||
|
* is "five-to-seven"), which is one entry re-added here — a ruling, not a
|
||||||
|
* refactor.
|
||||||
*
|
*
|
||||||
* Copy lives here as data, per SPRINT11's rule: client flavour is data, not code.
|
* Copy lives here as data, per SPRINT11's rule: client flavour is data, not code.
|
||||||
*/
|
*/
|
||||||
@ -72,17 +95,29 @@ export const NIGHTS = [
|
|||||||
+ 'to tie off to.',
|
+ 'to tie off to.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storm: 'storm_02_wildnight', site: 'backyard_01',
|
// SPRINT16 gate 4 — THE NEW SITE, and nothing else new: Tuesday's southerly
|
||||||
client: 'the Hendersons', addr: '14 Kurrajong St — the backyard',
|
// over D's swing lawn. The brief does what "plenty to tie off to" did for
|
||||||
brief: 'Back at the Hendersons\', and this is the night the whole week was pointing at. Rig it like '
|
// the carport — the client's own voice sells the trap (the crossbar between
|
||||||
+ 'they\'re watching, because Friday they will be.',
|
// the swing apexes is a dead-level rail and NOT an anchor; the apexes are
|
||||||
|
// 0.45 steel that bills $140) — and the kid gives the bed its reason.
|
||||||
|
storm: 'storm_03_southerly', site: 'site_03_swing_lawn',
|
||||||
|
client: 'the Delaneys', addr: '31 Ferndale Ave — the swing lawn',
|
||||||
|
brief: 'Same southerly as Tuesday, over a lawn you\'ve never rigged. The swing set is Ruby\'s — '
|
||||||
|
+ 'her dad reckons the crossbar\'s bolted right through, tie to that and save yourself a '
|
||||||
|
+ 'post. The veggie bed is her school project, so it comes home alive.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
storm: 'storm_02b_icenight', site: 'backyard_01',
|
storm: 'storm_02b_icenight', site: 'backyard_01',
|
||||||
client: 'the Hendersons', addr: '14 Kurrajong St — the backyard',
|
client: 'the Hendersons', addr: '14 Kurrajong St — the backyard',
|
||||||
brief: 'Less wind than last night and more ice, which is worse for the bed and easier on the rig. '
|
// SPRINT16 gate 4 wording note (D): this brief used to open "Less wind than
|
||||||
+ 'Last night before they land. Nobody\'s pretending the seedlings ride this one out — keep '
|
// last night" — true when the wild night (gust 30) sat at night 4. The wild
|
||||||
+ 'the sail on its feet and the ice off the windows.',
|
// night left the six-night rotation (see the ladder comment), so the ice
|
||||||
|
// night is now the most wind the week throws; the sentence moved with the
|
||||||
|
// facts. The teaching half — level with the player that the bed is already
|
||||||
|
// lost — is untouched.
|
||||||
|
brief: 'The most wind of the week and golf-ball ice with it. Last night before they land. '
|
||||||
|
+ 'Nobody\'s pretending the seedlings ride this one out — keep the sail on its feet and '
|
||||||
|
+ 'the ice off the windows.',
|
||||||
// SPRINT13 gate 1.4 — A's ruling, DESIGN CANON: night 5's garden is BEYOND
|
// SPRINT13 gate 1.4 — A's ruling, DESIGN CANON: night 5's garden is BEYOND
|
||||||
// SAVING, by design, at any price. Measured game-true (B's pay table and
|
// SAVING, by design, at any price. Measured game-true (B's pay table and
|
||||||
// A's probe, agreeing): bare bed 0.0 DEAD; best buyable garden 27.7; best
|
// A's probe, agreeing): bare bed 0.0 DEAD; best buyable garden 27.7; best
|
||||||
@ -99,6 +134,20 @@ export const NIGHTS = [
|
|||||||
// flag in the same commit or the verdict lies in the other direction.
|
// flag in the same commit or the verdict lies in the other direction.
|
||||||
gardenBeyondSaving: true,
|
gardenBeyondSaving: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// SPRINT16 gate 4 — THE NEW STORM, over a yard night 3 taught. C's pairing
|
||||||
|
// receipt (THREADS [C] 2026-07-20): the soaker MUST fly the corner block —
|
||||||
|
// the backyard caps hail cover at ~31% and the bet has no win there. The
|
||||||
|
// brief sells the WRONG default the way the carport got sold: fine hail
|
||||||
|
// through a 2 mm weave is exactly the night cloth loses, and the client is
|
||||||
|
// the one telling you it's easy. The forecast (C's stones/rain lines) is
|
||||||
|
// where the truth argues back — the brief stays in character.
|
||||||
|
storm: 'storm_06_soaker', site: 'site_02_corner_block',
|
||||||
|
client: 'the Vasilaros place', addr: '2 Bight Rd — the corner block',
|
||||||
|
brief: 'The Vasilaros place again — they asked for you by name this time. Hardly any wind in it, '
|
||||||
|
+ 'just rain and a fine rattle of hail; the kind of night you could rig in your sleep. '
|
||||||
|
+ 'Whatever you flew last time will do, they said.',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user