Commit Graph
1161 Commits
Author SHA1 Message Date
quanticsoul4772andClaude Opus 5.5 d39256e700 fix(tools): ship types read as their family; letterless destinations are unknown
AIS sends ship types as ITU-R M.1371 codes, so vessels_in_area grouped
"70", "74" and "79" as three types, and type: "tanker" matched nothing on
feeds that send codes. Codes now read as the family the vessel cards already
show (CARGO, TANKER, TUG), for rows, group_by and the type filter. The code
table moves to src/data/vesselTypes.js, which has no i18n import, so the
server-side tools can use it; vesselLabels re-exports it.

A destination with no letters ("0", "---") now counts as unknown.

Live, Singapore: "most by type: CARGO (3), TANKER (3), PASSENGER (1)"; "how
many tankers near Singapore" found 3.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-17 15:49:02 -07:00
Sunil56224972 97816a35de Merge remote-tracking branch 'upstream/main' into fix/terrain-same-site-gate-on-686 2026-10-18 03:36:52 +05:30
quanticsoul4772 661e1d7fa3 Merge remote-tracking branch 'origin/main' into feat/tools-area-breakdowns 2026-10-17 14:32:52 -07:00
Sameh Khamis 64d7ed3c61 Merge branch 'main' into codex/shared-geodesy 2026-10-17 11:42:58 -07:00
Said Turksever d64028d7ff Merge branch 'main' into test/earthquake-records-validation 2026-10-17 17:15:39 +01:00
Kevin Rajan 83fc895442 fix(agent): own portable i18n marker in World Packet boundary 2026-10-17 05:14:43 -05:00
Kevin Rajan e4f526415d style(agent): apply pinned Prettier output and remove temporary probe
Restore the main branch formatter unchanged and use byte-exact Prettier 3.9.6 formatting on the World Packet parity regressions.
2026-10-17 05:12:15 -05:00
Kevin Rajan f4a4df5044 test(agent): retrieve exact pinned formatter output for #880 (temporary) 2026-10-17 05:10:37 -05:00
German Khokhlov 69a78710cc feat(vessels): render on Navara (WIP: browser parity not yet run)
render/navara.js: one always-on-top engine.movers chevron bank (atlas of
every chevron icon at 32 px), MMSI pick ids, trail on engine.lines,
camera reads through the shared Navara layer view (extracted from the
aircraft renderer to src/layers/shared/render/navaraView.js, + camera
pickEllipsoid and canvas clientWidth). Registered in navaraMode.
QA: vessel qaDrawn; probe selected() falls back to getSelectedInfo;
qa-w1-layers vessel section; qa-vessels-engines side by side; --engine
for qa-vessel-cards/datum; qa-flights-engines military near pose 100 km.
Unit tests on the fake engine pass; browser runs NOT done (see progress).
2026-10-17 14:06:11 +04:00
Kevin Rajan cefbad3c2a refactor(agent): preserve compact voice contract in World Packet extraction
Reconcile #880 with current main. Delegate the view-state builder while keeping enabled-only layers, disabledLayerCount, provenance, context semantics, and existing voice behavior. Add parity and roster-read regressions; retain package export, boundaries and changelog fragment.

Preserve original GEV voice, layer and packet contributor credit.
2026-10-17 05:04:55 -05:00
Kevin Rajan c1dd4171db fix(ui): verify real PanelChrome rail ownership of compact Radio
Carry reviewed #816 onto current main without conflicts; preserve i18n updates, replace obsolete changelog edit with a fragment, and add a constructor-wiring regression alongside the owner and rail tests.

Credit: raysung-2026 (report), asturksever (review), quanticsoul4772 (Windows diagnosis).
2026-10-17 04:56:07 -05:00
Sunil56224972 77768e9fff Merge remote-tracking branch 'upstream/main' into fix/terrain-same-site-gate-on-686
# Conflicts:
#	CHANGELOG.md
#	docs/CURRENT-STATE.md
#	scripts/format-scope.json
2026-10-17 14:32:33 +05:30
Sunil56224972 5d0c367315 Merge remote-tracking branch 'upstream/pr-686' into fix/terrain-same-site-gate-on-686
# Conflicts:
#	CHANGELOG.md
2026-10-17 14:30:09 +05:30
German Khokhlov 1fe8267550 refactor(vessels): Cesium rendering behind a renderer interface
src/layers/vessels/render/cesium.js holds the Cesium objects the layer
built itself, one for one: the translucent BillboardCollection of
chevrons (centred, screen-aligned, never depth-tested), the horizon
occluder, window projection, canvas size, viewer.trackedEntityChanged,
and the trail (data/trailRenderer's entity polyline, kept on Cesium: the
engine.lines Primitive path did not draw below the photoreal mesh in
round 6 — the plan's T13/T15 blocker; flights does the same).
The logic imports no Cesium: geo math, Rgba WHITE, the chevron artwork in
icons.js, clicks through the engine's selection dispatcher (own-rule
owner, same rule). Registered as CESIUM_LAYER_RENDERERS['ais-live-vessels'];
src/layers/vessels joins the boundary rule.

