It feels like a forbidden cockpit' + '—then you realize the sources are public and the data is real.
', ), 'the owner-authored first-run line must ship exactly as written', ); // Menu order is the owner's, read straight off the markup. const order = [...html.matchAll(/data-first-run-choice="([a-z-]+)"/g)].map( (match) => match[1], ); assert.deepEqual(order, [ 'contacts', 'space-missions', 'environmental', 'explore', ]); assert.doesNotMatch( html, /data-first-run-choice="infrastructure"/, 'the removed tile must leave no markup behind', ); assert.match(startup, /styleManager\.initialRestorePromise/); assert.ok( startup.indexOf("loadingScreen.classList.add('hidden')") < startup.indexOf( "loadingScreen.addEventListener('transitionend', revealFirstRun", ), ); assert.match( startup, /initializeWelcome\?\.\(\{ styleManager, dataManager \}\)/, ); assert.match(css, /body\.ui-clean-view #first-run-launcher/); assert.match(css, /body\.recording-mode #first-run-launcher/); // Scoped, not a bare search: style.css has other reduced-motion blocks, and // matching one of THOSE would let the launcher's own opt-out be deleted. const reducedMotion = [ ...css.matchAll( /@media \(prefers-reduced-motion: reduce\) \{([\s\S]*?)\n\}/g, ), ].map((match) => match[1]); assert.ok( reducedMotion.some( (block) => block.includes('#first-run-launcher') && block.includes('.first-run-choices'), ), 'the launcher must keep its OWN prefers-reduced-motion block, covering the card and the scrolling list', ); // The card must be click-through until revealed and again while it fades out. const base = css.slice( css.indexOf('#first-run-launcher {'), css.indexOf('#first-run-launcher.visible'), ); assert.match(base, /pointer-events: none/); assert.match( css, /#first-run-launcher\.visible \{[\s\S]*?pointer-events: auto/, ); // The card is a flex column so its mission list can scroll on a short // viewport — and an AUTHOR `display` on this id outranks the UA's // `[hidden] { display: none }`, which would strand the card in the // accessibility tree until it is revealed or removed. assert.match(base, /display: flex/); assert.match(base, /max-height: calc\(100dvh/); assert.match(css, /#first-run-launcher\[hidden\] \{\s*display: none;\s*\}/); // Only the mission list may scroll: the heading, checkbox and status line // have to stay on screen at every height. const choicesBlock = css.match(/\.first-run-choices \{([^}]*)\}/)?.[1] || ''; assert.match(choicesBlock, /min-height: 0;/); assert.match(choicesBlock, /overflow-y: auto;/); }); test('the launcher keeps focus, restores it, and never disables the focused button', () => { const module = fs.readFileSync( new URL('./firstRunExperience.js', import.meta.url), 'utf8', ); // aria-disabled, never the `disabled` property: disabling a focused button // drops the keyboard to and strands the visitor outside the launcher. assert.match( module, /button\.setAttribute\('aria-disabled', String\(next\)\)/, ); assert.doesNotMatch(module, /button\.disabled = /); // Tab is confined to the launcher, and ESC always releases it. const keyboard = fs.readFileSync( new URL('./ui/surfaceKeyboard.js', import.meta.url), 'utf8', ); assert.match(module, /keyboard\.activate\(\)/); assert.match(module, /keyboard\.deactivate\(\{ restoreFocus \}\)/); assert.match(keyboard, /event\.key !== 'Tab'/); assert.match(keyboard, /event\.key === 'Escape'/); assert.match(keyboard, /target\?\.focus/); // Capture phase, so the app's global letter hotkeys cannot eat the launcher's keys. assert.match(keyboard, /addEventListener\('keydown', onKeyDown, true\)/); }); test('the DISPLAY rail starts collapsed on a first run, and a stored choice wins', () => { const ui = fs.readFileSync( new URL('./ui/panelPositionControls.js', import.meta.url), 'utf8', ); // The rail opened by default to advertise HUD / DETECT / 3D. Those default ON // now, so it was opening to offer controls for things already happening — // while competing with the mission card for the one first impression there is. assert.match( ui, /if \(panelId === 'pp-toggles' && stored === null\) collapsed = true;/, 'first run must leave DISPLAY collapsed', ); // The first-run default may only apply when NOTHING is stored: a visitor who // opened the rail keeps it open, and one who closed it keeps it closed. const block = ui.slice( ui.indexOf( 'stored = localStorage.getItem(this._panelCollapseStorageKey(panelId))', ), ); const guard = block.slice(0, block.indexOf('panelEl.classList.toggle')); assert.match(guard, /if \(stored === '1'\) collapsed = true;/); assert.match(guard, /if \(stored === '0'\) collapsed = false;/); assert.ok( guard.indexOf("stored === '0'") < guard.indexOf('pp-toggles'), 'the stored-state reads must come before the first-run default', ); }); // ── Voice: instruction-only, tool schema unchanged ───────────────────── test('the voice TOOL SCHEMA matches the pinned contract — the mission mapping is instructions only', () => { // Analyst layers and the separate satellite-pass tool deliberately extend the schema. // Canonical serialization pins every tool name, description, property and // ordering while allowing source formatting. Derived from the unchanged // release schema before formatting (the previous source-byte pin passed). const legacyTools = structuredClone(GEV_REALTIME_TOOLS).filter( (tool) => tool.name !== 'set_cyber_sonar', ); const hudLayout = legacyTools.find((tool) => tool.name === 'set_hud') .parameters.properties.layout; assert.deepEqual(hudLayout.enum, [ 'tactical', 'operator', 'minimal', 'cyber', ]); // Cyber deliberately adds one layout; first-run missions still change no tools. hudLayout.enum = hudLayout.enum.filter((layout) => layout !== 'cyber'); const cameraMotion = legacyTools.find((tool) => tool.name === 'move_camera') .parameters.properties.motion; assert.deepEqual(cameraMotion.enum, [ 'orbit', 'pan', 'tilt', 'rotate', 'rotate_globe', 'stop', ]); const block = JSON.stringify(legacyTools); // Re-derived for the voice layer manifest (generated layer enums, alias and // field hints), point-and-ask's pointer/referent arguments and the prompt // consolidation (shorter analyst, annotate_map and ISS wording), plus the // referent-only track_entity alternative (kept out of the model-facing // schema, which may not carry top-level anyOf), and Contacts requested-radius // list descriptions; the missions still ride existing tools. // Street Level and GNSS extend the manifest; broadcast caveats describe tracked and analyst aircraft. // Air quality (ECCC AQHI) extends the manifest. // Globe rotation adds one camera motion and its descriptions; Globe View // framing/default guidance updates only move_camera and mode descriptions. // Radio markup and the complete production-request comparison pin locality. // Restore the base camera/Globe View wording with the globe-rotation append. assert.equal(block.length, 30878, 'serialized tool schema length drifted'); assert.equal( crypto.createHash('sha256').update(block).digest('hex'), '7b95f0f4258896fe5c3316265a19f3a6602809d4e7e681ee7ecd8f2df514ef1f', 'the first-run missions must ride EXISTING tools: no schema edit, no cache bust', ); const instructions = fs.readFileSync( new URL('../server/providers/openai/instructions.js', import.meta.url), 'utf8', ); // ...and the mapping that makes them reachable by voice is one instruction // string, whose rollback is deleting that string. Anchored to a LIVE array // entry — a quote at the start of its own line — so commenting the paragraph // out reads as the removal it is, not as a passing substring match. assert.match( instructions, /\n\s+'NAMED VIEWS are shorthand/, 'the mission mapping must be an active instruction entry, not commented out', ); const mapping = instructions.slice( instructions.indexOf('NAMED VIEWS are shorthand'), ); const paragraph = mapping.slice(0, mapping.indexOf("',\n")); for (const layerId of [ 'local-datacenters', 'local-dams', 'telegeography-submarine-cables', 'local-firms', 'earthquakes', ]) { assert.ok( paragraph.includes(layerId), `mapping must name the existing ${layerId} enum value`, ); } assert.ok(paragraph.includes('zoom_to_globe')); assert.ok(paragraph.includes('set_layer_visibility')); }); test('every layer a mission drives is already in the shipped set_layer_visibility enum', () => { const tool = GEV_REALTIME_TOOLS.find( (tool) => tool.name === 'set_layer_visibility', ); const allowedLayers = tool.parameters.properties.layerId.enum; const missionLayerIds = Object.values(FIRST_RUN_MISSIONS).flatMap( (mission) => mission.layerIds || [], ); assert.ok(missionLayerIds.length > 0); for (const layerId of missionLayerIds) { assert.ok( allowedLayers.includes(layerId), `${layerId} must already be an allowed enum value`, ); } });