feat: the state becomes California, and the port fills with ships
**Stage 1 of one California.** The owner's complaint had two halves and this is
the first: the state board was a CROPPED SLAB. `california.ts` stopped at 38.05 N,
so the board disagreed with its own minimap about the shape of California in a
single frame, and Bug Fire's 93,733 acres burned off-frame while the panel said
all clear. Bounds now run 32.50-42.05 N / -124.50 to -114.0 W — Cape Mendocino,
the ruled Oregon parallel, the 120th-meridian corner into the Nevada diagonal.
**And it got cheaper.** 391,169 triangles to 375,351, while gaining the North
Coast, the Sacramento Valley, the Klamath knot, the Cascade arc, Shasta at 4,320 m
and Lassen at 3,190 m. Extending the bounds alone would have doubled the lattice
to 168,813 points and blown the mobile cap; coarsening cellLat 0.022 -> 0.0312 and
cellLng 0.027 -> 0.0383 holds it at ~83,800. The cell as a FRACTION of the board
moves 0.0030 -> 0.0033 — unchanged in frame — because the camera retreats to frame
whatever it is given. That argument was already written in the pack's own comment.
The second half — three boards becoming one world you zoom through — is NOT here.
Merging at Bay density would be 34.04M triangles, 13x the highest budget, and
merging at SoCal density would downgrade San Francisco from 40 m lots to 164 m.
Both delete the board every marketing still is shot from. `sf.ts` and `socal.ts`
are untouched by design.
**Aerial perspective, which the state board could not have had before.** The old
fog started at 1.15 board spans = 944 km, on a board whose longest diagonal is
820 km — so no pixel could ever be fogged. Fog now responds to camera altitude,
clamped to the authored pair as a ceiling.
`Atmosphere.aerial(env, view)` is a second pure method returning `{ near, far }`
and **deliberately no colour**. That is structural, not stylistic: it is why a
future camera-dependent term cannot reach `environmentKey()`'s colour fingerprint
and start rebuilding the PMREM cubemap on every camera step. Coarsening the
fingerprint instead would have hidden one instance and armed the mechanism. A
mutation-tested seam guard fails if anyone merges the two paths back together.
**The port.** Terminal Island rendered as a bare tan polygon with generic white
blocks while the chapter text called it the busiest port complex in the
hemisphere. Now six container yards drawn as canvas atlases, 56 gantry cranes at
varied boom angles, the 13 km San Pedro breakwater, the dredged channel. Five
buckets merging ACROSS ports the way airports.ts merges across fields, so a
second complex costs no extra draws: +11 draws and +4,377 triangles for all of it.
At vertical exaggeration 3.4 a 130 m gantry is 1.132 units tall against a 400 m
ship's 1.024 long — the crane is the taller object, and it is what makes a port
read as a port from altitude.
**Ships, and the wake carries the information.** Moored hulls have no foam,
verified at three terminals; a tug under way in the Main Channel trails a clean
Kelvin V. One hull geometry, one InstancedMesh, orientation from the BERTH rather
than the wire. The AIS gate strips sog 102.3, heading 511 and cog 360 — all mean
"not available" — with an explicit test that cog 358.7 SURVIVES, because a naive
range check on cog eats real headings near north.
"Empty or full" is not in AIS position reports and is not invented per ship. The
honest answer is at port level and is a better story: 348,691 of 460,467 boxes
left Los Angeles empty in July 2026, corroborated by FBX01 $7,491 inbound against
FBX02 $347 outbound.
**Radar and birds ship dark, and say why.** California is 0.47% wet and migration
is nocturnal and seasonal, so both layers have nothing to say on most days. The
panel reads "No radar feed is configured, so this board draws no weather. That is
a fact about this box, not about the sky."
Also recorded, and it matters beyond this commit: **the GPU on amd-server never
leaves 500 MHz of a possible 2725**, traced across 80 seconds of sustained load.
`bay-area/desktop` is fragment-bound at that clock and sits on the vsync deadline,
so a trivial change in fragment work flips it between 16.8 and 33.3 with geometry
identical to the digit. Every frame-time number measured on this box is a floor.
Two investigations reached two different wrong conclusions from single-run
comparisons before this was traced. Geometry is the gate; frame time is advisory.
No cap was raised.
Tests 1,340 -> 1,540, server 280 -> 295.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,137 @@
|
||||
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.** Every
|
||||
`city: "california"` shot in `scripts/brand-assets/shots.mjs` was photographed
|
||||
at 428 units across with a ruled crop along 38.05 N. Re-shoot with
|
||||
`npm run refresh`. The `chapter` indices are all still correct — the north
|
||||
chapter was added at the **end**, index 5, precisely so nothing re-points — but
|
||||
`california-relief`'s caption says "428 units across at 1,919 metres to the
|
||||
unit" and that number is now 554. The caption is the only edit needed.
|
||||
- **`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 one seam is still coarse
|
||||
|
||||
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. It is not visible on the boards that exist —
|
||||
the follow cameras sit low and stay there — but a continuous state-to-city
|
||||
descent would need this on the frame loop rather than on an event.
|
||||
|
||||
### 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
|
||||
@@ -196,28 +327,50 @@ Worth knowing before starting (the rest is in `~/.claude/skills/tera-capture`):
|
||||
going — or give one of them `--site`-style isolation first.
|
||||
- Commit tera first, then re-run, so the manifest records a clean sha.
|
||||
|
||||
## The Bay Area frame drop was the GPU, not the scene — closed
|
||||
## The GPU on this box never leaves 500 MHz, and every frame-time number here is a floor
|
||||
|
||||
Struck 2026-08-22. This section used to record a p95 of 33.3 ms on `bay-area`
|
||||
desktop as a real defect and carried a 33.4 ms allowance in
|
||||
`scripts/performance-budgets.json`. **Both were wrong, and the way they were
|
||||
wrong is the lesson.**
|
||||
**This is a machine fault, not a scene fault, and it invalidates frame-time
|
||||
measurement on amd-server until it is fixed.**
|
||||
|
||||
The shadow-map hypothesis is measurably false: 4096, 2048, 1024 and 256 all
|
||||
render the board in 1.21-1.31 ms. And the frame-time metric is not reproducible
|
||||
on this box at single-run granularity — two consecutive runs over a
|
||||
byte-identical `dist` gave 33.4 then 16.7 on the same cell, with triangle and
|
||||
draw counts identical to the digit. The card was sitting at **500 MHz of a
|
||||
possible 2725** through every run that reproduced the drop.
|
||||
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.
|
||||
|
||||
So it was GPU power management, and the "it reproduces" conclusion rested on two
|
||||
samples. Two draws from a bimodal metric is not reproduction. The allowance is
|
||||
removed and the cell is back to 16.7.
|
||||
The consequence: `bay-area/desktop` is fragment-bound at exactly this clock and
|
||||
sits on the vsync deadline. Same build, same geometry, viewport swept —
|
||||
|
||||
**Geometry is the gate here; frame time is advisory.** Triangle and draw counts
|
||||
are deterministic and identical across runs. Treat a single red p95 as noise
|
||||
until it reproduces across several runs, and record the GPU clock state next to
|
||||
the renderer string so the next person sees what this one did not.
|
||||
| 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user