summarizeAwarenessCohort() translated `reason`, which is retained in the
awareness context snapshot. Mark the four reasons with msg() instead; the
awareness panel already shows them through translateKnown().
The Local ADS-B layer copied the SDR controller's `message`, which is
Radio card text in the UI language, into its `error` stat. The controller
now publishes an English `errorText` next to the translated `message` for
every receiver error, and the layer reports that. A receiver is any
object with getState(); one that does not publish errorText still shows
its error from `message`, as before. The Radio card text is unchanged,
and the Layers panel still shows the error translated.
The Directions row errors (pointer blocked, no route, routing timed out,
no response from the routing service) and the route request failures
("Routing failed: {error}", "{error} — try again in a moment",
"Routing unavailable (HTTP {status})", the rate-limit and fallback
texts) were translated inside getStats(), so the layer snapshot that
voice tools and the analyst read carried Spanish or Russian text.
Fixed texts are marked with msg() and the interpolated ones are built
in English. The Layers panel translates them where it shows them,
through a ROUTING_ERROR_PATTERNS block in statusText.js that the
proxy's refusal texts move into. English reads as before. In other
locales two things differ slightly: a proxy text inside "Routing
failed: X" that is not one of the known refusals is now looked up in
the catalog instead of shown as given, and the "— try again in a
moment" shape is recognised on every layer's error, not only on
Directions.
The CCTV frame proxy marks a Street View fallback with X-CCTV-Source, but the portable source dropped the header and get_cctv_snapshot always reported a current view credited to the camera's agency. The source now keeps the recognised provenance, and the tool labels Street View frames as historical Google imagery, refuses synthetic placeholders, and reports unknown provenance without claiming a current view.
On Navara the military-awareness module has no controls, so its
releaseCameraOwnership was skipped silently and a city pill, voice
fly_to_location or reset ended the follow but kept the aircraft selected.
The trackers it would release are released directly instead, as on
Cesium (selection and follow both cleared).
destroy() now expires every retry attempt in flight, clearing its deadline
timer and aborting its signal at once rather than when the attempt settles,
and leaves no retry marked as running.
A successful terrain retry overwrote the cached fallback without releasing
it. The replaced fallback is now disposed as soon as the real terrain takes
its place on screen, or, when it stayed installed behind a 3D tileset, once
the next globe activation replaces it.
Inside a Navara follow the orientation controls frame the follow's target
and retarget its viewFrom instead of moving the camera, and the release
cancels only a running flight (a cancel is a camera command, which ends
the follow). Cesium's trackedEntity path is unchanged. Updated test:
cameraOrientationControls.test.mjs (orientation navigation starts a
flight before expecting its cancel; no flight, no cancel).
FollowHandle.target() returns the followed point, checked by the contract
on both engines. The contract's model check now waits for ready() before
looking for the model's pixels: the magenta point and sprites could fill
the region before the glTF loaded (qa-globe-contract cesium 73/73,
navara 79/79).
An Esri or keyless terrain attempt that never settled kept the single-flight
retry open forever, so neither provider was retried again. Each attempt now
counts as failed after 60 seconds and the retry backs off as usual; its
signal is aborted, and a provider that lands after the deadline is released
rather than installed or cached.
CURRENT-STATE now says only the construction fallback to OSM is retried, not
the switch made after repeated Esri tile failures.
An activation that re-showed a fallback while a retry was in flight armed a
second timer, so two retries could run at once and land out of order, leaving
the cache and the screen on different providers. Retries now run one at a
time; a request made meanwhile re-arms once the current one ends, and a
result that is obsolete or no longer wanted is disposed.
Imagery and terrain retries are now handled as each settles, so a stalled
terrain attempt no longer keeps recovered imagery off screen, or the reverse.
Main now takes release notes as one fragment per pull request (#787). The browser asks Google through /api/google/geocode, so the no-geocoder-request check now watches that route.
qa-floor-verify --engine=cesium|navara: on an engine without a viewer the
camera pin, the tile readiness, the mesh probes (engine.surface
sampleHeightDetailed, tiles only, excluding the layer's mover sets) and the
sprite census (the probe's drawn()) go through the engine. The Cesium path
is unchanged. Live AUS traffic: Cesium FAIL as on 7c7422e2 (13 contacts at
the -27 m geoid fallback); Navara FAIL with 1 of 15 low contacts under the
mesh (68.6 m against 121.4 m). Task 10b, checklist item 7.
The QA probe gains drawn(layerId): what a layer draws per object (glyph
image, scale, shown, screen rotation, alpha, position; its 3D model's
scale, shown, ready), from the flights layer's qaDrawn(), which reads the
renderer's handles, so it is the same on every engine.
qa-sprites-b5 and qa-enrich-ambient take --engine=cesium|navara: without a
viewer they read the layer through the probe and move the engine camera;
the military rows/cluster are skipped where the engine has no military
layer. Cesium verdicts unchanged (sprites 9/9; enrich 9/2, the same E8/E10
failures as on 7c7422e2); Navara: sprites 7/7 (flights rows and 3D cluster),
enrich 9/2 (the same two). Task 10b, checklist item 7.
renderDetails() revealed the card inside render(), whose preserveScroll() wrapper restored the previous scrollTop in its finally block, undoing the reveal. The reveal now runs in the DETAILS open handler after render() returns, once per opening; closing and refreshes do not scroll.
Task 10b:
- qa-w1-layers (flights): 3 focus (the selected object is the focus target,
at the followed centre ≤ 3 px), 4 tracking (held ≤ 3 px for 5 s; wheeled
in, ≥ 149 m), 5 the layer's queries (tracked info, contacts, nearby,
detectable; compared by --compare), 6 share links (the followed object
restored on the same and on the other engine). Clicks only on points the
canvas takes (the Navara attribution card covered the old empty point).
--compare reports the global pose's 2.3 px as the cameras' (Cesium
projects its own nadir 2.27 px off centre at 15,000 km).
Cesium 19/19, Navara 20/20, parity.
- qa-flights-engines (new): the same frozen synthetic fleet at the global,
city, orbit and near (JFK, oblique) poses on both engines, UI hidden;
side-by-side JPEGs (docs/migration/phase3/flights, models off and on),
the same objects in the frame, screen points ≤ 2 px, glyph fill colours
(brightest pixel at the glyph) within 3. 12/12 with models off and on.
- qa-labels: on an engine with flights but not military/satellites, the
deterministic 12,000 / 5,200 field is all aircraft, so detection is
gated on Navara too: 14/14 (Cesium 14/14 unchanged).
A superseded open wait cleared the shared cancelShown slot that belonged to the newer wait, leaving its auth listener and timer alive after close; open() now settles the previous wait first. A lookup whose viewer unmounted while the Street View service loaded still sent a billed getPanorama; describe() now rechecks the lookup after acquisition.
The standalone director971.test.mjs rebuilt the browser, storage and viewer
doubles that director.test.mjs already provides. Its cases now live in the
director suite on makeDirector/installSceneRuntime, which gains optional
seeded storage entries and a quota-full switch; existing tests are unchanged.
The checkpoint and failed-save-status cases are split so each fails on its own.
Windows runs npm.cmd through a shell, and passing an argument array with `shell: true` is deprecated in Node 24 (DEP0190). Every `npm run doctor` and every Pinokio install on Windows printed the warning. npmProcessSpec now takes the npm arguments and, on Windows, joins them into the command string; the arguments are fixed subcommands (`--version`, `ci`). Unix behavior is unchanged.