1
0

The city points at its own buildings, and the sky stops depending on an API

**Clouds were invisible to everyone who had not wired up NWS.** The layer
took `currentWeather()?.cloudCover ?? 0`, and `currentWeather()` is null on
any deployment without a weather source — which is the default, and the
exact configuration this repo is held to: a stranger clones it, runs one
command, and gets a city with no account and no key. Their sky was
permanently, silently empty. `atmosphere.ts` already models a sky when
nobody has observed one; it now models cover too, an observed reading
still wins outright, and the clouds are there on a bare clone.

**Both offices are pins on the city, and clicking one walks you in.** Each
pack has carried a real `site` since the sun needed one, and that
coordinate was known to the lighting and to nothing else — a visitor
looking at the board had no way to tell that two of those buildings are
ones they can go inside. The coordinates move to a tiny eagerly-imported
`offices/sites.ts` that the packs import *from*, because a pack is a 25 kB
lazy chunk and the board wants its pins long before anybody opens a door.
A test asserts the pack and the table hold the **same object**, not merely
equal values: a drifted coordinate would put the marker on one building
and the sun on another and both would look entirely plausible.

**Aircraft bank into their turns.** The roll channel existed and was never
written, so every turn was flat. Bank comes from the coordinated-turn
relation against the measured turn rate, damped by a first-order lag so it
settles rather than oscillates, and clamped at 30° like a real limiter.
Six regression tests, because roll is the one channel that feeds itself —
position and heading are recomputed from the last two observations and
wash out a bad value, while a NaN in the roll would persist for the life
of the track.

That fed straight into a real defect: `AdsbFlights` substituted
`heading: 0` for records with no `track` field, which is harmless for a
symmetrical dart and is a **sustained full-scale artefact** once aircraft
bank — a target whose real heading is 200° reported as 0° reads as a 160°
turn and pins the roll at its limiter for as long as it is in the feed.
Those records are dropped now. An aeroplane the feed will not give a
heading for is one this layer cannot draw honestly.

**The office empties out overnight.** A full complement of seated people
at one in the morning, under house lights that came on because the sun is
down, was the least believable thing left in the room once the clock
became real. A live roster always wins — an API that says the building is
empty is telling the truth about the building.

**Robots go somewhere.** They pick real addresses — a seat, a room — and
turn to face the seat when they arrive, rather than stopping at a random
angle. Godmode gets an office section: house lights forced on or off or
following the sun, robots and ceilings toggled, with a readout.

**The bundle is split.** Entry chunk 758 kB to 208 kB, with three.js and
satellite.js in a vendor chunk that survives an app deploy instead of
being re-downloaded on every one. Rollup's 500 kB warning still fires and
should — it now points at three.js, where it is true, instead of at our
code, where it was pointing at three.js all along.

Reviewers caught two false geography claims in the new prose ("both
shipped buildings stand in San Francisco" — one is across the estuary at
Alameda Point) and several miscounted figures. Fixed. In a codebase where
the comments are the design record, those are defects.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-07 04:18:00 -07:00
parent 51979feea0
commit 2d87d9f354
14 changed files with 2304 additions and 72 deletions
+13
View File
@@ -247,6 +247,16 @@ export interface OfficeScene extends StageScene {
/** Scene-space label anchors per presence id, for an HTML overlay. Empty at public depth. */
anchors: Map<string, THREE.Vector3>;
setCeilingsVisible(visible: boolean): void;
/**
* Draw the robots, or do not.
*
* Visibility only, deliberately. A hidden robot still walks and still moves
* the vectors the luminaires hold, so the fittings above it still come up —
* which is the useful half of the switch rather than a caveat: it is how you
* watch the ceiling respond without a figure in the way. Gating `tick` would
* freeze the building instead.
*/
setRobotsVisible(visible: boolean): void;
setLighting(state: LightingState): void;
/**
* The sun's height, in degrees, from whatever clock the app is running.
@@ -769,6 +779,9 @@ export function createOfficeScene(office: Office, options: OfficeSceneOptions):
setCeilingsVisible(visible) {
shell.ceilings.visible = visible;
},
setRobotsVisible(visible) {
if (robots) robots.group.visible = visible;
},
setLighting(state) {
kit.applyLighting(state);
paintHorizon(state);