1
0
This repository has been archived on 2026-08-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
tera/TODO.md
T
karti 4bd8481be1 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>
2026-08-23 06:20:18 -07:00

44 KiB
Raw Blame History

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.5042.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

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 notes and both alts 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:

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 Berthapproach?: 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-boardglyph-northglyph-bayglyph-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.