- Voice layer manifest (src/voice/layerManifest.js) generates the layer enums,
aliases and analyst field hints; a test fails when a catalog layer has no entry.
- analyst_query validates fields, operators, units, values and scopes, counts up
to 250,000 loaded records per layer, reports complete/partial/unanswered, tells
LAYER_OFF / NOT_READY / FEED_UNAVAILABLE apart from zero, refuses mismatched
follow-ups and never lets a superseded query replace follow-up memory. Seven
more layers expose analyst records.
- Tool results carry a code-built say line and a display card; the voice card in
the mic control shows captions, plan steps and the result
(src/voice/resultDisplay.js is the one adapter). Slow tools narrate their
current step out of band; holding Space while the assistant talks cuts it off.
Turn spans go to the debug log; caption transcription is metered; the log
omits what was said unless GEV_VOICE_LOG_CONTENT=1.
- Point and ask: the Space-down pointer snapshot resolves "this", "that one" and
"around here" through 'pointer' targets, and "the second one" through
referent:n from the last numbered list.
- Prompt states each policy once (instructions + tools 48.5k -> 46.7k chars).
- Voice benchmark harness (scripts/qa-voice-bench.mjs, scripts/voice-bench/)
shares its phrase table and grading with scripts/qa-voice-routing.mjs.
Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a
key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled
worldwide name index; wide views show named points. ALPR reads an hourly
OpenStreetMap extract. Cockpit place context and country, state and US county
outlines use bundled Natural Earth and US Census data. Overpass is used only
when an operator configures their own endpoint.
Also: camera arrivals no longer land below ground, keyless terrain retries on
429, bounded relation geometry for configured Overpass, one shared
OpenStreetMap credit, and separate tile and API transports for embedders.
Co-authored-by: theGaryLarson <[email protected]>
Co-authored-by: Milan Khanal <[email protected]>
As more sources arrive, each one reads the same everywhere it appears: its
PROVIDERS chip, coverage lines, overview points, image cones and a single
legend swatch. The palette lives in one place, PROVIDER_COLORS in
src/layers/streetLevel/policy.js: Mapillary green (#05cb63), Panoramax
purple, Google Street View blue.
Mapillary drops its age dimming and magenta panoramas: lines and overview
points are Mapillary green, cones are green too and 360° images keep their
ring shape; the 360°/FLAT filter still separates them, and the selected
sequence stays GEV cyan.
The provider contract loses `legend`: `colors.coverage` is the source's one
colour, and the core composes the legend as one swatch per active source
(its name, its colour) followed by Selected. The chip group takes an
optional per-chip colour as a `--chip-color` CSS variable, written only
when it changes; Street Level chips show it as a swatch dot and, while on,
as the chip's border, text and a faint fill.
The feature gate checks the legend has one swatch per source plus Selected
and that the Mapillary chip carries #05cb63.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The panel changed the imagery filter and provider switches by calling the
layer directly, which bypasses the data manager's params request, the only
path saved state and share links record. A link copied after picking 360°
or a SINCE window came back with the defaults.
The controls now send those changes through
dataManager.setLayerParams('street-level', ..., { origin: 'user' }), as
the CCTV panel does; the layer applies them through its own setParams.
The feature gate's 360° step now asserts the share hash carries `0.p.p`;
it times out on the previous controls.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Brings in #744 (share-link token ledger). package.json: keep the branch's
sorted scripts and add layer-token:next and layer-token:check.
Street Level's token moves from the provisional `3` to `0`: following
CONTRIBUTING, the provisional entry was removed and
`npm run layer-token:next -- street-level` assigned `0` (free digits in
order). It is added to the registry and to
src/data/layerStateTokenReservations.json, and
`npm run layer-token:check -- --base-ref upstream/main` passes (28
published, 1 new). No link with the provisional token was ever published.
Pinned token tests move with the ledger as CONTRIBUTING asks: 29
reservations, next free digit `3`, and the competing-PR and retired-digit
scenarios shift up one free digit (0->3, 3->4) so they still test the same
rules. The README share examples and the free-digit notes in
CONTRIBUTING and CURRENT-STATE now read `0`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Draped ground polylines and clamped billboards land on the top of the
Google Photorealistic 3D mesh, so on a street under trees or an overpass
the coverage seemed to float at roof height.
At street zoom on Google 3D (camera below 1,400 m, back above 1,800 m)
the core switches to a `terrain` surface mode. groundCast.js samples the
existing terrain service on the ground-floor grid (0.001 deg), interpolates
bilinearly and adds 2 m; Mapillary lines become plain polylines at those
heights, and cones and the position marker stand on the street, so the
mesh hides what is behind buildings and trees. A tile draws draped first
and swaps to its cast lines once the heights arrive, keeping the old lines
until the new primitives are ready. Lookups are once per tile and aborted
when the tile is dropped; a geoid fallback counts as unknown and leaves
lines draped. Other map stacks and higher views are unchanged.
Providers get getSurface() and groundCaster in their context and an
optional setSurface(mode); getUIState() exposes `surface`. The QA gate
checks draped on Esri and terrain on Google 3D.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
npm test is the only suite CI runs, and its discovery walked src/ alone, so
server/providers/transitHistory.test.mjs has never executed since it was
added. Its 8 tests pass, so this was silent coverage loss rather than a hidden
failure: the file reads like covered code in review and never ran anywhere.
Add server/ to the searched roots, and pin the property with a test that walks
the repository itself and compares the two sets, so a tree added later without
updating the roots fails loudly instead of going quiet.
Suite goes from 5090 to 5099 tests, 0 failures: the 8 tests in that file plus
the regression test added here.
`CONTRIBUTING.md` asks every runtime change to add an entry at the top of
`CHANGELOG.md`, so two pull requests in flight write into the same few lines and
git has nothing to merge on. Against `main` at `81eb443`, `git merge-tree` over
the 85 open pull requests GitHub reports as conflicting that aren't mine puts
`CHANGELOG.md` in the conflict set of 41 — more than any other path — and for 6
it is the only one. `docs/CURRENT-STATE.md`, the other file rule 3 names,
conflicts for 23, and merges cleanly for 23 of the 41: edits land in the section
for the area you touched, so they only collide when the areas do.
A pull request now adds `changelog.d/<pull request>-<slug>.md` instead. Distinct
filenames never conflict, and the pull request number makes them distinct for
free. `npm run changelog` splices the directory into `CHANGELOG.md` and empties
it; `npm run changelog:check` reports the batch and changes nothing.
Fragment text is copied through unchanged apart from trailing whitespace.
`src/annotations/drawTool.test.mjs` reads `CHANGELOG.md` as prose and slices a
fixed window after a heading, so an assembler that reflowed an entry could move
text across that window. Fragments are deliberately outside
`scripts/format-scope.json` for the same reason.
The assembler refuses a batch before writing anything when a filename is not
`<pull request>-<slug>.md` or two fragments claim one pull request number, and
a unit test runs that check over the real `changelog.d/`, so a fragment the
release could not assemble fails `npm test` in the pull request that adds it
rather than at release time.
Refs #781.
The proxy already restricted WHERE it would go — https, an allowlisted host, a
station_information/station_status path, no redirects, a 12-second deadline and
a 5 MB body cap — but nothing restricted HOW OFTEN, so the dev server would
relay an unbounded stream of requests at a third-party bikeshare operator on
any caller's behalf, one outbound fetch and one timeout timer each.
Admit 120 requests/minute per client address, global backstop 3x, refused with
429 and `Retry-After` before the target is even parsed. The ceiling comes from
the layer's own demand: `src/layers/bikeshare/registry.js` catalogues 15
systems, station information is cached per city after its first fetch, and
station status refreshes once per city every STATUS_POLL_MS (60s) with
in-flight requests deduplicated — about 15 requests a minute with everything
in range, so 120 is eight times a maximal session.
The method, host, path, redirect, deadline and size guards are unchanged.
Refs #264
Every distinct 5-decimal point was a permanent in-memory entry, a
permanent line in an uncapped `.gev-cache/terrain-heights.json`, and a
live upstream call. Nothing limited the rate, and coordinates were
checked for being finite rather than for being on Earth, so an
out-of-range pair was still a valid cache key.
Four bounds, each following a pattern this repo already uses:
- WGS-84 range is enforced in `parseTerrainPoints`, at the edge, so no
later stage has to. Poles and the antimeridian still parse.
- The point cache holds at most 50,000 entries and evicts oldest-first,
the shape `trackBackfillProxies` uses. Sized from measurement: this
checkout's cache file held 6,189 entries in 1.0 MB of real use, so the
ceiling is eight times that working set. Terrain does not move, so an
evicted point costs one refetch and never a wrong answer.
- A cache file above 24 MB is not read back, mirroring
`rocketLaunchesProxy`. An unmeasurable file is not treated as an
oversized one: without stat, readFile decides, exactly as before.
- The route carries an always-on limiter of 90/min per client, 270
global — the `/api/overpass` band, and an order of magnitude above
measured demand (a full test:track run drove 10 requests, peak 8/min).
The ceilings are constructor options so they can be exercised as
behaviour rather than asserted as constants; production passes none.
They are destructured in the body because `proxyErrorResponses.test.mjs`
slices these proxies to the first column-zero closing brace.
Route the six proxied fetches that still buffered upstream bodies without
a limit through the shared capped readers in src/sources/httpBody.js:
FIRMS CSV, CelesTrak TLE groups, adsb.lol /v2/mil (both the OK and
relayed-status paths), TomTom flow tiles, OpenSky states/all and adsbdb
enrichment lookups. Each cap is a bound sized well above the measured
payload, not a budget.
readResponseBytesCapped gains the same optional abort signal as the text
reader, with the same cancel-on-abort plumbing and reader release, so the
TomTom tile path can share the route deadline instead of reading
arrayBuffer() unbounded. Where a route already had a deadline, the same
signal now covers the body read as well as the headers. Two polls had no
deadline at all: the adsb.lol military poll gains a 12-second one and the
OpenSky states/all exchange, including its Basic-auth retry, a 20-second
one; both miss into their existing STALE paths.
FIRMS map-key status, the OpenSky token exchange and CCTV media are left
as they are.
Co-Authored-By: Claude Opus 5 <[email protected]>
FOLLOW puts the globe camera at eye height inside the street-level view; on
flat imagery or terrain there is nothing to stand among. The layer now takes
the application map stack controller (attachMapStackController), subscribes
to stack switches, offers FOLLOW only while the stack is 'photoreal', disables
the button with a MAP SOURCE hint elsewhere, and stops following when the
stack leaves Google 3D. Selection uninstall is a no-op when never installed.
The QA gate checks the disabled path and, when Google 3D is available, that
leaving it stops following; its caption-timeout dump now forces one render to
tell a missed update from a render fault.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
At phone width the right rail is the band between mid-screen and the credit
line, so a 16:10 viewer plus its bar and caption was cut off. Below 720 px
the viewer's height is derived from that band (clamped 110-320 px); floating
and expanded viewers reset to auto height. The QA harness checks the photo
and caption fit at 390x844, and CURRENT-STATE documents the docking rules
and phone sizing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Only a user's own collapse docks a floating window; cockpit entry,
restores and the Cyber accordion no longer dock it or delete its saved
place, and the accordion leaves floating windows alone.
- The viewer host records an adapter as active only after its mount
succeeds, so a library that failed to load once is retried; concurrent
opens share one mount, and unmounting mid-mount leaves nothing active.
- openNearest tries every active provider; one provider's error (no key,
offline) no longer hides the others' imagery.
- The SINCE readout shows its cut-off date as soon as the committed value
matches the thumb, even while the slider keeps focus.
- A floating window that was only dragged is bounded to the viewport and
scrolls instead of running off-screen.
- getStats summarises coverage (summarizeCoverage) instead of building a
full UI snapshot every second; disable() uses credits.hideAll(); smaller
cleanups in the viewer host and sequence snapshot.
- Unit tests: panel docking and header double press, viewer-host mount
retry/sharing/cancel, coverage summary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The viewer opens at the top of the panel, under the header, so the photo
is on screen without scrolling; its tools (EXPAND, FIT/FILL, FOLLOW, close)
sit in a bar above the image, clear of the provider's own overlays, and the
caption wraps instead of truncating.
- No separate ON/OFF or OPEN NEAREST PHOTO buttons: the header pill is the
layer switch, and a provider chip is lit only while the layer is on (the
last lit chip switches the layer off).
- SINCE is a stepped slider (any date to the last month) whose readout names
the cut-off date and is announced via aria-valuetext.
- The panel floats and resizes (portable spec); in a resized window the
viewer takes the spare height and only the settings scroll. SHRINK after
EXPAND, collapsing, or a header double-click docks it at its default size.
- FIT/FILL tooltips say what they do.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The rail's startup move of panels into #right-context-rail cleared every
panel's inline position, so a window restored as floating dropped back to
its rail place after a reload. Floating panels now keep their geometry.
- A header double-click never snapped a floating panel back in a real
browser: the drag handler's pointerdown preventDefault() suppresses the
mouse events a native dblclick is built from. Two presses within 400 ms and
6 px (no drag between) are now detected in pointerdown itself.
- Portable specs can ask to dock when collapsed (dockOnCollapse), and
PanelChrome exposes dockPanel(). Street Level opts in as a portable panel
(min 320 x 280) with dockOnCollapse.
- qa-panel-resize: the panel reader returns right/bottom (s/e handle points
were NaN), counts only visible rail panels like the rail does, parks the
window clear of the voice dock before the resize loop, and names the
element under the pointer when an assertion fails.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bring in portable, resizable rail panels. The Street Level rail rules get
the same :not(.panel-floating) guard as CCTV's so the panel can float too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Street Level is now a provider-neutral layer modelled on the iD editor's
photo overlay. The core owns what every imagery provider shares: the
enable state and a switch per provider, the imagery filter (360°/flat and
captured-since in relative days), one click handler routed by pick prefix,
one viewer host that mounts whichever provider's viewer adapter owns the
open image, the position marker, camera follow, one on-globe credit per
active provider, and share-link parameters (one bit per provider plus the
filter). Providers implement the contract in registry.js: coverage,
sequences, nearest-image lookup and a viewer adapter emitting a common
pose. Mapillary is the only registered provider; Google Street View,
KartaView and Panoramax slot in beside it.
Panel: a PROVIDERS chip row (one chip per registered provider, keyless
chips read as errors), relative-day SINCE options, a provider-neutral
caption whose link label follows the provider, and no vendor mark in the
header. The panel inner is a rail scroller like CCTV's.
Tests cover the registry, params codec, filter, pick router, UI-state
composition, the provider definition and coverage-tile decoding; the QA
harness switches the provider off and on and checks the filter.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Pure move: the layer id, panel id, CSS prefix, classes, exports and config
paths become provider-neutral (street-level, #street-level-panel, sl-);
the Mapillary-specific modules move under providers/mapillary/. Behaviour
is unchanged. The two sprite collections now register under distinct ids
instead of overwriting each other under 'mapillary'.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Street Level is imagery only from here on: coverage, sequences and the
embedded viewer. The Claude planner, map-feature and traffic-sign tiles,
the NDJSON feature stream, sprites, detection outlines and analyst records
go, along with @anthropic-ai/sdk, zod and the Anthropic key entry.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Preserve all published token assignments and add regression coverage for
all-enabled URL restoration, longer token lists, and manual provisional
token recovery after competing merges.
Builds on daikaginza's digits-first and merge-time allocation proposal.
Thanks to KnottyDyes for the review and regression-test suggestions.
Upstream took share token '2' for fire perimeters, so Street Level moves to
'3'. mapillary-js joins upstream's optimizeDeps include list instead of
overriding it; catalog and registry tests count the new upstream layers.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
The panel no longer shows a separate keyless call-to-action; the fieldset
gate and the layer-row tooltip already cover the missing-key case. The
planner flag is tri-state (null until status answers).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Some catalogs publish a full product title rather than the bare model, e.g.
"AXIS Q6155-E PTZ Dome Network Camera" — which the exact model/alias match
misses. Add a fallback: after the exact match, scan for a record whose
DISTINCTIVE token (a >=4-char model/alias word containing a digit or hyphen,
so "dome"/"network"/"ptz"/"hd" never qualify) appears as a whole word in the
query. The most specific (longest) matched token wins; if two records tie on
the longest matched token the result is ambiguous and returns null, so the
lookup never guesses. Exact matches are unchanged and still take precedence.
main's boundary manifest now requires every module to be owned; add the new
helper beside presentation.js in cctv-layer and the two application groups.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The Natural Earth and neighborhood packs were loaded with a JSON-attributed
import(). The Vite dev server answers that request with a JavaScript module,
which the browser rejects, so every analyst region scope failed and fell
through to a geocode lookup that could take 12-28 s.
- src/data/bundledJson.js fetches the pack from a new URL(..., import.meta.url)
asset in the browser; under Node the caller's attributed import runs.
- resolveRegionRingForQuery caps the geocode + admin-boundary rung at 3 s and
returns { error: 'region-timeout' }; the lookup keeps running and fills the
cache. The analyst query reports it as code 'region-timeout'. The Nepal
locator opts out of the budget.
- Unit tests for the loader, the timeout and the analyst error; the behavior
harness checks the Gulf of Mexico scope and retries Texas on region-timeout.
Co-authored-by: Bilawal Sidhu <[email protected]>
Adds MODIS NRT to Active Fires and a keyless Fire Perimeters layer (NIFC WFIGS) with
InciWeb incident links, served through a capped, cached /api/fire-perimeters proxy.
Co-authored-by: Gustavo Beneduzi <[email protected]>
Co-authored-by: James Cooke <[email protected]>
The harness called the compatibility facade's private _refreshTogglePanel,
which the layer-lifecycle refactor removed; Transit and Directions call
refreshLayerStats() on the bare lifecycle, which lacked it, so their rows
only repainted on the next tick. Add refreshLayerStats() to the lifecycle
and use it from the harness. With the crash gone, two further checks ran:
the selected-entity paint wait now also waits for Radio's own reveal, and
on phones the title bar sits 16 px from the top so both broadcast waves
stay on-screen.
Co-authored-by: Bilawal Sidhu <[email protected]>
Adds a Local ADS-B layer fed by your own receivers: a browser WebUSB
RTL-SDR (with FM listening) and local 1090/978 MHz decoder feeds
(LOCAL_RECEIVER_FEEDS, local and private addresses only). Local aircraft
draw in magenta with class, silhouettes, 3D models, trails and a position
sanity filter; the click card names the band and input that heard them.
Builds on Sameh Khamis's SDR prototype and the initial GEV port by
Mazeyar Moeini Feizabadi (#134); the decoder-feed design follows the
notes on #57. Closes#57.
Co-authored-by: Sameh Khamis <[email protected]>
Co-authored-by: Mazeyar Moeini Feizabadi <[email protected]>
The right-rail layout pass lifts the Context, CCTV, Weather and Recent
Imagery panel scrollers to measure their natural heights, which clamps
their scroll offset to zero, but it only restored scrollers tagged
data-rail-scroller. A layer that refreshes its Layers-panel row every
second snapped the Context panel back to the top while scrolling. Tag
every lifted scroller and pin the pairing with a test that reads the
measuring CSS.
Co-authored-by: Bilawal Sidhu <[email protected]>
Twenty layers now (weather, wind, cyclones and recent imagery landed
upstream); Street Level sits after Recent Imagery in the right rail and the
Cameras group, and the rail CSS names it beside the other panels.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
iPad and iPhone show "An error occurred while rendering. Rendering has
stopped." on load. Cesium's AtmosphereStageVS calls
czm_computeGroundAtmosphereScattering with its `out` parameters bound
directly to varyings:
czm_computeGroundAtmosphereScattering(
v_positionWC, lightDirection,
v_atmosphereRayleighColor, v_atmosphereMieColor,
v_atmosphereOpacity);
ANGLE's Metal backend wraps each `out` argument in an ANGLE_out(...)
helper taking `thread T &`, but vertex-output members live in
`__metal_generic` and cannot bind to `thread`, so the program fails to
LINK and Cesium tears down the render loop.
This is the only shader in Cesium that binds varyings to `out` params --
SkyAtmosphere (czm_computeScattering) and the ground-atmosphere fragment
path both route through locals, so they are unaffected and stay on.
ModelSceneGraph.configurePipeline attaches the stage under a single
condition, `fog.enabled && fog.renderable`, so clearing `fog.renderable`
keeps the broken program from ever being built.
`renderable` rather than `enabled` is deliberate: Fog.update returns
early when `enabled` is false and forfeits the density that drives 3D
Tiles screen-space-error scaling, and visualSettings saves/restores
`fog.enabled` around IR boost, which would resurrect the broken shader
mid-session.
Detection is a WebGL2 link probe of the same out-param/varying pattern,
so a future driver fix silently restores the effect with no code change,
OR'd with iOS/iPadOS platform detection as a backstop for when no probe
context can be created. iPadOS desktop mode is identified by the
Macintosh UA token plus maxTouchPoints > 1, since real Macs report zero
and navigator.platform is deprecated.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HoRD3yHVgfTHeikTmsUoHf
scripts/qa-mapillary.mjs drives a real browser through the rail panel,
the keyless gate, coverage, a ready-made feature plan, the viewer and its
expanded dialog at 1440×900 and 1280×800.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
MAPILLARY_CLIENT_TOKEN is a public client token and reaches the browser
through the explicit define block; ANTHROPIC_API_KEY stays server-side.
Both appear in POWER UP with links to create them.
Co-Authored-By: Claude Fable 5.1 <[email protected]>