Commit Graph
429 Commits
Author SHA1 Message Date
Mazeyar Moeini Feizabadi 45e9c5ab0a test: exercise emergency highlighting through current feed routes 2026-10-13 17:56:41 +02:00
Mazeyar Moeini Feizabadi ec80c89c1e fix: integrate GNSS and emergency features with current main contracts 2026-10-13 17:42:30 +02:00
Mazeyar Moeini Feizabadi 69e8b18e02 Merge rebased aircraft emergency contribution for HAL-522 validation
# Conflicts:
#	DATA_SOURCES.md
2026-10-13 17:25:47 +02:00
Leo PechnickiandClaude Opus 5.5 7d6f31e54d feat(squawk): carry emergencySource + observation time into analyst records
Review on #856 (kvnloo): the analyst projection flattened a 7700 squawk and
the readsb ADS-B emergency field into the same emergency/emergencyKind pair.

- mapAnalystRecord (flights, military) adds emergencySource ('squawk' |
  'ads-b' | null, from aircraftEmergency().source), emergencyBroadcast (new
  pure emergencyBroadcastPhrase: "broadcasting squawk 7700 (general
  emergency)" vs "ADS-B reports general emergency") and observedAtMs (the
  record's lastContactEpochMs: last transponder message, epoch ms).
- emergencySource is a queryable analyst text field; the voice payload passes
  it and emergencyBroadcast through, and observedAtMs only for aircraft with
  a status. Tool description asks the model to narrate with the phrase.
- Source-to-analyst fixture test: OpenSky 7700 vs adsb.lol emergency=general
  on squawk 1200 differ in provenance and timestamp; filterable by source.
- Voice tool-payload pins re-derived for the additive description change.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-13 17:23:39 +02:00
Leo PechnickiandClaude 15b13e947b fix(flights): address review — quotable squawk only, all military tint sites, no test churn
- aircraftEmergency: `source` is 'squawk' only when the Mode A code itself
  names the status; the Cockpit HUD no longer quotes an unrelated squawk
  (e.g. SQUAWK 1200 on an ADS-B `downed` status).
- military: new billboards and the untrack restore use the fleet tint, so an
  emergency stays red instead of snapping back to amber; tint helpers take the
  record already in hand instead of a second lookup.
- cockpit/cyber CSS: emergency colour via a custom property / theme token,
  meta line bolded so the status does not rely on colour alone in the HUD.
- tests: revert Prettier churn in unscoped test files; only semantic edits.
- docs/CURRENT-STATE.md: transponder emergency highlighting section.
- qa-emergency-squawk: (iv) RELEASE check; new files added to format scope.

Co-Authored-By: Claude <[email protected]>
2026-10-13 17:23:19 +02:00
Leo PechnickiandClaude eb7f059750 feat(layers): highlight aircraft broadcasting a transponder emergency
Flights and Military Flights tint aircraft red while the latest poll
carries squawk 7500/7600/7700 or an adsb.lol ADS-B emergency status
(general/unlawful/nordo/downed). Minimum-fuel and lifeguard priorities
are reported but not tinted. The Cockpit HUD aircraft-meta line names
the broadcast code in red. The status is not sticky: a cleared code
stops highlighting on the next refresh.

The analyst/voice query engine gains squawk, emergency and
emergencyKind fields. No new source or request: OpenSky state vectors
already carry the squawk and adsb.lol/readsb carries both fields.

Built by the Hex project (Crew Leo Agile dev team).

Co-Authored-By: Claude <[email protected]>
2026-10-13 17:22:29 +02:00
Leo PechnickiandClaude Opus 5.5 783c5b16f4 fix(gnss): date replayed snapshots by the proxy-reported age, not cross-clock fetchedAt
The layer compared the server's fetchedAt with the browser's clock, so a
skewed client clock could shift or void the 30-minute window. The proxy now
returns ageMs (its own now minus fetchedAt, at serve time, on fresh, cached
and stale answers); the source refuses a snapshot without a finite,
non-negative ageMs, and the layer dates rows as its now() - ageMs. fetchedAt
stays in the payload for display.

