1
0

feat: land on a real place, and let a wheel notch cross the seam

The owner has now asked four times why there are three boards, and the last
answer missed the point: reconciling the packs made them draw one California,
but you still *arrived* on the coarsest tier the product owns and still changed
boards by picking a name off a list. This changes both.

**The behaviour was already built and behind a second flag defaulted off.**
`handover()` in `ladder.ts` — promote/demote by camera stand-off, hysteresis at
0.9/1.15, a drag guard so a board never swaps under a finger — was pure, tested
and shipped a round ago, with `?handover=1` as the only way to see it. It is on
now, `?handover=0` turns it off. Measured on the deployed bundle first:
promotion into the Bay Area fired at the sixteenth wheel notch in from the state
pose, arrival clean, no boot card, no tab, no click on a name.

**And it did not work, for a reason worth writing down.** Landing on the Bay
with the flag on bounced straight back to the state board with no input at all.
Two wrong diagnoses on the way, both from reasoning instead of measuring:

  1. "sf's ceiling equals its own widest pose, so the band is too tight." It is
     not — `chapterStandoffMetres` puts the resting pose at 71.2 km against a
     demote threshold of 81.9 km.
  2. "the arrival flight carries the camera through the threshold, so guard on
     `arriving()`." Right about the cause, wrong about the mechanism: the guard
     went in and the bounce survived it.

A trajectory log settled it in one run. The handover tick arrived *before* the
first `arriving=true` sample, at 108,316 m — 1.5x sf's resting stand-off, which
is `arrivalStart`'s own offset. `arrive()` read:

    kit.setPose(from);
    arrival = { from, to: rest, elapsed: 0 };

`setPose` drives `OrbitControls`, which fires `change` **synchronously**, and
`main.ts` listens on that event. So the listener ran on the line *between* those
two statements: camera already 1.5x out, `arriving()` still false. A one-frame
ordering race, and the guard could not fire because the flag it reads was set one
statement too late. The assignment now goes first.

The guard stays, because the inequality behind it is structural rather than
incidental: `ARRIVAL_STANDOFF` is 1.5 and `DEMOTE` is 1.15, both global, so the
opening frame of *every* board sits outside that board's own retention band.
`handoverArrivalGuard.test.ts` asserts that relation and drives the real rule
through the opening stand-off to watch it demote, so neither the guard nor either
constant can be quietly simplified.

**The landing board is `DEFAULT_CITY_ID`, and it is the Bay Area.** It was
`CITIES[0]` in three places, which meant the state tier: seventeen districts over
1063x930 km at 1,919 m per unit, no city legible, and a left column whose first
offer is somewhere else to go. The detailed boards carry 52 and 47 districts at
94 and 391 m per unit. With free handover on, the state tier stops being a
destination and becomes what you get when you pull back — the role it is good at,
since it is the only board drawing 97.4% of California. A named constant rather
than reordering `CITIES`, because that array's order is the `?city=` fallback and
is read positionally by other consumers.

**Which caught a silent break in the capture harness, and this is the part that
would have cost a week.** `shots.mjs` and `films.mjs` built `?city=` only when a
shot declared one — and 4 of 21 shots and 1 of 4 films declare none, so they
inherited the app's default. Moving that default would have re-pointed five
pieces of marketing imagery at a different place while every filename, caption
and alt text stayed as it was. Both harnesses now name `california` themselves.
`performance-budget.mjs`'s `california` and `california-drive` cells had no query
at all for the same reason; `signature` would have failed them loudly rather than
mismeasuring, which is the harness working, but a harness that depends on an app
default reports someone else's change as its own flake.

Every harness also pins `handover=0`. A planted pose wider than a board's
retention band would otherwise demote to the coarser tier while the shutter is
open, and the frame that comes back is a real photograph of the wrong board.

Verified: bare URL lands on the Bay Area and stays there; `?city=california` and
`?city=socal` still deep-link; `?handover=0` stays put; zooming out from the Bay
demotes to the state tier at the second notch. 1,694 tests pass. All ten budget
cells pass with no cap raised, and every cell now measures the board it names.