W1 replay vs 7c7422e2: vessels/flights/military/localAdsb counts, known
objects, screen points, requests and shot sha256 identical.
qa-vessel-cards --data synthetic 2/2 and qa-vessel-datum (?qaFeed=
vessels:fixture) 4/4, the same as 6eec8f6c.

Tests moved with the code (same meaning): cameraHandoff (the click body
is handleClick), inputOwnership (the guard precedes the click rule's
read of the pick), renderers.test (vessels in the extracted list),
importDirections (an unmoved example layer is satellites), the
ownership services fixture (renderer service).
2026-10-17 12:47:14 +04:00
German Khokhlov 6eec8f6cb0 fix(qa): floor verification finds a contact's model by its icao24
getNearby returns the contact's label as `id` (the callsign when it has
one) and its key as `icao24`; the drawn rows are keyed by icao24, so the
model lookup missed and every contact was measured at its sprite. Under a
3D model that sprite is hidden and keeps the pre-floor height by design
(T7): the "buried at -27 m" contacts on Cesium (the geoid at AUS) were
those hidden sprites, at HEAD and on 7c7422e2, whose script measured every
sprite. The Cesium report also gives each contact's model state and the
floor cell the layer reads.

AUS, 100 s after enabling flights, both engines: 3 low contacts, 0 buried;
N3752 / DAL848 drawn by their models at 130.2 / 135.4 m over a 123.5 /
128.6 m mesh, the DEM-prior cell warm (123.5 / 128.6 m).
2026-10-17 12:12:00 +04:00
German Khokhlov 5ff8c5d7a6 test(qa): floor verification reads the app's ground floor and measures models where drawn
- The DEM prior column read a fresh import of src/data/groundFloor.js, a
  module the app does not use for its layers (they read the scene's
  surface services), so it was null for every contact. It now reads
  window.__godsEyeView.surfaceServices.groundFloor. The terrain proxy
  answers 200 and the AUS cells resolve within one or two polls (123-128 m);
  the "proxy 503" reading in the task-11 report was this misread.
- A contact a 3D model draws is measured at the model's translation (the
  probe's drawn() reports it now): its sprite is hidden and keeps the
  pre-floor height by design (T7), so measuring the sprite reported a
  buried aircraft that is not drawn.
2026-10-17 11:02:52 +04:00
German Khokhlov ea1e2f9c04 feat(localAdsb): render on Navara
Task 12b. src/layers/localAdsb/render/navara.js implements the Local
ADS-B renderer interface of 12a on Navara:
- markers: the shared aircraft renderer's glyph sets in magenta #ff4fd8
  (class silhouette and scale, ×3 near / ×0.5 far, always on top), drawn
  under every sprite layer as Cesium draws the entities; each frame writes
  what Cesium's CallbackProperties gave (position, screen rotation,
  visibility) and the 2,000 km display distance;
- the 978 MHz UAT ring: its own sprite set, a white ring atlas tinted
  #ffb3ef, Cesium's 24 px point plus its 1.5 px outline (27 px), the same
  distance scale and distance, pickable as its aircraft;
- models (the Flights GLBs, magenta MIX tint), trail and head: the shared
  aircraft renderer's; the camera never follows a local aircraft.
Registered by registerNavaraLayerRenderers (import()); the Navara catalog
now offers flights, military and local ADS-B.

- ?qaFeed=local-adsb:fixture: the local receivers' answer in the server's
  format (src/bench/w1Feeds syntheticLocalReceivers: 40 aircraft near JFK,
  10 on 978 MHz UAT) through the layer's own decoder-feed session
  (sources.localAdsbFeeds, null by default: the layer makes its own).
- qa-w1-layers --layer=local-adsb: a click selects a marker and shows its
  card with the receiver line, the camera does not follow, Escape leaves
  the selection (it never answered Escape), empty map releases it.