Tests: proxy cached/stale answers report ageMs on the server clock; the
source rejects missing/invalid/negative ageMs; the layer replay test drives
ageMs. qa-gnss stubs send ageMs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 17:20:43 +02:00
Leo Pechnicki 7e570a79ad fix(gnss): expire stale cells on re-enable; pin the unpruned return-after-window path
Re-enabling after a disable longer than the window no longer shows the
old cells until the next refresh. A healthy report arriving 31 minutes
after a degraded one, with no prune in between, starts clean; exactly
30 minutes still counts.
2026-10-13 17:20:43 +02:00
Leo Pechnicki fde8b2a64f fix(gnss): age degraded evidence and replayed snapshots on their own clocks
Maintainer review on #855 (mazy1998, 2026-10-06):

- records: presence (seenAt) and degraded evidence (degradedAt) age
  separately, so continuing healthy reports no longer keep an older
  degraded flag alive past 30 minutes; an aircraft returning after the
  window starts fresh. pruneGnssObservations ages the store with no new
  rows.
- Rows are dated by the snapshot's fetchedAt, which the proxy keeps on
  cached and stale answers: a replay never restarts the window, the
  proxy stops serving a stale snapshot older than the window, the source
  refuses an undated one, and cells expire during fetch failures and
  while there is no view anchor.
- Normalization accepts ADS-B versions 1 and 2 only; a missing, invalid
  or negative seen_pos is not a confirmed position from the last minute.
- qa-gnss: keyboard, 390x844 viewport (fresh page: isMobile reloads) and
  replay-age sections; the live cell assertion bins the returned rows
  with the layer's own rules instead of a raw row count.
2026-10-13 17:20:43 +02:00
Leo PechnickiandClaude Opus 5.5 6d4a2a02fe fix(gnss): separate GEV integrity classifier from GPSJAM aggregation; machine-readable provenance
Review on #855: the per-aircraft rule (NIC < 7 || NACp < 8, borrowed from
the US 14 CFR 91.227 ADS-B Out minima) is GEV-defined, not gpsjam's, and
gpsjam's map shows low navigation accuracy, not necessarily jamming.

- Primary cell state is now `navigation-integrity-anomaly` (medium/high) or
  `nominal`; `suspected-gnss-interference` is a secondary `interpretation`
  marked validated: false.
- GNSS_PROVENANCE / gnssProvenance({ windowMs }) records the classifier
  (gev-nic-nacp-v1, GEV, unvalidated), the gpsjam aggregation (formula,
  bands [0.02, 0.1], FAQ source) and the GEV 30-minute visited-view window,
  built from the constants the code applies. Exposed as layer.provenance
  and getStats().provenance; the proxy tags responses with `classifier`.
- Visible name "GNSS Integrity", legend "... low accuracy", blurb leads with
  low navigation accuracy and states the threshold is a GEV choice.
- Layer id `gnss-interference` and share token `0` unchanged.
- Voice aliases add "gnss integrity", "navigation integrity",
  "low navigation accuracy"; the interference aliases stay.
- Docs describe the method as GEV threshold + gpsjam aggregation.
- Tests: provenance object, renamed state/label, proxy classifier tag;
  qa-gnss asserts provenance and wording in the real app.

Hex project - Crew Leo Agile dev team.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-13 17:20:43 +02:00
Leo PechnickiandClaude 21201c78f0 fix(gnss): address review - reference source wiring, honest method, 429 backoff, QA render proof
- Route the adsb.lol GNSS source through createReferenceSources and the
  constructCatalog contract; app layer module is a thin pass-through.
- Describe the method honestly: gpsjam.org's published cell formula and
  bands; per-aircraft thresholds are the 14 CFR 91.227(c) ADS-B Out minima
  (NIC < 7, NACp < 8) plus readsb gpsOkBefore.
- Proxy: reuse the adsb.lol anchor parser, honour upstream Retry-After
  (clamped 5-120 s), serve stale during cooldown, cap the body size.
- Layer: redraw only when a cell id or band changes, treat a settling
  camera as a retry rather than a rejected enable, keep low cells visible,
  skip rows without finite coordinates, state the 3-aircraft rule in the
  legend blurb.
- Voice: gnss-interference in set_layer_visibility / show_data_layers_menu
  plus common aliases; voice pins re-derived.
