From 01dcecf6e9df3ebc0eafe89d335d58dcfbb4e3f2 Mon Sep 17 00:00:00 2001 From: Tom-Neverwinter <3943634+Tom-Neverwinter@users.noreply.github.com> Date: Fri, 11 Sep 2026 14:43:13 -0700 Subject: [PATCH 1/3] fix(qa): choose portable browser renderers outside macOS --- scripts/qa-cockpit-plates.mjs | 4 +++- scripts/qa-firstrun.mjs | 5 ++++- scripts/qa-floor-hold.mjs | 3 ++- scripts/qa-view-target-prewarm.mjs | 8 +++++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/qa-cockpit-plates.mjs b/scripts/qa-cockpit-plates.mjs index f7a8f052f..b5481cfe5 100644 --- a/scripts/qa-cockpit-plates.mjs +++ b/scripts/qa-cockpit-plates.mjs @@ -222,7 +222,9 @@ async function main() { args: [ '--no-sandbox', '--disable-setuid-sandbox', - ...(SWIFTSHADER + // Metal is a macOS-only ANGLE backend; off-Mac the GPU branch has to fall + // back to the software rasterizer or WebGL init fails outright. + ...(SWIFTSHADER || process.platform !== 'darwin' ? ['--use-gl=angle', '--use-angle=swiftshader'] : ['--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist']), '--disable-dev-shm-usage', diff --git a/scripts/qa-firstrun.mjs b/scripts/qa-firstrun.mjs index de41ba5a6..70a7106d0 100644 --- a/scripts/qa-firstrun.mjs +++ b/scripts/qa-firstrun.mjs @@ -491,7 +491,10 @@ async function main() { ...(executablePath ? { executablePath } : {}), args: [ '--no-sandbox', '--disable-setuid-sandbox', - '--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist', + // Metal is a macOS-only ANGLE backend; anywhere else it fails WebGL init. + ...(process.platform === 'darwin' + ? ['--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist'] + : ['--use-gl=angle', '--use-angle=swiftshader']), '--disable-dev-shm-usage', '--disable-background-timer-throttling', '--disable-renderer-backgrounding', '--window-size=1440,900', ], diff --git a/scripts/qa-floor-hold.mjs b/scripts/qa-floor-hold.mjs index 59542b547..798a3e560 100644 --- a/scripts/qa-floor-hold.mjs +++ b/scripts/qa-floor-hold.mjs @@ -37,7 +37,8 @@ import path from 'node:path'; const APP_URL = process.env.QA_BASE_URL || 'http://localhost:4173'; const argv = Object.fromEntries(process.argv.slice(2) .map((a) => a.replace(/^--/, '').split('=')).map(([k, v]) => [k, v ?? true])); -const ANGLE = String(argv.angle || 'metal'); +// Metal is a macOS-only ANGLE backend; defaulting to it off-Mac fails WebGL init. +const ANGLE = String(argv.angle || (process.platform === 'darwin' ? 'metal' : 'swiftshader')); const HEADFUL = !!argv.headful; // Austin airport apron by default — the site qa-floor-verify pins, where the // mesh sits ~150 m above the geoid and burial is unmistakable. diff --git a/scripts/qa-view-target-prewarm.mjs b/scripts/qa-view-target-prewarm.mjs index 272c2288e..d0d6cb711 100644 --- a/scripts/qa-view-target-prewarm.mjs +++ b/scripts/qa-view-target-prewarm.mjs @@ -39,7 +39,13 @@ const executablePath = chromeCandidates.find((candidate) => fs.existsSync(candid const browser = await puppeteer.launch({ headless: headful ? false : 'new', ...(executablePath ? { executablePath } : {}), - args: ['--use-angle=metal', '--enable-gpu', '--no-sandbox'], + // Metal is a macOS-only ANGLE backend; anywhere else it fails WebGL init. + args: [ + ...(process.platform === 'darwin' + ? ['--use-angle=metal', '--enable-gpu'] + : ['--use-gl=angle', '--use-angle=swiftshader']), + '--no-sandbox', + ], }); const page = await browser.newPage(); const failures = []; From 67504cd87143cab3bc060f8ec3749c9f1683324f Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Fri, 11 Sep 2026 14:50:57 -0700 Subject: [PATCH 2/3] fix(qa): align renderer evidence and attribution keyboard assertions --- CHANGELOG.md | 11 +++++++++++ TESTING.md | 11 +++++++++++ scripts/qa-cockpit-plates.mjs | 8 ++++---- scripts/qa-firstrun.mjs | 13 ++++++++----- scripts/qa-floor-hold.mjs | 11 ++++++----- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa76d97fd..08d2afc3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,17 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md ## [Unreleased] +### Fixed + +- Complete the first-run, view-target prewarm, cockpit-plates and floor-hold + browser harness renderer portability fixes contributed by Tom-Neverwinter. + macOS retains Metal; other platforms default to SwiftShader. Cockpit renderer + assertions and evidence labels follow the actual selected mode. Floor-hold + keeps its mesh and terrain assertions; software runs are not real-GPU evidence. + First-run QA now checks the existing attribution Escape-close/focus-return + behavior while preserving the launcher-underneath regression checks. + + - Datacenter and dam factories are available through scoped package exports with explicit context, overlay and render callbacks. The standalone app uses the same implementation and bundled datasets. diff --git a/TESTING.md b/TESTING.md index d812ea693..95eddb50c 100644 --- a/TESTING.md +++ b/TESTING.md @@ -218,3 +218,14 @@ While recording, call out anything in these areas — this is the feedback I mos - **No planes:** OpenSky data may be momentarily sparse; scroll out or wait a poll cycle. - **No GEV MIC button / voice errors:** `OPENAI_API_KEY` didn't load — use the console API for the annotation tests and skip the voice-only ones (§2). + + +## Browser harness renderers + +The first-run, view-target prewarm, cockpit-plates and floor-hold harnesses +select Metal on macOS and SwiftShader on other platforms. Cockpit-plates also +accepts `--swiftshader` on macOS; floor-hold retains `--angle=`. +Software runs validate their assertions but do not establish real-GPU visual +correctness. Floor-hold retains both mesh and DEM checks: an unavailable mesh +oracle fails the run even when the DEM check passes. Record the backend with +any screenshots and run GPU visual checks separately when needed. diff --git a/scripts/qa-cockpit-plates.mjs b/scripts/qa-cockpit-plates.mjs index b5481cfe5..bc42ab821 100644 --- a/scripts/qa-cockpit-plates.mjs +++ b/scripts/qa-cockpit-plates.mjs @@ -46,7 +46,7 @@ * node scripts/qa-cockpit-plates.mjs --url http://localhost:4268 --tag before * node scripts/qa-cockpit-plates.mjs --url http://localhost:4268 --teeth * - * Defaults to the real GPU (ANGLE/Metal). `--swiftshader` selects software GL, + * Defaults to ANGLE/Metal on macOS and software GL elsewhere. `--swiftshader` selects software GL, * which is deterministic but is NOT real-GPU evidence; the banner says which * backend actually ran and the run records it alongside the results. */ @@ -64,7 +64,7 @@ const APP_ORIGIN = new URL(APP_URL).origin; const TAG = getOpt('--tag', 'after'); const HEADFUL = argv.includes('--headful'); const TEETH = argv.includes('--teeth'); -const SWIFTSHADER = argv.includes('--swiftshader'); +const SWIFTSHADER = argv.includes('--swiftshader') || process.platform !== 'darwin'; const SHOT_DIR = path.resolve('qa-shots/cockpitplates'); /** Mirrors `SKY_PLATE_SCALE` in src/overlays/worldOverlayTokens.js. */ @@ -224,7 +224,7 @@ async function main() { '--disable-setuid-sandbox', // Metal is a macOS-only ANGLE backend; off-Mac the GPU branch has to fall // back to the software rasterizer or WebGL init fails outright. - ...(SWIFTSHADER || process.platform !== 'darwin' + ...(SWIFTSHADER ? ['--use-gl=angle', '--use-angle=swiftshader'] : ['--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist']), '--disable-dev-shm-usage', @@ -294,7 +294,7 @@ async function main() { console.log(` GL : ${renderer}\n`); record( SWIFTSHADER - ? 'running on software GL as requested (structural checks, not GPU evidence)' + ? 'running on software GL (structural checks, not GPU evidence)' : 'running on the real GPU, so the screenshots are real-GPU evidence', SWIFTSHADER ? softwareRenderer : !softwareRenderer, renderer, diff --git a/scripts/qa-firstrun.mjs b/scripts/qa-firstrun.mjs index 70a7106d0..24d82bd2b 100644 --- a/scripts/qa-firstrun.mjs +++ b/scripts/qa-firstrun.mjs @@ -332,14 +332,17 @@ async function runArbitrationSection(page, { shots, consoleErrors }) { `session=${escUnderLightbox.session}`, ); record( - 'the lightbox keeps its OWN ESC semantics (Cesium binds none, so it stays)', - lightboxAfterEsc.shown, + 'ESC closes attribution without dismissing the launcher underneath', + !lightboxAfterEsc.shown, `overlay shown=${lightboxAfterEsc.shown}`, ); - // The guard disarms the launcher; it must never break it. Close the overlay - // the way a visitor does and the key comes straight back. - await page.evaluate(() => document.querySelector('.cesium-credit-lightbox-close')?.click()); + // The attribution keyboard handler closes the overlay and restores focus. + // A second Escape belongs to the now-uncovered launcher. + const attributionFocusRestored = await page.evaluate(() => ( + document.activeElement === document.querySelector('#cesium-credits .cesium-credit-expand-link') + )); + record('closing attribution restores its disclosure focus', attributionFocusRestored); await sleep(300); const uncovered = await launcherState(); record('closing the lightbox hands the card back the key', diff --git a/scripts/qa-floor-hold.mjs b/scripts/qa-floor-hold.mjs index 798a3e560..00eac1807 100644 --- a/scripts/qa-floor-hold.mjs +++ b/scripts/qa-floor-hold.mjs @@ -15,7 +15,7 @@ * The contact must stay ON the mesh through phase B. Without the hold it falls * to the geoid, which at an inland field is ~150 m of burial. * - * node scripts/qa-floor-hold.mjs # real GPU (ANGLE/Metal) + * node scripts/qa-floor-hold.mjs # Metal on macOS, SwiftShader elsewhere * node scripts/qa-floor-hold.mjs --headful # watch it * QA_BASE_URL=http://localhost:4257 node scripts/qa-floor-hold.mjs * @@ -25,10 +25,11 @@ * under Metal — a backend comparison at one point, not to be confused with the * same-backend run-to-run spread (122.1 m vs 20.6 m on a cell centre) that * retired the hold chain's unvalidated tier. The - * default is the real GPU. Under `--angle=swiftshader` the rendered-mesh oracle - * is reported as unavailable rather than trusted, and the run falls back to the - * bare-earth DEM (fetched server-side, so the simulated outage cannot reach - * it) — a weaker but still decisive check, since the geoid sits ~150 m below it. + * default is Metal on macOS and SwiftShader elsewhere; `--angle` overrides it. + * Mesh samples describe the selected backend, not another GPU's rendered LOD. + * An unavailable rendered-mesh oracle remains a failed check, even if the + * separate bare-earth DEM check passes. Software evidence does not establish + * real-GPU visual correctness. */ import puppeteer from 'puppeteer'; import fs from 'node:fs'; From a8eae34787cad1964654dba485307e9436b58fce Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Fri, 11 Sep 2026 14:54:30 -0700 Subject: [PATCH 3/3] fix(qa): select the billboard mode measured by floor-hold checks --- CHANGELOG.md | 2 +- TESTING.md | 2 ++ scripts/qa-floor-hold.mjs | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08d2afc3c..e71f98787 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md browser harness renderer portability fixes contributed by Tom-Neverwinter. macOS retains Metal; other platforms default to SwiftShader. Cockpit renderer assertions and evidence labels follow the actual selected mode. Floor-hold - keeps its mesh and terrain assertions; software runs are not real-GPU evidence. + explicitly selects its measured 2D billboard mode and keeps its mesh and terrain assertions; software runs are not real-GPU evidence. First-run QA now checks the existing attribution Escape-close/focus-return behavior while preserving the launcher-underneath regression checks. diff --git a/TESTING.md b/TESTING.md index 95eddb50c..fb9ca960a 100644 --- a/TESTING.md +++ b/TESTING.md @@ -225,6 +225,8 @@ While recording, call out anything in these areas — this is the feedback I mos The first-run, view-target prewarm, cockpit-plates and floor-hold harnesses select Metal on macOS and SwiftShader on other platforms. Cockpit-plates also accepts `--swiftshader` on macOS; floor-hold retains `--angle=`. +Floor-hold explicitly selects 2D aircraft mode because it measures billboard +positions; the tracking suite covers the 3D handoff. Software runs validate their assertions but do not establish real-GPU visual correctness. Floor-hold retains both mesh and DEM checks: an unavailable mesh oracle fails the run even when the DEM check passes. Record the backend with diff --git a/scripts/qa-floor-hold.mjs b/scripts/qa-floor-hold.mjs index 00eac1807..82bdf6baa 100644 --- a/scripts/qa-floor-hold.mjs +++ b/scripts/qa-floor-hold.mjs @@ -110,6 +110,10 @@ await page.goto(APP_URL, { waitUntil: 'domcontentloaded', timeout: 120000 }); await page.waitForFunction(() => window.__godsEyeView?.viewer && window.__godsEyeView?.dataManager, { timeout: 150000 }); await sleep(12000); // let the boot fly-to settle before pinning +await page.evaluate(() => { + document.querySelector('#first-run-launcher:not([hidden]) [data-first-run-choice="explore"]')?.click(); +}); +await sleep(600); // let the launcher dismissal finish before taking evidence await page.evaluate((site) => { window.__SITE = site; }, SITE); const pin = () => page.evaluate(() => { @@ -125,7 +129,16 @@ const pin = () => page.evaluate(() => { v.camera.moveEnd.raiseEvent(); }); await pin(); -await page.evaluate(async () => { await window.__godsEyeView.dataManager.toggle('flights'); }); +const billboardMode = await page.evaluate(async () => { + const manager = window.__godsEyeView.dataManager; + const flights = manager.layers.get('flights').module; + // This harness measures billboard positions. A ready 3D model deliberately + // hides its billboard; that handoff is covered by track-regression instead. + flights.setParams({ models3d: false }); + await manager.toggle('flights'); + return flights.getParams().models3d === false; +}); +record('the billboard floor test is explicitly in 2D aircraft mode', billboardMode); /** Reads the contact's rendered height and the rendered mesh beneath it. */ const measure = () => page.evaluate(async (icao) => {