Still true and now a decision rather than a doubt: 97.4% of California has no
board below 242 km of stand-off, so zooming into the middle of the state lands on
coarse ground. That picture has been looked at. Authoring Sacramento, Fresno and
the Central Valley is what retires it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-24 00:13:23 -07:00
parent 6fde2f0c97
commit 6fc0b2b60e
7 changed files with 285 additions and 14 deletions
+1 -1
View File
@@ -203,7 +203,7 @@ try {
* opens, so no flight is needed — but the label is checked anyway, because
* "the chapter I did not click" is exactly as reorderable as the one I did.
*/
await shootApp(browser, "http://tera.lumbridgecorp.com:5210/?city=california", "art-tera.png", {
await shootApp(browser, "http://tera.lumbridgecorp.com:5210/?city=california&handover=0", "art-tera.png", {
at: AFTERNOON,
chapter: 0,
expect: "State",
+17 -1
View File
@@ -564,7 +564,23 @@ async function expose(page, instant) {
async function film(browser, spec, dir) {
const host = spec.door === "office" ? "office.lumbridgecorp.com" : "tera.lumbridgecorp.com";
const query = spec.city ? `?city=${spec.city}` : "";
/*
* **Both halves of this are explicit on purpose, and neither used to be.**
*
* `city` used to fall through to the app's own default when a shot did not
* declare one, and four of the twenty-one shots relied on that. The default
* then moved from the state board to the Bay Area, which would have re-pointed
* those four at a different place while every filename, caption and `alt`
* stayed exactly as it was — the silent kind of wrong this harness exists to
* prevent. So the fallback is named here rather than borrowed from the app.
*
* `handover=0` pins the free-camera tier promotion off. With it on, a planted
* pose wider than a board's retention band demotes to the coarser tier while
* the shutter is open, and the frame that comes back is a real photograph of
* the wrong board. A capture harness must not share a camera policy with a
* visitor.
*/
const query = `?city=${spec.city ?? "california"}&handover=0`;
const frames = frameOverride ?? spec.frames;
const page = await browser.newPage({
viewport: spec.viewport ?? VIEWPORT,
+17 -1
View File
@@ -1346,7 +1346,23 @@ async function serveFeed(page, name) {
async function shoot(browser, shot, frame) {
const host = shot.door === "office" ? "office.lumbridgecorp.com" : "tera.lumbridgecorp.com";
const query = shot.city ? `?city=${shot.city}` : "";
/*
* **Both halves of this are explicit on purpose, and neither used to be.**
*
* `city` used to fall through to the app's own default when a shot did not
* declare one, and four of the twenty-one shots relied on that. The default
* then moved from the state board to the Bay Area, which would have re-pointed
* those four at a different place while every filename, caption and `alt`
* stayed exactly as it was — the silent kind of wrong this harness exists to
* prevent. So the fallback is named here rather than borrowed from the app.
*
* `handover=0` pins the free-camera tier promotion off. With it on, a planted
* pose wider than a board's retention band demotes to the coarser tier while
* the shutter is open, and the frame that comes back is a real photograph of
* the wrong board. A capture harness must not share a camera policy with a
* visitor.
*/
const query = `?city=${shot.city ?? "california"}&handover=0`;
const page = await browser.newPage({
viewport: VIEWPORT,
deviceScaleFactor: preview ? 1 : 2,
+10 -3
View File
@@ -49,11 +49,18 @@ const VIEWPORTS = {
const SCENES = {
california: {
host: "tera.lumbridgecorp.com",
// Explicit, and it was implicit until the app's default board moved off the
// state tier. `signature` would have caught it — a board with no
// `california-overview` chapter never becomes ready — but a harness that
// depends on an app default is a harness that reports someone else's change
// as its own flake.
query: "?city=california&handover=0",
signature: "california-overview",
ready: () => document.getElementById("boot")?.hidden === true && document.querySelector("#chapters .chapter[data-view='california-overview']") !== null,
},
"california-drive": {
host: "tera.lumbridgecorp.com",
query: "?city=california&handover=0",
signature: "california-overview",
ready: () => document.getElementById("boot")?.hidden === true && document.querySelector("#chapters .chapter[data-view='california-overview']") !== null && document.querySelector("#chapters .chapter[data-view='la-sf-us-101']") !== null,
activate: () => {
@@ -66,7 +73,7 @@ const SCENES = {
},
active: () => document.querySelector("[data-control-mode='drive']")?.getAttribute("aria-pressed") === "true" && document.getElementById("play-hud")?.hidden === false,
},
office: { host: "office.lumbridgecorp.com", ready: () => document.getElementById("boot")?.hidden === true && document.getElementById("enter")?.textContent?.includes("Back to the city") === true },
office: { host: "office.lumbridgecorp.com", query: "?handover=0", ready: () => document.getElementById("boot")?.hidden === true && document.getElementById("enter")?.textContent?.includes("Back to the city") === true },
/*
* The two metro boards, added after they became the boards with the most on
* them and were still the boards nothing measured.
@@ -86,13 +93,13 @@ const SCENES = {
*/
"bay-area": {
host: "tera.lumbridgecorp.com",
query: "?city=sf",
query: "?city=sf&handover=0",
signature: "hayes-valley",
ready: () => document.getElementById("boot")?.hidden === true && document.querySelector("#chapters .chapter[data-view='hayes-valley']") !== null,
},
socal: {
host: "tera.lumbridgecorp.com",
query: "?city=socal",
query: "?city=socal&handover=0",
signature: "dtla",
ready: () => document.getElementById("boot")?.hidden === true && document.querySelector("#chapters .chapter[data-view='dtla']") !== null,
},
+39 -2
View File
@@ -602,6 +602,22 @@ export interface SceneHandle {
* `AERIAL_SUBJECT_CLEARANCE`.
*/
cameraStandoffMetres(): number;
/**
* Whether the unrequested opening move is still in the air.
*
* Exposed for one caller and one reason: `main.ts`'s free-camera handover must
* not read a stand-off that the *arrival* is still changing. `arrivalStart`
* begins the move at `ARRIVAL_STANDOFF` — 1.5x the resting stand-off — and the
* ladder demotes a board above `DEMOTE`, which is 1.15x its handover ceiling.
* 1.5 is greater than 1.15 for every board there will ever be, so the opening
* frame of *any* board is outside that board's own retention band and a
* handover that ran during it would immediately throw the visitor back to the
* coarser tier they came from. Which is exactly what it did.
*
* This is the same class of guard as the drag guard: not "the camera is
* somewhere odd" but "nobody has asked for this camera position yet".
*/
arriving(): boolean;
/** The reflectivity raster, or `null`. A no-op without a precipitation layer. */
setPrecip(field: RadarField | null): void;
/** Tonight's migration, or `null`. A no-op without a migration layer. */
@@ -1382,8 +1398,26 @@ export async function createScene(
}
const rest = heroPose(openingPose, orbitMaxDistance);
const from = arrivalStart(rest, orbitMaxDistance);
kit.setPose(from);
/*
* **Record the move before making it.** `kit.setPose` drives
* `OrbitControls`, which fires `change` *synchronously*, and `main.ts`
* listens on that event to decide whether the camera has left this board.
* Written the obvious way round — pose first, then bookkeeping — that
* listener runs on the line between them, sees a camera 1.5x further out
* than this board's own resting stand-off and an `arriving()` that is
* still false, and hands the visitor back to the coarser tier before the
* opening move has drawn a single frame.
*
* That is precisely what shipped: landing on the Bay Area bounced to the
* state board with no input at all, and the guard written to prevent it
* could not fire because the flag it reads was set one statement too late.
* The trajectory log that found it showed the handover tick arriving
* *before* the first `arriving=true` sample, at the same stand-off.
*
* So the assignment goes first. Nothing else in this function changed.
*/
arrival = { from, to: rest, elapsed: 0 };
kit.setPose(from);
},
setLighting: (state) => {
lighting = state;
@@ -1429,6 +1463,9 @@ export async function createScene(
},
setFires: (view) => fireLayer?.setFires(view),
setFireSmoke: (visible) => fireLayer?.setSmokeVisible(visible),
arriving() {
return arrival !== null;
},
cameraStandoffMetres() {
// `metresPerUnit` and NOT `unitsToMetres`, which is the vertical
// conversion and divides the exaggeration back out. A stand-off is a
@@ -1661,7 +1698,7 @@ const HERO_ELEVATION_DEG = 29;
const HERO_DISTANCE = 0.9;
/** How much further out the camera stands before the move, as a multiple. */
const ARRIVAL_STANDOFF = 1.5;
export const ARRIVAL_STANDOFF = 1.5;
/** How much higher, as a multiple. Larger than the stand-off: the move descends. */
const ARRIVAL_LIFT = 2.2;
/** How far round the board it swings, in radians. Negative is anticlockwise. */
+85 -6
View File
@@ -230,6 +230,40 @@ const CITIES: { id: string; label: string; city: City }[] = [
{ id: "socal", label: "SoCal", city: SOCAL },
];
/**
* The board a bare URL lands on. **The detailed one, not the state tier.**
*
* ## Why this is not `CITIES[0]`
*
* It used to be, implicitly, in three places — a `wantedCity` initialiser, the
* office's return-link builder and the boot fallback — and the board it named
* was the 1,919 m-per-unit state tier. So the first thing every visitor saw was
* the coarsest representation the product owns: seventeen districts over
* 1063x930 km, no city legible, and a left column whose first offer is a list of
* somewhere else to go. The detailed boards carry 52 and 47 districts at 94 and
* 391 m per unit, and they are what the engine is actually for.
*
* With the free-camera handover on, the state tier has stopped being a
* destination and become **what you get when you pull back** — which is the role
* it is good at, since it is the only board that draws 97.4% of California.
* Landing on the Bay and zooming out to the state is the same map at two
* scales. Landing on the state and hunting for a city is three products.
*
* ## Why the constant rather than reordering `CITIES`
*
* Because `CITIES` order is load-bearing elsewhere: twenty-nine capture guards
* in `scripts/brand-assets` and every cell in `scripts/performance-budget.mjs`
* aim with an explicit `?city=`, and the budget harness binds each scene to the
* `data-board` it asserts precisely because an unknown `?city=` falls back to
* the first entry rather than failing. Reordering the array to change a landing
* page would silently re-point that fallback and every consumer that reads the
* array positionally. A named constant changes the one thing intended.
*
* `?city=california` still goes straight to the state board, and nothing about
* the deep link changed.
*/
const DEFAULT_CITY_ID = "sf";
/** The corridor's scale doors, and the office each detailed board arrives near. */
const CALIFORNIA_DESTINATIONS = new Map<string, { cityId: string; officeId: string }>([
["los-angeles", { cityId: "socal", officeId: "mateo-court" }],
@@ -495,7 +529,7 @@ let cityId = "california";
* to be against the intention, and the intention is recorded synchronously in
* the click handler.
*/
let wantedCity = "california";
let wantedCity = DEFAULT_CITY_ID;
let office: OfficeScene | null = null;
/** The pack used by `office`; kept separate from the currently selected door. */
let builtOfficeId: string | null = null;
@@ -880,7 +914,7 @@ function cityDoorUrl(cityWanted?: string): string | null {
// Carried so that stepping out of the office into SoCal lands in SoCal. The
// page load is the cost of telling the truth about where you are; arriving in
// the wrong metro on top of it would not be.
if (cityWanted !== undefined && cityWanted !== CITIES[0]?.id) {
if (cityWanted !== undefined && cityWanted !== DEFAULT_CITY_ID) {
url.searchParams.set("city", cityWanted);
}
return url.href;
@@ -2442,7 +2476,36 @@ function activateBoard(record: MountedBoard): void {
let dragging = false;
/**
* Free-camera promotion across the seam. **Behind a flag, and off by default.**
* Free-camera promotion across the seam. **On, and `?handover=0` turns it off.**
*
* ## Why it is on now
*
* Because the owner asked three times why there are three boards, and this is
* the answer to that question rather than a nicety. A wheel notch over the Bay
* now promotes to the detailed Bay Area board and a notch back out demotes to
* the state tier: one map, three scales, entered by moving rather than by
* picking a product off a strip of tabs. Measured on the deployed bundle before
* this default changed, promotion fired at the sixteenth notch in from the state
* pose and the arrival was clean — no boot card, no tab, no click on a name.
*
* The two reservations below were real and both are now discharged. The
* oscillation one had a *cause* rather than a risk — see the arrival guard in
* `maybeHandover`, which is the whole of it. The coarse-descent one is a
* property of the packs and not of this rule, and it is recorded as such:
*
* ## What is still true, and is now a decision rather than a doubt
*
* 97.4% of California's area has no board below 242 km of stand-off. Zooming in
* over Fresno lands on 806 m lots and 2 km-wide freeway symbols with nothing to
* hand over to, and the rule correctly refuses. That picture has now been
* looked at — it is a dark, coarse blur, and it is what the state tier honestly
* is at that range. The judgement made here is that a visitor who deliberately
* zooms into the empty middle of the state and finds it coarse has learned
* something true, and that this costs less than a tab strip that tells every
* visitor there are three products before they touch anything. Authoring
* Sacramento, Fresno and the Central Valley is what retires it.
*
* ## The original argument, kept because it is the reason the guard exists
*
* The rule itself — `handover` in `engine/ladder.ts` — is pure, tested and
* shipped. What is deliberately not shipped is letting a wheel notch fire it,
@@ -2460,7 +2523,7 @@ let dragging = false;
* the door into Mateo Court rejecting with `wrong-city` while the Southland is
* visibly on screen, and nothing throws.
*/
const FREE_HANDOVER = new URLSearchParams(location.search).get("handover") === "1";
const FREE_HANDOVER = new URLSearchParams(location.search).get("handover") !== "0";
/**
* Whether a board may be built before anybody asks for it.
@@ -2476,6 +2539,21 @@ const PREFETCH_ENABLED = true;
function maybeHandover(record: MountedBoard): void {
if (!FREE_HANDOVER) return;
if (inside || fogDip !== null || record !== visibleBoard) return;
/*
* The opening move is not a camera position anybody asked for, and reading a
* stand-off out of it is how this feature spent a round looking broken.
*
* `arrivalStart` begins every board's arrival at 1.5x its resting stand-off
* and `ladder.ts` demotes above 1.15x the handover ceiling, so the first
* second of *any* board is outside that board's own retention band. Landing on
* the Bay Area with the flag on therefore bounced straight back to the state
* board with no input at all — measured, twice, before this line existed. It
* is not an sf quirk and it is not a tuning problem: 1.5 > 1.15 holds for
* every board there will ever be, which is why the fix is a guard rather than
* a number. `handoverArrivalGuard.test.ts` pins that inequality so the guard
* cannot be deleted as redundant.
*/
if (record.handle.arriving()) return;
if (record.handle.controlMode() !== "overview") return;
const target = record.handle.stageScene.controls.target;
const [lat, lng] = record.handle.world.unproject(target.x, target.z);
@@ -6061,9 +6139,10 @@ async function boot() {
* black screen.
*/
const wanted = new URLSearchParams(location.search).get("city");
const first = CITIES.find((c) => c.id === wanted) ?? CITIES[0];
const fallback = CITIES.find((c) => c.id === DEFAULT_CITY_ID) ?? CITIES[0];
const first = CITIES.find((c) => c.id === wanted) ?? fallback;
await building(`Building ${first?.label ?? "the city"}`, () =>
mountCity(first?.id ?? "california"),
mountCity(first?.id ?? DEFAULT_CITY_ID),
);
// There must be a mounted scene before remote snapshots have anywhere to go.
+116
View File
@@ -0,0 +1,116 @@
/**
* The one inequality that makes the arrival guard necessary.
*
* `main.ts`'s `maybeHandover` refuses to run while `handle.arriving()` is true.
* That line looks like belt-and-braces and is not: without it, landing on any
* board with the free-camera handover on threw the visitor straight back to the
* coarser tier they came from, with no input at all. It was measured on the
* deployed bundle twice — `?city=sf` alone stays on the Bay Area, `?city=sf` with
* the handover on reported "San Francisco Bay Area" and then "California".
*
* The cause is arithmetic rather than tuning, which is why it is asserted here
* rather than left to a screenshot. `arrivalStart` opens every board's
* unrequested move at `ARRIVAL_STANDOFF` times the resting stand-off, and
* `handover` demotes a board once the camera passes `DEMOTE` times that board's
* handover ceiling. A board's ceiling is its own widest authored pose, so the
* opening frame sits outside its own retention band for as long as
* `ARRIVAL_STANDOFF > DEMOTE` — and it does, for every board there will ever be,
* because both are single global constants.
*
* So this file exists to fail the day somebody "simplifies" one of three things:
* the guard, `ARRIVAL_STANDOFF`, or `DEMOTE`.
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { ARRIVAL_STANDOFF } from "../engine/scene.ts";
import { DEMOTE, HANDOVER_STANDOFF_M, PROMOTE, handover } from "../engine/ladder.ts";
import SAN_FRANCISCO from "../cities/sf.ts";
import SOCAL from "../cities/socal.ts";
import CALIFORNIA from "../cities/california.ts";
const BOARDS = [CALIFORNIA, SAN_FRANCISCO, SOCAL].map((city) => ({
id: city.id,
bounds: city.bounds,
metresPerUnit: 111_320 / city.latScale,
}));
describe("the arrival guard", () => {
it("is required, because the opening move starts outside every board's retention band", () => {
assert.ok(
ARRIVAL_STANDOFF > DEMOTE,
`arrival opens at ${ARRIVAL_STANDOFF}x resting and a board is demoted above ` +
`${DEMOTE}x its ceiling — if this ever stops being true the guard in ` +
"maybeHandover may be reconsidered, and until then it may not",
);
});
it("would demote a board on its own opening frame, for each board that has a ceiling", () => {
/*
* The failure the guard prevents, reproduced through the real rule: put the
* camera where `arrivalStart` puts it — 1.5x the resting stand-off, which for
* a board at rest on its own widest pose is 1.5x its ceiling — and ask the
* ladder what board that is. It answers "a coarser one", every time.
*/
for (const [id, ceiling] of Object.entries(HANDOVER_STANDOFF_M)) {
const board = BOARDS.find((candidate) => candidate.id === id);
assert.ok(board, `no pack for ${id}`);
const centre = { lat: (board.bounds.minLat + board.bounds.maxLat) / 2,
lng: (board.bounds.minLng + board.bounds.maxLng) / 2 };
const opening = handover({
boards: BOARDS,
current: id,
lat: centre.lat,
lng: centre.lng,
standoffM: ceiling * ARRIVAL_STANDOFF,
dragging: false,
});
assert.equal(
opening,
"california",
`${id}'s opening frame does not demote, so this test is asserting nothing`,
);
// And once the move has landed, the same board is stable: no promotion out
// of it and no demotion off it. That is the state the guard hands over to.
const settled = handover({
boards: BOARDS,
current: id,
lat: centre.lat,
lng: centre.lng,
standoffM: ceiling,
dragging: false,
});
assert.equal(settled, null, `${id} is not stable at its own resting stand-off`);
}
});
const SF_CEILING = HANDOVER_STANDOFF_M.sf ?? 0;
it("still promotes on the way in, which is the behaviour being protected", () => {
// Below the ceiling by more than the hysteresis band: the Bay Area is the
// right board for a camera this close to San Francisco.
const promoted = handover({
boards: BOARDS,
current: "california",
lat: 37.77,
lng: -122.42,
standoffM: SF_CEILING * PROMOTE * 0.9,
dragging: false,
});
assert.equal(promoted, "sf");
});
it("never swaps a board out from under a finger", () => {
const held = handover({
boards: BOARDS,
current: "california",
lat: 37.77,
lng: -122.42,
standoffM: SF_CEILING * PROMOTE * 0.9,
dragging: true,
});
assert.equal(held, null);
});
});