- qa-gnss: stub the route in-page (request interception stalls Cesium's
  geometry workers), assert every cell is built, cover all bands over land
  and sea, screenshot every state with the Data Layers row open.

Hex project - Crew Leo Agile dev team.

Co-Authored-By: Claude <[email protected]>
2026-10-13 17:20:03 +02:00
Leo PechnickiandClaude eefcb338b3 feat(layers): GNSS interference layer from ADS-B NIC/NACp integrity
Bins airborne ADS-B v1/v2 aircraft that report degraded navigation
integrity (NIC < 7, NACp < 8, or readsb's GPS-loss flag) into 0.5 degree
cells over a rolling 30-minute window, coloured with the gpsjam.org
bands. Keyless: reads the capped adsb.lol point snapshot around the view
through /api/gnss-integrity, which returns integrity fields only, caches
60 s per whole-degree anchor, backs off on upstream 429 and serves the
last snapshot on failure. Adds scripts/qa-gnss.mjs.

Built by the Hex project (Crew Leo Agile dev team).

Co-Authored-By: Claude <[email protected]>
2026-10-13 17:19:23 +02:00
Said TurkseverandClaude Opus 5.5 b1efd22f02 feat(street-level): one imagery source at a time; IC8 review fixes
- One provider is lit at a time: lighting a chip darkens the others, and a
  chip click records every registered provider's switch in one update so
  the share link keeps the swap (also off Google 3D, where Street View is
  not listed). The layer starts with the first default-on provider
  (Mapillary, free); params that light several keep the one already on,
  so reapplying them never flips (litByParams). The now-unreachable
  "several lit" chip paths are gone.
- Leaving Google 3D during a Street View lookup ends it without an error
  or a stuck loading state (openImage's early return resets it; the
  lookup loop rechecks availability).
- A source swap during a lookup no longer reports "no imagery".
- Street View lit off Google 3D says the panel needs the Google 3D map.
- Comments trimmed to the why; keySetupCore.test.mjs back to upstream's
  formatting plus the one new test (was a 551-line reformat).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:10:58 +01:00
Michael JohnstonandClaude Opus 5.5 577a395bc3 Merge upstream main and retain PR 581 changelog entry
CHANGELOG-only conflict: the bullet stays first under [Unreleased], ahead of
the entries upstream added there. No code changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 09:27:10 -04:00
Said TurkseverandClaude Opus 5.5 64d939f3d2 feat(street-level): Street View polish — name, filters, legend, marker
- The chip reads GOOGLE STREET VIEW (and its caption link likewise).
- Providers declare the imagery filters they honour (`filters`, default
  pano + since). Street View declares none: every panorama is 360° and it
  draws no coverage to thin, so its lookups ignore the filters and the
  panel hides IMAGERY and SINCE while it is the only lit provider.
- The legend lists only providers that draw coverage lines, with
  Selected only beside one: Street View alone shows no legend.
- Providers may pick their on-globe marker (`marker`); Street View's is a
  Google-blue field-of-view cone from a white-ringed dot, in place of
  GEV's amber pin, and switches back for a Mapillary photo.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 14:05:57 +01:00
Said TurkseverandClaude Opus 5.5 48ca99a38a fix(street-level): keep OPEN STREET VIEW whole beside a long coverage line
.scene-btn (flex: 1) loads after street-level.css and outranked the
button's flex: 0 0 auto, so with Mapillary and Street View both lit the
button shrank to its 72px minimum and its one-line label ran past the
panel edge. Scope the rule under .sl-meta-row; the coverage line wraps
instead. The live gate gives the share-link check 30 s, as the link is
written only once the view rests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 13:36:01 +01:00
Matt Van Horn 3cf9a8d65d feat(cctv): add NYC DOT Traffic Management Center camera pack
Register the keyless webcams.nyctmc.org catalog as a live CCTV source,
synthesize frame URLs from validated camera UUIDs, and keep the nearest
cameras to the five borough cores under the existing catalog ceiling.
2026-10-12 19:18:45 +00:00
Kevin Rajan 76b003bc95 merge: align PR #917 with upstream main (8b8ccc9a)
Preserve narrow behavior and test changes while reconciling unrelated upstream work. Original reported/maintainer credit remains in PR history.
2026-10-12 12:31:13 -05:00
Kevin Rajan 9d59e555c2 style: format slow-CI timing assertions 2026-10-12 10:34:47 -05:00
Kevin Rajan 9501dc121b test: preserve strict default timing with opt-in slow CI gate 2026-10-12 10:33:01 -05:00
Said TurkseverandClaude Opus 5.5 76db4114ff Merge upstream/main into pr/google-street-view
Conflicts with upstream's Street Level provider composition and share-state
codec, resolved:

- Street View's switch moves from its own share option (`g`) to upstream's
  provider-switch field (`r`, e.g. `0.r.google-1`); a link without it keeps
  Street View off. Docs and the browser gate follow.
- createApplicationStreetLevel keeps upstream's injectable provider list;
  its default, once the application supplies its Street Level source, is
  Mapillary plus Google Street View. Without that source there are none, as
  upstream intends.
- The coverage hint keeps upstream's empty-composition message and joins
  every switched-on provider's hint otherwise.
- CHANGELOG, README and SECURITY keep both sides' entries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 11:32:05 +01:00
kapturner d8b4b0d80f Merge branch 'main' into feat/import-geo-files 2026-10-11 21:18:37 -05:00
quanticsoul4772andClaude Opus 5.5 123bf55f39 fix(qa): repoint stale mutation anchors after the September refactors
The three mutation gates matched source text that has since moved or been
reflowed, so 37 of their 108 reverts reported STALE and could no longer
prove anything:

- qa-floorhold-mutations (2): the neighbour-floor code moved from
  src/data/groundFloor.js into src/services/groundFloor.js (ac977bd).
- qa-flyroute-mutations (8): src/cameraVerbs.js was reflowed by the
  formatter (2aca7ae); the anchors now match the formatted lines.
- qa-firstrun-mutations (27): the launcher markup, styles and startup moved
  to src/ui/templates/welcome.html, src/ui/styles/first-run.css and
  src/app/startupChrome.js; the rail default to panelPositionControls.js and
  the radio ESC handling to radioBindings.js (a67bc60 and follow-ups). The
  tool-schema pin now edits a tool description, since the context-mode enum
  it used to edit no longer lives in tools.js.

Every revert still reintroduces the same defect. Results: floor-hold 22/22,
fly_route 19/19, first-run 67/67 caught, with all source files restored.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:52:30 -07:00
Sameh Khamis 3ba8c6d61b Use a neutral compact switch codec and clarify provider size limits 2026-10-09 18:47:04 -07:00
Sameh Khamis 7b1b2df764 Preserve custom Street Level provider switches in shared state 2026-10-09 17:53:47 -07:00
Sameh Khamis 3b840ebdf3 Merge branch 'feat/optional-catalog-sources' into refactor/route-imagery-sources
# Conflicts:
#	CHANGELOG.md
#	docs/CURRENT-STATE.md
2026-10-09 17:50:12 -07:00
Sameh Khamis 0d9bfdf9de Merge branch 'fix/optional-street-level-providers' into feat/optional-catalog-sources 2026-10-09 17:49:41 -07:00
Sameh Khamis 893294c7d4 Assert Street Level expansion timeout and document provider precedence 2026-10-09 17:49:26 -07:00
Sameh Khamis 99988cb348 Separate route and recent imagery acquisition from application assembly 2026-10-09 17:42:00 -07:00
Sameh Khamis 5756ae92a8 Make catalog feed configuration optional and explicit 2026-10-09 17:35:38 -07:00
Said TurkseverandClaude Opus 5.5 e823e15025 fix(street-level): point a key-gated layer at Street View; button beside the hint
Review of #966: with a Google Maps key but no Mapillary token, switching
Street Level on read KEY REQUIRED / "Needs MAPILLARY_CLIENT_TOKEN" with
nothing about Street View, which stayed off (billing), so the layer looked
broken.

- While the switched-on providers lack their keys, the layer reports the
  switched-off ones that would draw with a key already set
  (keyAlternatives: Street View on Google 3D with a Google key). The
  panel's KEY REQUIRED tooltip, its coverage line and the layer row's
  tooltip then read "Needs MAPILLARY_CLIENT_TOKEN — add it in Provider
  Settings, or light STREET VIEW to use your Google Maps key"
  (keySetupWithAlternatives). Off Google 3D the plain KEY REQUIRED stays.
  Street View stays off by default.
- OPEN STREET VIEW moves beside the coverage hint: at the default panel
  width the PROVIDERS row has no room for it, so it wrapped onto a line of
  its own.
- The panel gate's corner drag names what lay under the pointer when it
  fails.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 00:05:58 +01:00
Sameh KhamisandClaude Opus 5.5 f5c4f3fda3 fix(qa): restore Google 3D before switching Street View off in gate cleanup
The Street View step's cleanup switched the Street View chip off after the
step had left Google 3D, where the chip is hidden by design. The click then
threw "failed to find element", replacing the step's real result (here, a
Google key refusal) with a selector error. Return to Google 3D first, then
switch the chips and restore the original map.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 11:05:43 -07:00
Sameh Khamis d587249ce9 fix(setup): stop presenting ChatGPT OAuth as the default voice fallback
Doctor and dev-fresh told every user without an OpenAI key that voice could
use ChatGPT OAuth, whether or not Codex was installed. Say voice is off
until a key is added or a Codex sign-in is selected, and mark that path
experimental.
2026-10-08 11:02:52 -07:00
Sameh Khamis fb1596d2f1 Merge main into PR #621 (ChatGPT OAuth for Realtime voice)
Resolve conflicts with the voice-core captions, panel resize and MCP
panel key changes by keeping both sides.
2026-10-08 11:00:54 -07:00
Chase Mills 2e25e20497 Merge branch 'main' into feat/import-geo-files 2026-10-08 10:54:38 -05:00
Said TurkseverandClaude Opus 5.5 ef396655fc feat(street-level): OPEN STREET VIEW, and Street View only on Google 3D
- OPEN STREET VIEW in the PROVIDERS row opens Street View's nearest
  panorama at the ground under the middle of the screen, without moving
  the camera: Street View no longer needs a pointer. It is disabled (and
  says so) until the camera is at street zoom.