qa-w1-layers local-adsb: Cesium 12/12, Navara 13/13, --compare parity
(global pose: the 2.27 px camera offset only). qa-flights-engines
--layer=local-adsb: 14/14 (fills exact; UAT rings side by side in
docs/migration/phase3/localAdsb).
2026-10-17 06:50:15 +04:00
German Khokhlov 0b8cb855fa refactor(localAdsb): Cesium rendering behind a renderer interface
Task 12a. The Local ADS-B logic (index.js, models.js) no longer imports
Cesium. src/layers/localAdsb/render/cesium.js holds what it built, one
for one: the CustomDataSource of marker entities (billboard with
per-frame position/rotation/visibility CallbackProperties, the 2,000 km
DistanceDisplayCondition, the 978 MHz UAT ring as a PointGraphics), the
model PrimitiveCollection (models load, pose and leave through the shared
aircraft renderer's model part, adopting that collection), the selected
aircraft's trail (data/trailRenderer, as before) and live head segment,
and the camera reads. Registered for 'local-adsb' in the renderer
registry, created at init(viewer, {engine}).

- Clicks go through the engine's selection dispatcher (drill 8) instead of
  the layer's ScreenSpaceEventHandler, with the layer's own rule kept as is
  — a new `lookUnder` owner kind: its marker on top, or under picks no
  layer owns and under trail lines, is selected (and re-selected);
  another layer's contact on top, or empty map, releases only its own
  selection; a trail line alone does nothing; no gesture rule. To the
  other owners it is another layer, as its pick-owner registration made
  it before. (The dispatcher's `drillThrough` would have taken the marker
  from under another layer's contact, which the old handler never did.)
- models.js reads the camera through the renderer's view (frustum and
  horizon tests, ground snap owner) and draws through renderer.models
  (loadFleet/add/remove/destroy/setPose: the same MIX-tinted glTF options,
  the same HPR frame with the Flights nose offset). The shared aircraft
  renderer gains a loadModel seam (default Cesium.Model.fromGltfAsync).
- Cesium math → src/geo; the model tint is an Rgba equal to the old
  Cesium.Color; check-import-directions: src/layers/localAdsb/ joins the
  rendered layers.

Tests moved with the code, same meaning:
- index.test: the services fixture creates the layer's Cesium renderer and
  the selection dispatcher; the click harness scripts the picks on a fake
  engine registered for the fake viewer (was a fake ScreenSpaceEventHandler)
  and the app's pick owners (trails, a Flights contact) are registered in
  the pick registry (was an injected picking service).
- cockpitModels.test: the GLB loads are the aircraft renderer's loadModel
  calls (Cesium.Model.fromGltfAsync by default), each asynchronous:false.
- trackingClickGesture.test: the dispatcher's empty click goes to the
  plain owners (filter(plain)).
- renderers.test: local-adsb has an extracted Cesium renderer.
New: the dispatcher's look-under rule beside a plain owner (7 click cases).

W1 replay (flights, military, localAdsb) vs a fresh 7c7422e2 run: counts,
known objects, screen points, tracking poses, requests and every shot's
sha256 identical.
2026-10-17 05:50:30 +04:00
German Khokhlov 941b308c29 test(qa): floor verification logs each contact's probe answer and DEM prior (10b review 6)
On Navara each contact row now carries the surface probe's source and tile
size and the coarse DEM floor (cachedGroundFloor). The buried contacts
(SWA4871 60.5 m and AAL2592 -27 m against a 121-126 m mesh) have no DEM
prior (null: the terrain proxy answers 503 here) and the mesh floor sampler
latches nothing without one, so their sprite heights stay the barometric
fallback; the same rows bury on Cesium when the same contacts are low.
2026-10-17 05:34:41 +04:00
Matt Van Horn 052435473e feat(cctv): add Houston TranStar camera pack
Register the keyless TranStar snapshot catalog as a live CCTV source,
synthesize frame URLs from validated numeric camera ids, and keep the
nearest cameras to Houston's metro cores under the existing catalog ceiling.
2026-10-16 18:26:01 -07:00
German Khokhlov 9dc446bd24 feat(military): render on Navara
Task 11b. src/layers/military/render/navara.js: the shared Navara aircraft
renderer with the military's sprite slot (SPRITE_LAYER_ORDER 'military',
under flights) and jet.glb's nose offset; registered by
registerNavaraLayerRenderers (now a table of lazily imported factories,
flights and military), so the Navara catalog offers military and the main
chunk stays without three/Navara.

