Merge current main before wind weather prototype

# Conflicts:
#	CHANGELOG.md
#	docs/CURRENT-STATE.md
#	scripts/package-boundaries.json
This commit is contained in:
Bilawal Sidhu committed 2026-09-15 15:36:25 -05:00
commit 1a52cea265
71 files changed
+10553 -5631

No files matched your search

+95 -16
View File
@@ -6,6 +6,30 @@ Voice and HUD snapshots reuse the existing feedState classifier. Analyst follow-
Satellite and local infrastructure layers expose on-demand analyst records through their current factory owners. Analyst counts and ranks explicitly cover only bounded examined loaded records (default 2,000 per new layer, core satellite rows before dense extras); omitted records can change nearest/count and satellite distance is ground distance. Existing tools and result fields remain available.
Realtime voice composes separate connection, response/tool, Radio handoff, input
and audio-meter, cost, viewport-context and diagnostic owners. The existing
controller exports and session/backend contracts remain available. Tool protocol,
response wording, push-to-talk timing, model preferences and Radio confirmation
order are preserved. Stopped connections, stale offers and delayed captures cannot affect a
replacement session; audio meters release failed initialization and reject revoked
frames. Delayed action results and post-capture continuations cannot resume a
stopped conversation or send output into a replacement. See [voice ownership](VOICE-OWNERSHIP.md).
The application shell composes focused state owners for navigation, destination
lookup/orbit, Cockpit, visual settings, panel layout, aircraft display and layer
bindings. Keyboard/display subscriptions have a separate lifetime; existing
layer, scene and voice methods remain available through a delegation facade.
Disposal settles pending globe resets and removes camera-entry listeners before
asynchronous restoration; replacing a manager detaches its old Directions
services. See [UI ownership](UI-OWNERSHIP.md).
Director's ordered shot runner is exported independently of rendering, UI,
storage and scene content. The existing scene controller supplies an adapter for
visual/layer state, camera travel, media holds and release. Playback cancellation,
preview cleanup, saved projects and all scene assets/attribution are preserved.
Timeline extraction and a shareable scene/data-pack format are planned in
[Director](DIRECTOR.md); those follow-up capabilities are not implemented yet.
Search framing and annotations request semantic map features from an explicit source. The Overpass adapter owns bounded queries, member/tag decoding and request deadlines; callers retain candidate ranking, outline caching, deferred retries and scene placement. Empty, transient and throttled outcomes remain distinct. Traffic sources return road records and installation sources return mapped records with freshness/saturation metadata, so their layers no longer decode upstream elements. ALPR already normalizes its records in the source. Default providers, footprints, road directions, exact-viewport retries and source attribution are unchanged.
Nepal media preloads survive repeated camera-flight updates, but Stop, event disable and replacement remove abandoned frames and revoke pending Facebook sessions. Fallback evidence-card clicks respect the shared drawing-tool pointer lease.
@@ -2441,9 +2465,9 @@ its criteria cannot be silently ignored.
| Traffic | OSM Overpass (+ optional TomTom live flow) | `src/data/traffic.js` | `/api/overpass` + `/api/tomtom` | viewport-driven |
| CCTV | Austin + Caltrans (CA) + TfL London + Ontario 511 + Fintraffic (FI) + DriveBC (BC) + TxDOT (TX) + Estonia (Tallinn, Tarktee) + Live Traffic NSW + Open Calgary Open Data + Street View fallback | `src/data/cctv.js` | `/api/cctv` | 10s (active) |
| Radio | Radio Browser (public-domain station directory) | `src/data/radio.js` | `/api/radio/stations`, `/api/radio/click/:uuid` | 45 min directory refresh |
| Transit 🚌 | Operator GTFS-Realtime VehiclePositions (7 keyless regions, `src/data/transitFeeds.js`) | `src/layers/transit/` via `src/app/layers/transit.js` | `/api/transit` | 15s (poll + delayed playback) |
| Bikeshare 🚲 | GBFS (Lyft + BCycle) | `src/data/bikeshare.js` | `/api/gbfs` | 60s |
| Directions 🧭 | OSRM on FOSSGIS servers (OpenStreetMap) | `src/data/directions.js` | `/api/route` (`steps=1`) | on placement / mode change |
| Transit 🚌 | Operator GTFS-Realtime VehiclePositions (7 keyless regions, `src/data/transitFeeds.js`) | `src/layers/transit/` via `src/app/layers/transit.js` | `/api/transit` | 15s (poll + delayed playback) |
| Datacenters ▣ | OSM extract (bundled) | `src/data/localLayers.js` | — | static |
| Dams ▰ | OpenInfraMap/OSM extract (bundled) | `src/data/localLayers.js` | — | static |
| Submarine Cables ◠ | TeleGeography public map (bundled) | `src/data/telegeographySubmarineCables.js` | — | static |
@@ -2532,7 +2556,9 @@ What a card needs is a resolved ground anchor: a step whose ground cell has not
answered yet opens nothing rather than a card hanging at sea level. The cells
are re-read for up to 90 seconds, which outlasts a cold terrain round trip;
reroute, CLEAR and disable cancel that.
Transit is off by default, and share links use token `j`. The layer polls
Transit is off by default, and share links use token `j`. The movement panel order
places Transit directly after Street Traffic and before Bike Share. Cameras and
utilities retain their own groups. The layer polls
registered feeds near the camera every 15 seconds, below a 3,000 km altitude
gate. Seven keyless regions are available: Boston, Austin, Minneapolis–St Paul,
Helsinki, the Netherlands, Norway and South East Queensland. Mode silhouettes
@@ -2557,10 +2583,19 @@ clipped to the displayed time. The browser retains up to 15 minutes and 128
fixes per vehicle, under a global 32 MiB history budget. Capacity pressure may
shorten that history. Only the selected vehicle has a trail: completed segments
are batched primitives, and a small moving head ends at the marker. Trail
geometry is bounded to 2,048 prepared vertices; history changes rebuild the
body, while ordinary playback updates the head and segment visibility.
Both normal and depth-fail instance colours follow the age ramp once per display
second or style change. Subdivision crossings update only changed visibility
geometry is bounded to 640 prepared vertices, prioritizing the active corridor
and shortening the oldest geometry first. Active and future marker corridors
retain their own 640-vertex budget; long report intervals use coarser subdivisions
than 25 m instead of dropping an admitted vehicle. Supported scenes use ground polylines
classified against both terrain and 3D tiles, including after map-stack changes,
with per-segment age colours. History changes rebuild the body. The head is one
short draped corridor, clipped to displayed time by a material uniform; only
subdivision crossings rebuild it. Ordinary frames upload no head geometry.
Without ground-polyline support, ordinary primitives retain a mode-coloured
0.55-alpha depth-fail path, including the head, without dimming it to near-black.
Instance attribute updates write back the same typed array returned by Cesium;
its getters return copies, so reading a second time discards a visibility or colour edit.
Normal instance colours follow the age ramp once per display second or style change. Subdivision crossings update only changed visibility
attributes. Deselecting releases selection geometry; marker paths retain only
active and future corridors.
@@ -2578,22 +2613,27 @@ ingestion, eviction uses receipt-ordered queues, and capacity loss is disclosed
when history is recreated.
**Surface and route limitations.** Heights are aligned to work with Google 3D
tiles. Unknown historical surfaces leave gaps in a trail. Sparse reports can
tiles. Draped history is prepared on selection even while marker floors are unresolved;
Cesium resolves its surface. Without ground-polyline support, unknown historical
surfaces leave gaps. A selected body follows layer enablement and vehicle existence,
so an off-screen head does not hide an on-screen tail. Sparse reports can
cut across corners; these paths are not surveyed route geometry. Subway markers
are projected to street level because the feed does not provide depth, and the
selected card explains that choice. Actual street-level views still require
visual inspection.
**Appearance.** Normal fleet frames are 20 px, growing to 30 px on selection;
NVG, thermal and noir frames are 30/36 px. Source rasters are 48/96 px before padding.
External halos are 1/1.5 px in normal and 3/3 px in sensors, calculated from the
CRT, NVG, thermal and noir frames are 26/39 px. Source rasters are 48/96 px before padding.
External halos are 1/1.5 px in normal, 1.25/1.25 px in CRT and 2/2 px in sensors, calculated from the
final unpadded display size. Six modes, two sizes and three profiles cap the
raster cache at 36 variants. Normal mode colours are bus `#5EF08A`, tram
`#FFC24A`, subway `#FF4538`, rail `#D9A6FF`, ferry `#5FD6FF`, and unknown
`#D8DDE5`. Selection keeps the mode colour. Sensor bodies use solid rounded envelopes with at least 60% opaque white coverage
of the padded display footprint, without interior panel lines. Sensor input is opaque white with a
fully opaque black halo; black-hot reverses the contrast through the
thermal effect. Creation, mode changes, selection, deselection, map presets and
`#D8DDE5`. Selection keeps the mode colour. Sensors reuse each normal silhouette's exact
outer path, filled opaque white without interior panel lines, with an opaque
`#05080C` halo. Only the external padding grows; the unpadded silhouette remains
at CRT size. There is no envelope body. Black-hot reverses the contrast and
Ironbow maps the white input to its hottest palette entry through the thermal
effect; sprite code never applies a red tint. Creation, mode changes, selection, deselection, map presets and
Cockpit vision use the same sprite styling function.
Sensor signatures remain in-scene. The thresholds in `qaMetrics.js` require
@@ -2603,14 +2643,17 @@ of its phosphor peak white (0.65992 from RGB 0.16/1/0.22), halo minimum ≤0.25
with the centre minus sampled background reported without gating. Each preset needs at least
six verified, unobscured sprites; insufficient coverage is UNEXERCISED.
The sampler excludes overlapping footprints, including otherwise ineligible
sprites, and verifies pick ownership across all nine sampled core pixels.
sprites, and chooses nine source-raster points in the widest solid white band, avoiding
window/panel gaps. Equal-width tram bands prefer the upper solid body over the
geometric centre; the nine-point spacing and all thresholds remain unchanged.
The sampler then verifies pick ownership at all nine points.
Noir samples also exclude its deliberately vignetted outer field, using shader
settings and screen position rather than observed brightness. Selected-trail
diagnostics report every condition even when selection fails.
Transit brackets use the normal-composite overlay surface: a 1.25 px mode-colour
stroke over 3.25 px dark backing. Other contact themes retain their existing
behavior. The selected trail uses a 2 px core over 4 px backing, mode-coloured
behavior. The selected trail uses a 3 px core over 5 px backing, mode-coloured
in normal and white input in sensors. Alpha falls linearly from 0.80 at the head
to 0.45 at two minutes, 0.20 at ten minutes and 0.08 at fifteen minutes.
@@ -2633,7 +2676,10 @@ heights, and reuses caller-owned sample and segment storage.
collections above CCTV and Bikeshare. Traffic, Bikeshare and Transit share camera
sensitivity claims; the original sensitivity returns after the last release.
Visibility checks combine the camera frustum and ellipsoid occlusion at no more
than 4 Hz; missing map bounds do not admit the globe's far side. Frame work
than 4 Hz; missing map bounds do not admit the globe's far side. Camera arrivals
rebuild an unprepared or cold marker corridor during that visibility sweep,
adopt a loaded surface, and update both heightPending and surfaceReady diagnostics.
A resolved display sample clears its pending-height gate without waiting for a poll. Frame work
visits moving visible vehicles, up to 64 pending height changes, and the selected
head. Rotation visits visible membership at 5 Hz, with selected rotation per
frame. The transit render hold exists only for visible positional or orientation
@@ -2647,6 +2693,15 @@ destruction also releases collections and registrations.
QA_BASE_URL=http://localhost:4305 QA_TAG=keyless node scripts/qa-transit.mjs
```
`QA_BASE_URL=http://localhost:4305 node scripts/qa-transit-recovery.mjs` is the
focused keyless camera-arrival regression. With destination tiles loaded, it
injects nine disclosed moving-bus reports and delays their floor acquisition,
selects the hidden vehicle, then releases the floor and calls camera.setView.
Within two seconds it requires a visible marker, a prepared visible body and
at least six of eight paired off/on trail pixel samples. It uses production
selection, visibility and geometry owners and writes screenshots plus JSON;
it does not establish Google 3D acceptance.
The full run samples completed frames at 1920×1080 CSS pixels, records DPR, and
measures moving fleets of 800 and 3,000 vehicles with 128 fixes each and DETECT
on. Street/metro targets are respectively: transit frame CPU p95 ≤1/4 ms, frame
@@ -2677,6 +2732,30 @@ injected playback independently of live fleet activity. Thermal checks verify
style activation before reading generated controls; DETECT checks set density
and exercise the real OFF/restore button. Cesium render errors fail selection
acceptance.
The `trail-visible` section requires a visible Google 3D tileset. It selects a
scripted straight bus with CapMetro metadata under the isolated `qa-trail-capmetro`
feed ID, with nine retained fixes, runs for 20 seconds at each
300/600/900 m view (55/45/45 degrees), then freezes playback and waits for sustained tile and primitive readiness.
It captures off/on/off/on framebuffer patches at eight distinct projected surface
points outside the padded sprite and selection bracket. `Google 3D selected trail visible at 6 of 8 projected samples`
prints the selected key, fix count, elapsed display time, path length, hit count,
per-point coordinates/hits and renderer strategy. At least six points need a
repeatably changed green core or dark backing at the same pixels. Repeated off
and on channels and their contrast deltas must agree within 8 RGB levels;
background refinement, overlapping samples and unchanged dark roads fail. The dark
backing must reduce summed RGB by at least 40%, and either match needs RGB
distance of at least 40, so a faint depth-fail wash cannot satisfy visibility. Artifacts are
`<tag>-<altitude>m-trail-visible.jpg` and matching JSON. Run the same harness
against the base and patched keyed builds; partial runs remain diagnostic.
Node checks run Cesium's final geometry pipeline, including batch IDs and
position encoding: 1,576 bytes per head subdivision and 2,014,128 bytes for the
1,278-instance body. The body plus a separate 81,792-byte instance reserve totals
2,095,920 bytes, below the 2 MiB body limit; the head remains below its separate
2 KiB/frame geometry limit. Ordinary frames upload no head geometry. These are
pipeline buffer measurements, not measured GPU time or instance-texture uploads. The same 20-second browser interval scopes
WebGL uploads to the trail body/head, forces one body rebuild, reports head CPU
p95/max, and gates body uploads at 2 MiB and head uploads at 2 KiB/frame.
Required empty or unexercised checks fail acceptance. The passing final line is
`HARNESS: PASS (0 unexercised)`. A `QA_SECTIONS` subset is diagnostic only and
cannot claim full acceptance.
+90
View File
@@ -0,0 +1,90 @@
# Director: reusable scene playback
Director is the scene playback system. The first extraction separates ordered
shot execution from the scene editor and application rendering. This document
also records the proposed next steps; the sharing format and interactions below
are a design plan, not features already available.
## Implemented boundary
`src/director/playback.js`, exported as `gods-eye-view/director`, provides:
- `buildPlaybackQueue(scenes, startSceneId, { single })`: ordered scenes/shots,
retaining the existing round-robin behavior.
- `playSceneQueue(queue, { token, adapter, previousScene, releaseOnFinish })`:
sequential execution, cancellation checks and scene release.
The runner imports no renderer, UI, storage, recipe or dataset. An adapter supplies
these phases, in order: `selectShot`, `applyVisual`, `applyLayers`, `travel`,
`settle`, `hold`, `completeShot`. Each receives `{ scene, shot, index, total,
token }`. It also supplies `releaseScene(scene, token?)` and optionally `complete()`.
Callbacks may return promises. Each playback invocation requires its own adapter
state and token; the caller owns camera arbitration and concurrent-run refusal.
The runner checks cancellation after each phase. Adapters must also pass the
abort signal or liveness predicate into pending work: a check between phases
cannot stop a camera flight or network request already in progress. Cleanup
between scenes and on preview exit runs without an aborted token. A refused
initial handoff starts no shot. Final cleanup runs on completion, Stop or failure;
`releaseOnFinish: false` retains the final scene for ordinary playback. Exceptions
propagate so the caller can restore its controls. Empty/already-cancelled queues
acquire no resources. This low-level API expects normalized scene objects;
untrusted file validation belongs at the import boundary.
`src/scenes/playbackAdapter.js` connects this runner to the existing
`SceneDirector`. The latter still owns editor state, saved-project import/export,
camera ownership, seeking, clocks and presentation. Pack-specific layer states,
media holds and map preferences retain their current implementation. This first
slice does not make the entire scene controller renderer-independent.
All authored scenes, assets, IDs, source links, attribution and existing JSON
projects are preserved. The Nepal sequence remains the contribution introduced
by @manjunath22466 in #590. Its clips use source links/embeds; comparison images
and river coordinates are bundled. See the
[event pack provenance](../public/events/bhote-koshi-2026/README.md).
## Next changes, in order
1. **Timeline and content separation.** Extract pure duration/seek calculations
and a clock with an explicit lifetime. Move content-specific hold, layer and
presentation rules behind registered scene-pack adapters. Keep existing
recipes and content intact. Test direct load, forward/backward seek, replay,
media timeout and Stop at every pending transition.
2. **Versioned scene document.** Build on the existing version-3 project export
and its importer. Specify a schema and migrations before changing writes.
Keep old imports and local-storage projects readable; reject unsupported
versions without replacing the current project. Include stable scene, shot,
anchor, pack and action IDs. Separate portable authored state from transient
loading state and run diagnostics. Bound document size, collection lengths,
numeric values and nesting; show errors at the affected field.
3. **Camera directions.** Define named geographic anchors and explicit
move/hold segments: start and destination, duration, easing, heading, pitch,
roll and stop duration. State coordinate units, altitude reference and
terrain-relative behavior. Support the current camera-pose shots first;
paths, look-at targets and pauses follow with deterministic seek behavior.
4. **Data packs and placement.** Define a pack manifest with format/version,
source, attribution/license and optional integrity/size metadata. Distinguish
where bytes live (relative files, a URL, or an eventual bundle) from where
features appear (geographic bounds, anchors, transforms and elevation rules).
Start with existing GeoJSON/image/media adapters. Resolve relative paths
against an explicit document base; define CORS, caching, missing-asset and
offline behavior. Fetch only through registered source adapters with bounded,
cancellable loading. Scene files carry no credentials or executable modules.
5. **Declarative interactions.** Give pickable features stable IDs and connect
clicks to a small action registry: show a text/source card, focus an anchor,
seek a shot, or change an allowed layer state. Define selection feedback,
keyboard equivalents and precedence with drawing/tracking controls. Validate
target references and cancel pending actions on Stop, replacement or teardown.
No JavaScript evaluation or arbitrary HTML in scene files. Branching must
have explicit loop/transition limits and reproducible reset/seek semantics.
6. **Sharing and authoring.** Add validated preview/import/export, missing-pack
diagnostics and an optional self-contained bundle. Preserve attribution with
each pack; a share link does not change an asset's license. A future node graph
could author this same document, but playback and the format will not require
a graph editor.
Each step must preserve existing scene editing, keyboard playback, camera
arbitration, layer cleanup, media timing and provider fallback. Acceptance includes
portable runner tests, existing Director regressions and browser playback with
Stop/seek/replay, plus tracking and voice checks. Use reproducible local fixtures
for failures and cancellation, alongside live visual checks for actual content.
+35
View File
@@ -0,0 +1,35 @@
# Application UI ownership
`src/ui/applicationShell.js` constructs controls, connects their explicit inputs
and coordinates shutdown. `shellFacade.js` preserves the methods used by layers,
scene playback and voice. It delegates; new behavior belongs with its owner.
| Owner | Responsibility |
| --- | --- |
| `navigationController.js` | Navigation generation, tracking release and camera authority |
| `locationNavigation.js` | Destination lookup/selection, orbit, world jumps and globe-reset completion |
| `cockpitCoordinator.js` | Cockpit entry/rollback, readouts and the single Display portal |
| `visualSettings.js` | Effects, detection overrides, visual restoration and render-loop lifetime |
| `panelChrome.js` | Disclosure, docking, layout and Cockpit panel restoration |
| `aircraftDisplay.js` | Shared commercial/military 3D display preferences and controls |
| `layerBindings.js` | Data-manager attachment, selected-tracker adoption and camera-entry listeners |
| `displayBindings.js` | Keyboard/display subscriptions and frame-rate monitor lifetime |
| `shareRestoration.js` | Initial share/layer restoration and cancellation |
| `shellFeedback.js` | Loading feedback, notices and toast lifetime |
Owners receive named services, elements, operations and readers. They do not
receive the application shell itself. Readers resolve a replaceable collaborator
at use time; operations cross an ownership boundary without copying its state.
The facade exposes existing properties as delegates rather than duplicate state.
Shutdown has two phases. First revoke input, deferred navigation, camera-entry
listeners, lookups, timers and rendering work. Then await Context restoration,
release sensor overrides while the manager is still attached, detach layer
services and destroy scene resources. Late callbacks cannot acquire the camera.
Manager replacement also releases subscriptions and the previous Directions
module even if the replacement does not provide a Directions layer.
Source-contract tests follow both sides of delegated wiring. Lifecycle tests
exercise pending reset cancellation, stale camera callbacks, replacement and
idempotent teardown. Browser acceptance covers the composed application, including
share restoration, scene commands, navigation, Cockpit, display and voice.
+48
View File
@@ -0,0 +1,48 @@
# Realtime voice ownership
`realtimeController.js` composes the session and owns its status, UI bindings and
overall lifetime. It coordinates startup and shutdown. `realtimeFacade.js`
preserves existing methods and properties by delegation, without copying state.
The session adapter, action runner and backend interfaces remain unchanged.
| Owner | State and responsibility |
| --- | --- |
| `realtimeConnection.js` | Start generation, request cancellation, peer/data channel, acquired microphone stream and playback element |
| `realtimeTurns.js` | Active and superseded responses, pending confirmations, deduplicated calls and action cancellation |
| `realtimeRadio.js` | Speaker ducking, prepared playback, stronger-action reservations and playback observers |
| `realtimeInput.js` | Physical Space gesture, microphone mute, input/output meters and their frame/listener lifetime |
| `realtimeCost.js` | Next-session preferences and the current session's model-bound meter |
| `realtimeViewport.js` | One retained image, bounded deletion identities and capture generation |
| `realtimeDiagnostics.js` | Bounded error history and sanitized optional diagnostics |
Pure input policy, preferences and protocol-response policy have separate modules.
Owners receive named readers, operations or focused collaborators. Cross-owner
cancellation uses operations; response handling does not own Radio's reservations
or viewport deletion bookkeeping.
Shutdown first invalidates acquisition and action work, then closes transport,
stops audio analysis, releases media and clears conversation/input state. Full
removal also detaches UI, keyboard, annotation and Radio observers. Radio takeover
retains confirmed playback through that same shutdown path. A late offer, peer callback,
viewport capture or cancelled meter frame cannot enter a replacement session.
Action and post-capture continuations retain their conversation identity before
publishing output, queueing a response or changing status.
Behavioral invariants remain in force: a 500 ms Space hold claims push-to-talk;
short control taps and text entry remain native; a click-started session stays
open-mic. Every accepted or refused tool call receives its terminal output once.
New typed input supersedes old intent. Radio waits for the spoken confirmation and
verified muted playback. A live cost meter stays bound to its negotiated model,
even when the next-session preference changes.
Run the unit suite and both live recorded-audio modes against a configured local
server:
```sh
node scripts/qa-voice-wav.mjs http://localhost:4173
node scripts/qa-voice-wav.mjs http://localhost:4173 --push-to-talk
```
The second mode uses the actual Space shortcut, verifies microphone acquisition
and release, and checks the same globe/Radio outcome. Tracking and scene-control
acceptance exercise voice's shared application operations separately.