- Off the Google 3D map Street View does not appear at all: no chip,
  legend swatch, globe credit, layer source or button, and Google's script
  is not loaded (the viewer has no prewarm; the script loads on first use).
  The share option keeps the user's choice for when Google 3D returns.
- A ground click measures the camera height at the click rather than
  reading the cached flag.
- A chip without a usable key explains it instead of lighting; it darkens
  beside a lit chip, and the other chips keep switching, so a refused
  Street View key cannot lock Mapillary out.
- The gate checks that nothing of Street View shows off Google 3D even when
  a share link switches it on, presses OPEN STREET VIEW on Google 3D, and
  asserts that each chip it switches really switched.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 12:55:08 +01:00
Michael Johnston 3cfef150d1 Merge upstream main and retain PR 581 changelog entry 2026-10-08 00:43:14 -04:00
Said TurkseverandClaude Opus 5.5 7bfcf177ac fix(street-level): IC8 review of the Street View provider
Google's Maps Platform terms forbid Street View imagery beside non-Google
maps, so the provider declares requiresMapStack: 'photoreal': off Google
3D its chip is dashed with the reason, lookups and opens skip it, and
leaving Google 3D closes its panorama.

- Ground clicks: only a click on the map itself (scenePick.isSurfacePick:
  nothing picked or Google 3D tile content) at street zoom opens Street
  View, and the camera stays put; higher up the hint says to zoom in.
  scenePick.pickGroundPosition replaces the copy in Directions.
