85be0b13f8
The owner asked for one map, with detail arriving in the background rather than
behind a wait. The interface already said one place (4bd8481: one ladder, no tab
strip, no boot card). This is the world catching up: on a desktop both metros are
now resident within seconds of landing, and the switch that used to be a build
becomes a fog dip over a cache hit.
**Measured, three runs each, California -> The Bay after an 8 s dwell:**
the longest blocking task falls from **521 ms to 58 ms** (before 700/501/521,
after 58/58/61) and long tasks from 2 to 1.
**And the cost, stated rather than omitted.** Over ten seconds sitting on the
state board: 601 frames -> ~571, p95 frame interval 18.0 -> 18.7 ms, and **one
long task of ~500 ms that did not exist before**. The build is not chunked yet,
so this moves a freeze from the moment of interaction to a moment nobody asked
for. That is a real trade and the follow-on that removes it is chunking the
build. `PREFETCH_ENABLED` in main.ts turns the whole lane off in one line.
A parked board draws nothing: 371 draws per frame before the lane lands a board
and 371 after, patched at the GL entry points. The performance budget reproduces
every cell to the digit.
**Three defects found on the way, two of them latent for a while.**
1. `environmentRig` had no idea what off-stage meant. `createScene` calls
`apply()` unconditionally, two hundred lines above the `present` check, and
the rig holds one probe per kind: on a key miss it convolves a new one,
disposes the one the visible board is using, and repoints every applied scene
at the replacement. A board built ahead of the camera is observed at its own
centre — about forty minutes of apparent solar time across the state — so its
key differs by construction. The board on screen would have changed colour
because something invisible finished loading. Harmless until today only
because every build was followed within ~800 ms by the swap that presented it.
Fixed with `ApplyOptions.offstage`; the regression test was written first and
failed first.
2. `mounting` was assigned and never cleared. Harmless while the only reader was
the next `mountCity` wanting something to abort. It stopped being harmless the
moment a second lane asked "is a foreground build in flight?", because the
honest answer after the first mount of the session was permanently yes — the
background lane armed exactly never, and the only symptom was a feature that
silently did nothing. Found by measuring, not by reading.
3. `buildBoard`'s `onProgress` ends in `bootProgress`, which raises the switch
pill. A prefetch would have put "Building The Bay Area... terrain 42%" over a
visitor who did nothing — the exact chrome that removing the tab strip was
for. `quiet` closes it.
**The policy is pure and lives with the eviction policy it has to agree with.**
`prefetchTarget` and `canAdmit` in boards.ts take scalars, never a camera, so
boards.ts keeps CONTRACT section 1's no-DOM/no-WebGL/no-three promise and
prefetchPolicy.test.ts can assert against the real pack bounds. Two regimes,
because collapsing them was the first version's mistake: with free room the gate
is simply "is there a slot", since the common path is to land on the state at
1,551 km and click a metro, and on that path a proximity trigger fires never;
with the cache full — the handheld case, capacity two with California pinned —
proximity is the only thing that justifies an eviction. The discs are asserted
non-overlapping against the shipped bounds (92 + 146 km of reach across a 314 km
gap), which is the covering-set argument residentCapacity already rests on.
**Also fixed, and separately load-bearing: reconcile's flag parser lied.** It
prefix-matched, so `?reconcile=palette` selected nothing and produced an empty
set — indistinguishable from the flag being absent. Every photograph taken to
judge a rule could have been a photograph of the unreconciled board with no way
to tell. Exact names now, with a warning that says "the flag is NOT off".
And two consumers were reading the raw packs beside a reconciled World:
`buildLadder` derives every rung's stand-off from `focus`, and `createMinimap`
was handed `entry.city` next to `handle.world`. Both now read `world.city`.
Latent with the flag off — `reconciledCity` returns by identity — which is what
kept it alive: it corrupts the measurement rather than announcing itself.
**What I did NOT ship, having tried it.** `roads` on by default. The rule exists
to stop California drawing 1,919 m freeways, and `city.roads` is not what draws
them: California is the one board with `roadTraffic`, so `scene.ts:816` takes the
`createFreewayWorld` branch and `createRoads` — the only reader of `Road.width` —
is never called for it. The visible corridor is a deliberate atlas glyph sized so
DRIVE mode can drive down it. Photographed at three chapters the rule moved
Downtown LA not at all (empty diff bbox), FiDi by RMSE 0.0006, California by
0.001% of pixels; the only measurable effect anywhere was -112 triangles on
Southern California. See DEFAULT_RULES for the whole argument.
1,688 + 295 tests, every gate, chapter-identity OK against the unmodified
fixture, ui-smoke PASS, budget PASS with private-request checks clean on all ten
cells.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
182 lines
7.3 KiB
TypeScript
182 lines
7.3 KiB
TypeScript
/**
|
|
* Everything that reads a pack must read the pack the World is drawing.
|
|
*
|
|
* ## The bug this is downstream of
|
|
*
|
|
* `World`'s constructor is `this.city = reconciledCity(city)` (`world.ts:116`),
|
|
* so the moment any reconciliation rule is on, there are **two** Californias in
|
|
* the process: the object `cities/california.ts` exported, and the rewritten one
|
|
* the engine is actually projecting, exaggerating and drawing roads from.
|
|
*
|
|
* Two consumers were reading the first one while the camera stood in the second:
|
|
*
|
|
* - `buildLadder` (`main.ts`) derives every rung's stand-off from `focus`, so
|
|
* a raw-pack ladder measures a world nobody is looking at — the Places rail
|
|
* silently disagreeing with the camera about where it is.
|
|
* - `createMinimap` was handed `entry.city` beside a reconciled `handle.world`,
|
|
* so the plan view drew a different California than the frame beside it.
|
|
*
|
|
* Neither is visible with the flag off, because `reconciledCity` returns the
|
|
* pack **by identity** when no rule is on — which is exactly what made the bug
|
|
* survive: it is latent until the day somebody turns a rule on to take a
|
|
* measurement, and then it corrupts the measurement rather than announcing
|
|
* itself.
|
|
*
|
|
* ## What is asserted
|
|
*
|
|
* Not "main.ts calls the right function" — that is a source-text assertion and
|
|
* `integration/sceneWiring.test.ts` already owns that genre. This asserts the
|
|
* *property*: for every pack and every rule, the reconciled object differs from
|
|
* the raw one in the columns that rule owns, so a consumer holding the wrong one
|
|
* is holding provably different numbers. A future consumer wired to the raw pack
|
|
* is a bug this file describes even if it cannot name the call site.
|
|
*/
|
|
import assert from "node:assert/strict";
|
|
import { after, describe, it } from "node:test";
|
|
|
|
import CALIFORNIA from "../cities/california.ts";
|
|
import SAN_FRANCISCO from "../cities/sf.ts";
|
|
import SOCAL from "../cities/socal.ts";
|
|
import { RULES, reconciledCity, setReconcile, setReconcileWarn } from "../cities/reconcile.ts";
|
|
import type { Rule } from "../cities/reconcile.ts";
|
|
import type { City } from "../engine/types.ts";
|
|
|
|
const PACKS: readonly (readonly [string, City])[] = [
|
|
["california", CALIFORNIA],
|
|
["sf", SAN_FRANCISCO],
|
|
["socal", SOCAL],
|
|
];
|
|
|
|
after(() => {
|
|
setReconcile(null);
|
|
setReconcileWarn(null);
|
|
});
|
|
|
|
/** The columns each rule owns, as the reader of a pack would see them. */
|
|
const OWNED: Readonly<Record<Rule, (city: City) => unknown>> = {
|
|
projection: (city) => city.lngScale ?? null,
|
|
exaggeration: (city) => city.verticalExaggeration,
|
|
roads: (city) => city.roads.map((road) => road.width).join(","),
|
|
ground: (city) => `${city.coastFalloff}|${JSON.stringify(city.palette ?? null)}`,
|
|
};
|
|
|
|
describe("a reconciled pack is not the pack the module exported", () => {
|
|
it("returns the very same object when nothing is on", () => {
|
|
setReconcile(false);
|
|
for (const [id, pack] of PACKS) {
|
|
assert.equal(reconciledCity(pack), pack, `${id} was copied with the flag off`);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The load-bearing one. If a rule changes nothing for every pack then a
|
|
* consumer reading the raw pack is harmless and this whole file is theatre —
|
|
* so the file has to prove it is not.
|
|
*/
|
|
it("changes the column its rule owns, for at least one pack, for every rule", () => {
|
|
for (const rule of RULES) {
|
|
setReconcile([rule]);
|
|
const moved = PACKS.filter(([, pack]) => {
|
|
const read = OWNED[rule];
|
|
return read(reconciledCity(pack)) !== read(pack);
|
|
});
|
|
assert.ok(
|
|
moved.length > 0,
|
|
`rule "${rule}" changed nothing on any pack — either the rule is dead ` +
|
|
"or OWNED is reading the wrong column",
|
|
);
|
|
}
|
|
});
|
|
|
|
it("never mutates the pack the module exported", () => {
|
|
const before = PACKS.map(([, pack]) => JSON.stringify(pack));
|
|
setReconcile(true);
|
|
for (const [, pack] of PACKS) reconciledCity(pack);
|
|
setReconcile(false);
|
|
for (const [index, [id]] of PACKS.entries()) {
|
|
assert.equal(JSON.stringify(PACKS[index]![1]), before[index], `${id} was mutated in place`);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The worker's half of the contract. `World` posts `this.city` to the terrain
|
|
* worker as a structured clone and the worker builds a second `World` from it;
|
|
* `reconciled: true` is what stops that second pass applying every rule again
|
|
* on top of itself. Without it the exaggeration rule would square.
|
|
*/
|
|
it("is idempotent, so the worker's second pass is a no-op", () => {
|
|
setReconcile(true);
|
|
for (const [id, pack] of PACKS) {
|
|
const once = reconciledCity(pack);
|
|
assert.equal(once.reconciled, true, `${id} did not mark itself reconciled`);
|
|
assert.equal(reconciledCity(once), once, `${id} was reconciled twice`);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("the rule parser", () => {
|
|
it("takes exact rule names", () => {
|
|
setReconcile(null);
|
|
for (const rule of RULES) {
|
|
// Through the public surface: setReconcile with an explicit list is what
|
|
// a node script uses, and the query-string path shares `parse`.
|
|
setReconcile([rule]);
|
|
const read = OWNED[rule];
|
|
const moved = PACKS.some(([, pack]) => read(reconciledCity(pack)) !== read(pack));
|
|
assert.ok(moved, `"${rule}" did not select itself`);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The defect: `parse` prefix-matched, so `?reconcile=palette` selected nothing
|
|
* and produced an empty set — **identical to the flag being absent**. A
|
|
* photograph taken to judge a rule was a photograph of the raw board, and
|
|
* nothing in the picture could tell you which.
|
|
*/
|
|
it("warns loudly rather than silently reading an unknown rule as off", async () => {
|
|
const said: string[] = [];
|
|
setReconcileWarn((message) => said.push(message));
|
|
setReconcile(null);
|
|
const { activeRules } = await import("../cities/reconcile.ts");
|
|
const search = { search: "?reconcile=palette" };
|
|
const globals = globalThis as { location?: unknown };
|
|
const had = "location" in globals;
|
|
const previous = globals.location;
|
|
globals.location = search;
|
|
try {
|
|
const on = activeRules();
|
|
assert.equal(on.size, 0, "an unknown rule must not select a real one");
|
|
assert.equal(said.length, 1, "an unknown rule must warn exactly once");
|
|
assert.match(said[0] ?? "", /unknown rule "palette"/);
|
|
assert.match(said[0] ?? "", /the flag is NOT off/);
|
|
} finally {
|
|
if (had) globals.location = previous;
|
|
else delete globals.location;
|
|
setReconcileWarn(null);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The other half of prefix-matching: one letter used to select a whole rule,
|
|
* which is fine until two rules share it. `p` must now select nothing.
|
|
*/
|
|
it("does not accept an abbreviation", async () => {
|
|
const said: string[] = [];
|
|
setReconcileWarn((message) => said.push(message));
|
|
setReconcile(null);
|
|
const { activeRules } = await import("../cities/reconcile.ts");
|
|
const globals = globalThis as { location?: unknown };
|
|
const had = "location" in globals;
|
|
const previous = globals.location;
|
|
globals.location = { search: "?reconcile=p" };
|
|
try {
|
|
assert.equal(activeRules().size, 0, '"p" selected a rule by prefix');
|
|
assert.equal(said.length, 1);
|
|
} finally {
|
|
if (had) globals.location = previous;
|
|
else delete globals.location;
|
|
setReconcileWarn(null);
|
|
}
|
|
});
|
|
});
|