1
0

feat: the boards stop being three products

The owner asked twice why there are still three separate boards. The honest
answer, and what this round executes: **it feels like three boards, but not
because the scale jumps 20x — because the three packs draw three different
Californias, and the loudest difference is that the mountains are four times
taller on one of them.**

**THE 20x HORIZONTAL SCALE JUMP IS INVISIBLE**, and measuring that collapsed the
cost of this whole round. `World.project` is a uniform scale in x/z with no
vertical term, and a uniform scale leaves a perspective image identical — so a
camera carried across the seam on matched true-metre offsets draws a
pixel-identical horizontal frame. 1,919 -> 94 m/unit costs nothing to look at.
Rescaling was never the problem. A boot card, a tab strip and a 4.17x vertical
deflation were.

**THE PAUSE WAS MOSTLY FAKE.** A switch covered the screen for 1,715 ms but only
608 ms blocked the main thread; the page drew 46 of 69 frames with nothing to
show, because the outgoing board had already been disposed. `mountCity` now
retains it: the incoming board builds BEHIND a live, interactive picture, and
`stage.setScene` fires only on completion. Measured across all six directions,
three runs each — boot card yes -> **no**, opaque cover 726-1,415 ms -> **0**,
blank frames 21-46 -> **exactly 1**, wall clock down 12-29%, blocked main thread
down 15-47%. A return to a board already seen links **zero** shader programs and
blocks **zero** milliseconds: 298-312 ms of camera flight where it was ~1,600 ms
behind a card. Disposal had been throwing away the shader cache too — linkProgram
ran 38, 59, 78, 109, 127 across five mounts and never reused one.

**The transition is a fog dip, not a crossfade**, through the `setAerialFog` seam
built last round. Every both-boards-live crossfade breaks a budget — ca+sf is
2,640,307 triangles against bay-area's 2,600,000 cap — and a fade never lands
inside the harness's sample window, which is the "a cap you do not measure is a
cap you do not have" failure this repo already argues against. The dip costs zero
triangles and zero draw calls, and it hides the 4.17x deflation, the 4,025 m
projection disagreement and the vanishing 2 km freeway symbols at once, because
all three happen at maximum obscuration. It is also diegetic: a descent through
haze.

The first dip was wrong and the photograph caught it: collapsing to 6% of board
SPAN turned the whole night frame into one flat field — the exact "turns the map
off" failure the risk list named. Re-anchored to 70% of camera STAND-OFF, so the
coastline survives and only the relief melts.

**One ladder, one places list.** 26 authored chapters become 24 rungs sorted
descending by STAND-OFF, not altitude — by altitude they interleave badly and
altitude cannot tell a low oblique from a high plan. The three-board tab strip is
off by default; the left column is now one scrolling list of all 24 rungs under
three region headings that does not change when the board does. Only which row is
lit changes. Label collisions are resolved in the ladder and never in a pack, so
the 29 index-aimed capture guards are untouched.

The minimap stops turning through 90 degrees between boards: every board is
pinned to a rectangle with California's proportions.

**SF and SoCal are not regressed**, and that was the acceptance that mattered:
95.9-98.8% of board pixels are delta-0 against a baseline hash-verified identical
to what the live site serves, and every one of the 34-70 surviving pixels per
frame is an aircraft or a hull.

**A real defect found only by photograph:** `minimap.setMarkers()` had zero call
sites. Every marker on every board was gone — the LA studio's door dot, the Bay
Area's eight company markers — dropped when the minimap went per-board.
Typecheck, tests, budgets and the console were all green with that bug in.

Also fixed: two capture presets that lied. `look.mjs`'s `glyph-la` and `glyph-sf`
claimed California chapter closeups and returned SoCal and Bay Area frames,
because they aimed by chapter index and the indices had moved. Aiming is now by
identity, with a guard test.

NOT SHIPPED, DELIBERATELY: the pack merge. At Bay density it is 34.04M triangles,
13x the highest budget — dead, not a trade. At SoCal density it is 1.99M and fits
today, and the price is San Francisco rendering at 164 m lots instead of 40 m,
i.e. SF looking the way SoCal looks now. SF and SoCal carry every marketing still
on the site. That is the owner's decision and it is worthless as an argument and
decisive as a photograph, so it ships as a measurement artifact with a
side-by-side still and is wired into nothing. The four data reconciliations that
would make one world honest — one exaggeration rule, roads in metres, one
projection centre, one coastline convention — are behind TERA_RECONCILE, default
OFF.