- Viewer: poses go out at once and again when the date and address
  arrive, so an open never hangs on that lookup; one lookup per panorama,
  seeded by the nearest lookup; a close settles a pending open; failed
  lookups are asked again; poses follow position changes, not pano_changed.
- Loader: the gm_authFailure handler is installed once, so a retried load
  reports a refusal once.
- Viewer host: a prewarm that lands after another provider took the host
  is released; FIT/FILL availability is set on every open.
- Provider: a refusal while switched off reports no error over another
  photo; SINCE compares against the end of Google's month; messages name
  the referrer and network causes; FLAT says Street View is 360° only.
- The layer row names the key of the providers that are on; the MCP panel
  CSP admits Street View's tiles, control images and photos.
- Tests for each, against fakes that now follow Google's event order; the
  browser gate restores its state, waits instead of sleeping, aims its
  panel clicks and corner drags, and asserts the camera stays put.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 02:59:51 +01:00
Said TurkseverandClaude Opus 5.5 22b0009138 feat(street-level): Google Street View as the second imagery provider
Google Street View registers beside Mapillary as provider `google`. It runs
on the Maps JavaScript API with the browser GOOGLE_MAPS_API_KEY, loaded on
first use (mapsLoader.js), and uses Google's own StreetViewPanorama as its
viewer adapter, emitting provider-neutral poses (address and month in the
caption, a link to Google Maps, steps along Google's arrows, FOLLOW on
Google 3D). Its chip starts off because every panorama is billed to the
key; the share option is `g`.

