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 b25f217e3e feat: real fire on the boards, the LA office as a twin, and a night sky worth reading
The world stops being a simulation of California and starts being California.

**THE PROMOTION GATE WAS THE FIRST COMMIT, BEFORE ANY ORANGE PIXEL EXISTED.**
On today's live store the SoCal board contains 22 incidents. Every one has NULL
acreage and fifteen are nameless LA County dispatch numbers. Drawn naively that
is 22 orange marks over Los Angeles on a day nothing is burning — in a frame that
contains no other warm colour, so one glyph would be the most salient object on
the board and twenty-two would spend its credibility permanently.

`acres >= 10 AND contained < 80 AND type != 'RX' AND last_seen = max(last_seen)`
returns 0 on SoCal, exactly 5 on California, 0 on the Bay — same body, same day,
three correct answers. The empty board is a deliverable, not a fallback: it says
"No active fire on this board — CAL FIRE and WFIGS, just now", states that 21
records were gated and why, lists the largest fires burning OUTSIDE the frame
with distances, and counts the hot pixels it is deliberately not drawing.

**The privacy leak is structurally impossible rather than carefully avoided.**
cloud-1 serves a projection; the four home-relative columns never leave that box.
`observations.threat` was the one that nearly got through — it is
`(16/distance)^2 x log10(acres) x momentum x containment x wind-alignment`, so
with acreage and containment public it inverts to a distance circle around a
house and three fires give an intersection. A grep of the built bundle for
distance_km, bearing_deg, threat, 7762 and the street name returns nothing.

**Deliberately not used, and both would have produced a confident wrong answer:**
the store's `air` table retains only the last parameter of each poll, so all 78
rows read "Good" while the live feed reports ozone 101 "Unhealthy for Sensitive
Groups" — haze driven off it would clear the sky during a smoke event. And
`weather` is written only inside the NWS alerts loop, so a quiet day stores no
wind at all. Tera's own per-region NWS wind is already correct and already what
the clouds drift on.

Satellite detections are drawn as evidence and never as incidents. The permanent
industrial heat source 4.7 km from the owner's house is flagged persistent and
dropped, asserted by a test that first proves it is present in the fixture.
MODIS integer confidence and VIIRS string confidence are branched on `sat`.

**The LA office is a twin.** Its entire authored second storey — Model Loft,
Model Bay, The Materials Room, 430 lines nobody had ever stood in — is reachable
on foot: a walker crosses level-1 to level-2 in 73 fixed steps, floorY 0 to 5,
verified against the real pack rather than a synthetic plan. Its two studio
devices read real hardware through a field-allowlisted bridge: mute, volume and
reachability only. Never level, because there is no passive level upstream and
obtaining one would record a room with people in it. Never dB, because upstream
is gainPct across four different native scales. The bridge refuses all writes.

Fixed at its root: an anonymous visitor was getting permanently at-rest
instruments backing off against a 401. The tier moves into `createDeviceSource`,
so anon gets the living simulator three file headers already promised.

**Item 8 is closed, not fixed, and the correction is the point.** The Bay Area
"stutter" was GPU power management — the card sat at 500 MHz of 2725 through
every run that reproduced it, 4096/2048/1024/256 shadow maps all render in
1.21-1.31 ms, and two consecutive runs over a byte-identical dist gave 33.4 then
16.7. The allowance is removed and the cell is back to 16.7. Geometry is the
gate; frame time is advisory.

Item 7 was re-scoped after measuring: 1,069,006 of the Bay Area's 2,265,056
triangles were the second submission of the same buildings into the shadow pass.
Mobile now has its own triangle caps and bay-area mobile draws 1,266,096.

Also: bridges and the freeway corridor light up at night as emission, not lights
— 1,614 deck lamps and 18 tower heads on the Bay in two draw calls. The single
change that made US-101 legible was moving its edge lines from the lit material
to the unlit one: retroreflective paint, the argument the SFO night frame already
makes. California went 21,991 lamps to 4,051, clustered at the 17 town districts,
because a rural interurban corridor genuinely is unlit.

Tests 1137 -> 1340, server 280. All ten budget cells pass on first attempt with
no cap raised.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 18:01:11 -07:00

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

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.

Re-shoot the time-lapse films

All 38 product stills were re-shot on 2026-08-22 against the night-infrastructure work, so the manifest records SHOTS_COMMIT b7f5c41 with SHOTS_DIRTY true — that tree was not committed when the shutter opened, and the manifest says so rather than naming a commit that would render something else. Re-run shots.mjs --manifest-only once it is committed. The two share cards were not re-shot and did not need to be: capture.mjs shoots both at 11:20 and 17:30, so nothing that changed after dark is in them. The films were not — they were last shot on 2026-08-07 at 9c9e78f. films.ts in lumbridge-v4 records the tera commit each reel was shot at, so a stale reel is visually stale and no caption edit fixes it — check that commit against tera HEAD before assuming a reel is current.

What the reels predate: ACES tone mapping and the runtime PMREM environment rig; a sea that reflects, with a swell normal map, Fresnel and sun glitter, in place of a flat MeshLambertMaterial that had no specular term at all; a world-space sky dome replacing the screen-space gradient, which at dusk painted the sunset band along the bottom of the picture, under the board; terrain that casts shadows; a California board with the Basin and Range, Death Valley, the Salton Sea and San Diego on it; SFO, LAX, both bridges and a freeway that reads as a road; aircraft that move, carrying registration and type; a rebuilt LA studio; the studio device hardware; and a new UI chrome with a first-run flow.

npm run refresh -- --films-only --rough   # rough cut first: ~20 s a reel, and it is
                                          # the cheapest way to find out a camera is
                                          # pointed at the wrong thing
npm run refresh -- --films-only           # ~90 s per 180-frame film on the Radeon

Worth knowing before starting (the rest is in ~/.claude/skills/tera-capture):

  • Check the run prints the Radeon and not SwiftShader. The software path is ~15x slower — 22 minutes a film rather than 90 seconds — and it reports success.
  • hero-soma is a different animal: chrome: "bare", midnight to midnight, and a two-stage encode (a CRF 18 master, then 1440w CRF 28 and 960w CRF 30 delivered).
  • The hero camera is worth reconsidering. Whole-board was tried and rejected because the edge of the terrain plate showed against the sky. That seam no longer exists: the sea now runs eighteen board spans and ends past the fog's far plane.
  • After tuning the hero, run npm run check:hero in lumbridge-v4.
  • #onboarding-host is in FURNITURE.CLUTTER now, and #mode-dock and #play-hud are in FURNITURE.BARE. Every new overlay has to be added to one of those two lists on the day it lands, because a frame with a stray card in it still renders and still looks deliberate. The mode dock proved it: it shipped inside the tera share card for a fortnight and nobody noticed.
  • The two capture scripts both serve on port 5210. Two of them at once is EADDRINUSE a minute into a run, so do not start a film while a stills pass is 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

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.

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.

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.

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.

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.