From b7f5c41da5892dd0aeff95f92ef6efe439e8c2c8 Mon Sep 17 00:00:00 2001 From: Kartios Date: Sat, 22 Aug 2026 13:30:23 -0700 Subject: [PATCH] feat(brand): a shot can take a body, so the last hand-made stills are generated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three pictures on /arena — the chase camera behind the EV, the crow in flight, the aircraft over the board — were the only imagery on the site with no generator script anywhere in either repo. They were made by hand, and so they were a single daylight frame served to dark-mode readers too, with no pair to choose from. `shots.mjs` gains a `play` capability beside `aim`: a shot names a control mode, the script clicks the app's own mode dock, and only then shoots — with the HUD deliberately left in frame, because on these three the body and its readout ARE the subject. `#mode-dock` and `#play-hud` are in FURNITURE.BARE rather than CLUTTER for that reason, and there is a comment at the `hide()` call so a later tidy-up does not crop the subject out. Three guards, each verified by deliberately making it fire: - the mode must be OFFERED, not merely present — asking for `drive` on the Bay Area board now says `"drive" is not offered here — the dock is showing [overview, actor]`; - `aria-pressed="true"` AND `#play-hud` unhidden, the same pair `performance-budget.mjs` waits on; - the HUD's own body chip, so asking for `Explore` when an anonymous visitor gets a crow says `the play HUD says "Crow", not "Explore"`. The body guard runs twice, at the click and again at the shutter, because a completed route or a bound ends a mode mid-wait and hands back an orbit wide shot under the right filename. `untilProgress` exists because the first delivery drifted: `run: 9000` gave 55% of the leg in a 1x preview and 69% in the 2x render — a different stretch of road, with a town's edge across the top of the frame. Road traffic runs from page load, so a duration is really "boot plus a bit". Waiting on the app's own route percentage lands preview and delivery on the same frame; overshoot throws rather than shooting. The crow and the aircraft are built inert, so a duration is exact for them and only the car needed the gate — measured, not assumed. The run log now echoes the HUD (`[Drive] 56 mph · US-101 | assisted · 55% · chase`), because "shot drive-101-day" is equally true of a parked car, a finished route and a correct frame. The crow moved from the state board to the Bay Area. On California a one-metre bird stands against ground drawn at 1,919 m to the unit with an oak the size of a hill behind it, and the night frame would have been a black rectangle with a bird in it. `crow-nav-v1`'s `source` said "Tera / California" and that was wrong on the environment's own terms as well — `crowNav.ts` runs a ±120 m box with no terrain at all. One frame is honestly weak and is recorded rather than dressed up: `drive-101` at night. From behind, on an unlit corridor, the EV is two tail lamps and a roof strip. Four hours were shot between 21:15 and 20:05 and the corridor's ground is equally black at all of them, so the hour is not the lever; both real fixes are in the engine and are in TODO.md. It is still the honest night frame. Co-Authored-By: Claude Opus 5 (1M context) --- TODO.md | 66 +++++- scripts/brand-assets/refresh.mjs | 42 +++- scripts/brand-assets/shots.mjs | 371 ++++++++++++++++++++++++++++++- 3 files changed, 471 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index 7281cc3..ac2d32c 100644 --- a/TODO.md +++ b/TODO.md @@ -21,7 +21,7 @@ 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 only two failures +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 @@ -29,15 +29,77 @@ that otherwise produce confident wrong output: - **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, and it is the product not the capture + +Canvas-region mean luminance, 1600w: `crow-nav` 23.2, `california-flight` 13.6, +`drive-101` **7.8** — against `fidi` 16.0, `golden-gate` 8.2 and the `bay-bridge` +frame this file already calls out, 8.3. Nothing in it needs a lift to be legible — +the road, the markings, the panel, the HUD and the dock all read at native +exposure — but the *subject* nearly disappears: seen from behind on an unlit +corridor the EV is two tail lamps and a roof strip, and the hour is not the lever. +Shot at 21:15, 20:45, 20:20 and 20:05 (sun −15.7° to −3.1°) the corridor's ground +is equally black, and by 20:05 the plan view has flipped to its daylight styling +while the scene has not, which is worse. The two candidate fixes are both in the +engine: give the EV a headlamp spill on the road ahead, or let the corridor's +terrain keep some moonlight the way the city boards' ground does. + +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. + ## Re-shoot the time-lapse films -The 25 product stills and both share cards were re-shot on 2026-08-22 at the +The 28 product stills and both share cards were re-shot on 2026-08-22 at the airports-and-bridges commit and are current. **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 diff --git a/scripts/brand-assets/refresh.mjs b/scripts/brand-assets/refresh.mjs index bb0a090..8c62612 100644 --- a/scripts/brand-assets/refresh.mjs +++ b/scripts/brand-assets/refresh.mjs @@ -26,9 +26,9 @@ * So: one command, and the parts you can skip are flags rather than the memory * of which script does what. * - * ### The two failures this must never report as success + * ### The three failures this must never report as success * - * Both of them produce output that looks deliberate, which is why they are + * All three produce output that looks deliberate, which is why they are * checked here rather than left to the eye: * * 1. **SwiftShader.** `launch()` in `harness.mjs` asks for `--use-angle=vulkan` @@ -46,6 +46,14 @@ * surfaces it as its own headline rather than as one line of a stack trace * fifty lines up the log. * + * 3. **A `play` guard firing.** Three stills take control of a body before the + * shutter opens — the EV on US-101, the crow over the Bay, the aircraft on the + * corridor — and `enterPlay()` in `shots.mjs` refuses to shoot when the dock + * did not hand over the mode, when the HUD names a different body, or when the + * route ran out during the wait. Every one of those otherwise delivers an orbit + * wide shot under a filename that says an agent is driving. Same treatment as + * the chapter guard, and for the same reason: re-running cannot fix it. + * * ### It re-stamps the provenance manifest, narrowly * * `PROVENANCE.json` records a SHA-256 for every tracked binary and @@ -154,6 +162,19 @@ async function fingerprint() { const SOFTWARE_RENDERER = /GPU:.*(SwiftShader|llvmpipe)|no GPU \(/i; /** `chapter()` in `shots.mjs`, and the office picker's equivalent beside it. */ const PACK_REORDERED = /a city pack was reordered|the OFFICES table\s*\n?\s*changed|is "[^"]*", not "/; +/** + * `enterPlay()` in `shots.mjs` refusing to shoot a body it did not get. + * + * The third headline, and here for the same reason the second one is: three of + * the stills are pictures of an agent driving, flying and gliding, and every way + * that can go wrong produces a frame that still renders, still looks deliberate + * and is of the wrong thing — an overview wide shot with `drive-101-day` written + * on it. Re-running does not fix any of them. Either the dock stopped offering + * the mode, or the body is not the one the caption describes, or the leg ran out + * from under the wait. + */ +const BODY_REFUSED = + /is not offered here|the mode dock has no|the dock did not take|the play HUD says|did not survive the wait|was already \d+% done|carries no percentage/; class StepFailure extends Error { constructor(step, headline, detail) { @@ -217,6 +238,23 @@ function run(step, command, args, { cwd = ROOT } = {}) { ); return; } + if (code !== 0 && BODY_REFUSED.test(log)) { + rejectRun( + new StepFailure( + step, + "a play guard fired — a shot did not get the body it asked for", + "Three stills take control of something before the shutter opens: the EV on US-101, the\n" + + "crow over the Bay, the aircraft on the California corridor. Each asks the mode dock for a\n" + + "mode, waits for `aria-pressed` and the play HUD, and asserts which body the HUD names.\n" + + "One of those checks refused, and re-running will not fix it: the mode is unavailable on\n" + + "that board, or the body changed, or the route ran out during the wait. A frame shot past\n" + + "any of those is an orbit wide shot with the right filename.\n" + + "`node scripts/brand-assets/shots.mjs --list` prints what each shot asks for. Do not\n" + + "weaken the guard.", + ), + ); + return; + } if (code !== 0) { rejectRun(new StepFailure(step, `${step} failed (exit ${code})`, "The child's own output is above.")); return; diff --git a/scripts/brand-assets/shots.mjs b/scripts/brand-assets/shots.mjs index c544a69..18dfc39 100644 --- a/scripts/brand-assets/shots.mjs +++ b/scripts/brand-assets/shots.mjs @@ -83,6 +83,12 @@ const ROOT = join(HERE, "..", ".."); * See the long note beside `aimAt` for how each number reaches the engine, and * for why the chapter under an aimed shot is chosen so that the blurb still in * the panel is true of the frame. + * + * `play` is the third camera, and the only one that is not a camera at all: it + * asks the app for a **body**. The three shots that carry it are pictures of an + * agent doing something — the chase camera behind the EV, the crow in the air, + * the aircraft on the corridor — and in those three the play HUD is the subject + * rather than clutter. See the long note beside `enterPlay`. */ const SHOTS = [ { @@ -342,6 +348,129 @@ const SHOTS = [ alt: "The same airport at night, its dark field surrounded by the lights of the Los Angeles basin running to the horizon.", }, }, + /* + * ---- The three that take a body ---------------------------------------- + * + * Everything above is a camera looking at the world. These three are the + * world being *driven*, and they exist because /arena needs a picture of each + * spatial environment actually running: `drive-101-v1`, `crow-nav-v1` and + * `california-flight-v1` are scored against Tera's vehicle, actor and + * aircraft controllers, and the play HUD across the top of each frame is the + * observation those environments are built out of. + * + * They replace three hand-made screenshots. Those were the last pictures on + * lumbridgecorp.com that no script could reproduce, which meant they were also + * the last pictures nobody could re-shoot when the engine moved: all three + * still showed a left column with "Explore as your crow →" in it, a "FULL + * VIEW" badge no visitor to the site ever sees, and a single daylight frame + * served to dark-mode readers as well. Being a script is the point of them. + */ + { + id: "drive-101", + door: "tera", + city: "california", + chapter: 1, + expect: "101", + /* + * A place on the leg, not a number of seconds, and `freeway`'s `settle: 3000` + * next door is the argument for it: the traffic simulation has been running + * since page load, so a duration is only ever "boot plus a bit" and boot is + * not a constant. The same nine seconds put this car at 55% of the leg in a + * 1x preview and 69% in the 2x render, which is a different stretch of road + * — the second one had the edge of a town across the top of the frame, where + * buildings drawn at 1,919 m to the unit stand beside a road built at one. + * + * 55% is open oak country with both carriageways in the frame and other + * traffic on them, which is the picture: an agent alone on a road is not + * being asked anything. + */ + play: { mode: "drive", expect: "Drive", untilProgress: 0.55 }, + place: "US-101, at the wheel", + caption: + "Taking the body is an input the app already accepts: the Drive button on the dock hands the vehicle controller the held keys and the follow camera, and the strip across the top is that controller reporting itself — speed, which road, whether the assist or a person is steering, how far along the leg, which camera. It is the same state Arena's drive-101 environment builds its observation out of, drawn for a human rather than serialised for a policy, which is the whole claim these pictures exist to make: the environment is this, running, and not a diagram of it. The unbadged Lumbridge EV is on the asphalt of the one corridor built at a metre to the metre, on a board that is otherwise 1,919 metres to the unit.", + day: { + at: "2026-08-06T09:00:00-07:00", + note: "Nine in the morning, the sun across the carriageway rather than down it, so the median and the shoulders keep an edge under the car. Everything on the readout is the simulation's own number — 56 mph is what the controller is doing, not a figure somebody typed into a mock-up.", + alt: "A chase camera above and behind a dark car driving a divided freeway through dry oak country, another vehicle further down the same carriageway, and a heads-up strip across the top of the frame reading its speed, the road name, the assist state and the route progress.", + }, + night: { + at: "2026-08-06T21:15:00-07:00", + note: "Quarter past nine, and this corridor at night is what a car on an unlit road actually is: two tail lamps, the markings taking the light back, and a second set of lamps up the carriageway. Nothing here is lit that would not be — the hills either side of US-101 have nothing on them to switch on, which is exactly the argument for shooting the crow over a city instead.", + alt: "The same chase camera at night, the car reduced to its tail lamps with the lane markings and edge lines catching what light there is against black hills, and the same heads-up strip along the top.", + }, + }, + { + id: "crow-nav", + door: "tera", + city: "sf", + chapter: 0, + expect: "Whole Board", + /* + * The Bay Area board rather than California, which is where the hand-made + * original was taken. Two reasons, and the second is the one that decides it. + * + * The crow is authored at about a metre. On the state board a metre is + * 0.0005 of a scene unit and the frame around it is bare ground drawn at + * 1,919 m to the unit, so the original is a bird in front of a featureless + * sand-coloured wall with one oak the size of a hill behind it — the same + * scale mismatch the `freeway` note warns about, and it reads as a rendering + * fault. On the Bay Area board a metre is a hundredth of a unit and there is + * a city under the wings. + * + * And the night frame has to stand on its own. It is served to every + * dark-mode reader on the page and it is the only frame they get; a crow + * over unlit valley floor at half past nine is a black rectangle with a bird + * in it. Over San Francisco the ground carries itself. + */ + play: { mode: "actor", expect: "Crow", run: 5000 }, + place: "Over San Francisco, as a crow", + caption: + "The same dock, a different body. Explore hands the held keys to an articulated crow, and the wings are not a clip being played: the controller carries a pose — flap, glide, bank, tuck, perch — and a phase whose rate rises with throttle and falls as the reserve goes, so what the bird is doing is a readout of the same numbers the HUD is showing. Airspeed and altitude in place of a speedometer, and the flight energy that beating spends and gliding does not, which is why Arena's crow-nav environment can price powered circling: it costs the one quantity the goal also needs.", + day: { + at: "2026-08-06T09:20:00-07:00", + note: "Twenty past nine, over the downtown towers with the bay, a crossing and the far shore stacked up behind them — the whole board is in one frame because the body is above all of it, which is the one camera the chapter list has no button for.", + alt: "An articulated crow seen from behind, wings spread, flying over the towers of downtown San Francisco in morning light with the bay, a crossing and the far shore beyond, and a heads-up strip along the top reading its airspeed, altitude, pose and flight energy.", + }, + night: { + at: "2026-08-06T21:35:00-07:00", + note: "Twenty-five to ten, and the frame has inverted: by day the bird is the dark thing on a pale board, and at night it is the dark thing on a board made of windows. Same body, same altitude, same pose — only the hour was asked for.", + alt: "The same crow at night, silhouetted against the lit windows of the San Francisco towers below and the dark bay beyond, with the same heads-up strip along the top.", + }, + }, + { + id: "california-flight", + door: "tera", + city: "california", + chapter: 0, + expect: "State", + /* + * Chapter 0 rather than 1, and it matters for the same reason an aimed shot + * picks its chapter: the blurb left in the panel is a caption inside the + * photograph. "State" says the two cities are joined as one route board, + * which is exactly what the aircraft in the frame is flying between; "101" + * would be talking about a road the frame does not contain. + * + * The aircraft is on the California board and nowhere else — `main.ts` + * builds it only for that pack — so this shot cannot move to another board + * the way the crow did. Five seconds from the LA waypoint is far enough that + * it is over ground rather than sitting on its start, and near enough that + * the basin is still what is underneath at night. + */ + play: { mode: "aircraft", expect: "Flight", run: 5000 }, + place: "The California corridor, flying", + caption: + "The third body and the only one with a published route: the distributed-electric aircraft starts at Los Angeles on a three-waypoint corridor to San Francisco, and Fly takes it off its own autopilot and gives you the yoke. The HUD is in knots, metres and watt-hours because that is what an aircraft has, and the battery is the reason the environment is not a pure altitude race — Arena's california-flight scores waypoint progress against energy, and climbing is the cheapest way to farm a number that is not the goal.", + day: { + at: "2026-08-06T18:00:00-07:00", + note: "Six in the evening, five seconds off the Los Angeles waypoint on a heading of 320°, with the coast ranges throwing across the corridor and the Pacific past the wingtip. The altitude on the readout is above sea level, not above the ridge under it — the controller keeps both, and the one that terminates an episode is the clearance.", + alt: "A distributed-electric aircraft seen from behind and above, flying north-west over green coastal mountains in late afternoon light with a freeway threading the valley below and the Pacific beyond, and a heads-up strip along the top reading its airspeed, altitude, flight mode and battery.", + }, + night: { + at: "2026-08-06T21:20:00-07:00", + note: "Twenty past nine, and three things are carrying a frame that has no daylight in it: the basin's lit ground in the corner, the moon's path broken across the water, and the aircraft's own lights — red on the port wingtip, green on the starboard, white strobes and a tail light — which are unlit materials, so they are the only part of this picture that does not care what the sun is doing.", + alt: "The same aircraft at night over the lights of the Los Angeles basin, the same heads-up strip along the top.", + }, + }, { id: "office-floor", hide: ["#device-section"], @@ -634,6 +763,203 @@ async function aimAt(page, rect, toPx, aim) { await page.waitForTimeout(900); } +/** + * ---- Taking a body ---------------------------------------------------------- + * + * `aim` points a camera at the world. `play` asks the app to hand over a *body* + * — the car, the crow, the aircraft — and then photographs the world moving + * under it, with the play HUD deliberately left in the frame. + * + * It is the same kind of input `aim` is, and for the same reason: the engine has + * no back door. There is no `?mode=drive`, and `main.ts` keeps the control mode + * in a module-scoped `ControlModeState` that nothing outside it can write. What + * it does have is the dock — `