Google offers no coverage layer for other maps, so the provider draws
none: with it on, a click on bare ground at street zoom opens its nearest
Google-collected outdoor panorama (groundClick.js). A key Google refuses
reads KEY REJECTED and fails a pending open at once; a lookup Google never
answers fails after 10 s.

Core: providers may declare `groundClick` and `defaultOn`; poses may carry
`capturedAtPrecision` and `title`; the coverage line keeps every
provider's hint; opening one provider's image releases another's
prewarmed viewer; FIT/FILL hides for viewers without render modes. CSP
script-src admits https://maps.googleapis.com. The browser gate gains two
Street View steps (the keyed one skips as 'no Google key' in CI).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 02:32:06 +01:00
Sameh Khamis 7592718415 Merge remote-tracking branch 'origin/main' into integration/mcp-panel-key-pr958 2026-10-07 15:10:44 -07:00
Sameh Khamis b5cb75bcf9 fix(voice): resolve follow-up review edge cases 2026-10-07 14:21:46 -07:00
Sameh Khamis 1594db9497 Merge main into feat/voice-core and reconcile layer capabilities 2026-10-07 13:14:31 -07:00
jibraaan 09cefa6f39 Merge remote-tracking branch 'upstream/main' into fix/mcp-shared-panel-key
# Conflicts:
#	CHANGELOG.md
2026-10-07 23:39:53 +05:00
kapturnerandClaude Opus 5.5 644a341b4c fix(import): address review on metadata, bounds, navigation, clock and GPX
- Imported metadata stays inert: descriptions, native labels, properties and
  KML extended data are dropped from every entity; names are kept as short
  plain text. GeoJSON loads with a describe that builds no HTML, and
  simplestyle (including marker-symbol) is dropped, so no icon asset is
  fetched for a file and GeoJSON draws in its row's color.
- Bound geometry before loading: features and vertices are counted in the
  parsed GeoJSON, the KML document and while reading GPX, and a file over
  20,000 features or 250,000 vertices is refused before Cesium builds it.
- Imports run one batch at a time, so concurrent picks or drops cannot pass
  the 12-file cap.
- Fly-to framing walks long tracks by index instead of spreading them into a
  call.
- Fly-to is an immediate navigation through the application shell; the fly
  after an import is a deferred navigation stamped when the batch starts and
  re-asserted just before flying.
- Dated imports never change the shared clock: imported sources expose no
  clock, entities are available for all time, and a KML gx:Track is drawn as
  its line.
- GPX is read into GeoJSON with one line per track segment, without times.
- KML/KMZ rows show a hatched swatch (they keep their own styles); keyboard
  focus is kept on the same control after hide/show and moves to the next
  row after remove.
- Unit regressions for each, and the rendered QA now imports hostile
  metadata, dated KML/GPX and a two-segment track, and fails on any request
  for a file's contents or icon assets, a clock change, or surviving
  metadata.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 08:50:35 -05:00
