794 lines
48 KiB
Markdown
794 lines
48 KiB
Markdown
# Open work on Tera
|
||
|
||
Short, and only things that are decided but not done. Anything speculative belongs
|
||
in an issue, not here.
|
||
|
||
## The state board became the whole state — what this leaves open
|
||
|
||
`src/cities/california.ts` now runs 32.50–42.05 N and -124.50 to -114.0 W. Four
|
||
things fall out of it that somebody has to pick up.
|
||
|
||
- ~~**The marketing stills are frames of a board that no longer exists.**~~
|
||
**Done 2026-08-23.** All 21 shots and all four films were re-shot at `e7a8aa0`
|
||
plus this round's uncommitted work; `california-relief`'s caption now says 554
|
||
units and names the north. See "The imagery was re-shot" below for what that
|
||
run found, and for the two manifests that are still waiting on a clean sha.
|
||
- **`scripts/performance-budget.mjs`'s header comment is now stale in one row.**
|
||
It records `california/mobile` at 389,843 triangles; the extended board
|
||
measures 373,825. The caps in `performance-budgets.json` were rewritten
|
||
downward to match. Nobody owned that comment this round.
|
||
- **`ARCHITECTURE.md` §12.2 lists the span constants that stay spans**, and the
|
||
list is the decision, not a survey. If a future round wants physical units for
|
||
the satellite dome or the cloud tile, read the reason each one is where it is
|
||
before moving it — two of them were tuned by photograph and one says outright
|
||
that it is arbitrary.
|
||
- **The Bay Area desktop frame-time cell was red on all three runs that
|
||
measured this**, at p95 33.3, 33.2 and 33.3 ms against a 16.7 budget. Recorded
|
||
here rather than claimed green, and recorded with everything beside it,
|
||
because the section below asks for exactly that:
|
||
|
||
- geometry is identical to the digit across all three runs — 2,264,928
|
||
triangles, 207 draws of a 320 cap — and unchanged from before this work to
|
||
within 52 triangles (2,264,876);
|
||
- `src/cities/sf.ts` was not touched and nothing in this change is reachable
|
||
from that board's geometry;
|
||
- the Bay Area **mobile** cell renders the same scene, with the same
|
||
per-camera lighting listener attached, at 16.7-16.8 ms on every run — so a
|
||
per-frame CPU regression is ruled out, because it would show on both;
|
||
- `pp_dpm_sclk` read `0: 500Mhz *` of an available 2725 throughout, with
|
||
`gpu_busy_percent` at 0, which is the state the section below names as the
|
||
cause.
|
||
|
||
Three draws from a bimodal metric on a card parked at a sixth of its clock is
|
||
still not a scene defect. It is now three, though, and the honest state of this
|
||
cell is "red on this box, cause identified, not reproduced anywhere else".
|
||
|
||
**Re-measured after the layers were wired, and it is now six.** The final run
|
||
of this round read p95 33.3 ms on all three attempts again, with the geometry
|
||
identical to the digit across every attempt — 2,264,956 triangles and 209
|
||
draws of a 320 cap — and **identical to the pre-round baseline for that board**,
|
||
which is the number that matters: the Bay Area gets no port, no fleet and no
|
||
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 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
|
||
the current altitude, which collapses a chapter flight to a few dozen
|
||
recomputations instead of sixty a second.
|
||
|
||
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.
|
||
|
||
**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
|
||
|
||
- The Klamath knot is four chains at four bearings, which reads as "not a
|
||
north-south range" and does not yet read as the Trinity Alps in particular.
|
||
- The northern Sierra tapers into the Cascade arc through the Diamond Mountains
|
||
and Honey Lake, and from the state pose that corner still reads as Basin and
|
||
Range domes rather than as the end of a range. It is arguably correct — Honey
|
||
Lake really is Basin and Range — but it was not checked against a photograph
|
||
of the real ground.
|
||
- There is **no Sacramento, Redding, Chico or Santa Rosa** on the board. The
|
||
block count is 8,881 against a pack test that caps it at 9,200, and the
|
||
Central Valley's new northern half is farmland with nothing built on it. The
|
||
state capital being absent is the most visible gap in the extension. Adding it
|
||
is a district and about 300 lots; the budget that has to be checked first is
|
||
draw calls on the california mobile cell, not triangles.
|
||
|
||
## The port, the ships and the sky are wired — what that leaves open
|
||
|
||
`main.ts` now supplies all four layer factories and feeds the three that take a
|
||
feed. `ARCHITECTURE.md` §13 is the design; these are the things it does not
|
||
cover.
|
||
|
||
- **A dark navy tower stands in the water off San Pedro, and it is not ours.**
|
||
Roughly 1.5 km south-east of the Terminal Island shoreline, a single
|
||
building-sized block sits on open water. It is **pre-existing**: it is in
|
||
`/tmp/tera-look/socal-check.png`, photographed before the port kit was wired,
|
||
when that waterfront was still a bare tan plate. Nothing in the port kit, the
|
||
vessel layer or this wiring puts it there. What changed is that the new
|
||
Harbour chapter frames it — it was previously only ever seen from the
|
||
whole-board pose, where it is two pixels. It belongs to whatever places
|
||
anonymous blocks on the Southland board, and it wants a land mask.
|
||
- **The radar sheet has never been seen against a real feed.** Both sky
|
||
projections answer 404 on cloud-1 today, so the layer was photographed against
|
||
a synthetic fixture served to `look.mjs --api`. Two things that showed up
|
||
there and want re-checking against a real composite: a 50 dBZ core draws at
|
||
`PRECIP_ALPHA_MAX` 0.92, which is opaque enough to hide the terrain under it
|
||
(correct for a severe cell, and unreachable on a normal California day at
|
||
0.47% wet and 20-35 dBZ, where the same ramp renders as a translucent smudge);
|
||
and `promoteRadar`'s sentence prints `observedAt` as a raw ISO string in the
|
||
middle of an otherwise plain-English line.
|
||
- **`Services` still does not carry the two sky services.** `server/src/app.ts`
|
||
constructs `createRadarService` and `createBirdsService` at the route rather
|
||
than on `Services`. `sources.radar` / `sources.birds` are now on the health
|
||
body — `routes/health.ts` reads them off the config, which is what every other
|
||
line in that route does — but a future route that wants either service cannot
|
||
reach it through `Services` the way every other one can.
|
||
- **The sky is polled by the clock, not by a watcher.** `askSky` fires once per
|
||
board and then only when the body's own `ttlSeconds` has expired, checked on
|
||
the once-a-minute tick that already exists. That is deliberate — a fourth
|
||
polling ladder in `adapters/http.ts` to re-ask a question whose answer changes
|
||
twelve times an hour is machinery bought for nothing — but it does mean the
|
||
two sky feeds have neither `watchFires`'s visibility check nor its failure
|
||
back-off. A feed configured with a 30-second TTL would be asked once a minute,
|
||
and a box answering 500 would be asked again every minute rather than backing
|
||
off. Both are fine at 300 s and 600 s; neither is fine at 5 s.
|
||
- **Every hull on the board is modelled and the AIS licence is still unread.**
|
||
`VesselsSourceId` is `'none' | 'modelled' | 'cloud1'`, the modelled body
|
||
carries "not an observation of any vessel", and `vesselSummary` says the live
|
||
feed is not configured. When `/api/sea` lands, the only change in `main.ts` is
|
||
where `body` comes from — but the aisstream terms decide whether a real
|
||
position may be shown to the public at all, and nobody has read them.
|
||
- **Nothing photographs the sky layers in CI.** `layerSeams.test.ts` asserts the
|
||
wiring from source at both ends, which catches a deleted line; it cannot catch
|
||
a layer that builds and draws nothing. The fixture that proved this one works
|
||
lives in a scratchpad and is not committed.
|
||
|
||
## One command re-shoots the imagery: `npm run refresh`
|
||
|
||
```sh
|
||
npm run refresh # build, stills, cards, films, both manifests
|
||
npm run refresh -- --dry-run # what it would shoot; no browser, no build
|
||
npm run refresh -- --stills-only # …--cards-only, --films-only; they compose
|
||
npm run refresh -- --rough # films at 24 frames, for looking at
|
||
npm run refresh -- --skip-build # you just built; do not build again
|
||
```
|
||
|
||
`scripts/brand-assets/refresh.mjs` exists because re-shooting used to be a
|
||
*procedure* — build first, because the scripts photograph `dist/` and not `src/`;
|
||
then three commands in an order that matters; then check the run actually got the
|
||
GPU. A procedure that lives in somebody's head gets half-run, and the evidence was
|
||
in this file: the stills were re-shot at one commit and the films at another two
|
||
weeks older, and nothing said so until a human compared two files.
|
||
|
||
It prints a diff of the deliverables at the end — new, changed with the size delta,
|
||
byte-identical — and it **fails loudly and specifically** on the three failures
|
||
that otherwise produce confident wrong output:
|
||
|
||
- **SwiftShader.** Software rendering makes the same pictures fifteen times slower
|
||
and reports success. Any child that prints a software renderer stops the run.
|
||
- **A chapter `expect` guard firing.** That means a pack was reordered under the
|
||
shot list, so re-running cannot fix it. `node scripts/brand-assets/shots.mjs
|
||
--list` prints what the shot list believes.
|
||
- **A `play` guard firing.** A shot that takes a body did not get the one it asked
|
||
for — see the section below. Also not a flake, and also not fixed by re-running.
|
||
|
||
Two things it does **not** do, on purpose: it does not commit, and it does not
|
||
re-stamp the manifests afterwards. Commit tera first, then
|
||
`shots.mjs --manifest-only` and `films.mjs --manifest-only`, so the manifests name
|
||
a clean sha rather than a dirty tree.
|
||
|
||
## A shot can take a body now — `play` in `shots.mjs`
|
||
|
||
Three stills are pictures of an agent rather than of a place: `drive-101`
|
||
(chase camera behind the EV on US-101), `crow-nav` (the crow over the Bay Area
|
||
board) and `california-flight` (the aircraft on the corridor). They replace three
|
||
hand-made screenshots in `lumbridge-v4/apps/web/public/arena/`, which were the
|
||
last imagery on the site no script could reproduce — and so also the last that
|
||
nobody could re-shoot when the engine moved. All three still showed a left column
|
||
reading "Explore as your crow →", a "FULL VIEW" badge no visitor to the site ever
|
||
sees, and one daylight frame served to dark-mode readers as well.
|
||
|
||
`play` drives the mode dock, which is the same class of input `aim` is: no back
|
||
door, just the control a reader uses. `#mode-dock` and `#play-hud` stay **in** these
|
||
three frames deliberately — they are what the picture is of — while remaining in
|
||
`FURNITURE.BARE` for the share cards.
|
||
|
||
Three guards, all of them tested by making them fire, because every way this goes
|
||
wrong renders a plausible-looking frame with the right filename:
|
||
|
||
- the dock button must be *offered*, not merely present — the EV and the aircraft
|
||
exist on the California board alone, and driving also needs US-101 or I-5 to be
|
||
the current chapter, so elsewhere the button is `hidden` and clicking it is a
|
||
no-op that leaves an orbit wide shot;
|
||
- `aria-pressed` **and** `#play-hud`, exactly as `performance-budget.mjs` does —
|
||
`transitionControlMode` substitutes a fallback for a request it will not honour;
|
||
- the HUD's own chip names the body ("Drive", "Crow", "Flight"), checked at the
|
||
click *and again at the shutter*, because a route that completes or a bound that
|
||
is hit ends the mode mid-wait.
|
||
|
||
`refresh.mjs` surfaces all three as their own headline, next to SwiftShader and the
|
||
chapter guard.
|
||
|
||
**`untilProgress`, not a duration, for anything that was already moving.** The crow
|
||
and the aircraft are built inert, so *n* ms after the dock hands them over is the
|
||
same place every time — measured identical at 1x and 2x. Road traffic is not: it
|
||
runs from page load, so a duration is really "boot plus a bit", and the same
|
||
`run: 9000` put the EV at 55% of the leg in a 1x preview and 69% in the 2x render,
|
||
which is a different stretch of road with the edge of a town across the top of the
|
||
frame. `untilProgress` waits on the app's own route percentage instead, so preview
|
||
and delivery agree. Overshoot throws rather than shooting.
|
||
|
||
### ~~`drive-101`'s night frame is the weak one~~ — fixed in the engine, 2026-08-22
|
||
|
||
This section used to say the frame's subject nearly disappeared, that the hour was
|
||
not the lever, and that both candidate fixes were in the engine: *give the EV a
|
||
headlamp spill on the road ahead*, or let the corridor's terrain keep some
|
||
moonlight. **The first one landed.** The EV now throws a low beam on the road in
|
||
front of it, the corridor carries continuous edge lines, a yellow median pair and
|
||
cat's eyes, and the frame was re-shot at the same camera and the same hour.
|
||
Canvas-region mean luminance, 1600w, before → after: **7.8 → 10.2** overall, and
|
||
**5.5 → 11.7** on a 580×470 crop around the car and the road ahead of it. It reads
|
||
as a car with its lights on rather than as two tail lamps.
|
||
|
||
**What is still true, and is a capture limit rather than a light one.** The EV is
|
||
about 55 px wide in a 1440 px frame and always will be from this camera:
|
||
`applyFollow` in `roadTraffic.ts` puts the chase camera 0.72 back and 0.92 up with
|
||
its target 0.32 ahead, which is a fixed 38° looking-down pose, so the road fills
|
||
the picture and there is no horizon in it. The stretch of road is the only lever
|
||
the shot list has, and it was swept: `untilProgress` 0.64 puts the car on a bend
|
||
but loses the far carriageway, 0.72 lands it at an off-ramp with a settlement's
|
||
buildings — drawn at 1,919 m to the unit — across the right of the frame, and 0.82
|
||
is a wider carriageway with the car smaller again. **0.55 is still the best of
|
||
them** and stays. If the car is ever wanted larger, that is a change to the chase
|
||
offsets in `roadTraffic.ts`, not to `shots.mjs`.
|
||
|
||
The crow moved to the Bay Area board for the same reason and it worked: over a city
|
||
the night frame is the *better* of its pair. The hand-made original was on the state
|
||
board, where a one-metre bird stands in front of ground drawn at 1,919 m to the unit
|
||
with an oak the size of a hill behind it.
|
||
|
||
### ~~`bay-bridge` was aimed at the wrong thing~~ — re-aimed, 2026-08-22
|
||
|
||
The old frame ran the crossing diagonally out of the top-right corner, left the
|
||
entire right half of the picture as open water, and contained **only one of the two
|
||
shores the bridge joins** — so its own `note` had to say so, and its caption
|
||
described a wide bay view rather than the bridge the id promises.
|
||
|
||
It now seeks to 37.8085, −122.363 (the causeway just east of Yerba Buena, which is
|
||
the midpoint of *what has to fit* rather than of the structure, because the panel
|
||
eats the left 310 px) at `zoom: -4`, and drags `azimuth: -45` to turn the chapter's
|
||
almost-due-north stance broadside to a crossing that runs ENE at about 57°.
|
||
`elevation: -6` drops the camera far enough to put Marin, Angel Island and the
|
||
Golden Gate along the top, which is what stops the water reading as empty. Both
|
||
landings are inside the frame, with San Francisco's skyline at one end and the East
|
||
Bay shore at the other, and the whole 2013 topology — two suspension towers, the
|
||
deck coming down onto the island, the third tower, the piered causeway — is legible
|
||
across the middle. Caption, both `note`s and both `alt`s were rewritten to the frame
|
||
that is actually delivered.
|
||
|
||
Night, 1600w: canvas-region mean **8.3 → 16.7**, and on a 420×110 strip lying on the
|
||
causeway — the half of the crossing the old frame did not contain at all — peak
|
||
**18.7 → 134.9**. The night frame no longer needs a lift before anything appears,
|
||
which is what this file used to record about it.
|
||
|
||
### ~~The Golden Gate needs an earlier hour than everything else~~ — retired
|
||
|
||
`golden-gate`'s night frame was shot at 20:50 (sun −7.5°, nautical twilight) with a
|
||
`note` explaining the exception: the other night frames are cities carried by their
|
||
own windows, and a bridge over open water had none, so 21:35 rendered this one as a
|
||
black rectangle. **The deck now carries its own lamps**, so it is back on the
|
||
standard 21:35 with everything else and the special case is gone from `shots.mjs`.
|
||
|
||
Worth knowing before anyone "fixes" the number: the canvas-region mean went **8.2 →
|
||
7.0** at the deeper hour, because losing the residual sky costs more mean luminance
|
||
than 1,600 lamps over open water add back. **Measure the subject, not the canvas,
|
||
on a bridge frame.** On the deck the lamp run, both tower silhouettes and the red
|
||
light over each tower head all read clearly at 21:35, where at 20:50 there was no
|
||
light on the structure anywhere.
|
||
|
||
## The imagery was re-shot on 2026-08-23 — what that run found
|
||
|
||
`npm run refresh` at `e7a8aa0` + this round's uncommitted work: **21 shots x 2
|
||
frames**, both share cards, all four films, both manifests. Every run printed
|
||
`ANGLE (AMD, Vulkan … Radeon RX 6700 XT (RADV NAVI22))` and not SwiftShader; no
|
||
chapter guard and no `play` guard fired. Timings on this box, for budgeting:
|
||
build 7 s, stills 545 s, cards 26 s, films 416 s.
|
||
|
||
**Both manifests are dirty and that is the one thing still owed.**
|
||
`SHOTS_COMMIT` and `FILMS_COMMIT` are `e7a8aa0` with `SHOTS_DIRTY` /
|
||
`FILMS_DIRTY` **true**, because the tree was not committed when the shutter
|
||
opened — the port, vessel and night-light work of this round is in every frame
|
||
and in no commit. That is the manifests being honest rather than naming a sha
|
||
that would render something else. Once tera is committed:
|
||
|
||
```sh
|
||
node scripts/brand-assets/shots.mjs --manifest-only # no browser, no render
|
||
node scripts/brand-assets/films.mjs --manifest-only
|
||
```
|
||
|
||
then commit lumbridge-v4 and deploy it.
|
||
|
||
### Two new shots, and one of them needs a page
|
||
|
||
- **`harbour`** — socal chapter 5, aimed at 33.7405/-118.2565 broadside across
|
||
Terminal Island. Four crane rails at four bearings, the container yards, hulls
|
||
alongside and one under way, the Vincent Thomas across the Main Channel, and at
|
||
night a red obstruction light on every gantry apex.
|
||
- **`california-fire`** — the state board with the fire feed behind it. See the
|
||
section below; it is the only frame here that is not the keyless experience.
|
||
|
||
Both are in `shots.ts` and both are delivered as WebP. **Neither is on a page.**
|
||
Every one of the other nineteen ids is named explicitly by something in
|
||
`lumbridge-v4/apps/web/src`, so a new shot appears in the manifest and nowhere
|
||
else until somebody places it. That is a handoff, not a defect.
|
||
|
||
### The fire feed is photographable now, from a fixture
|
||
|
||
`drawsFire()` gates the whole layer on `access.feeds?.fires === true`, which
|
||
comes from `/health`. A keyless capture — which is every other frame here — can
|
||
therefore not photograph fire at all, while `tera.lumbridgecorp.com` answers
|
||
`"fires":"cloud1"` and every anonymous visitor to the deployed site does get it.
|
||
The site was about to ship an imagery set in which the most visible thing the
|
||
engine gained this round did not exist.
|
||
|
||
`shots.mjs` now has `serveFeed()` and a per-shot `feed` field. It fulfils exactly
|
||
two routes and 404s the rest of `/api/v1`, and the body is
|
||
`scripts/brand-assets/fires-2026-08-23.json` — the verbatim response from the
|
||
live feed on 23 August 2026, kept whole. **Do not trim that file.** `promote()`
|
||
counts the rows it refuses and the panel prints that number, so a fixture with
|
||
the refused rows deleted would draw the same eight fires under a sentence
|
||
claiming a gate that never ran.
|
||
|
||
Three consequences worth knowing before extending this:
|
||
|
||
- the synthetic `/health` says `auth.mode: "none"`, which resolves the visitor to
|
||
`member` and keeps **`#tier-adds`** out of the frame. That card is the sign-in
|
||
offer, it is shown only to `anon`, and it is **not** in `FURNITURE.CLUTTER` —
|
||
so the day this pipeline meets a real API it will arrive in the art silently,
|
||
exactly the way `#mode-dock` did;
|
||
- `california-fire` is the only shot clocked to **2026-08-23** rather than
|
||
2026-08-06, because the panel prints the body's own `fetchedAt` and how long
|
||
ago that was. A fixture from the 23rd photographed on the 6th reads "fetched
|
||
2026-08-23 · just now";
|
||
- the hot-pixel layer draws every FIRMS detection inside `bounds`, and `bounds`
|
||
is a lat/lng **rectangle**. On the state board that puts a handful of cyan
|
||
points over open water and over Baja in the night frame. They are real
|
||
detections and the rectangle is the honest gate the layer has; it is worth
|
||
deciding whether a board wants a land test before the count in the panel is
|
||
ever quoted as a California number.
|
||
|
||
### The marine layer decided two of the day frames, and it should decide any more
|
||
|
||
`atmosphere.ts` models the Pacific marine layer on **apparent solar** time, and
|
||
the curve is 0.95 before dawn, 0.6 at nine, 0.2 at eleven, 0.4 again by four. Two
|
||
shots were on 09:10 and 09:20 — a little after eight solar, where the layer is at
|
||
about three quarters — and at a whole-board or 32 km standoff that is not weather
|
||
in the picture, it is a white lid over the entire subject. `bay-relief`'s caption
|
||
is about eighty-three named hills and four octaves of value noise, none of which
|
||
was visible; `peninsula`'s is about twenty miles of city, likewise.
|
||
|
||
Both moved to the late afternoon (17:00 and 17:10) and both frames are now
|
||
legible. The compromise is forced: the layer is thinnest at solar 11-14, which is
|
||
**12:07-15:07 on a wristwatch here** — the exact hours a heightfield has no
|
||
shadow. Around five is thin enough to see the ground and low enough that the
|
||
ranges still throw.
|
||
|
||
**Any future wide day frame on the Bay board has this constraint.** Close and low
|
||
poses do not — `fidi` at 10:10 and `crow-nav` at 09:20 are both clear, because
|
||
the camera is under the deck.
|
||
|
||
⚠️ **A 1x `--preview` does not predict the cloud in the 2x delivery.** The deck
|
||
animates on elapsed time and the two runs have different settles (3000 ms against
|
||
6000), so the puffs land somewhere else. Framing, pose and light all preview
|
||
faithfully; cloud does not. Judge cloud on the delivered frame, which is how
|
||
`peninsula`'s note lost a sentence about SFO that was true of the preview and not
|
||
of the render.
|
||
|
||
### Four captions had been falsified by engine changes, and nothing catches that
|
||
|
||
A caption is prose next to a camera pose. When the engine moves under it, the
|
||
frame still renders, still looks deliberate, and still ships. All four of these
|
||
were found by opening the delivered frame and reading its own note back:
|
||
|
||
- **`california-flight` day** ended "and the Pacific past the wingtip". Aerial
|
||
perspective now follows camera altitude, so at 1,413 m the ocean is inside the
|
||
haze rather than beyond it and there is no sea in the frame at all.
|
||
- **`california-flight` night** promised "the basin's lit ground in the corner"
|
||
and "the moon's path broken across the water". The frame is over dark coastal
|
||
mountains: no basin, no water. What actually carries it is the aircraft's own
|
||
navigation lights and the corridor's road markings.
|
||
- **`lax` day** said "the traffic overhead is the real traffic: registration and
|
||
type come off the same feed". **It cannot be.** Every frame but
|
||
`california-fire` is shot keyless, so `TrafficSource` is the bundled simulator
|
||
and not ADS-B. A caption may not borrow a fact from a deployment the shutter
|
||
never opened on — and this is the failure mode to watch for now that one shot
|
||
*does* have a server.
|
||
- **`golden-gate` day** said the towers "throw along the deck". They throw across
|
||
the water beside it, which is checkable and was checked.
|
||
|
||
### `fidi-day`'s day poster had drifted onto the wrong renderer
|
||
|
||
`poster: 0.86` was chosen as "a few minutes before sunset: the last of the low
|
||
sun down the length of Montgomery". On ACES with the world-space sky dome, 0.86
|
||
is 20:09 with the sun at **+0.4°** and the towers are unlit silhouettes — the
|
||
frame measured mean luminance **33** out of 255 against the night poster's 21.
|
||
That is not a pair, and it matters more than a poster usually would: `Film` ships
|
||
`preload="none"`, so the poster is what most readers see for most of the time the
|
||
film is on their screen. Now `0.76` — twenty past six, sun twenty degrees up,
|
||
towers lit and throwing, mean **87**.
|
||
|
||
The other three films' posters were checked the same way and are fine
|
||
(147/24, 106/21, 155/46 day against night). **Check a poster pair by measuring
|
||
both, not by reading the clock**: the fractions were all chosen on an older
|
||
renderer and this is the one that had moved.
|
||
|
||
### Smaller things the re-shoot turned up and did not fix
|
||
|
||
- **`freeway` and `drive-101` are nearly the same photograph.** Both are the
|
||
chase camera on the 101 chapter at 09:00; the only difference is where on the
|
||
leg they land — 47% against 57%. `freeway` gets there with `settle: 3000`,
|
||
which is the duration-not-position trap `drive-101` exists to document. It has
|
||
landed on 47% on two consecutive runs, so it is reproducible in practice on
|
||
this box, but it is a duration and it is measured from boot. It cannot simply
|
||
be given a `play` block: the 101 chapter is *already* in drive mode, and
|
||
`enterPlay` clicks the dock button, which would toggle it off.
|
||
- **`office-desks` slices the hardware card.** The panel is taller than the
|
||
viewport, so `Desk speaker`'s controls are below the fold and its header sits
|
||
on the frame edge. That is the exact condition `shot.hide` was invented for,
|
||
and it cannot be used here because the hardware *is* the subject.
|
||
- **`#sky-section` stays in the product stills, deliberately.** It prints "No
|
||
radar feed is configured … that is a fact about this box, not about the sky"
|
||
twice, which reads like a hole — but `tera.lumbridgecorp.com` answers
|
||
`"radar":"none"` and `"birds":"none"` today, so a real visitor sees the same two
|
||
sentences. It is not the `#tier` case (local disagreeing with deployed); it is
|
||
the product's own voice. **If either feed is ever wired, this decision has to be
|
||
taken again**, because then the local frame and the site would disagree.
|
||
- **`#sea-section` stays too**, and it is the one that earns it: the harbour is
|
||
modelled identically here and on the deployed site, and its sentence is what
|
||
keeps a frame full of ships honest about their being modelled rather than
|
||
observed.
|
||
- **`PROVENANCE.json`'s two card records now name a date they were not made on.**
|
||
`refresh.mjs` re-stamps the share cards narrowly — it substitutes the `sha256`
|
||
in the text and nothing else, which is what keeps that diff two lines instead
|
||
of a reflow — so `intakeDate` still reads `2026-08-22` for files written on the
|
||
23rd. `check-provenance` only validates the format, so nothing catches it. It
|
||
is one line in the one document whose entire job is accuracy.
|
||
|
||
## The vessel layer has two gaps that belong to other files
|
||
|
||
Both stated in code comments already; recorded here so they are not only there.
|
||
|
||
- **Pier 300 and the East Basin take no port calls.** The straight run from the
|
||
nearest point on `Port.channel` to those berths is 1.9 km and 2.9 km and
|
||
crosses Terminal Island, so `BERTH_APPROACH_REACH_METRES` (1100) gates them out
|
||
and they keep a static hull alongside rather than driving a container ship over
|
||
a container yard. The proper fix is an optional per-berth approach path on
|
||
`Berth` — `approach?: LatLng[]`, water-side first — which `socal.ts` could then
|
||
author for Pier 300 as the real route out through Angels Gate and north-east
|
||
across the outer harbour. `harbourCalls` would prefer it to the nearest-point
|
||
projection; about ten lines once the field exists. The field is on
|
||
`src/engine/types.ts`, which the vessel work did not own.
|
||
- **The tug's own solid is correct, cheap, and sub-pixel at every SoCal pose we
|
||
ship.** Its payoff is banked for the Bay board and for closer poses rather than
|
||
realised today.
|
||
- `engine/nightlights.ts` now imports `craneLights` from `engine/ports.ts`. No
|
||
cycle — `ports.ts` imports only three, terrain, types and world — but the two
|
||
files are coupled and want moving, and re-photographing, together.
|
||
|
||
## The GPU on this box never leaves 500 MHz, and every frame-time number here is a floor
|
||
|
||
**This is a machine fault, not a scene fault, and it invalidates frame-time
|
||
measurement on amd-server until it is fixed.**
|
||
|
||
Traced 2026-08-23: `/sys/class/drm/card*/device/pp_dpm_sclk` sampled every two
|
||
seconds through eighty seconds of sustained rendering — 2,264,956 triangles at
|
||
1440x900 — reports `0: 500Mhz *` on all forty samples. It never ramps.
|
||
`power_dpm_force_performance_level` is `auto`. The card's own ceiling is
|
||
**2725 MHz core and 1000 MHz memory**, so it is running at roughly 18% of core
|
||
and as low as 96 MHz of VRAM clock.
|
||
|
||
The consequence: `bay-area/desktop` is fragment-bound at exactly this clock and
|
||
sits on the vsync deadline. Same build, same geometry, viewport swept —
|
||
|
||
| viewport | pixels | p95 |
|
||
|---|---|---|
|
||
| 1440x900 | 1.296 MP | **33.3** |
|
||
| 1280x800 | 1.024 MP | 16.7 |
|
||
| 1152x720 | 0.829 MP | 16.8 |
|
||
| 1024x640 | 0.655 MP | 16.7 |
|
||
|
||
The cell is ~27% past what this board sustains at 500 MHz. So a build that adds a
|
||
trivial amount of fragment work flips it from 16.8 to 33.3 while its geometry is
|
||
identical to the digit, and a build that removes some flips it back. **That is
|
||
what makes single-run frame-time comparisons on this box worthless**, and it is
|
||
how two separate investigations here reached two different wrong conclusions.
|
||
|
||
**Do not "fix" this by raising a cap.** Geometry is the gate — it is deterministic
|
||
and identical across runs. Frame time is advisory until the card clocks properly.
|
||
|
||
**Two ways this diagnosis was reached wrongly first, both worth remembering.**
|
||
One investigation blamed the desktop shadow map: disproved, because 4096, 2048,
|
||
1024 and 256 all render the board in 1.21-1.31 ms. Another blamed a camera
|
||
`change` listener rebuilding the PMREM environment: disproved three ways — every
|
||
field `environmentKey()` fingerprints is bit-identical from 500 m to 67,600 m of
|
||
camera altitude; 105 camera-lighting applies produced **0** rebuilds; and the
|
||
budget harness runs under `reducedMotion: "reduce"`, so its camera is
|
||
**stationary** and fires no `change` events at all during the sample window.
|
||
Both wrong conclusions came from comparing a single run against a single run.
|
||
|
||
To settle it properly: `power_dpm_force_performance_level` needs root. Forcing it
|
||
to `high` and re-running the matrix would give the first trustworthy frame-time
|
||
numbers this project has had.
|
||
|
||
## The aeroplane glyph is still larger than the Golden Gate
|
||
|
||
`GLYPH_MAX_SCALE` in `src/engine/flights.ts` is 52 and that is a mitigation, not
|
||
a cure. The glyph has a screen-space *floor* — never smaller than legible — which
|
||
scales by the distance to the **aircraft**, when what makes it look wrong is how
|
||
far the camera is from **what it is looking at**. At a whole-board pose those are
|
||
the same thing; at the Golden Gate chapter the bridge is two units from the camera
|
||
and the traffic is two thousand, so the floor fires hard on the aeroplane and not
|
||
at all on the bridge.
|
||
|
||
A ceiling of 52 was chosen because the raw scale at 1,160 units — the far end of
|
||
the orbit over the California corridor — is 51.0 at a 60-degree field of view, so
|
||
anything lower shrinks aeroplanes at a pose people actually use. (26 was tried
|
||
first and put the glyph at 0.0123 of the frame, against the 0.012 at which
|
||
`flights.ts` says the wings stop resolving.) 52 takes the worst case from about
|
||
two and a half times the bridge's main span down to about one and a half.
|
||
|
||
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.
|
||
|
||
## One California is descendable now, and vertical exaggeration is what stops it
|
||
|
||
Recorded 2026-08-24, at `e5b9339`, with the photographs that found it. That
|
||
commit removed the three things keeping the camera off the merged board — the
|
||
orbit floor, the missing metro rungs and the board-switching doors — and what
|
||
is left is one cause with two faces.
|
||
|
||
**`verticalExaggeration` is a per-board constant and this board is not one
|
||
scale.** `reconciledExaggeration` derives it so the tallest blended peak fills
|
||
`RELIEF_IN_FRAME` of the board span: 15.00 for California against a 5,132 m
|
||
blended peak, 5.78 for the Bay Area, 3.41 for the Southland. All three are
|
||
correct *for framing that board whole*, and 15 is what makes the Sierra readable
|
||
at 1,551 km. The merged board is the first one a visitor can traverse from
|
||
1,551 km down to 7.7 km, and the constant that frames the state buries the city:
|
||
|
||
- **At FiDi (7.7 km) the camera is inside the terrain.** San Francisco's relief
|
||
is drawn 2.6x taller here than on its own board — Twin Peaks' 280 m becomes
|
||
4.2 km of drawn height, which is 2.2 units on a board where the whole descent
|
||
is about 2. The photograph is `/tmp/tera-look/one-fidi.png`: a grey wall, a
|
||
Bay Bridge overhead, and buildings as needles because `world.metres` runs
|
||
building heights through the same exaggeration.
|
||
- **At the Bay rung (45 km) the ground is visibly faceted.** 300 m cells
|
||
(`UNIFIED_FINE_METRES`) at 15x turn every hill into angular shards. The cell
|
||
size is the known gap; the exaggeration is what makes it *visible*, because
|
||
vertical error scales with it and horizontal error does not.
|
||
|
||
So exaggeration has to fall toward 1 as the camera descends, which is what an
|
||
orthophoto globe does and why none of them exaggerate at all. **It is not a
|
||
constant this file can simply lower**: 15 is load-bearing at the state pose, and
|
||
the Sierra at 5.78 is a bruise on a plate.
|
||
|
||
The blocker is the one `TODO.md` already names for the terrain quadtree, and it
|
||
is the same blocker: **everything is placed once at scene build against
|
||
`groundAt`.** Buildings, landmarks, bridges, airports, ports, markers and road
|
||
ribbons all read a ground height at build time, so an exaggeration that changes
|
||
with altitude moves the ground out from under every one of them. Whatever
|
||
carries the exaggeration ramp has to carry re-placement with it, and that is the
|
||
same machinery a streaming terrain needs. They should be built together or the
|
||
second one rewrites the first.
|
||
|
||
Two smaller things the same photographs turned up, neither of them blocking:
|
||
|
||
- **Building heights ride the board's exaggeration.** `world.metres(heightM)` is
|
||
`(m / metresPerUnit) * verticalExaggeration`, so a 60 m building on the merged
|
||
board is drawn at 15x while the same building on the Bay board is at 5.78x.
|
||
On a 160 m lot that is a needle. Relief and structures arguably want different
|
||
treatment, and nothing has ever had to decide because no board carried both at
|
||
two scales.
|
||
- **The metro lots are still 160 m** (`DETAIL_LOT_METRES`), chosen against the
|
||
Southland's own lot size and explicitly not San Francisco's 40 m because the
|
||
board has to fit one budget. At 45 km that reads as scattered cubes rather
|
||
than as a city. A second, nearer detail tier at 40 m — bounded to the
|
||
districts actually inside a much tighter reach than `DETAIL_REACH_M` — is the
|
||
shape of the fix, and it must be measured rather than assumed: the Bay board's
|
||
83,137 lots are ~1M triangles against this board's whole 400,000 cap, so the
|
||
tier is affordable only because the reach is small. Do not start it before the
|
||
exaggeration ramp, or it will be tuned against relief that is about to change.
|