Commit Graph

365 Commits

Author SHA1 Message Date
type-two
e727e49325 Merge remote-tracking branch 'origin/lane/d'
# Conflicts:
#	THREADS.md
2026-07-20 19:30:45 +10:00
type-two
a79f714c86 Merge remote-tracking branch 'origin/lane/b'
# Conflicts:
#	THREADS.md
2026-07-20 19:30:45 +10:00
type-two
82691678eb Merge remote-tracking branch 'origin/lane/c'
# Conflicts:
#	THREADS.md
2026-07-20 19:30:45 +10:00
type-two
91266e2a99 Lane D S15: gates 2.3 + 1.4 judged and closed — the editor is iterative (three-position bed study, receipts), the honest line replays S14 to the decimal on the scratch merge (418/0/0), pergola sentence filed
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:28:35 +10:00
type-two
ef6b3d0028 gate 2.2 closed: adopt C's converged storm store byte-identical; score panel becomes a view of it; [B] THREADS closing entry (pins reproduced, timings, seam receipts)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 18:32:07 +10:00
type-two
0bb0e9eb6d Lane C S15: converge gate 2.2 onto B's THREADS proposal — B's filename/global/source/default taken, storm+time API kept; THREADS: seam receipt + gate-1.3 storm-side zero-delta confirmation
editor_stormsel.js -> editor_storm.js (B's name); globalThis.EDITOR_STORM
(B's name); setStorm/setTime carry B's source tag in the payload; default
becomes B's storm_03_southerly at my receipted t=40 (the pin stays pinned
in c.test — it never needed to be the default, and the c.test assert now
pins the CONVERGED pair instead so a private default cannot regrow).

Zero-delta second harness: envelope.html week mode, both shipped yards,
all five nights vs my S13 tables — byte-identical everywhere (site_02 x
earlybuster all 11 rows; backyard h1-h3 Pa/hint 266/946-1037/2093-2505/
1844-1933; branch ladder 0.88/0.76; t1 southerly 15.45). Measured on
lane/c @ gate-2.2 commit atop main c182fc1, pre-E-re-bake; zero expected
under the 1.00 ruling, zero measured, nothing to flag.

Selftest 413/0/0; mutations 6 (default drift) and 7 (source dropped)
red-then-green on top of the five in the previous commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 18:23:31 +10:00
type-two
d6f04d5e43 editor: one storm picker + a progress line that paints (S15 gates 2.1/2.2, B's halves)
editor_storm.js owns THE storm selection and THE storm list; SCORE IT's
dropdown is a view of it (default = southerly, C's old default, kept on
purpose). The score panel renders scoreSite's progress ticks — flight x/y,
elapsed — and audit.html gets the same line. C's half: subscribe
editor.wind.js per the THREADS proposal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 18:19:44 +10:00
type-two
b1d2d2a8e6 score: the sweep gets a heartbeat — chunked driver yields between flights, same numbers to the byte (S15 gate 2.1)
findCandidates/priceCandidate/judgeSweep extracted so the sync auditSweep and
the new auditSweepAsync are ONE copy of the math; scoreSite drives the async
path with onProgress ticks (sweep/fly/separation phases). Yield is a
MessageChannel task, not setTimeout — Chrome's intensive timer throttling
turned an occluded run into one flight per minute, measured. Three new
asserts, mutation-checked red-then-green in one sitting (impure chunk + a
swallowed tick = all three red with the intended diagnostics).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 18:19:44 +10:00
type-two
3a2e4ff5e1 Lane C S15 gate 2.2: ONE storm picker — editor_stormsel.js is the shared storm+time selection; wind panel becomes a view of it + JUMP TO WORST SECOND
The store: one owner for 'which storm, which second' on the editor page.
Garbage keys refused loudly, no-op writes silent (the anti-ring guard that
lets a subscriber clamp from inside its own handler), listener errors
contained, default = the gate-2.3 pin (wildnight @ 60 s). editor.wind.js
drops its private stormKey/time and re-exports the store's STORM_KEYS —
one list, same array. B's editor_score.js patch proposed in THREADS.

Polish (my call): JUMP TO WORST SECOND — argmax of speedAt over grid ×
storm, venturi throats passed as exact probes, dt = the scrubber's own
0.5 s step. Writes the SHARED second. site_02 × wildnight: 51.11 m/s
@ 60.5 s at (-7.5, 0), the funnel plateau, found by scan not patience.

Selftest 413/0/0 (410 + 3). Five mutations red-then-green: no-op guard
off, validation off, singleton doubled, comparator flipped, probes
dropped — each reds its own assert with its own message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 18:05:33 +10:00
type-two
cebc14e2b1 Lane E S15: THREADS — gate 1.2 receipts, the exact world.js patch for the integrator (leadFor precedent), ruling constraint satisfied by construction, palette proposals for the next yard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:49:38 +10:00
type-two
2a540523ff Lane E S15 gate 1.2: RULE 4 pins — manifest===GLBs both directions (4a), runtime===manifest across all three shipped sites (4b); compared against the manifest, never a literal; both mutation-checked red-then-green (post 1.0->0.9)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:49:38 +10:00
type-two
8aa49528e3 Lane E S15 gate 1.2: ratings.js — factoryExtras(type), the runtime end of the manifest; refuses to guess for ambiguous types, warns once
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:49:38 +10:00
type-two
e46ff18648 Lane E S15 gate 1.2: re-bake sail_post top_anchor 0.90 -> 1.00 per the gate 1.1 ruling; factory emits the per-type anchor ratings manifest (anchor_ratings.gen.js) from the re-imported GLBs, ambiguity-aware, --only-guarded; asset_report carries per-asset anchor extras
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:49:38 +10:00
type-two
d8e2ecea80 [B] gate 1.3 zero-delta table (all three yards, 0 deltas @ c182fc1) + gate 2.2 storm-picker seam proposal for C
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:47:55 +10:00
m3ultra
c182fc14cb editor: the honest edges — Enter commits, bounds are seen, keyboard zoom, separation ruled (S15 gate 3 + gate 1.1 ruling)
Gate 1.1 RULED in THREADS: the honest post is 1.00 — the shipped value
becomes canon, E re-bakes to match, and the adoption mechanism must make
a baked hint reach JSON-declared anchors (same integration, no 0.90
window). B's re-measure becomes a zero-delta verification.

Gate 3: numInput/textInput commit on Enter (idempotent — a no-op commit
pays no rebuild); boundsFaults() rides the validation cache so header,
panel and export read one measurement, export leads with _OUT_OF_BOUNDS
(not _INVALID — that key's sentence would lie); the yard rectangle draws
as an amber line where fence-off sides hid it; +/- zoom on the wheel's
own curve; separation ruled a measured pin with the SITE panel saying so.
New a.test mutation-checked both ways in the browser harness. 410/0/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:11:05 +10:00
m3ultra
3dee2a7c83 Merge origin/main: S14 lane integration + review fixes; TEETH becomes SPRINT16
Two same-day integrations reconciled: their SPRINT15 (post ruling, editor
iterative, honest edges) stands and runs first because it re-measures the
numbers TEETH pins; the TEETH plan moves to SPRINT16 with a fires-after-15
banner, its D gate rewired to the landed swing lawn. ROADMAP arc 0 records
the fork. THREADS keeps both sides chronologically.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 10:34:21 +10:00
m3ultra
4a5e5bc037 Sprint 15 plan: TEETH — ledger/warranty/rep, the membrane night, site_03, six nights
ROADMAP.md is the new growth canon: five arcs from the shipped week to
DESIGN.md's landscaping business (teeth → job board/clients → slow
pressure → the season → the water table), with the earned principles up
front — every arc ends live, no mechanic without a surface, balance is
measured, one new variable per night. SPRINT15.md and the lane prompts
draw arc 1's gates; John resolved S14 gate 4 as all-three-sequenced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 19:10:14 +10:00
m3ultra
f179d6c25e docs: panel comment tells the truth; README wears the shipped name; THREADS review entry
main.js said panel:false in the comment and passed panel:true for two
sprints — the call was right (B's panel is the only prep UI), the
comment now says so. README retitled HARD YARDS with the SHADES codename
noted. THREADS carries the fresh-eyes review receipts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 19:03:36 +10:00
m3ultra
b74dfd80db rigging: the fabric bet is reachable — F cycles cloth/membrane in prep
FABRIC and setFabric() shipped in SPRINT6 and the sim honoured porosity
all along, but no key, click or HUD line ever reached them — every rig
ever played was DEFAULT_FABRIC. F toggles the two entries, the panel
shows the fabric, the splash teaches it, the blurb announces the switch.
Session-level assert pins the cycle, the $0 price, and the refusal of
unknown ids.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 19:03:36 +10:00
m3ultra
b7a069069d debris: delete the dead sail.nodes seam — sail._applyDebris is the one contact model
The SPRINT2-era duck-typed path (if sail.nodes → applyToSail) has been
dead since decision 5 chose option b, and was a double-apply waiting for
the day anyone exposed .nodes. The sail key on step()'s world arg is
accepted and ignored, documented.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 19:03:36 +10:00
m3ultra
9d6db356d7 sail: break diverged corners on the production path; enforce pond cap post-flow
A NaN corner load compares false to every rating, so a diverged corner
held forever while NaN spread node-to-node — watchDivergence only ever
guarded the tests. Non-finite load now breaks the corner instantly with
reason:'divergence', load zeroed for the HUD.

The pond per-node cap only ran on the rain-add: once rain stopped,
consolidation could stack a basin node past POND_MAX_KG_M2 unbounded.
Clamp moved post-flow, every step. Also fixes the dumpPond assert that
compared dumped to itself through a helper that returned its argument.

All three mutation-checked red before landing green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 19:03:36 +10:00
type-two
6f68fbf618 Sprint 15 plan + prompts; README points at the renamed remote 2026-07-18 18:00:50 +10:00
type-two
008b78a0d4 Sprint 14 integration: merge all lanes (406/0/0), fix palette _v1 suffix bug, [I] entry 2026-07-18 17:58:44 +10:00
type-two
0414d34b9e Lane D S14: THREADS — gate 3.2 cold authoring pass, and the palette ships both new props inert
Headline for A: editor.js palette entries 239/243/257/260 omit the _v1 suffix every
other entry carries, so swing_set_01 and tree_jacaranda_01 404, never draw, and
adopt at ratingHint 1.00 instead of E's designed 0.45 and 0.95/0.52/0.40.
adoptAnchor's `rating_hint ?? 1` turns a missing model into the best steel in the
game, and the palette writes the bad name into the exported site JSON. Negative-
controlled both directions on a local patch; editor.js untouched on lane/d.

Also: site_03 candidate authored, scored (82.5 s, winnable at $80, 11 clean of 81)
and played (trap bites: t1c breaks, 83.9 vs a bare 83.7). Phantom sail verified
dead across a live site transition. Friction filed by owner; three candidate
findings checked and withdrawn.

Selftest 406/0/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 17:45:13 +10:00
type-two
0b858abc9d Lane D S14: site_03_swing_lawn — the site_03 candidate, authored cold in the editor
Gate 3.2. Data only; inert until a week.js NIGHTS entry names it.

SCORE IT (storm_03_southerly, funnel ON, 11 anchors, 82.5 s):
WINNABLE at $80 — 81 quads in band, 11 clean, 16 marginal. Cheapest honest
line h3,t2,t2b,p1 at $65. Bare bed 83.7 FULL, best clean 90.0, separation
+6.3 HP. The best-READING line is MARGINAL at 91.8 — the prettiest number in
the yard is the one that breaks. Collateral exposure $230 on an $80 job.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 17:32:27 +10:00
type-two
3432ea0d1c Sprint 14 integration: merge a/e/c/b (406/0/0), mount B's score + C's wind panels in editor.html 2026-07-18 16:52:36 +10:00
type-two
e49ad04f99 Merge remote-tracking branch 'origin/lane/b'
# Conflicts:
#	THREADS.md
2026-07-18 16:47:17 +10:00
type-two
a2ed98b4db Merge remote-tracking branch 'origin/lane/c'
# Conflicts:
#	THREADS.md
2026-07-18 16:47:17 +10:00
type-two
04f47586eb Merge remote-tracking branch 'origin/lane/e'
# Conflicts:
#	THREADS.md
2026-07-18 16:47:17 +10:00
type-two
c32e456132 Lane B S14: THREADS — gate 2.1 + 2.3 receipts, the converged pin, three wrong probes, skip audit zero, invoice line 2026-07-18 16:42:33 +10:00
type-two
d946636de1 Lane B S14 gate 2.3: converge onto C's pin — their knobs, their guard, my chain
C measured my proposed pin and it could not have failed. Converging rather than
landing a second pin that disagrees. Knobs are now C's:
  site_02_corner_block / storm_02_wildnight / throat (-6,y,0) / t=60.0 / exact ===

Three wrong answers before this, all the same failure:
  1. backyard_01 has venturi: [] — nothing to detect. (I found this one.)
  2. site_02's BED is funnel-blind, Δ 0.0000 m/s. (Both of us, independently.)
  3. t=30 on the wildnight is worth 0.44%. I MISSED THIS ONE: my throat
     measurement read a healthy Δ +4.948 m/s and I trusted it, but that was on
     storm_03b_earlybuster, not the wildnight the pin flies. A sensitivity
     measured on one storm says nothing about another. The mutation check I was
     proud of would have passed on the wrong storm.

Adopted C's vacuity guard and its 25% threshold — one number, not two. It is
the check that would have caught my proposal on its own: mutation asks 'did it
move', the guard asks 'is the funnel worth enough here that a wiring bug could
not hide in the rounding'.

Game side now imports main.js's createWindRouter instead of retyping its two
wiring lines (C's correction). My old version built BOTH sides with
windForSite, so it could only have caught a bad input, never a router that
routed wrong.

CONVERGENCE RECEIPT — my chain reads C's numbers to the decimal:
  t=60 throat  editor 47.38  game 47.38  exact === true  funnel-off 31.60 = 33.3%
  t=30 throat  on 18.55  off 18.46  = 0.44%   (C's finding, reproduced)

What my half adds over C's: C pins windForSite (the builder) against the game;
this pins buildScoringWorld — the path SCORE IT actually runs — against the
same router. Three chains, one number.

Kept the garden bed as a SECOND probe on C's explicit offer: what the sail
shades is what the audit is about, and equality there is a real claim even
though the funnel is not what decides it. Labelled as carrying no funnel
tripwire, because it cannot.

Mutation-checked: setting t back to 30 reds the guard and both tripwires with
the exact diagnostic (0.44%, 18.46 -> 18.55) while the EQUALITY asserts stay
green — which is precisely C's argument, reproduced on my own chain.

Selftest 375 passed / 0 failed / 0 skipped.
2026-07-18 16:41:36 +10:00
type-two
33bc530e78 [C] THREADS: gate 2.2 landed, gate 2.3 pin (probe/second moved, with measurements), skip audit 2026-07-18 16:29:50 +10:00
type-two
19c39acc8d Lane B S14: collateral exposure on the card + the night-5 invoice line (pool item)
COLLATERAL ON THE CARD. 'Winnable at $80' is an incomplete sentence on a yard
where letting a corner go also bills $180 of carport, so the card now prices
it — through world.collateralFor(), the one resolver (A's structKey fix), never
a second copy of that lookup in a panel. Deduped per STRUCTURE the way main.js
bills ('two beams letting go is one carport gone'): site_02's four carport
anchors read $180 total, not $720. The bill also rides on the margin-flag line
itself, because 'knife-edge' and 'knife-edge AND $180' are different authoring
decisions. An unpriced label renders UNPRICED — not scored, in red, never
'free' — which is the gutter bug's shape and the lie the card exists to prevent.

Verified A's structKey fix rather than assuming it: an editor-shaped carport
(id s1 + collateralKey) prices to $180, a second one (s2) also $180, and the
negative control (id s1, no collateralKey) reads null = not scored. My first
test reported a false alarm because I renamed a SHIPPED spec's id without the
palette's collateralKey — A's editor writes that field explicitly, so the
editor path was never broken.

THE NIGHT-5 INVOICE LINE (pool item, offered S13, accepted). Night 5's garden
is beyond saving by design, and the job sheet was the last surface still
dangling 'up to $45 / paid on what's left of the bed at dawn' — a maximum
nobody can earn, phrased as a rigging problem the player could solve.

NO ECONOMY CHANGE, deliberately. week.js's own comment forbids quote and settle
diverging ('the job sheet promises a number the invoice doesn't pay'), and
zeroing the quote would break that in the opposite direction — settle really
does pay the bonus proportionally. So the NUMBER stays and its CONDITION tells
the truth, which is the job sheet's own idiom:
  nights 1-4  'paid on what's left of the bed at dawn'        (unchanged)
  night 5     'scales with the bed at dawn — and tonight the ice takes it
               whatever you rig'
and the ledger names it too, because an unexplained small number next to
'night lost' reads as the player's failure:
  night 4  'garden bonus — 18% of the bed          +$8'
  night 5  'garden bonus — 18% of the bed, beyond saving from the start  +$8'

Verified in the running game on all five nights: flag fires on 5 only, every
quoted and paid figure identical (garden $45, total $136, bonus $8).

Selftest 374 passed / 0 failed / 0 skipped.
2026-07-18 16:29:41 +10:00
type-two
15ea431b23 Gate 2.3: pin editor wind == game wind exact, and fix the stale-wind seam
The pin (co-owned with B, probe/second moved and argued in THREADS):
site_02_corner_block / storm_02_wildnight / the authored throat centre
(-6,0) / t=60.0 / exact ===. B proposed backyard_01, the garden bed and
t=30; all three are places the funnel contributes ~nothing (backyard_01's
venturi is [], the bed measures 0.0000 m/s of funnel even on site_02, and
t=30 is worth 0.4%), so that pin could not have failed. A vacuity guard
now asserts the funnel is worth >25% AT the pin, so it cannot rot back
into decoration.

Also fixed, found by looking: editor.wind rebuilt its wind in commit()
rather than on 'change', so any EXTERNAL mutation + markDirty() re-rendered
the panel against the previous site's wind. The readout kept insisting a
restored gap was 24% aligned. Refresh moved to the listener, so the overlay
is a function of the site as it is, whoever changed it.

368/0/0 (362 baseline + 6). All six mutation-checked in one run: dropping
setVenturi from windForSite reds the pin (31.60 vs 47.38 m/s), the guard
and the sweep; normalizeAxis as identity reds the fold; drifting the
shelterAtten mirror 0.5 -> 0.62 reds the volume assert.
2026-07-18 16:27:03 +10:00
type-two
ff7c1f7985 Lane B S14 gate 2.3: pin the editor's scored wind to the game's played wind
Two chains built independently — GAME (loadSite -> createWorld -> dress ->
windForSite) and EDITOR (siteClone-shaped object -> buildScoringWorld ->
windForSite) — asserted EXACTLY equal at t=30, speed and vector, at every
probe on both shipped yards. Exactly, not nearly: a tolerance is where the
funnel-off bug hid, and it moved the S13 headline 91.5 -> 39.8.

The mutation checks caught my own probe TWICE before this went green, which is
the only reason it is worth anything:
  1. I proposed backyard_01 in THREADS. It declares venturi: [] — no funnel to
     lose, so a funnel-off regression could never turn it red.
  2. So I moved to site_02 — and its BED is 6.08 m from a radius-5 throat.
     Starving that yard of its venturi moved the bed probe by 0.000 m/s.
Both versions passed their equality assert and both were worthless.

Measured sensitivity on site_02/earlybuster at t=30 (funnel on/off, trees
present/hidden):
    bed      ( 0, 1)  12.452  dV +0.000  dT +0.000   <- blind, not a pin site
    throat   (-6, 0)  15.910  dV +4.948  dT +0.000   <- funnel tripwire
    near tr1 ( 7,-1)   8.593  dV +0.000  dT -4.891   <- shelter tripwire

Mutation-checked: starving the editor clone of its venturi turns the @throat
equality red (plus both funnel asserts), while bed/tree stay green exactly as
the table predicts.

Also recorded as a pin, because it is load-bearing for anyone reading the
score card: the corner block's funnel does NOT reach its garden bed. It is
aimed at the rigging zone (the site JSON says so: q1 is 3.5 m inside the
radius), so on that yard the funnel is a hardware-load story, not a
garden-exposure one.

Selftest 374 passed / 0 failed / 0 skipped (362 baseline + 12).
2026-07-18 16:16:29 +10:00
type-two
f3bf038515 Sprint 14 gate 1 close: round-trip pinned, pool items landed, free-carport fixed
Gate 1.5 -- the round trip. a.test drives the editor's OWN placement function
(placeEntry, exported for exactly this) through
template -> place -> export -> parse -> validateSite -> createWorld -> dress,
then asserts the GLB-backed anchors ADOPTED and the carport's 0.22/0.30 came
out the far side. Mutation-checked with five deliberate breakages; four went
red and ONE DID NOT -- the key-order test shuffled only root keys, every one
of which is in KEY_ORDER, so the alphabetical fallback it claimed to protect
was never reached. Rewritten around unknown keys, re-mutated, red in both
halves. A determinism test that passed while testing nothing.

Picking bug, found by dispatching real PointerEvents rather than reading the
code: clicking an object's FOOT selected nothing, because the pick handle is a
cylinder centred 1.7 m up and from a raised camera the ray has already run
~2 m horizontally by the time it climbs there. pickRef now falls back to
nearest-placed-thing within 1.3 m of the ground hit -- camera-angle
independent, so it can't rot when the pitch changes.

Phantom sail (pool, filed unlanded in S13) -- LANDED and negative-controlled.
Teardown factored into disposeSailView() and called from loadSiteInto before
refreshCameraSolids (which reads sailView and would re-register a disposed
mesh as a camera collider). Rig state goes with the view. Verified through the
real game both ways: with the fix removed the cloth still hangs over the
corner block, screenshotted.

Dev-line (pool) -- two findings, both landed. The line counted `pieces` only,
so it read "debris 0" while seven leaves flew; now reports both populations
off C's leafCount. And index.html's boot-failure handler wrote into #dev,
which is display:none off localhost -- so a failed boot on partly.party showed
a stranger a blank page. It has its own visible #fatal element now.

E's gap 2, runtime half -- and it was worse than "one click away": nextId
namespaces structures s1, s2..., so the FIRST carport the editor places was
already free. collateralFor/wreckStructure/isWrecked now resolve through
structKey (site collateralKey, then GLB collateral_key, then id last so every
existing site is untouched). Negative-controlled: id-equality prices an
editor carport to null. E's tie_off:false honoured at both ends. E's swing set
and jacaranda are in the palette behind a `requires` gate, so they switch
themselves on when the ANCHOR_TYPE widening merges.

Also: `return t.skip(msg)` is a fake pass -- Suite.skip pushes a new result and
lets the current test record as pass. a.test had six; all converted to the
documented 'SKIPPED --' idiom.

Selftest 373/0/0, no skips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:14:34 +10:00
type-two
4c1086c805 Wind authoring for the yard editor: venturi gizmo (axis as a LINE, mod pi), tree-shelter volumes, wind-field overlay
Gate 2.2. Mounts into Lane A's editor through the published seams only —
panel at reserved order 50, every gizmo parented to EDITOR.overlay, venturi
drag through registerTool, site.wind mutated then markDirty(). No second wind
validator (validateSiteWind is already inside A's validateSite) and no private
harness: windForSite() is the only wind this file builds.

Gizmos are drawn from wind.core.venturi/shelters — what the wind actually
flies — not from the site JSON, so a mis-wired setVenturi shows up as a funnel
that vanishes rather than a number nobody reads.
2026-07-18 16:06:16 +10:00
type-two
af0d64ffe3 Lane B S14: audit.html onto the shared scorecard engine — one copy of the run logic
The page keeps its rendering and loses its logic. A third front-end (A's
editor) was about to make sweep-fly-pick-judge exist twice, which is the exact
failure sweep.js was extracted to prevent, one level up.

Behaviour-preserving, and measured that way rather than reasoned: both shipped
sites were pinned BEFORE the refactor and reproduce to the digit after —
backyard_01/wildnight 16 cands, 3 clean, 2 marginal, bare 35.7 tattered, best
p1,p2,p3,p4 60.5, sep MEETS held 63.8; site_02/earlybuster 66 cands, 20 clean,
1 marginal, bare 82.6 full, best tr1,tr1b,q1,q4 91.3 full.

Caught in the act by that check: __audit.cands went undefined because scoreSite
returns the COUNT where the old local was the LIST. A refactor that only got
read would have shipped it.
2026-07-18 16:03:59 +10:00
type-two
fcf1016e4e THREADS [E]: palette audit table, two temptation props, five negative controls, D unblocked 2026-07-18 16:03:08 +10:00
type-two
ba062cf679 assetcheck/look.html: the verify that answers "is this the right shape"
Dims, tri counts and node names all pass on a bike leaning the wrong way and
a gutter that never fell. The Blender contact sheet is close but renders
through Blender's view transform — it showed this sprint's jacaranda as
near-white lavender when the material is #8C7FC0, so a colour call made there
is a call about Blender, not about the game.

Smallest thing that answers the question: the game's own vendored three.js,
daylight, a ground plane, and the 1.7 m ref capsule beside whatever you name.

    /tools/assetcheck/look.html?a=swing_set_01,tree_jacaranda_01&cam=10,4.5,22

It caught two things this sprint the numbers could not: the lilac reads lilac
in the real renderer (the thumb was lying), and the swing set's wreck lands on
+Z with a 2.9 m reach against a 0.95 m standing footprint — which is now baked
as a placement extra and asserted.

Sized at draw time, every time: a tab that boots hidden reports 0x0 and you
get a correct scene rendered into nothing. That cost me the first screenshot
of this very page, so it is in the MANUAL gotcha list where it already was.
2026-07-18 16:01:39 +10:00
type-two
b9b64604b3 Palette audit + two temptation props: honest extras, in the factory
SPRINT14 gate 3.1. The editor is about to offer these GLBs to an author by
name, which turns every silent field in them into a trap for whoever places
one. Audited the placeable set, fixed the gaps in the factory (nothing
hand-edited), and added the two props D needs to author a yard worth playing.

THE AUDIT, and what it found:

- Four `*_anchor` nodes carried no rating_hint at all — door_anchor,
  pickup_anchor, grip_anchor, window_light_anchor. adoptAnchor does
  `rating_hint ?? 1`, so silence there does not mean "unrated", it means
  RATED PERFECT: a shed door skin outranking a concreted post, conjured out
  of a missing field. They now deny it explicitly (`tie_off: false`).
- The carport's price resolved only by coincidence: collateralFor() matches a
  STRUCTURE whose site id equals the anchors' collateral string, and site_02
  happens to name its structure "carport". The editor will generate
  "carport_2" for the second one, and the trap would have gone free — the
  gutter bug reborn in the sprint meant to bury it. The GLB now carries
  `collateral_key`, like the house does for "gutter". Lane A: the runtime
  half is yours (collateralFor could fall back to it).
- The trampoline is unpriced, and now says so rather than omitting it.

Three new rules make the audit permanent, and each is proven able to fail:
no silent anchors, no collateral string nobody prices, no baked anchor_type
outside the checked ANCHOR_TYPE enum. Rule 2 found the carport gap on its
first run — an unfaked negative control.

THE PROPS:

- swing_set_01 (+ wrecked) — the palette's honest middle. It had a ceiling
  (gum fork 1.0) and a trap (carport beam 0.22) and nothing in between. Two
  apex anchors at 0.45, typed `swing_frame` (NOT `post`: the type string is
  what the player reads before committing, and "post" promises concrete),
  and a crossbar that is the most anchor-looking object in the game and is
  not one. Priced at $140 — proposal, reasoning baked beside it, A rules the
  number: gnome 25 < gutter 90 < swing 140 < carport 180.
- tree_jacaranda_01 — a second species whose LADDER is the feature.
  Gum: 1.00/0.88/0.76, climb freely, pay 24%.
  Jacaranda: 0.95/0.52/0.40, climb at all, pay 58%.
  That makes tree choice a real decision instead of "is there a tree".
  Unpriced by ruling, same as the bike: no limb-failure event exists to see.

Both wreck/orientation claims are measured in browser coords, not reasoned —
`wreck_falls_toward: "+Z"` is baked AND asserted against the mesh.

Selftest 376/0/0. Two consecutive full factory runs: 44 files byte-identical.
2026-07-18 16:01:27 +10:00
type-two
6825794d98 Lane B S14 gate 2.1: SCORE IT — shared scorecard engine + editor panel
scorecard.js: the audit run-logic that audit.html had grown moves into one
shared engine both front-ends import (build the scoring world, sweep, fly the
winners, pick the best line, judge the separation block). Takes a site OBJECT,
not a path — loadSite always returned an object, so the fetch was never the
audit's business. Builds its OWN dressed world on a re-pointable wind proxy so
the editor's calm stub can never reach a score; every m/s still comes from
windForSite() via sweep.js/gardenfly.js.

editor_score.js: front-end only, zero audit logic. Mounts at A's reserved
order 40, renders the card with A's contract classes, says the funnel state
first and loudly, and marks itself stale when the yard changes underneath it.
2026-07-18 15:56:50 +10:00
type-two
05d6128be7 [C] THREADS: ask A for the lane-module load seam (editor.html imports only editor.js) 2026-07-18 15:53:51 +10:00
type-two
ad8db1b709 Lane B S14: THREADS — two seam asks for A (module loading; why I need no wind seam) + gate-2.3 probe proposal for C 2026-07-18 15:53:43 +10:00
type-two
16dee119d6 Sprint 14 gate 1: the yard editor core + two shipped bugs it found
web/world/editor.html + js/editor.js. Loads any existing site or an empty
template and renders the REAL dressed world through the same
loadSite/createWorld/dress path the game boots -- not a diagram, and no
second renderer to keep in sync. Place/drag/delete on the ground plane by
raycast, writing the SAME named-key site-JSON shapes world.js already reads
(the gnome pattern; there is deliberately no generic props[] and the editor
does not invent one). Live validateSite into a panel, canonical
stable-key-order export with a loud _INVALID key, visible _design/_why
authoring fields, and an export panel that says export != shipped.

Published the DOM seam contract in editor.html's header so B's SCORE IT and
C's wind authoring can start against it immediately -- mountPanel + a class
kit, the way E's .letterhead kit worked in Sprint 12.

Two rendering bugs in world.js that the editor surfaced in its first ten
minutes, both verified by looking at the yard rather than by reasoning:

- the graybox house was built unconditionally and dress() only retires it
  when a house GLB loads, so site_02_corner_block -- which declares no house
  -- has been shipping a 16x3x6 m grey slab across night 3's whole north
  horizon, in solids, on the public URL.
- SHED alone was bound raw while SHED_TABLE/GNOME/BIKE all get their degrees
  converted, so SHED.rotY was undefined, the shed's quaternion was all NaN,
  and three.js silently declines to draw a NaN world matrix. The Hendersons'
  shed has never rendered; ladder.js has been leaning the ladder on thin air.

Also: createWorld threw on a site with no shedTable (unreachable for both
shipped sites, reached by the editor template on its first frame). Now null,
which every consumer already guards and which CONTRACT.world permits.

Selftest 362/0/0. Asserts for the two fixes land with the round-trip test in
the next commit -- pushing now because B and C are blocked on the seam.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:46:01 +10:00
type-two
f02f91cabc Sprint 14 plan + prompts: the yard editor — sites are makeable 2026-07-18 14:50:26 +10:00
type-two
f13aad6411 docs/MANUAL.md: dev manual — tool map, asset + site cookbooks, earned gotchas 2026-07-18 14:35:20 +10:00
type-two
03c6cd1c09 Sprint 13 integration: merge all lanes (362/0/0), apply B's pre-authorized gardenfly resolution, [I] entry 2026-07-18 14:30:13 +10:00
type-two
edb3377818 Merge remote-tracking branch 'origin/lane/d'
# Conflicts:
#	THREADS.md
2026-07-18 14:26:16 +10:00
type-two
646efa7244 Merge remote-tracking branch 'origin/lane/b'
# Conflicts:
#	THREADS.md
2026-07-18 14:26:16 +10:00