kapturnerandClaude Opus 5.5 2fec1091d4 fix(import): fly to points-only files without going underground
viewer.flyTo frames ground-clamped billboards before the destination's
terrain has loaded, and their bounding sphere then sits kilometers below
the surface: flying to a points-only GeoJSON from another city left the
camera about 2.3 km underground (a black screen). Frame the features'
longitudes and latitudes at the surface instead, and let the existing
camera ground guard lift the eye once the terrain can be measured
(src/ui/flyToDataSource.js, with tests).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 08:50:35 -05:00
kapturnerandClaude Opus 5.5 57b8d8b23c feat: import GeoJSON, KML, KMZ and GPX files onto the globe
DISPLAY ▸ Import (and drop onto the globe) puts the person's own files on
the map as Cesium data sources, clamped to the ground, one color per file,
with a row to show/hide, fly to or remove each. Files stay in the browser
for the session.

KML/KMZ are sanitized before Cesium reads them (network links, remote
icons/styles, models, photo/screen overlays and balloon HTML removed; KMZ
images served from blob URLs), so an imported file cannot make the page
fetch anything. KMZ is read with a small dependency-free zip reader on the
platform DecompressionStream, with a zip-bomb limit. Bounded file size,
entity count and number of files.

Tests: src/data/geoFileImport.test.mjs, src/ui/geoFileImport.test.mjs.
Gate: scripts/qa-geo-import.mjs. The import half of #890.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 08:50:35 -05:00
jibraaanandClaude Opus 5.5 e363b6a14f fix(mcp): share one panel key across an install's stdio servers
Fixes bilawalsidhu/gods-eye-view#927. Each stdio MCP server made its own
random panel key, and panel_request accepts only the key in the panel
page. A client that starts two server processes for one connection and
reads the page from one while sending the page's requests to the other
(seen in Claude Desktop on Windows) therefore always got "Only the God's
Eye View panel may make this request".

The stdio entry now passes a key shared by the install, created once in
.gev-cache/mcp-panel-key. The file is staged 0600 and published with
link(), so processes starting together agree on one key and never read a
half-written one. If it cannot be stored, the process keeps a key of its
own, as before. createLocalMcpServer still makes a fresh key unless given
one, so in-process servers (the /mcp HTTP plugin, tests) are unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 18:00:38 +05:00
Said TurkseverandClaude Opus 5.5 f33cd54e2b fix(panels): a collapsed window keeps its chosen height; handle early tile failures
- A floating portable panel saved its measured box, so dragging a
  collapsed window stored its 50 px header strip and a reload clamped it
  to the minimum. It now saves the inline size the user chose. A unit
  test and a collapse -> drag -> reload -> expand step in the panel gate
  pin it.
- Coverage awaits the tile fetch and the one-time terrain table load
  together, so a tile that fails before the table loads is reported at
  once instead of surfacing as an unhandled rejection.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 23:27:04 +01:00
Said TurkseverandClaude Opus 5.5 1fa129ea8e fix(street-level): camera ownership, mesh refresh, panel CSP (Sameh's review)
- Photo framing and FOLLOW claim the camera through the application's
  navigation, injected with attachNavigation({ run, subscribeHandoff }):
  aircraft and satellite tracking is released first, the cockpit refuses,
  and FOLLOW stops when another feature takes the camera. No UI imports
  in the layer.
- The mesh sampler follows the mesh floor's rules: it waits for the
  visible tileset to finish streaming (visibleTilesetLoaded, now exposed
  by the mesh floor service), needs a real bare-earth prior and keeps to
  the shared meshFloorSampleWithinPrior window, which cameraFollow now
  uses too. A cell is probed again once the camera is half as far from
  it (down to 40 m), so a coarse early sample is replaced and redrawn.
- The MCP globe panel's CSP allows the Mapillary Graph API and its image
  CDN (https://*.xx.fbcdn.net).
- The tile proxy reads bodies with the shared readResponseBytesCapped.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 23:04:33 +01:00
Said TurkseverandClaude Opus 5.5 630a1abdae test(street-level): click panel controls only once the panel holds still
CI's fixture gate timed out on the header ON/OFF pill: page.click reads a
point and then presses it, and the rail can still be resizing the panel
after coverage arrives, so the press missed. Panel clicks now wait for
the panel to hold still and check that the control is what lies under the
point before pressing. Also correct the header note on --fail-on-retry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 22:11:15 +01:00