- The follow passes between aircraft layers on Navara as on Cesium: the
  follow-handle → tracked-handle map and the "follow being started" handle
  are shared by every aircraft renderer of the page, so a layer whose follow
  another layer took reads the new owner's tracked handle (and its
  gevSelectionOrigin) — before, it read a bare follow handle and reported
  'programmatic'.
- trackById on a selected-but-not-followed contact follows it again (as
  flights since 10b; on Navara any camera command ends a follow).
- qaDrawn (the probe's drawn()) for military, shared with flights
  (src/layers/aircraft/qaDrawn.js).
- ?qaFeed=military:synthetic[:count]: seeded readsb rows (1 000 by
  default, 150 near JFK, 12 on the ground, 8 types), the first 25 hexes
  shared with the synthetic civil flights.
- qa-w1-layers: sections 4–6 read the --layer's module (were 'flights');
  section 5 keeps the queries beside the voice answers (the voice block
  overwrote them, so --compare never compared queries); for military: the
  follow passes military → flights → military, and flights leaves the 25
  shared aircraft to military while it is on (9 975 / 10 000 / 9 975).

qa-w1-layers --layer=military: Cesium 25/25, Navara 26/26, --compare parity
(queries included; the known 2.3 px camera offset at the global pose only).
2026-10-17 05:05:40 +04:00
German Khokhlov 3a0a3b0551 test(military): the colour constants equal the Cesium.Color values they replaced 2026-10-17 04:52:33 +04:00
German Khokhlov 7c0891deda refactor(military): Cesium rendering behind a renderer interface
Task 11a. The military flights logic no longer imports Cesium: it draws
through the shared aircraft renderer (src/layers/aircraft/render/cesium.js)
with the military's parameters in src/layers/military/render/cesium.js
(sprite-order slot 'military', jet.glb's 180° nose offset), registered for
'cesium' in the renderer registry and created at init(viewer, {engine}).
The same pure move as flights in 10a:

- Fleet: the BillboardCollection and its sprite-order slot (enable keeps
  restoreSpriteOrder, as before); sprites stay Billboard handles.
- Models: fleet/tracked/preload glTF loads (asynchronous:false, MIX tint at
  MODEL_COLOR_BLEND_AMOUNT, IR boost as `unlit` → the UNLIT CustomShader),
  the model collection, setPose. The per-class specs keep their own nose
  offset: setPose takes an optional offsetDeg (default the layer's), on
  both aircraft renderers (same arithmetic, test added).
- Tracked: the ENU billboard Entity with its position/colour/rotation
  callbacks (#FFD166, transparent amber once the model owns the visual),
  viewFrom, followed through viewer.trackedEntity + the one exact tracked
  frame; refocusTrackedById through follow.refocus.
- Trail: the entity trail and the floored live head segment.
- View: camera/scene reads, frame number, frustum and horizon tests,
  render requests, frame events.
- Clicks and Escape go through the engine's selection dispatcher (the
  ScreenSpaceEventHandler and keydown listener are gone); the cross-layer
  untrack watches the renderer's follow. Same rules: cockpit clicks are
  inert, the tracked visual/model is a no-op, another layer's pick is not
  empty space, an unowned pick is.
- Colours are engine-neutral Rgba (MIL_ICON_COLOR #FFB800,
  TRACKED_ICON_COLOR #FFD166, AMBER_TRANSPARENT), NearFarScalar →
  nearFarScale; Cesium math → src/geo.
- check-import-directions: src/layers/military/ joins the rendered layers
  (cesium only in render/cesium*.js).
- The military registry and the flights' duplicate suppression are
  untouched.

Source-text tests updated with the moved code, same meaning:
- militaryAwareness.test: military's hasContact guard reads fleet.visible().
- trackingClickGesture.test: the military gesture rules are the
  dispatcher's (selection gesture before select, click classifier before
  the empty-space clear), military answers the empty click with a
  user-origin clear — the same assertion flights already had.
- ownership.test: the services fixture names selection and renderer; the
  app-composed services build the layer's Cesium renderer.
- renderers.test: flights and military have extracted Cesium renderers.

New tests: the military logic over a plain-data renderer and a fake engine
(amber fleet, click-select, #FFD166 tracked glyph, no-op on the tracked
visual, empty-click and Escape release, the keyboard left on destroy); the
plain-data renderer moved to src/testSupport for both aircraft layers;
setPose with a per-model offset.

W1 replay vs 7c7422e2 (the 10a reference run): every layer's counts, known
objects, screen points, tracking pose after 2 s, requests and the
city/global/orbit shots (sha256) identical. qa-military-names (it checks
the installations layer): the same verdict as 7c7422e2 (the zoom-markers
assertion fails on both). Main chunk 2,890,763 B (−2.9 KB).
2026-10-17 04:52:15 +04:00
quanticsoul4772 db73c6908e Merge branch 'main' into feat/tools-area-breakdowns 2026-10-16 17:18:17 -07:00
quanticsoul4772 2a6ba1180d Merge branch 'main' into feat/tools-area-breakdowns 2026-10-16 17:09:08 -07:00
Sameh Khamis 0c3723e930 Merge branch 'main' into feature/geo-uri-search 2026-10-16 17:06:38 -07:00
Sameh Khamis f0a04fbbaa Move the changelog entry into changelog.d and watch the Google geocode proxy in QA
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.
2026-10-16 16:39:28 -07:00
Sameh Khamis ac258e98d9 Merge branch 'main' into feature/geo-uri-search
# Conflicts:
#	CHANGELOG.md
2026-10-16 16:39:21 -07:00
Sameh Khamis 53bb05abed Merge branch 'main' into fix/tomtom-cross-site-gate
# Conflicts:
#	CHANGELOG.md
#	SECURITY.md
#	docs/CURRENT-STATE.md
#	scripts/package-boundaries.json
#	src/tooling/environmentProviders.test.mjs
2026-10-16 16:39:03 -07:00
German Khokhlov ba1191b2e5 test(qa): floor verification on both engines
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.
2026-10-17 03:20:44 +04:00
German Khokhlov 132a941e9a test(qa): sprite and enrichment checks on both engines through the probe
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.
2026-10-17 03:15:12 +04:00
quanticsoul4772 d604b97a4d Merge remote-tracking branch 'origin/main' into fix-1056
# Conflicts:
#	src/tools/queries/aviation.test.mjs
2026-10-16 15:50:13 -07:00
Sameh Khamis 19e2979eb5 Merge branch 'main' into fix/geo-distance-dateline 2026-10-16 15:40:43 -07:00
German Khokhlov 03a0ff3a01 test(qa): flights parity checklist on both engines, side by side
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).
2026-10-17 02:35:32 +04:00
Sameh Khamis cab69d282b Merge branch 'main' into fix/cctv-missing-numbers 2026-10-16 15:34:29 -07:00
quanticsoul4772 3c3ea269a9 fix(setup): stop Node's DEP0190 warning when npm runs on Windows (#1057)
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.
2026-10-16 15:28:39 -07:00
German Khokhlov b14bf7e013 feat(flights): render on Navara
Task 10b. src/layers/aircraft/render/navara.js implements the aircraft
renderer contract on the engine contract alone (no three, no Cesium):
- Fleet glyphs: banks of engine.movers sprite sets (one per distance scale
  and depth mode the handles ask for; flights: one, 'always', Cesium's
  NearFarScalar), growing by doubling, from a glyph atlas of every
  aircraftIcon() kind at the fleet (64 px) and tracked (192 px) rasters
  (data/aircraftIcons: AIRCRAFT_ICON_KINDS, aircraftIconCells,
  aircraftIconAtlasLayout, drawAircraftIconAtlas). Sprite handles keep the
  Billboard field vocabulary as setters; a frame's writes commit once at
  the end of the frame listener that made them (a microtask outside one).
- Models: banks of model mover sets per glTF and look; handles report
  ready from MoverSet.ready() and computedScale from minimumPixelSize kept
  on the CPU (radiusM in the spec, the class's nativeRadiusM); poses are
  Cesium's HPR frame (modelMatrix) turned by Navara's glTF axes
  (Z_UP_TO_X_UP: its loader is already Z-up; chosen side by side with
  Cesium among six candidates).
- Tracked glyph: a sprite whose position/colour/rotation callbacks are read
  every frame in preRender; follow = engine.camera.follow with the tracked
  readout's card as its label (gevFollowHandle); current() maps the follow
  back to the tracked handle, also while follow() raises followChanged.
- Trail and live head: engine.lines (alpha 0.85, depth-fail 0.4, geodesic)
  in the gev-trail pick namespace.
- View: camera/scene adapters over engine.camera.pose() (registered with
  the engine so shared helpers project through it), frustum and horizon
  tests, frame number, engine frame events.
flights/render/navara.js registers it for 'navara' from the Navara mode's
import(); the Navara catalog now offers Live Flights.

Also: tracking a contact that is selected but no longer followed follows it
again (a camera command ends a Navara follow; on Cesium a search flight
releases trackedEntity): trackById/select re-follows instead of only
re-publishing. Behaviour change on Cesium only in that case.

Tests: the renderer on the fake engine (contract, atlas, banks, per-frame
commits, growth, tracked callbacks, follow label, model pose, trails) and
the flights logic over it (fleet, click-select, follow, re-follow after a
camera command, release, no set left behind).
2026-10-17 02:15:04 +04:00
Sameh Khamis c6f0f9db0a Share one antimeridian-safe distance and move the changelog entry
The flights records and the mesh-floor sampler kept identical copies of the equirectangular distance; both now import src/data/approxDistance.js. The changelog entry moves to changelog.d (#787), and CURRENT-STATE notes the antimeridian behaviour.
2026-10-16 15:01:29 -07:00
Sameh Khamis ccd38c0c44 Merge branch 'main' into fix/cctv-missing-numbers
# Conflicts:
#	CHANGELOG.md
2026-10-16 14:59:28 -07:00
quanticsoul4772 5acf254f8b Merge remote-tracking branch 'origin/main' into upd-1056 2026-10-16 14:22:29 -07:00
German Khokhlov 7f13ef4a87 refactor(flights): Cesium rendering behind a renderer interface
Task 10a. The flights logic no longer imports Cesium: every Cesium object it
built moved one for one into the shared aircraft renderer
(src/layers/aircraft/render/cesium.js; contract in render/contract.js), with
the flights' parameters in src/layers/flights/render/cesium.js, registered
for 'cesium' in the renderer registry and created at init(viewer, {engine}).

- Fleet: BillboardCollection + sprite-order slot; sprites stay Billboard
  handles (field vocabulary documented for the Navara renderer).
- Models: fleet/tracked/preload glTF loads (asynchronous:false, MIX tint, IR
  boost as `unlit` → the UNLIT CustomShader), the model collection, setPose
  (same HPR frame with airplane.glb's 180° offset).
- Tracked: the ENU billboard Entity with its position/colour/rotation
  callbacks, viewFrom, followed through viewer.trackedEntity + the one exact
  tracked frame (kept as is; engine.camera.follow on Cesium would also end
  the follow on any camera command, a behaviour change).
- Trail: the entity trail (data/trailRenderer, unchanged: its Primitive
  predecessor failed below the mesh) and the live head segment.
- View: camera/scene reads, frame number, frustum and horizon tests,
  projection, render requests, frame events, evidence render-loop hold.
- Clicks and Escape go through the engine's selection dispatcher (the
  ScreenSpaceEventHandler and keydown listener are gone); the cross-layer
  untrack watches the renderer's follow.
- Colours are engine-neutral Rgba (src/layers/shared/color.js), bit-equal to
  the Cesium.Color constants they replace; NearFarScalar → nearFarScale.
- Cesium math in the logic → src/geo.
- check-import-directions: cesium only in render/cesium*.js and three/Navara
  only in render/navara*.js for flights, aircraft and shared; a layer's
  Navara renderer only through navaraMode's import().

Source-text tests updated with the moved code, same meaning:
- readLayerSource reads a layer's render/*.js and, for the aircraft layers,
  aircraft/render/*.js (GLB-load, tracked-entity, CallbackProperty checks).
- modelScale.test: _normalBillboardScaleByDistance reads nearFarScale(...).
- spriteOrder.test: flights' enable calls fleet.restoreOrderOnEnable(); the
  renderer wires it to restoreSpriteOrderOnEnable('flights', viewer).
- trackingClickGesture.test: the flights gesture rules are the dispatcher's
  (selection gesture before select, click classifier before the empty-space
  clear); flights answers the empty click with a user-origin clear.
- militaryAwareness.test: flights' hasContact guard reads fleet.visible().
- ownership.test: the services fixture names selection and renderer.

New tests: color, the Cesium aircraft renderer (contract, collections, pose,
loads, frustum/horizon, tracked entity, follow, trail head, adopted
collections), the flights logic over a plain-data renderer and a fake engine
(fleet, click-select, no-op on the tracked visual, empty-click release), the
boundary rule.

W1 replay vs 7c7422e2: counts, known objects, screen points (0 px), tracking
poses (0 m), requests and shots (same sha256) identical. Main chunk
2,890,982 B (+6.2 KB, +0.21 %: the renderer indirection).
2026-10-17 01:04:25 +04:00
German Khokhlov 9f1a0611fa fix(ui): the "kept in the link" notice is readable on Navara
On Navara the engine's persistent notice came up right after the share
restore's NOT ON THIS ENGINE notice and replaced it at once, so the names
of the linked layers this engine lacks were never seen (and a later finite
notice would in turn have wiped the engine notice for good).

- ShellFeedback keeps a persistent notice that a finite one covers and
  brings it back when the finite one ends; a persistent notice that arrives
  under a live finite one waits for it.
- Notices take a dwellMs; "Kept in the link: …" stays 15 s.
- scripts/qa-engine-notice.mjs (headless): a Cesium share link with a layer
  Navara lacks, opened with ?engine=navara: notice shown with the layer
  name, still up 14 s later, then the engine notice is back. 4/4 (before
  the fix the notice never appeared).

T8-T9 review item 2.
2026-10-16 23:27:47 +04:00
quanticsoul4772 1d418fd5af Merge remote-tracking branch 'origin/main' into fix-1056-ais0
# Conflicts:
#	CHANGELOG.md
2026-10-16 12:06:22 -07:00
German Khokhlov 907cb2092d fix(globe): Navara redraws after a map change while its tiles arrive
Phase-4 QA: after a map-stack switch at the whole-globe view Navara
sometimes showed the new map only seconds later. Navara 0.2.1 reports idle
and stops drawing before a raster tile it fetched arrives, so the last
frame stays stale until something else asks for one. Every engine.maps
change (base imagery, terrain, photoreal tiles, their release) now asks for
a frame and keeps asking every 250 ms for 6 s, then the engine idles again.

scripts/qa-map-switch.mjs switches the stack (photoreal / Esri / OSM) at
18 000 km on each engine, waits for the tiles, hashes the picture, forces
frames and hashes again: a change means a stale picture. Before: Navara 3/8
switches drew the new picture by themselves; after: Navara 8/8, Cesium 8/8.
2026-10-16 23:00:15 +04:00
German Khokhlov bc281ab246 refactor(voice): W1 actions on the engine
The voice runner drives the globe engine (an `engine` option, by default
the one registered for the viewer) instead of the Cesium viewer:
track_entity, stop_tracking (camera.following().stop()), frame_overhead
(camera.flyToSphere with the pose heading), fly_to_location, zoom_to_globe
(follow released through camera.following()), adjust_camera_zoom,
next_iss_pass / next_satellite_pass, select_nearest_aircraft and the
analyst view context, get_current_view_state, get_entity_context
(projector.worldToWindow), the view target (picker.pickPosition, then
camera.pickEllipsoid, then picker.pickGlobe) and its moveEnd prewarm, and
the viewport samples. getBasemapLabelContext takes the engine or a viewer
with a registered engine (the HUD).

Without a viewer the tools that still need one (VIEWER_ONLY_ACTIONS:
control_scene, annotate_map, clear_annotations, fly_route, move_camera,
control_cockpit, set_context_mode, control_radio, control_cctv) answer a
structured refusal (code UNAVAILABLE_ON_ENGINE) instead of throwing.

Point-and-ask picks with engine.picker and keys the camera pose from
engine.camera.pose(); the viewport capture reads engine.host.canvas and
waits for the engine's postRender (both engines keep their drawing
buffer: Cesium's contextOptions, Navara's own WebGL2 canvas, S4).

Tests: gevActionsEngine.test.mjs runs each moved action on the fake
engine. gevActions.test.mjs keeps its expectations; its runner helper now
registers an engine for the test viewer (the Cesium engine over it, or a
fake engine at its positionCartographic) and the two harness cameras gain
roll and frustum for camera.pose().
2026-10-16 22:52:21 +04:00
German Khokhlov ec7dcc944e test(qa): an engine-neutral layer probe
- src/app/qaProbe.js → window.__godsEyeView.layers: list() (with
  `available`), unavailable(), available(id), enable/disable(id) (a user
  toggle through the layer manager), objects(id) → [{ id, label, position,
  screen }], screenOf(id, objectId), selected(id), following(), movers(),
  framesDrawn() - all through the engine and the catalog's availability, the
  same on Cesium and Navara. Installed after the tools.
- src/app/layers/qaSources.js: `?qaFeed=flights:synthetic:10000,
  vessels:fixture` (dev server only, allowQaRegistration) replaces those
  sources with seeded synthetic answers served through the live sources'
  own parsers (same snapshot shape; loaded with import() only then). The
  pure generators moved from src/tooling/w1Feeds.js to src/bench/w1Feeds.js
  (the browser graph may not reach src/tooling); the W1 recordings
  re-export them.
- scripts/qa-w1-layers.mjs: the parity checklist's frame, --layer
  --engine=cesium|navara with the fixture feeds, JSON per run and
  --compare (objects on screen per pose, 5 known objects within 2 px).
  Filled: 1 view, 8 idle, 9 errors/no leftover mover sets after 10
  on/off; 2-7 report TODO for the layer tasks.
- qa-perf, qa-labels, qa-overlay-baseline and qa-label-readability take
  --engine= and use the engine (camera, frames, render requests, tiles) and
  the probe (layers, following, movers) instead of the viewer; checks whose
  layers an engine lacks are SKIPPED with the reason (Navara today), as are
  qa-perf's animated-style checks there (Cesium post stages).

Verdicts, Cesium, before → after: qa-perf 24/24 → 24/24, qa-labels 14/14 →
14/14, qa-overlay-baseline (datacenters, firms, detection-50) 2 measured +
1 skipped → the same. Navara: qa-perf 14 passed, 3 skipped; qa-labels 3/3,
1 skipped; qa-overlay-baseline 3 skipped; qa-w1-layers flights: Cesium 5/5
(10 000 synthetic aircraft), Navara skipped (no flights renderer yet).
2026-10-16 22:52:18 +04:00
Said Turksever 4ae6d154f9 Merge branch 'main' into feat/changelog-fragments 2026-10-16 19:33:08 +01:00
German Khokhlov b0cbed081b feat(app): layer catalog per engine
- src/app/layers/renderers.js: the layer renderer registry
  (registerLayerRenderers(engineKind, { [layerId]: factory }),
  rendererFor(layerId, engine), layerAvailableOn(layerId, engineKind)).
  Every application layer is registered for Cesium as CESIUM_BUILTIN (it
  still draws itself until its render/cesium.js); Navara factories come with
  the layer tasks. On it, the stable availability API phase 4 injects into
  its layer list: createLayerAvailability(engineKind) →
  { engine, isAvailable(id), available(), unavailable() } with { id, name }
  rows (src/app/layers/directory.js holds the ids and names in catalog
  order; a test keeps it equal to the constructed catalog).
- createApplicationCatalog({ engineKind, availability }) constructs only the
  layers the engine can show, in the same order (shared instances made
  once, on demand); the catalog carries `availability` and the others'
  metadata (`unavailableMetadata`). Cesium builds the same 30 layers in the
  same order as before; Navara builds none yet (no Navara renderers) - the
  standalone app now asks the catalog for it instead of making an empty one.
- catalogControlServices: a role whose layer the engine leaves out gets an
  empty stand-in (absentLayerControlServices folded in); any other missing
  layer is still a wiring error.
- LayerLifecycle(viewer, { engine }): every module call (init, enable,
  update, disable, destroy) gets { signal, engine } as its second argument;
  viewer is null on Navara.
- Links: a link that turns on layers this engine leaves out keeps them in
  the durable state (re-saved links carry them; back on Cesium they turn
  on) and activates nothing; a notice names them ("NOT ON THIS ENGINE · Kept
  in the link: …"). The Navara notice lists the engine's data layers from
  the registry.
- The dev launcher's CCTV wiring check matches the new catalog form.
2026-10-16 22:07:55 +04:00
German Khokhlov e48841072d Merge remote-tracking branch 'origin/main' into i18n-spanish 2026-10-16 21:25:39 +04:00
German Khokhlov 7d5a484796 refactor(overlays): layer labels through the engine
The world overlay host already projects through engine.projector and tests
the horizon with projector.horizonVisible; the last Cesium value on its
path, the snapshot of an entry's cullPosition, is now a geo Cartesian3.

Navara's horizonVisible is Cesium's EllipsoidalOccluder.isPointVisible:
tested against Cesium's own on 8 000 eye/point pairs (600 m to 20 000 km,
points under, on and over the ellipsoid; no disagreement) and at the limb
(a label 40 m under the ellipsoid just past the horizon is hidden, with its
cullPosition lifted 100 m it is shown). It ran per label per frame with
three new vectors each call; it now uses scratch vectors. Micro-measure
(500 labels per frame, no scavenge during the run): 7 941 B per frame
before, 51 after.

The W1 sources that projected with Cesium's SceneTransforms themselves -
the icon orientation's exact projection and the launch replay overlay - go
through the engine's projector when their scene or viewer has a registered
engine. Satellites, vessel labels and aircraft recession project nothing
themselves (the host does).

scripts/qa-overlay-engines.mjs publishes the same labels on both engines at
a city, an orbital and a limb pose and compares the painted rectangles:
worst 0 / 2 / 0 px; on both, the far-side label is not painted, the lifted
limb label is and the buried one is not.
2026-10-16 21:14:52 +04:00