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/scripts/look.mjs
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

284 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Render the built app and write a PNG you can open and judge.
*
* The defects this exists for cannot be asserted. "The ocean has no specular
* response", "the board ends in a hard diamond edge", "the aircraft are
* illegible at board scale" are all true of code that typechecks, passes every
* test and meets every performance budget. The only instrument that finds them
* is a picture, so this makes taking one cheap.
*
* node scripts/look.mjs <name> [--url <path-and-query>] [--phone] [--at <iso>]
* [--wait <ms>] [--click <text>] [--api <origin>]
*
* Writes /tmp/tera-look/<name>.png. `--at` pins the clock, because the sun's
* position is computed from the real one and a shot taken at 03:00 tells you
* nothing about how the water reads at noon.
*
* This box has an AMD card and no monitor; the ANGLE/Vulkan flags below are what
* make Chrome render headless here rather than falling back to a blank canvas.
*/
import { spawn } from "node:child_process";
import { mkdirSync, readFileSync } from "node:fs";
import { createServer } from "node:net";
import { chromium } from "playwright";
const args = process.argv.slice(2);
const name = args[0] ?? "look";
/**
* The frames this project keeps taking, by name.
*
* `node scripts/look.mjs sf-night` and nothing else — no query string to
* remember and no timestamp to get wrong. The point is not convenience, it is
* that two people asking for "the night board" get the same photograph: an
* acceptance criterion that reads "shoot sf-night and look at it" is only worth
* writing if `sf-night` means one thing.
*
* Every field is a default. An explicit flag on the command line still wins, so
* a preset is a starting point rather than a cage, and adding one is two lines.
*/
const PRESETS = {
// The fire boards. California is the one with fires on it on a normal day;
// SoCal is the one that must be *empty* and say so — see ARCHITECTURE.md §9.2.
"fires-california": { url: "/?city=california" },
"fires-socal": { url: "/?city=socal" },
// The LA studio's upper floor. The door lands on the SF studio, so the shot
// switches rooms and waits for the swap.
"mateo-loft": { url: "/?city=socal&view=office", click: "LA HQ" },
// Night. 04:35Z is 21:35 in Los Angeles: full dark, and well clear of both
// twilight edges, so a shot taken a minute late is the same shot.
"sf-night": { url: "/?city=sf", at: "2026-08-23T04:35:00Z" },
"sky-night": { url: "/?city=california", at: "2026-08-23T04:35:00Z" },
"socal-night": { url: "/?city=socal", at: "2026-08-23T04:35:00Z" },
// The aeroplane glyph, at the four stand-offs its clamp has to serve: a whole
// board, two chapter closeups on that board, and a detailed metro.
"glyph-board": { url: "/?city=california" },
"glyph-la": { url: "/?city=california", click: "^LA$" },
"glyph-sf": { url: "/?city=california", click: "^SF$" },
"glyph-bay": { url: "/?city=sf", click: "The Bay" },
// The opening move, landed. `--reduced` collapses it to a cut, which is what
// makes an arrival frame reproducible.
"hero-california": { url: "/?city=california" },
"hero-socal": { url: "/?city=socal" },
"hero-sf": { url: "/?city=sf" },
};
const preset = PRESETS[name] ?? {};
const flag = (f, d) => {
const i = args.indexOf(f);
if (i !== -1) return args[i + 1];
return preset[f.replace(/^--/, "")] ?? d;
};
const has = (f) => args.includes(f);
const OUT = "/tmp/tera-look";
mkdirSync(OUT, { recursive: true });
/**
* The port is asked for, not guessed, and the build is then verified.
*
* This used to draw a random port in 47004899 and start `vite preview
* --strictPort` on it. When the draw collided with an abandoned preview from an
* earlier run — and this box accumulated a hundred and forty-seven of them in
* one afternoon — the new preview exited on the strict-port check, `page.goto`
* succeeded against the squatter, and Playwright silently photographed somebody
* else's dist. Three shots came back byte-identical while the bundle under them
* provably changed. A screenshot tool that can photograph the wrong build is
* worse than no screenshot tool, because you believe it.
*
* So: take a port from the kernel rather than from `Math.random`, then read the
* URL the preview actually bound out of its own stdout, then fetch `/` and
* assert it is the `dist/index.html` sitting on this disk. Any one of the three
* would have caught it; all three cost nothing.
*/
const PORT = await new Promise((resolve, reject) => {
const probe = createServer();
probe.once("error", reject);
probe.listen(0, "127.0.0.1", () => {
const { port } = probe.address();
probe.close(() => resolve(port));
});
});
// `detached`, and vite's own binary rather than `npx`, because of the *other*
// half of the orphan story: `npx` is a wrapper, `server.kill()` killed only the
// wrapper, and the preview it had spawned went on holding its port forever. A
// hundred and forty-seven of them accumulated in one afternoon. Detached gives
// the pair a process group, and killing the group at the end kills both.
const server = spawn(
new URL("../node_modules/.bin/vite", import.meta.url).pathname,
["preview", "--port", String(PORT), "--strictPort"],
{ detached: true, stdio: ["ignore", "pipe", "pipe"] },
);
const shutdown = () => {
try {
process.kill(-server.pid, "SIGTERM");
} catch {
/* already gone */
}
};
process.on("exit", shutdown);
const bound = await new Promise((resolve) => {
let seen = "";
const settle = setTimeout(() => resolve(null), 30000);
const read = (chunk) => {
seen += String(chunk);
const match = /http:\/\/(?:localhost|127\.0\.0\.1):(\d+)/.exec(seen);
if (match) {
clearTimeout(settle);
resolve(Number(match[1]));
}
};
server.stdout.on("data", read);
server.stderr.on("data", read);
});
if (bound === null) {
console.error(`look: vite preview never announced a URL on ${PORT}; is dist/ built?`);
shutdown();
process.exit(1);
}
if (bound !== PORT) {
console.error(`look: preview bound ${bound}, not ${PORT} — refusing to photograph it`);
shutdown();
process.exit(1);
}
// And the served page is this checkout's build, hashed script tag and all.
{
const served = await fetch(`http://localhost:${PORT}/index.html`).then((r) => r.text());
const onDisk = readFileSync(new URL("../dist/index.html", import.meta.url), "utf8");
const bundle = (html) => /src="([^"]*\/assets\/[^"]+\.js)"/.exec(html)?.[1] ?? null;
if (bundle(served) === null || bundle(served) !== bundle(onDisk)) {
console.error(
`look: :${PORT} is serving ${bundle(served)}, dist/ holds ${bundle(onDisk)}` +
`something else owns that port. Not photographing it.`,
);
shutdown();
process.exit(1);
}
}
const phone = has("--phone");
const browser = await chromium.launch({
channel: "chrome",
args: [
"--use-gl=angle",
"--use-angle=vulkan",
"--enable-unsafe-swiftshader",
"--ignore-gpu-blocklist",
],
});
/**
* `--reduced` asks the page for `prefers-reduced-motion: reduce`.
*
* Which is not only an accessibility check. The opening arrival — `arrive()` in
* `scene.ts`, and `beginOfficeArrival` in `main.ts` — collapses to a cut under
* this preference, so a shot taken with it is the resting frame and nothing
* else, whatever the machine's load did to the four and a half seconds before
* it. Without it a slow build and a fast one photograph different cameras.
*/
const context = await browser.newContext({
viewport: phone ? { width: 390, height: 844 } : { width: 1600, height: 1000 },
deviceScaleFactor: 2,
timezoneId: "America/Los_Angeles",
...(has("--reduced") ? { reducedMotion: "reduce" } : {}),
...(phone
? {
isMobile: true,
hasTouch: true,
userAgent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 " +
"(KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
}
: {}),
});
await context.clock.setFixedTime(new Date(flag("--at", "2026-08-21T20:00:00Z")));
const page = await context.newPage();
/**
* `--api http://127.0.0.1:8431` photographs the dist against a real server.
*
* `vite preview` serves the static build and nothing else, so without this every
* frame is the **keyless** experience: `/api/v1/health` 404s, `access` resolves
* with no `feeds` at all, and the weather, the aircraft, the satellites and the
* fires are all off. That is the right default — CONTRACT §0's stranger is
* exactly that visitor, and most frames should be shot as they see them — but it
* makes the one question a fire board exists to answer unphotographable.
*
* Proxied through Playwright rather than through a Vite config, for two reasons.
* The build is not modified, so what is photographed is byte-identical to what
* ships; and the response is *fulfilled* rather than redirected, so the page
* sees a same-origin answer and no CORS header has to exist on the API for a
* screenshot to work.
*/
const api = flag("--api", "");
if (api !== "") {
const base = api.replace(/\/+$/, "");
await page.route("**/api/v1/**", async (route) => {
const url = new URL(route.request().url());
try {
const response = await route.fetch({ url: `${base}${url.pathname}${url.search}` });
await route.fulfill({ response });
} catch (error) {
// A refused upstream must look like a refused upstream, not like a hung
// request: the app's own degraded paths are part of what is being judged.
console.log(`look: api proxy failed for ${url.pathname}${error}`);
await route.fulfill({ status: 502, body: "{}", contentType: "application/json" });
}
});
}
const errors = [];
page.on("console", (m) => {
if (m.type() === "error") errors.push(m.text());
});
await page.goto(`http://localhost:${PORT}${flag("--url", "/")}`, {
waitUntil: "networkidle",
timeout: 60000,
});
await page.waitForTimeout(Number(flag("--wait", "10000")));
// The first-run flow covers the scene it is teaching you about.
try {
await page.getByText(/^Skip$/).first().click({ timeout: 2500 });
await page.waitForTimeout(1200);
} catch {
/* already dismissed, or not shown */
}
/**
* `--click` may be given more than once, and they run in order.
*
* One click opens a door; two get you somewhere inside it. The LA office's
* upper storey is the case that forced this: the first click opens the
* building and the second flies to a viewpoint on the floor above, and there is
* no single label that does both.
*/
const clicks = args.flatMap((arg, i) => (arg === "--click" ? [args[i + 1] ?? ""] : []));
if (clicks.length === 0 && typeof preset.click === "string") clicks.push(preset.click);
for (const click of clicks) {
if (click === "") continue;
try {
await page.getByText(new RegExp(click, "i")).first().click({ timeout: 5000 });
await page.waitForTimeout(8000);
} catch {
console.log(`look: could not click ${click}`);
}
}
const path = `${OUT}/${name}.png`;
await page.screenshot({ path });
console.log(`look: wrote ${path}`);
// Two 404s on a static preview are the zero-config path working: see deploy/STATIC.md.
const real = errors.filter((e) => !/404|Failed to load resource/.test(e));
console.log(real.length === 0 ? "look: no console errors" : `look: ERRORS ${JSON.stringify(real)}`);
await browser.close();
shutdown();
// Explicit, because the preview's stdout pipes are inherited by a grandchild
// and Node will happily wait on them for the rest of the afternoon.
process.exit(0);