Tests 1,570 -> 1,651, server 295. All ten budget cells pass, no cap raised.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 06:20:18 -07:00
parent bcac6aa41a
commit 4bd8481be1
32 changed files with 6628 additions and 264 deletions
+207 -4
View File
@@ -51,7 +51,7 @@ things fall out of it that somebody has to pick up.
sky layer, and its triangle count did not move by one. The card read 500 MHz of
2725 through that run as well.
### Aerial perspective is wired, and one seam is still coarse
### Aerial perspective is wired, and the coarse seam is now a live defect
Fog now follows camera altitude (`aerialReach` in `engine/atmosphere.ts`, §12.2).
`main.ts` recomputes the rig on the controls' `change` event, throttled at 2% of
@@ -62,9 +62,32 @@ What that does **not** cover is a camera moved by something other than
`OrbitControls`: the drive, actor and aircraft follow-cameras set the pose
directly each frame and do not fire `change`. Those poses fall back to the
one-hertz clock tick, so a fast descent under a follow camera steps the fog up
to four times instead of easing it. It is not visible on the boards that exist —
the follow cameras sit low and stay there — but a continuous state-to-city
descent would need this on the frame loop rather than on an event.
to four times instead of easing it.
**Promoted from hypothetical to live, 2026-08-23.** This entry used to end "a
continuous state-to-city descent *would* need this on the frame loop rather than
on an event". That descent is now the thing being built: the state-to-metro
transition is a **fog dip** — collapse the outgoing board's `setAerialFog`
toward the camera over ~0.3 s, swap in the obscured frame, lift the incoming
board's over ~0.5 s — chosen over a crossfade because a crossfade cannot afford
the triangles (ca+sf is 2,640,307 against the Bay Area's 2,600,000 cap) and
because a dip is the only transition that hides the 4.2x vertical-exaggeration
pop, the 4 km projection slide and the vanishing 2 km freeways at once, since
all three happen at maximum obscuration.
That makes fog a **transition mechanism** rather than an ambience setting, and a
mechanism driven by an event the descent does not fire is a mechanism that does
not run. Two specific consequences:
- A dip driven from a follow camera — leaving the corridor drive into the Bay
Area is the obvious case — gets the one-hertz tick, so a 300 ms collapse gets
at most one step. The dip either does not happen or happens as a single jump.
- The 2%-of-altitude throttle is a threshold on *altitude*, and a dip changes
near/far with the altitude held still. Nothing in the current trigger notices
that at all.
So this moves onto the frame loop, and it moves as part of the transition work
rather than after it.
### The north is authored, and three things in it are thin
@@ -530,3 +553,183 @@ The complete fix is to clamp against the camera's focus distance rather than the
aircraft's, so the glyph collapses toward its authored size whenever the viewer
has zoomed in on something near, at any aircraft range. That is a signature change
through `glyphScale`, `tick` and their callers.
The four presets that photograph this clamp used to cover three stand-offs, not
four, and two of them were pictures of a different board — see "The capture
harness was aiming by position" below. They are now `glyph-board` (the whole
state), `glyph-north` (Shasta and the Klamath knot), `glyph-bay` (the Bay Area
oblique) and `glyph-fidi` (downtown San Francisco): four aerial stand-offs over
three orders of magnitude, all four aimed by `data-view`.
## The capture harness was aiming by position, and two presets were lying
Fixed 2026-08-23. Recorded because the *class* of defect is the one this repo
keeps paying for, and because two of the three fixes are guards rather than
changes.
**What was wrong.** `scripts/look.mjs`'s `glyph-la` and `glyph-sf` were
documented as "two chapter closeups on that board" — California — and were
`--click "^LA$"` and `--click "^SF$"`. Chapters 04 and 05 of the state board are
not camera poses: `main.ts:4485` matches them against `CALIFORNIA_DESTINATIONS`
and calls `switchCity()`, so both presets left California and returned frames
byte-comparable to `hero-socal` and `hero-sf`. Two of the four stand-offs the
glyph set claimed to cover were duplicates of two others, and nothing failed.
**What photographing the fix turned up.** California has exactly **two** aerial
poses on it. Of its six chapters, one is the whole board, two are doors, and two
`la-sf-us-101` and `la-sf-i-5` — call
`requestControlMode("drive")` at `main.ts:4493`, so clicking either lands a chase
camera on a freeway rather than a stand-off. "Two chapter closeups on that board"
was never available. The glyph ladder is spread across boards instead, which is
a better ladder: `glyph-board``glyph-north``glyph-bay``glyph-fidi`.
**The three things that landed with it.**
- `scripts/look.mjs` takes `--chapter <data-view> [--expect <short label>]`, so
every preset aims at a chapter's **identity** rather than at its position or at
the text printed on it. Both of the other two have now produced a photograph of
somewhere else in this repo.
- `scripts/performance-budget.mjs` gives every scene a **signature chapter**
one `data-view` that exists on that board and on no other — and refuses to
become ready until it is on screen; `california-drive` aims at
`data-view='la-sf-us-101'` instead of at chapter index 1. `?city=` falls back
to the first board rather than failing, silently, so a `bay-area` cell that had
quietly measured California would have passed its 2.6M-triangle cap by a factor
of six and stayed green through any regression a phone would choke on. A budget
that cannot fail.
**A signature chapter rather than the pressed board tab, and that is a lesson
rather than a preference.** It was written against `[data-board]` first and
broke inside the hour, because the tab strip is chrome and chrome is precisely
what the continuity work is allowed to change. A chapter id is pack data. Every
harness in this repo should key on the thing a camera resolves through, not on
the thing a designer is free to move: `check-chapter-identity.mjs` demotes tab
drift to a printed note for the same reason, and `switch-cost.mjs` detects
arrival from `#chapters` rather than from either the tab or the boot card.
- `scripts/check-chapter-identity.mjs` + `scripts/fixtures/chapter-identity.json`
pin every chapter's `data-view`, position, printed number and short label under
every `?city=`, and `src/test/chapterIdentity.test.ts` re-states the pack half
of it in `npm test` with three negative cases that are watched failing.
**The 26 aims that are still index-aimed, and are deliberately left that way.**
`scripts/brand-assets/shots.mjs` (21), `films.mjs` (4) and `capture.mjs` (1, the
Open Graph card) each point at `#chapters .chapter[N]` and assert a short label.
21 of the 26 aim at a board a `?city=` reaches — 12 Bay Area, 6 California, 3
SoCal. Re-pointing them means re-shooting the imagery on lumbridgecorp.com, which
is an owner decision rather than a refactor, so this round landed the check that
*reports* when they would break instead of moving them.
`check-chapter-identity.mjs` reads those three files and prints every aim whose
label no longer matches the board it points at.
The label assertion on its own is **not** enough and the reason is in the data:
"Whole Board" is chapter 01 of both metro boards and "The Valley" is a different
valley on each of them, so a reorder that preserves labels passes all 26 and
shoots the wrong place. Identity is `(board, chapter id, position)` together.
## A board switch is measurable now: `node scripts/switch-cost.mjs`
The pause between one board and the next was the most-complained-about moment in
the product and the one moment nothing measured, because `performance-budget.mjs`
waits `warmup-ms` and throws away exactly the window the switch happens in.
Baseline at `bcac6aa`, desktop 1440x900, `--repeat 3`, medians. `covered` is
click-to-`#boot`-hidden; `blocked` is Long Tasks inside that window; `blank` is
frames that issued zero WebGL draw calls:
| direction | covered | opaque | blocked | longest | frames | blank | live |
|---|---|---|---|---|---|---|---|
| california→sf | 1,715 | 1,415 | 608 | 526 | 69 | 46 | 65% |
| socal→sf | 1,627 | 1,326 | 523 | 459 | 69 | 46 | 68% |
| california→socal | 1,109 | 809 | 400 | 312 | 45 | 22 | 64% |
| sf→socal | 1,027 | 726 | 328 | 245 | 44 | 21 | 68% |
| sf→california | 1,120 | 820 | 340 | 196 | 49 | 26 | 70% |
| socal→california | 1,061 | 761 | 319 | 205 | 47 | 24 | 70% |
Two things to read off it.
**Two thirds of every pause is not work.** 64-70% of the cover is a live,
animating page with an empty world in it, because the outgoing board is disposed
before the incoming one is built. Retaining boards converts a 1.0-1.7 s
full-screen modal into a 0.2-0.6 s freeze of a live picture and costs zero new
triangles.
**Cost belongs to the destination, not to the pair.** The two arrivals at the Bay
Area differ by 5%, the two at California by 6%, and Bay Area against California is
1.6x. Nothing is reused between boards today — `three` refcounts shader programs
per material and deletes them at zero, so disposing a board deletes its programs
and returning relinks them — and this table is what will show that changing.
**And the residual is a separate, nameable defect.** The 526 ms task on an
arrival at the Bay Area is main-thread instance placement for 83,137 buildings.
Retention does not touch it and will make it *more* visible: today it hides
behind a full-screen card with a percentage on it, which reads as busy, and
afterwards it will freeze a live picture, which reads as a hang. Chunking that
placement across frames, or moving it behind the worker, is its own piece of work
with its own payoff.
The command reports `card` and `arrival` beside the durations for exactly that
reason. `card=yes` with two thirds of the frames blank is the disposal model;
`card=no` with `blank` near zero is retention, and the same command measures
both, because arrival is detected from `#chapters` rather than from the card.
A single direction run against the in-flight retention build read
`card=no`, 1 blank frame of 60, and a 508 ms single task — which is the shape
this section predicts: the modal is gone and the hitch is not.
## The quadtree's stated blocker was the wrong one
Recorded 2026-08-23 so the next round does not lose a week re-deriving it. The
terrain quadtree has been deferred repeatedly on the grounds that the synchronous
sampler stands in its way. **It does not, and the code says so in one line.**
- `elevationAt` has exactly **one** production caller: `world.ts:545`, inside
`computeField`. Nothing in the render path samples it. Everything else reads
the cached `Field` through `elevationSampled` / `isLandSampled` /
`inParkSampled` / `groundAt`.
- `world.lattice()` — the thing that hands out flat arrays — has exactly **two**
callers, both in `terrain.ts` (282 and 404).
- Measured on this box: `elevationAt` is 1.97 µs/call on SF and 11.98 µs on
California, so a complete permanently-resident base pyramid (levels 0-2, 15
land tiles, **183 ms** on SF, covering the whole board at 370 m cells) makes
every point query synchronous by construction — descend-to-finest-resident is
integer arithmetic against today's *two* binary searches in `cellIndex`. No
caller ever needs an `await`. The base has to be complete because
`minimap.ts:407` samples a 160x160 grid across the **whole** board, including
ground the camera never looks at; level 2 satisfies it exactly.
The API a quadtree must replace is therefore one accessor and two call sites. The
real blockers, none of which had been written down:
1. **Placement drift.** Buildings, landmarks, bridges, airports, ports, markers
and road ribbons are placed **once** at scene build against `groundAt`. A
level-3 (185 m) representation sits p95 119 m / max 453 m from level 5, so if
the resident level under a placed object changes after placement it moves by
up to **1.15 scene units** on SF — against a lot of 0.42 units. Buildings
float or sink. This, not the sampler, is what the deferral was groping at.
2. **The shadow caster.** Selected at the shadow map's own texel error a quadtree
caster is 826,206 triangles against today's 53,806 — 15x. View-distance
weighted it is 1.3x at the wide pose and 2.9-4.2x at the close ones, i.e.
+100k to +174k triangles, against 183,824 of headroom on bay-area/mobile and
39 free draw calls. The caster must be bounded by the *visible* selection
rather than selected independently, because **never raise a cap**.
3. **Determinism.** `performance-budget.mjs`'s own comment says "geometry here is
deterministic". A streaming tree makes triangles a function of time and camera
path. It survives only because the harness runs `reducedMotion: "reduce"` on a
planted pose and waits 3,000 ms of warmup — and realistic selections settle in
232-830 ms — but that has to become an explicit `settled()` gate the harness
waits on, not a hope.
Two more facts worth keeping. A single statewide lattice at SF fidelity is dead —
23.7M points, ~74 s of worker, 142 MB — because `buildAxis` refines per **axis**,
so two focus regions at opposite corners refine nearly the whole board. And the
board that would demonstrate the mechanism paying for itself is **SoCal**, not
SF: 48.4% of SoCal's lattice is the per-axis cross's arms (64,677 of 133,566)
against SF's 31.0%, and SF is the one board where a single hand-drawn rectangle
is exactly right — its 52 districts union to 9.6% of the board and its one
`focusRegion` box is 9.2%.
**And today's SF board is not error-bounded, which nobody had noticed.**
Cell-centre height error against the analytic truth is p99 12.0 m / max 64.9 m in
the fine region and p99 29.1 m / **max 459.3 m** in the coarse one — 27.2 px at
the default whole-board pose, out in the bay. The board is cheap because the pack
declares 90% of it unimportant. That is a defensible trade and it is not the same
thing as being right.