scripts/report-engine-coupling.mjs counts the engine-specific code left
outside src/globe/cesium and src/globe/navara: layer renderer lines per
engine, Cesium imports, `viewer` mentions, engine-name checks, raw Navara
reads, Navara ground passes, Cesium SDK loads and ScreenSpaceEventHandler
users. engineCoupling.test.mjs fails npm test when a count grows past
scripts/engine-coupling-baseline.json, so each step towards drawing
through the globe contract can only lower them.
scripts/qa-contract-suite.mjs runs src/globe/testing/contractSuite.mjs in
headless Chrome on the Cesium and Navara of a keyless dev server, which
until now only ran on the fake engine. The page imports the suite from the
dev server, starts at a fixed view, waits for the camera to rest and lets
the pointer through the panels. Cesium passes 57 of 57 checks, Navara 64
of 67; docs/CURRENT-STATE.md names the three that fail.
Main's camera, voice, Cockpit and layer work now runs on the globe
contract where this branch draws through it:
- The voice tilt to an angle, its pivot about the place under the
crosshair and the frame limit from orbit run through the camera rig;
src/globeInView.js measures a camera pose with src/geo instead of a
Cesium camera. The free-look height limit and the globe-in-view
recovery stay Cesium-only for now.
- Cockpit takes main's own-layer entry rule. Double-clicking the
followed track goes through cockpitHost.routeDoubleClick, so it works
on both engines.
- Space Missions' failed-entry rollback and GhostMaps' age filter run
in the engine-neutral layer code. On Cesium an expired report is
dropped in place; on Navara its product is redrawn.
- Wildlife Sightings joins the catalog Cesium-only: Navara leaves it
out, and the shell builds its card only for the real layer, so a
Navara page no longer fails to start on its stand-in.
- One instruction sentence is shorter, which keeps the Realtime prompt
within its 55,500-character budget with the Sentinel-2 basemap.
- Publish classificationName next to the raw NHC code; unknown codes stay
unexpanded.
- Publish staleCacheFetchedAt only while the proxy serves its cache.
- Touch the shared store on clear only after this layer published, so a
feed failure no longer evicts another layer's dangling selection.
- Drop the unreachable no-storm branch and the unused return value.
- Tests: split the heading fixture into due north and stationary, cover
the unavailable and failure paths with the real store, and check that
clear/disable leave another layer's selection alone; fold the fake-store
test into the real-store one.
Cesium lifts a camera below its globe every frame, between the cockpit's 20 Hz updates, so an eye placed under the terrain (an aircraft reading below it on a cold floor cell) jumped up and down. The cockpit now also keeps its eye above the engine's own surface. Navara's ground floor stands aside while the camera inputs are off, since the cockpit places the camera itself.
The data refresh is committed and its provenance (extract, timestamp, MD5,
filter) stays in the dataset README. The script needed osmium-tool and could
only re-verify while Geofabrik still listed that extract, so it goes with its
test.
Navara now draws at CSS pixels without MSAA, like Cesium; Navara HD keeps the device pixel ratio and MSAA 4. Only one Navara variant is ever kept. A parked engine is released after three minutes and its WebGL context is lost. Three leaks that kept a released Navara alive are fixed, the canvas context drops its unused antialias buffer, cloud shadows use two 256 px cascades, and the tile cache is budgeted from the screen buffers.
Name each Rio feed in its credit instead of repeating the licence, drop the
deprecation history from the bus note, list Rio among the feeds with a
route-name table, and shorten the route-table header and test comments.
- Navara 0.2.1 never frees pick batch ids, and the mover banks rebuilt
their instanced meshes on each attach, leaking about 260 KiB of
WebAssembly memory per switch. A per-view pool hands released ids back
out; the long soak now asserts native growth of at most 32 KiB per switch.
- Navara 0.2.1 sometimes drops the first show:false update on a new
vector layer, so a zone turned off right after a switch back stayed
drawn. Polygon displays hide by deleting their layers and rebuild them
when shown.
- Camera verbs drive both engines through the globe contract; the Cesium
viewer rig and the tests of states it can no longer reach are gone.
- Flights and military tracking share src/layers/aircraft/tracking.js.
- The geo import scenario reads movers only where the engine has them.
- Add the Navara section to docs/CURRENT-STATE.md and a changelog fragment.
Both SMTR feeds now publish Position.speed in m/s. Compared with how far
vehicles moved between reports on 2026-11-05, the published values ran
3.1x (buses) and 3.6x (BRT) below the observed speed, so the km/h
conversion divided real speeds by 3.6. Drop speedUnit and the conversion.
- src/geo drops the ported debug argument checks and the tests that
bitExact already covers; package boundaries accept a directory entry,
so src/geo/ is listed once per group.
- Cesium no longer carries movers and lines nobody draws with: they are
optional engine capabilities. The fake engine and the Cesium camera use
the shared cameraMath; unused contract tables, clock.ticked, the
'finest' surface detail and Navara click statistics are gone.
- ALPR keeps one overlay core and one presentation model for both
engines, bikeshare one station bookkeeping, the Navara ground effects
one definition helper; image atlases, view centres, mover banks and
colours are shared.
- The legacy switch route, the clock offset and rate controls and other
unused exports are removed.
- The acceptance runs the Navara draw, geo import and scope checks;
scenarios use the harness switch and page helpers; the main chunk
check keeps the size budget only and runs once in CI.
The launches layer swallowed every fetch error in its update, so the
manager treated a 429 or timeout on /api/launches as a successful start.
Context then committed the isolating mode with no missions: the other
layers stayed off, unrelated enables were refused, the camera stayed at
the full-globe view and the roster kept reading "loading".
Until the first index loads, the update now reports the failure. The
manager fails the enable closed and both entry routes roll back to the
pre-entry layers. The camera pose captured at entry is restored when the
mode is left before any index loaded, and the roster shows a failed
state instead of loading. A valid empty index still enters, and a
refresh failure after a loaded index keeps its records.
An installation card on Navara now names an unnamed feature and its
class in the UI language, and an ALPR camera keeps its context label as
English data that is translated when shown.
Arabic catalog strings carry optional marks ("تتبّع"), but people type
without them, so "تتبع" found no suggestion. Fold the marks out of both
the draft and the candidates before comparing.
Brings the Active Fires intensity work: the FRP filter, the confidence
switch, the ember-to-white-hot ramp and the far-zoom heatmap.
The filter, switch and ramp work on both engines. The heatmap drapes on
Cesium through the layer's Cesium renderer, which also fades the markers
across the hand-off; it goes with that renderer on an engine switch and is
built again on the next Cesium globe. Navara keeps heat cells for the far
bands, now coloured on the FRP ramp.
Main's new layers (drone airspace, OSM places, sea marks, ski pistes) are
Cesium-only for now: they join the layer directory and the Cesium catalog,
are built afresh for each Cesium catalog, and show as unavailable on Navara.
The zoom-out guard is a scene part that attaches to Cesium globes only. The
awareness range pulses and their echoes draw on Cesium through the awareness
display's viewer; Navara keeps the plain range ring. Camera-input wakes go
through the display, and the analyst keeps the session-owned memory with
main's area presentation. A disposed voice runner aborts work in flight, and
an engine switch still keeps the last answer.
Modules main added that read Cesium while they load (zoomOutGuard,
awarenessEcho, the ground-sampling scratch in localGeojsonCore) now build
those values on first use or use src/geo, so a Navara page still starts
without the SDK. The OSM view box reads the engine's view rectangle.
Main grew its own main chunk by 93,873 bytes gzip-9 since the merge base, so
the baseline is raised to the merged size.
Pointing the scaled uniforms at the scaler's own vectors (bc1b56b6) left the atmosphere unscaled: takram's materials read the AtmosphereParameters.DEFAULT vectors themselves, so the frame dimmed (luma 161 against 214) and qa-navara-bloom found nothing to bloom. The scaler writes those vectors in place again, and a page-wide map keeps each one's value from before it was first scaled: a globe built later on the page takes its base from there instead of from the scaled value. Bloom measures as before (214.4 → 240.3), and qa-strict-context-loss passes 3 of 3 under eight busy threads.
The sky, aerial perspective and clouds materials hold the vectors of takram's AtmosphereParameters.DEFAULT, one per page. The look's scaler multiplied them in place and took its base from the first value it saw, so a Navara globe built later on the same page (after the parked one lost its WebGL context) could record an already scaled value as its base and draw the atmosphere scale² bright: a washed-out, nearly uniform frame. The fourth case of qa-strict-context-loss failed this way under CPU load (3 of 3 with eight busy threads; 3 of 3 pass now). Scaled uniforms now point at the scaler's own vectors, which it puts back each frame if a material wrote into them; the page's vectors are never written.
Since the weather layer stopped handing Navara the Cesium global, the shared shell read ContextLimits, Math and Rectangle from an undefined SDK and drew nothing on Navara (qa-weather-engine-switch timed out). On Navara the texture limit stays unset, as Cesium's never-created context left it.
The quick commands "Reset to globe view" and "Show earthquakes" reached t()
through a .map(), so the catalog check never saw them and every language
showed them in English. They are now marked with msg() and translated.
Review of the 71 strings from #1507 in all eleven languages:
- zh-Hant is regenerated from zh-CN with OpenCC s2tw, as its guide says;
40 entries had Taiwan vocabulary instead.
- ar "Dock to the right" names the left side, where RTL puts that rail.
- ja "What am I looking at?" no longer asks what the assistant sees.
- Provider Settings, dock, provider, live and map use each glossary's terms.
- de drops the du-form; ja drops spaces between Japanese and Latin text.
- Calques, register and the example commands read naturally.
The long soak's heap slope counted the per-aircraft dead-reckoning history (POSITION_HISTORY_LIMIT fixes for 10 000 aircraft, about 14 MiB over the first 2.5 min of polls) as switch growth: a run with the same workload and no switches grows as much and then levels off. With --assert the soak now waits until the flights feed has been read more times than the history keeps before it switches; over 500 switches the heap then grows 1.07 KiB per switch.
qa-strict-context-loss records, only when no non-blank frame came, how many frames the running and the watched engine drew in 3 s and how much of the last one differs from its first pixel.
index.html no longer loads /cesium/Cesium.js and widgets.css: the Cesium scene loads both before it builds its viewer. A cold Navara start requests nothing of the Cesium library again, and qa-strict-cold-chunks asserts that without the known-gap allowance. The strict harness puts an accessor in place of the Cesium global on every page and fails a scenario that reads it before Cesium.js has run; GEV_QA_CESIUM_TRAP=record lists every such read site instead.
Every Cesium read that a Navara page made was surveyed with the strict acceptance (GEV_QA_CESIUM_TRAP=record). Math on those paths now uses src/geo: screen annotations, weather and wind center readings, cyclone picks, scene data pack polygons and images (new navaraGeometry.js), the awareness subject, the orbit offset of camera verbs, voice frame_overhead, the agent world packet and Directions colours and ground picks. The celestial ring's Sun and Moon come from a bit-identical port of Cesium's Simon1994 model, JulianDate and TEME rotation (src/geo/simon1994.js, julianDate.js, transforms.js); a Cesium globe still applies its ICRF rotation once loaded.
Traffic, Street Level, the Nepal event and locator and geo import build on Cesium's math or entity model on either globe; their enable (or the import) now loads the SDK first through src/app/loadCesium.js, synchronously when it is already there. A production page guards the global so a missed read is reported and starts the load. No module reads Cesium while it loads (scripts/check-cesium-load-time.mjs, run by npm test). Cesium.js is still loaded by index.html here.
A poll that returned the same stale snapshot dropped STALE from the
Contacts cohort until the next answer, because the in-flight loading flag
outranked stale and the source revision ignored the provider name. A
source that has answered now reads its status without the loading flag;
one that has never answered (AIS first connect) still reads as loading,
so it shows no STALE before any snapshot exists.
The look's sun follows the page clock, so the frame these checks compare was as bright as the hour they ran at: in the acceptance run the half-strength bloom changed the frame by 0.29 against 3.5 earlier, and at dusk the full bloom changes nothing (luma 13).
Measured over the second half the heap still grew about 20 KiB per switch in the acceptance run (117.8 to 123.8 MiB from switch 250 to 500), so the narrower window did not separate warm-up from growth; the original check stays.
Under the soak workload V8's compiled code and the layers' caches grow for about 250 switches and then the heap is flat (121.4, 121.7, 120.4, 119.1 MiB after 250 to 400 switches), so a line fitted from switch 20 measured the warm-up. Over the second half the threshold stays 10 KiB per switch; the run before the layer effect fix measures 19 KiB there.
Layers re-create their ground effects on every attach. Navara's removePass leaves the EffectPass undisposed, so each switch kept its EffectMaterial in three's program and material caches, together with the uniforms' DataTextures and their Float32Array data (earthquakes: about 47 KB of buffers and one WebGL program per switch). engine.LayerEffectDesc now disposes the raw pass on delete, as the app's own effects do.
Asking the canvas again during the switch made the reuse depend on getContext at that moment: with new contexts refused (qa-strict-failure-matrix, cold Navara to Cesium) the rollback to the parked Navara threw and left no globe.
The aircraft scene binding re-tracked with origin share-restore, which stayed on the entity: a later trackedEntityChanged for it (cockpit exit, refocus) never stamped navigation, and the aircraft lost its selection origin. Flights, military and satellites now set a transient gevRestoring mark around follow.start, and the aircraft binding resumes with the origin the aircraft was selected by.
qa-strict-prod-smoke opens the keyless vite preview on each engine, switches once and fails on any page error. qa-strict-cold-chunks reports Cesium.js at a cold Navara start as a known gap instead of a failure.
This reverts ae13a56f. In the production build every 'cesium' import is the global that Cesium.js defines; with the script loaded only when a Cesium globe started, modules evaluated at boot (cesiumConstants, annotationEngine) threw ReferenceError and neither engine started. A cold Navara start loading Cesium.js stays a known gap.
The sprite and point mover shaders rate each contact by its anchor against the sweep with the cyberSonar.js sampler maths; the engine sonar scene feeds the theme, settings and shared sweep angle each drawn frame and keeps the fleet cadence only while the sweep runs over contacts.