release: v0.1.0 — one-click install, keyless boot, in-app Provider Settings (#142)

The first formal release.

- One-click install via Pinokio; keyless boot lands on a live Esri World
  Imagery satellite globe with keyless terrain, with automatic OSM fallback
  and graceful degradation when terrain is unavailable.
- Provider Settings (the POWER UP panel): add, replace, or remove API keys
  inside the app; credential files made owner-only before any secret is
  written; external keys shown read-only; the panel refuses shared or proxied
  servers.
- Keyless capability responses for the optional HUD summary and place search.
- Aircraft-identity voice answers cover operator, type, and route, and say so
  plainly when enrichment is unavailable.
- README rewritten keyless-first; concise 0.1.0 changelog section; CI
  workflow included.

Gates: 2,672 unit tests pass / 0 fail, build clean, tracking regression
108/108, map-source tray QA pass, setup doctor ready.
This commit is contained in:
Bilawal Sidhu authored and GitHub committed 2026-08-31 17:49:45 -05:00
1 parent 6d83bb6008
commit b6da93b8ac
170 files changed
+5800 -853

No files matched your search

+1 -1
View File
@@ -137,7 +137,7 @@ export const CLASS_MODEL_URL = {
bizjet: '/models/airplane.glb', uav: '/models/airplane.glb',
};
/** Real per-class GLBs (2026-08-15 Hangar fleet, selected assets; CC-BY 4.0 —
/** Real per-class GLBs (2026-08-15 Hangar fleet, owner picks; CC-BY 4.0 —
* provenance in public/models/README.md). Every asset is vertex-baked to the
* airplane.glb convention: Y-up, X = length, Z = span, nose −X (so the layers'
* MODEL_HEADING_OFFSET_DEG = 180 applies unchanged), origin at bbox centre,
+1 -1
View File
@@ -18,7 +18,7 @@
* white / cyan-tracked / amber-military, plus .withAlpha fades) keeps working —
* no per-glyph hardcoded colors that would fight the tint.
*
* MIXED-SET UPDATE (2026-08-15, selected asset set): airliner/widebody/
* MIXED-SET UPDATE (2026-08-15, owner Hangar picks): airliner/widebody/
* turboprop/helicopter use the "refined" recognition-chart redraw;
* quadjet/glider use the "bold" chart-symbol redraw; light/fastjet keep the
* original drawings. Raster fidelity doubled (192px source, same 96 coords).
+1 -1
View File
@@ -1,7 +1,7 @@
// src/data/aisLiveVessels.test.mjs
// Focused tests for the AIS feed-status derivation helper (Batch 10, finding H3/AIS)
// and the vessel vertical-datum seam (2026-07-27 datum pass — see
// the vessel datum contract in docs/CURRENT-STATE.md).
// docs/superpowers/specs/2026-07-27-vessel-datum-design.md).
import { test } from 'node:test';
import assert from 'node:assert/strict';
import * as Cesium from 'cesium';
+1 -1
View File
@@ -167,7 +167,7 @@ test('P0: a pre-disposal close event cannot erase the post-disposal socket', asy
assert.deepEqual(context.adapter.debug().generations, [2],
'the replacement must NOT reuse generation 1');
// The pre-disposal socket's close finally lands — review's exact interleaving.
// The pre-disposal socket's close finally lands — the exact adversarial interleaving.
first.flushClose();
const debug = context.adapter.debug();
+1 -1
View File
@@ -3,7 +3,7 @@
* client-side in the layers ("how many flights over Texas?", "biggest fire
* near LA?", "which ships are headed to Oakland?").
*
* Analyst-query behavior is documented in docs/CURRENT-STATE.md:
* Design (owner-ratified, docs/voice-engine-evaluation-2026-07-23.md §5.3):
* - ENGINE (this module) is pure query logic over plain record arrays; it
* renders nothing. SURFACES (voice narration, panels, detection brackets)
* consume the returned result set — the engine/surface seam is the
+15 -15
View File
@@ -140,7 +140,7 @@ const GEO_PROGRESS_NOTIFY_BATCH_LIMIT = 10;
// Throttle for placeholder repaints — the projection RAF loop must not
// re-fill a 1080p canvas on every frame while a feed image is still loading.
const PLACEHOLDER_REPAINT_MS = 750;
// v1 key is retired dead data (product rule #3, §9.3 — WIPE CLEAN, no
// v1 key is retired dead data (owner decision #3, §9.3 — WIPE CLEAN, no
// legacy import): kept here only as a documented constant so nothing ever
// re-reads it by accident. Exported for the unit suite's "v1 is ignored"
// assertion; there is NO read path for this key anywhere in the module.
@@ -366,7 +366,7 @@ let _cardFetchMode = 'steady';
* 20/28/40 tiers resume when loading completes (see refreshAmbientCards).
*/
const CCTV_AMBIENT_CARD_DRAIN_CAP = 16;
// Global static-frame pacing (field finding 3): the pacer ticks at the burst
// Global static-frame pacing (owner finding 3): the pacer ticks at the burst
// spacing (250 ms) but cardFetchPolicy gates launches — cold fill (selected
// cards still missing their FIRST frame) allows up to 4 in-flight fetches at
// 250 ms spacing; steady state keeps the salvaged Part C gate of at most one
@@ -403,7 +403,7 @@ let _projectionOverlayOwnerId = null;
* thumbnail absent because its monitor plane is the active representation.
*/
let _activeCameraCardEnabled = false;
// Hover-summoned card (follow-up round 2, item B): pointing at a cardless camera
// Hover-summoned card (owner round 2, item B): pointing at a cardless camera
// icon shows its card immediately as a PINNED entry (budget-exempt, top
// draw-pass declutter priority).
/** Min spacing between hover scene.pick calls (event-driven, user gesture). */
@@ -693,7 +693,7 @@ function safeWindowLocalStorage() {
*
* v2 entries carry provenance: `{ values: <7-field offsets>, source: 'manual',
* savedAt: <epoch ms> }`. The v1 key (`CCTV_CALIBRATION_STORAGE_KEY_V1`) is
* NEVER read here — product rule #3 (§9.3): wipe clean, no legacy import.
* NEVER read here — owner decision #3 (§9.3): wipe clean, no legacy import.
*
* @param {{getItem:function}|null} [storage] - Injectable storage (defaults
* to `window.localStorage`); lets the unit suite test this pure of a DOM.
@@ -1186,7 +1186,7 @@ function buildCatalogFromSources(rawSources) {
* tileset is present (OSM fallback) this returns true so ground sampling is
* not permanently blocked.
*
* Task 5 (spec correction, spec §2): a HIDDEN tileset (`show === false`,
* Task 5 (review correction, spec §2): a HIDDEN tileset (`show === false`,
* i.e. a globe stack is active) must NOT report ready — Cesium 1.138's
* the shared sampler can only inspect *visible* 3D tilesets, so a sample taken
* against the hidden Google tileset would silently miss.
@@ -1456,7 +1456,7 @@ function refreshProjectionTextures(record) {
// Only swap when the canvas content actually changed since the last swap.
// Frames land every ~10 s but this runs at 1 Hz — swapping an UNCHANGED
// canvas re-uploads the texture for nothing, and each material image
// reassignment is a flash opportunity on the live plane (field test
// reassignment is a flash opportunity on the live plane (owner field test
// 2026-07-04: intermittent white flashes on the monitor plane).
if (runtime.canvasStamp === runtime.lastSwappedCanvasStamp) return;
@@ -1884,7 +1884,7 @@ function drawProjectionFrame(record) {
// and a fresh 1920x1080 texture upload; the plane renders its white
// base color (planeMaterial color = WHITE, alpha .95) for the frame or
// two Cesium needs to rebind, which IS the periodic white flash from the
// field tests (2026-07-04 and 2026-07-30).
// owner field tests (2026-07-04 and 2026-07-30).
const signature = projectionFrameSignature(runtime);
runtime.drawnImageStamp = runtime.imageStamp;
if (signature !== null && signature === runtime.lastFrameSignature) {
@@ -2732,7 +2732,7 @@ function refreshHorizonCulling() {
// ---------------------------------------------------------------------------
// Ambient card tier (2026-07-29 design — spec:
// docs/CURRENT-STATE.md)
// docs/superpowers/specs/2026-07-29-cctv-ambient-cards-design.md)
// ---------------------------------------------------------------------------
/** Returns (creating on demand) the stable frame slot for a camera id. */
@@ -2805,7 +2805,7 @@ function refreshAmbientCards() {
});
}
// Field finding 4: current card holders rank with the 20% incumbency
// Owner finding 4: current card holders rank with the 20% incumbency
// distance discount, so a small camera move never batch-swaps the ring.
// Item C: passing the viewport dims + per-candidate screen anchors routes
// the budget fill through the screen-distribution grid, so periphery
@@ -2840,7 +2840,7 @@ function refreshAmbientCards() {
})),
{ limit: cardLimit }
);
// Field finding 2: grace must never apply to the active camera — drop any
// Owner finding 2: grace must never apply to the active camera — drop any
// lingering grace entry and keep it out of the retained-card baseline.
if (activeId) {
_cardIds.delete(activeId);
@@ -2911,7 +2911,7 @@ function pushAmbientCardEntries() {
}
/**
* Throttled MOUSE_MOVE hover pass (follow-up round 2, item B): pointing at a
* Throttled MOUSE_MOVE hover pass (owner round 2, item B): pointing at a
* camera icon that has no card summons its card immediately. This is
* EVENT-DRIVEN picking on a user gesture, not steady-state work — the
* ≥120 ms throttle caps it at ~8 scene.pick calls/s while the pointer is
@@ -3008,7 +3008,7 @@ function hoverFetchCardFrame(record) {
}
/**
* Card-frame pacer tick (field finding 3): launches AT MOST one fetch per
* Card-frame pacer tick (owner finding 3): launches AT MOST one fetch per
* tick, with cardFetchPolicy deciding whether a launch is allowed. Cold fill
* — any selected card still missing its FIRST frame — bursts up to 4
* in-flight fetches at 250 ms spacing so arriving in a new area populates
@@ -3592,7 +3592,7 @@ function ensureGizmo() {
}
/**
* §9.1 activation obstruction probe (LOCKED product rule): on camera
* §9.1 activation obstruction probe (LOCKED owner decision): on camera
* ACTIVATION only, fire ONE scene.pickFromRay along the frustum axis
* (mount → cap-center direction). If it hits the tiles closer than the pose
* range, clamp the plane's effective range just short of the first hit so the
@@ -4362,7 +4362,7 @@ const cctvLayer = {
// remain eligible for true empty-space deselection.
const pickedId = resolvePickId(picked);
if (pickedId !== null) return;
// Item A (follow-up round 2): the scene pick found no camera — try the
// Item A (owner round 2): the scene pick found no camera — try the
// painted ambient cards. The cards canvas is pointer-events:none (this
// handler owns the events), so a click landing on a card's rect selects
// its camera exactly like a click on the icon. Cesium click positions
@@ -4451,7 +4451,7 @@ const cctvLayer = {
_removeFocusAppearListener = null;
stopProjectionLoop();
stopGeometryLoadQueue();
// Ambient cards tear down COMPLETELY on disable (product design point 6):
// Ambient cards tear down COMPLETELY on disable (owner design point 6):
// source entries, pacer timer, in-flight handlers, and caches.
teardownAmbientCards();
hideCctvVisuals();
+7 -7
View File
@@ -1,6 +1,6 @@
// src/data/cctv.test.mjs — CCTV v2 pure frustum geometry (computeFrustumGeometry).
//
// Locks the CCTV frustum geometry described in docs/CURRENT-STATE.md:
// Locks the §2a math of docs/plans/2026-07-03-cctv-v2-design.md:
// - the far-cap (monitor plane) corners lie ON the plane through capCenter
// perpendicular to the frustum view axis (ε < 0.5 m) — this is the geometric
// invariant that welds the wireframe corner rays to the plane entity;
@@ -402,7 +402,7 @@ test('ground clamp lifts the CAP CENTER only — the rectangle stays rigid (true
assert.equal(g.capCenter.alt, floor, 'cap center clamps exactly to the floor');
// Corners derive rigidly from the lifted center: alt = floor ± cos(pitch)·halfH.
// The bottom pair sits BELOW the floor (tiles occlude it) — per-corner clamping
// is what flattened the wireframe into a fan (field test 2026-07-04).
// is what flattened the wireframe into a fan (owner field test 2026-07-04).
const upVert = Math.cos(toRad(-24)) * g.halfH;
for (const key of ['tl', 'tr']) {
assert.ok(Math.abs(g.corners[key].alt - (floor + upVert)) < 1e-6, `${key} alt ${g.corners[key].alt}`);
@@ -415,7 +415,7 @@ test('ground clamp lifts the CAP CENTER only — the rectangle stays rigid (true
test('clamped pose keeps corner/plane coincidence and the rigid 2·halfW × 2·halfH span', () => {
// The wireframe corner rays must terminate exactly on the monitor plane's
// corners AT THE DEFAULT AUSTIN POSE — this is the case that diverged by
// ~47.5 m under per-corner clamping (field test 2026-07-04).
// ~47.5 m under per-corner clamping (owner field test 2026-07-04).
const g = computeFrustumGeometry(AUSTIN_FABRICATED_CAMERA, AUSTIN_GROUND);
const d = viewDir(41, -24);
const capEnu = enu(g.mount, g.capCenter);
@@ -1090,7 +1090,7 @@ test('heading wrap: heading 350° produces a symmetric cap (left/right corners e
// ---------------------------------------------------------------------------
// Task 5 — calibration v2 store + CAL badge (design §3b/§3c as amended by the
// LOCKED product rules §9.2/§9.3: wipe-clean v2 store, panel-only badge).
// LOCKED owner decisions §9.2/§9.3: wipe-clean v2 store, panel-only badge).
// ---------------------------------------------------------------------------
/** Minimal in-memory localStorage stand-in for pure store-IO unit tests. */
@@ -1167,7 +1167,7 @@ test('calibration v2: a corrupt v1 key never leaks into the v2 store (v1 is dead
});
// v2 key is untouched/empty — v1's presence must have zero effect.
const restored = readCalibrationStoreV2(storage);
assert.equal(restored.size, 0, 'v2 store must start empty — no legacy import (product rule #3, §9.3)');
assert.equal(restored.size, 0, 'v2 store must start empty — no legacy import (owner decision #3, §9.3)');
assert.ok(!restored.has('austin-42'));
});
@@ -1190,7 +1190,7 @@ test('deriveCalBadge: RAW PRIOR for everything else (all Austin Open Data today)
// ---------------------------------------------------------------------------
// Task 5 (height-datum fix): regime-aware ground resolution pure helpers.
// the height-datum contract in docs/CURRENT-STATE.md.
// docs/superpowers/specs/2026-07-05-entity-height-datum-design.md §2.
// ---------------------------------------------------------------------------
test('surfaceRegimeKey: globe hidden (photoreal) → google-3d; globe visible → terrain-globe', () => {
@@ -1236,7 +1236,7 @@ test('normalizeCoverageMode: garbage keeps the current mode', () => {
assert.equal(normalizeCoverageMode(3, 'on'), 'on');
});
// Unchanged-frame signature (white-flash fix, field test 2026-07-30)
// Unchanged-frame signature (white-flash fix, owner field test 2026-07-30)
// ---------------------------------------------------------------------------
/** Builds an RGBA buffer from [r,g,b] triples. */
+9 -9
View File
@@ -1,7 +1,7 @@
/**
* @module cctvCards
* @description Screen-space thumbnail cards for the citywide ambient CCTV
* tier described in `docs/CURRENT-STATE.md`.
* tier (design: docs/superpowers/specs/2026-07-29-cctv-ambient-cards-design.md).
* Replaces the rejected world-space static-plane ring with small canvas cards
* anchored to each LOD-selected camera's screen position, showing its latest
* paced static frame.
@@ -18,10 +18,10 @@
* persistence, retry pacing, cache pruning) and supplies ready-to-draw stable
* frame-slot references to the host.
*
* Zero-flicker contract (product requirement):
* Zero-flicker contract (owner requirement):
* - An AMBIENT entry whose frame slot has no drawn frame yet (`stamp === 0`)
* renders NOTHING — no placeholder, no chip. The camera icon alone carries
* it. Sole documented exception (follow-up round 2, item B): a PINNED entry
* it. Sole documented exception (owner round 2, item B): a PINNED entry
* (hover-summoned, `entry.pinned === true`) paints its chrome immediately —
* explicit user gesture wants instant feedback — with an empty thumb area
* until its fast-tracked frame lands.
@@ -48,7 +48,7 @@ export const CCTV_FRAME_CANVAS_W = 192;
export const CCTV_FRAME_CANVAS_H = 108;
/**
* Min screen separation between accepted card anchors (greedy declutter).
* Field test 2026-07-30: 130 read too sparse once the HUD safe-zone
* Owner field test 2026-07-30: 130 read too sparse once the HUD safe-zone
* filter started dropping cards as well. 112 still exceeds the card box width
* (104 px) so accepted boxes cannot overlap. This is THE density knob.
*/
@@ -59,8 +59,8 @@ export const CCTV_CARD_SAFE_TOP_MAX_PX = 150;
/** Bounded thumbnail cache (frame slots kept beyond the live card set). */
export const CCTV_FRAME_CACHE_MAX = 96;
// ─── Altitude scaling (field test finding 5, 2026-07-29) ──────────────
// Validated curve: cards are full size at street level, "start to get
// ─── Altitude scaling (owner field-test finding 5, 2026-07-29) ──────────────
// Owner-decided curve: cards are full size at street level, "start to get
// smaller" from ~1,800 m, "scale down progressively" to ~0.45 by 6,000 m,
// keep shrinking slightly and alpha-fade out across 7,500→9,500 m, and are
// fully hidden above that ("just the icons" at the highest zooms).
@@ -71,7 +71,7 @@ export const CCTV_CARD_FADE_END_M = 9_500;
export const CCTV_CARD_SCALE_AT_MID = 0.45;
export const CCTV_CARD_SCALE_MIN = 0.35;
// ─── Frame-fetch pacing (field test finding 3, 2026-07-29) ────────────
// ─── Frame-fetch pacing (owner field-test finding 3, 2026-07-29) ────────────
/** Steady-state global gate: one card-frame fetch per second. */
export const CCTV_CARD_FETCH_STEADY_SPACING_MS = 1_000;
/** Cold-fill burst spacing between fetch launches. */
@@ -150,7 +150,7 @@ export function declutterCctvCards(candidates, { minSepPx = CCTV_CARD_MIN_SEP_PX
}
/**
* Altitude-driven card scale + opacity (field test finding 5,
* Altitude-driven card scale + opacity (owner field-test finding 5,
* 2026-07-29 — curve constants above). Piecewise, monotonic non-increasing
* in both channels:
* - ≤1,800 m: full size, fully opaque.
@@ -178,7 +178,7 @@ export function cardScaleForAltitude(cameraHeightM) {
}
/**
* Cold-fill burst pacing policy (field test finding 3, 2026-07-29), as
* Cold-fill burst pacing policy (owner field-test finding 3, 2026-07-29), as
* a pure decision so the pacer tick stays trivially testable. While any
* selected card still lacks its FIRST frame (`coldFill`), up to
* `CCTV_CARD_FETCH_BURST_LIMIT` fetches may be in flight with
+2 -2
View File
@@ -129,7 +129,7 @@ test('CCTV card module cannot resurrect a canvas, projection, listener, or priva
for (const pattern of forbidden) assert.doesNotMatch(source, pattern);
});
// ─── cardScaleForAltitude — the validated altitude curve (finding 5) ────
// ─── cardScaleForAltitude — the owner-decided altitude curve (finding 5) ────
test('cardScaleForAltitude: full size and opacity at or below 1,800 m', () => {
assert.deepEqual(cardScaleForAltitude(0), { scale: 1, alpha: 1 });
@@ -137,7 +137,7 @@ test('cardScaleForAltitude: full size and opacity at or below 1,800 m', () => {
assert.deepEqual(cardScaleForAltitude(NaN), { scale: 1, alpha: 1 });
});
test('cardScaleForAltitude: hits the validated waypoints', () => {
test('cardScaleForAltitude: hits the owner-decided waypoints', () => {
const mid = cardScaleForAltitude(CCTV_CARD_SCALE_MID_M);
assert.ok(Math.abs(mid.scale - CCTV_CARD_SCALE_AT_MID) < 1e-9, 'scale ~0.45 at 6,000 m');
assert.equal(mid.alpha, 1);
+1 -1
View File
@@ -2,7 +2,7 @@
* @module cctvGizmo
*
* Direct-manipulation calibration gizmo for the CCTV layer (design:
* the CCTV calibration contract in `docs/CURRENT-STATE.md`).
* docs/superpowers/specs/2026-07-05-cctv-viewshed-gizmo-design.md §3c).
*
* Two layers:
* - Pure drag math (this top section): ray↔axis closest-point, ray↔plane
+1 -1
View File
@@ -1,5 +1,5 @@
// src/data/cctvGizmo.test.mjs — pure drag math for the CCTV calibration gizmo
// documented in docs/CURRENT-STATE.md.
// (docs/superpowers/specs/2026-07-05-cctv-viewshed-gizmo-design.md §3c).
//
// Locks:
// - closestParamOnAxis returns the metre-parameter along the AXIS of the
+12 -11
View File
@@ -7,15 +7,16 @@
* card set, keeps that set stable across small camera moves (eviction
* grace), and paces static-frame refreshes per source.
*
* The engine retains zoom-scaled budgets, distance-ranked in-view selection,
* an eviction-grace planner, and source-aware refresh cadences, retargeted
* from the rejected world-space static-plane ring to the
* Engine adapted from Manjunath's Part C work (`fix/cctv-part-c-review`):
* the zoom-scaled budgets, distance-ranked in-view selection, the
* eviction-grace planner, and the source-aware refresh cadences are his,
* retargeted from the rejected world-space static-plane ring to the
* screen-space thumbnail cards (see
* the ambient-card behavior documented in `docs/CURRENT-STATE.md`).
* docs/superpowers/specs/2026-07-29-cctv-ambient-cards-design.md).
*/
// Ambient-card budgets (follow-up round 2, item C: raised 16/24/32 → 20/28/40 —
// "a lot of empty space"). Tunable as a set, together with the card
// Ambient-card budgets (owner round 2, item C: raised 16/24/32 → 20/28/40 —
// "a lot of empty space"). Owner-tunable as a set, together with the card
// scale waypoints (1,800/6,000/9,500 m, cctvCards.js) and
// CCTV_CARD_MIN_SEP_PX: budgets say how many cameras HOLD cards, the
// waypoints and separation say how many fit on screen.
@@ -34,7 +35,7 @@ const PROVIDER_STATIC_REFRESH_MS = Object.freeze({
/**
* Returns the bounded ambient-card budget for the current viewer height.
* Card counts stay inside the 20..40 range (follow-up round 2): street level
* Card counts stay inside the 20..40 range (owner round 2): street level
* keeps the overlay sparse, metro scale earns the full ring.
*
* @param {number} cameraHeightM
@@ -52,7 +53,7 @@ export function cctvLodBudgets(cameraHeightM) {
}
/**
* Selection-level incumbency (field test finding 4, 2026-07-29): a
* Selection-level incumbency (owner field-test finding 4, 2026-07-29): a
* camera currently holding a card ranks with its distance discounted by this
* factor, so a small camera move never batch-swaps the ring — a non-carded
* camera displaces a carded one only when it is meaningfully (>20%) closer.
@@ -144,17 +145,17 @@ export function blendCenterRankKm(
return (1 - w) * km + w * spread * fraction;
}
// Screen-distribution grid (follow-up round 2, item C — "a lot of empty space"):
// Screen-distribution grid (owner round 2, item C — "a lot of empty space"):
// pure nearest-first selection clusters winners at screen center (nearest ==
// most central at typical view pitch) and leaves the periphery bare. The
// viewport is bucketed into this grid and every occupied cell gets its best
// candidate before global rank fills the rest. Tunable together with
// candidate before global rank fills the rest. Owner-tunable together with
// the budgets above.
export const CCTV_CARD_GRID_COLS = 5;
export const CCTV_CARD_GRID_ROWS = 4;
/**
* Screen-space distribution pass (follow-up round 2, item C). Buckets the
* Screen-space distribution pass (owner round 2, item C). Buckets the
* viewport into a CCTV_CARD_GRID_COLS × CCTV_CARD_GRID_ROWS grid, assigns
* each candidate to its cell (anchors just outside the viewport clamp to
* the edge cells), ranks within cells by effective distance (`rankKm` —
+5 -5
View File
@@ -1,6 +1,6 @@
// src/data/cctvLod.test.mjs
// Pure LOD-engine tests, adapted from earlier Part C suite
// zoom-scaled card budgets, nearest-first in-view
// Pure LOD-engine tests, adapted from Manjunath's Part C suite
// (fix/cctv-part-c-review): zoom-scaled card budgets, nearest-first in-view
// selection, video exclusion, the eviction-grace planner, and source-aware
// static-frame pacing.
import { test } from 'node:test';
@@ -35,7 +35,7 @@ function candidates(count, options = {}) {
}
test('cctvLodBudgets scales the card budget from 20 to 40 with view height', () => {
// Follow-up round 2 (item C): budgets raised 16/24/32 -> 20/28/40.
// Owner round 2 (item C): budgets raised 16/24/32 -> 20/28/40.
assert.equal(CCTV_AMBIENT_CARD_MIN, 20);
assert.equal(CCTV_AMBIENT_CARD_MID, 28);
assert.equal(CCTV_AMBIENT_CARD_MAX, 40);
@@ -71,7 +71,7 @@ test('selectCctvLod tolerates malformed candidate rows', () => {
assert.deepEqual(selected.cardIds, ['cam-ok']);
});
// ─── Selection-level incumbency (field test finding 4) ────────────────
// ─── Selection-level incumbency (owner field-test finding 4) ────────────────
test('incumbentRankKm discounts incumbents by the 20% factor', () => {
assert.equal(incumbentRankKm(10, false), 10);
@@ -121,7 +121,7 @@ test('selectCctvLod: no incumbents means plain nearest-first (unchanged behavior
assert.deepEqual(plain.cardIds, withEmpty.cardIds);
});
// ─── distributeCctvCards — screen distribution (follow-up round 2, item C) ──────
// ─── distributeCctvCards — screen distribution (owner round 2, item C) ──────
// Grid: 5 cols x 4 rows. At viewW 1000 / viewH 800 each cell is 200x200 px.
test('distributeCctvCards spreads clustered candidates across cells', () => {
+2 -2
View File
@@ -2,7 +2,7 @@
* @module cctvViewshed
*
* Viewshed presentation for the CCTV layer (design:
* the CCTV viewshed geometry contract).
* docs/superpowers/specs/2026-07-05-cctv-viewshed-gizmo-design.md §3a/§3b).
*
* Two responsibilities, both pure of layer state:
* - Color identity: a stable per-camera hue (golden-angle spaced over the
@@ -30,7 +30,7 @@ const LINE_ALPHA_ACTIVE = 1.0;
* Stable hue (degrees, [0, 360)) for a camera's position in the id-sorted
* catalog. Golden-angle spacing keeps any local cluster of neighbor cameras
* visually separated; id-sorting makes the assignment deterministic across
* sessions for a stable catalog (design §3a, open question Q4).
* sessions for a stable catalog (design §3a, owner question Q4).
* @param {number} index - Camera index in the id-sorted catalog.
* @returns {number} Hue in degrees.
*/
+1 -1
View File
@@ -1,5 +1,5 @@
// src/data/cctvViewshed.test.mjs — viewshed hue assignment + frustum volume
// geometry documented in docs/CURRENT-STATE.md.
// geometry (docs/superpowers/specs/2026-07-05-cctv-viewshed-gizmo-design.md §3a/§3b).
//
// Locks:
// - cameraHue is golden-angle spaced and deterministic (color identity is
+1 -1
View File
@@ -42,7 +42,7 @@ for (const layer of LAYERS) {
assert.match(regime, /if \(!_models3dEnabled\) return false;/,
'OFF must keep Cockpit AIR contacts in 2D');
assert.doesNotMatch(regime, /!_models3dEnabled\s*&&\s*!_cockpitContactMode/,
'Cockpit must not bypass the user-visible Display toggle');
'Cockpit must not bypass the owner-visible Display toggle');
});
test(`${layer.name}: the pilot's own airframe stays hidden in cockpit`, () => {
+1 -1
View File
@@ -92,7 +92,7 @@ export function rankContactMatch({ query, hex = '', callsign = '', registration
* (`track_entity`) is a MUTATION fulfilling "follow that one" — returning an
* ambiguity for the caller to resolve would cost a round-trip mid-demo, and
* the model's observed response to a non-ok track result is to retry with
* different guesses rather than to ask (field session 2026-08-21,
* different guesses rather than to ask (owner field session 2026-08-21,
* 23:48). So the lookup always commits. What it owes the caller is STABILITY:
* hex is unique and always present, so the same query resolves to the same
* contact for as long as both are loaded, instead of flipping between polls
+1 -1
View File
@@ -4,7 +4,7 @@ import * as Cesium from 'cesium';
* Per-layer data attribution registered into Cesium's credit display.
*
* Legal requirement (see DATA_SOURCES.md, findings H10/H11 in
* every third-party data layer this app can
* docs/pre-ship-audit-2026-07-01.md): every third-party data layer this app can
* display carries its own license and required attribution — ODbL (OSM
* datacenters/dams, adsb.lol, Overpass roads), CC BY-NC-SA (TeleGeography
* cables), NASA FIRMS, CelesTrak, USGS, City of Austin, GBFS operators, OpenSky.
+1 -1
View File
@@ -30,7 +30,7 @@ export const AIRCRAFT_BRACKET_ALPHA_FLOOR = 0.35;
* stays a pure policy module with no Cesium dependency; detectionPolicy.test.mjs
* imports the real constant and pins the two together so they cannot drift.
*
* It MOVES WITH THE DEFAULT (0.05 → 0.03 → 0.01; final value 2026-08-24).
* It MOVES WITH THE DEFAULT (0.05 → 0.03 → 0.01; owner final lock 2026-08-24).
* That pin is the tripwire
* for exactly this change, and the decision it forces is which of two things the
* approval attaches to: the bracket BRIGHTNESS, or the slider POSITION. It is
+1 -1
View File
@@ -36,7 +36,7 @@ test('the bracket floor anchor mirrors the real keyhole default it is calibrated
test('the default OUTSIDE setting reproduces the approved 0.35 floor exactly', () => {
// Byte-identical at the default, at every keyhole alpha, with the setting
// passed explicitly and with it omitted. This is the accepted look; only
// passed explicitly and with it omitted. The owner approved this look; only
// the off-default range is allowed to change.
assert.equal(aircraftBracketAlphaFloor(KEYHOLE_OUTSIDE_OPACITY_DEFAULT), AIRCRAFT_BRACKET_ALPHA_FLOOR);
for (const alpha of [0.01, 0.05, 0.2, 0.34, 0.35, 0.36, 0.7, 1]) {
+1 -1
View File
@@ -11,7 +11,7 @@
* 2. Free-form name/description text ("5TH ST / WEST AVE", "N LAMAR BLVD") is
* full of STREET names that merely contain a cardinal word. Reading a bare
* "West" there as a facing direction mis-orients the camera with false
* confidence (59 of ~1000 Austin cameras hit this — field review
* confidence (59 of ~1000 Austin cameras hit this — owner adversarial review
* 2026-07-04). There, only explicit travel forms ("WESTBOUND"/"WB") count —
* leave `allowBare=false` (the default).
*
+1 -1
View File
@@ -1,5 +1,5 @@
// directionToHeading — two matching modes. The regression that motivated the
// split (adversarial field review, 2026-07-04): bare cardinal words were
// split (owner adversarial review, 2026-07-04): bare cardinal words were
// matched in free-form Austin camera names, so a street like "5TH ST / WEST
// AVE" was mis-read as a west-facing camera with false high confidence.
import { test } from 'node:test';
+1 -1
View File
@@ -1,5 +1,5 @@
// src/data/fireAnchors.js — DEM ground anchors for rendered FIRMS detections
// (field finding 2026-07-21: at close/oblique zoom over high country,
// (owner field finding 2026-07-21: at close/oblique zoom over high country,
// fire dots anchored at ellipsoid height 0 read as buried inside the terrain
// ~1-2 km below the visible surface).
//
+1 -1
View File
@@ -1,5 +1,5 @@
// src/data/fireAnchors.test.mjs — DEM ground anchors for rendered FIRMS
// detections (field finding 2026-07-21: close-zoom fire dots read as
// detections (owner field finding 2026-07-21: close-zoom fire dots read as
// buried under high terrain because anchors sat at ellipsoid height 0).
//
// Locks the module's two jobs:
+1 -1
View File
@@ -85,7 +85,7 @@ test('buildCellCard: plural noun, max FRP and newest age, accent passthrough', (
assert.equal(card.accent, accentForSeverity('orange'));
});
// Field finding 2026-07-21: anchors must sit on the DEM once the shared
// Owner field finding 2026-07-21: anchors must sit on the DEM once the shared
// ground floor is warm — and the cached per-fire position must re-anchor when
// the floor lands AFTER the first (cold, height-0) render. Distinct coords
// from every other test in this file (module caches persist across tests).
-1
View File
@@ -320,7 +320,6 @@ export function createFirmsHeatmapLayer({
lastUpdate: _lastUpdate,
loading: _loading,
stale: _stale,
keyRequired: _keyRequired,
error: _keyRequired ? 'KEY REQUIRED' : (_stale ? staleText : _error),
loadingLabel,
};
+23 -29
View File
@@ -17,7 +17,7 @@
*
* Press Escape or click empty space to deselect a tracked flight — the camera
* is released IN PLACE (no flyTo), so the user keeps the context they were
* looking at (product rule 2026-07-02).
* looking at (owner decision 2026-07-02).
*/
import * as Cesium from 'cesium';
import { aircraftIncludedInNearby } from './aircraftNearbyPolicy.js';
@@ -100,14 +100,14 @@ const FOCUS_EVIDENCE_DEV = import.meta.env?.DEV === true;
/** Amber tint for known-military aircraft rendered by this layer (matches the military layer's icon color). */
const MIL_TINT = Cesium.Color.fromCssColorString('#FFB800');
// --- Ground traffic (product change 2026-07-03: "absolutely we should see planes
// --- Ground traffic (owner reversal 2026-07-03: "absolutely we should see planes
// taxiing and landing") -----------------------------------------------------------
// Present-but-grounded planes are RENDERED instead of being skipped: same class
// silhouette + rotation pipeline, clickable/trackable/detectable, sticky metadata
// updating normally. Landing/takeoff is a TRANSITION — the on_ground flip restyles
// the existing billboard in place, never a removal. Ground planes draw no trails
// and are excluded from the ambient enrichment sweep (click-to-enrich still
// works). In 3D mode they take model slots like airborne planes (product rule
// works). In 3D mode they take model slots like airborne planes (owner decision
// 2026-07-03 — no air/ground distinction), placed by the one-shot ground snap
// (see _modelDisplayPosition).
//
@@ -121,7 +121,7 @@ const MIL_TINT = Cesium.Color.fromCssColorString('#FFB800');
const GROUND_SCALE = 0.8;
/** Fleet (untracked) billboard tint: amber for known-military, white otherwise.
* Ground traffic gets NO special tint (validated behavior 2026-07-03 field test). */
* Ground traffic gets NO special tint (owner verdict 2026-07-03 field test). */
function _fleetBillboardColor(icao24) {
return isMilitaryIcao(icao24) ? MIL_TINT : Cesium.Color.WHITE;
}
@@ -131,7 +131,7 @@ function _fleetBillboardScale(icao24, klass) {
return (CLASS_SCALE_2D[klass] || 1) * (_flightData.get(icao24)?.onGround ? GROUND_SCALE : 1);
}
/** Depth-test policy for aircraft billboards. Round 5 (product invariant
/** Depth-test policy for aircraft billboards. Round 5 (owner directive
* 2026-07-06: "I just want the planes and their lines to ALWAYS be
* visible... evenly applied"): EVERY contact renders depth-test-free at
* every distance — grounded, low, and airborne alike. The photoreal mesh
@@ -157,7 +157,7 @@ const MODEL_MIN_PX = 24; // floor so distant models stay visible WITHOUT
// min-pixel blob (was 54 — far planes at the All radius became white
// star-bursts); ~matches the 2D icon size so the model↔billboard read is consistent
const TRACKED_MODEL_MIN_PX = 40; // keep the glTF silhouette comparable to the selected 2D glyph at handoff
export const TRACKED_MODEL_MAX_PX = 200; // selected close-range tracked-target feel
export const TRACKED_MODEL_MAX_PX = 200; // owner-selected close-range tracked-target feel
const MODEL_NATIVE_RADIUS_M = 34.41;
const MODEL_SCALE = 1; // airplane.glb is transform-applied and baked to real-world meters
// Per-mode caps. Each model is its own draw call (no instancing yet), so these bound the frame cost.
@@ -246,7 +246,7 @@ const _modelGen = new Map();
/** Lifecycle epoch; bumped on destroy so an in-flight load from a PREVIOUS init can't settle
* against a new lifecycle's globals (which destroy cleared). Captured by _ensureModel. */
let _modelEpoch = 0;
/** DEFAULT-ON in PROXIMITY (product invariant 2026-08-22). A fresh boot never runs
/** DEFAULT-ON in PROXIMITY (owner directive 2026-08-22). A fresh boot never runs
* layer-state restoration, so this initializer — not the codec — is what the app
* actually starts with; it must stay in lockstep with the `models3d` default in
* `layerState.js` and `this._models3dEnabled` in ui.js, or the DISPLAY rail would
@@ -457,7 +457,7 @@ const TRACKED_BILLBOARD_SCALE_BY_DISTANCE = new Cesium.NearFarScalar(
);
function _normalBillboardScaleByDistance() {
// Preserve the established close-range 3× scale. Any smaller user-visible
// Preserve the established close-range 3× scale. Any smaller owner-visible
// default belongs in a separate evidence-backed proposal.
return new Cesium.NearFarScalar(1000, 3.0, 8000000, 0.5);
}
@@ -599,7 +599,7 @@ let _trailBackfillToken = 0;
const RENDER_DELAY_SEC = 30;
/** @constant {number} Polls an aircraft may miss before removal (transient OpenSky dropouts). */
const MISSING_POLL_LIMIT = 3;
// --- Landed-plane fast cull (field report 2026-07-02: "phantom" planes
// --- Landed-plane fast cull (owner field report 2026-07-02: "phantom" planes
// lingered ~2 min at airports after touchdown). OpenSky's on_ground flag LAGS
// the actual landing, so a landed plane's last airborne-classified fixes show
// it low + slow on the runway; when such a plane then drops out of the poll,
@@ -652,7 +652,8 @@ function _approxDistanceKm(lat1, lon1, lat2, lon2) {
function _likelyLanded(icao24) {
const info = _flightData.get(icao24);
if (!info) return false;
// Round 7: the fast cull only applies to contacts that were AIRBORNE
// Round 7 (owner: "fewer planes than OpenSky's own map" + "parked planes
// never heal"): the fast cull only applies to contacts that were AIRBORNE
// this session — its original target, the post-LANDING ghost. OpenSky's
// ground coverage flaps constantly, so fast-culling every grounded contact
// put parked planes in an evict/re-enter churn: each re-entry was a
@@ -1579,14 +1580,14 @@ function _noteTrackedModelLoadFailure(url, err) {
/**
* The TRACKED aircraft's own model regime — DEFAULT-ON, camera-distance driven
* (product invariant 2026-08-19). Unlike the fleet, this does NOT consult the
* (owner directive 2026-08-19). Unlike the fleet, this does NOT consult the
* DISPLAY-rail `models3d` toggle: the selected contact is a single model, it is
* what the camera is pointed at, and zooming in on a target should resolve it
* into an aircraft without the operator arming anything. The toggle keeps
* owning the FLEET (`_modelRegimeActive`), which is the draw-call budget.
*
* Thresholds + hysteresis live in trackedModelRegime.js: enter at
* TRACKED_MODEL_ENTER_ALT_M (150_000 m — the playtested swap distance,
* TRACKED_MODEL_ENTER_ALT_M (150_000 m — the owner's playtested swap distance,
* deliberately NEARER than the fleet's 800 km ceiling this used to inherit),
* hand back only above TRACKED_MODEL_EXIT_ALT_M, so orbiting AT the boundary
* cannot flap billboard↔model. See that module's header for why the tracked
@@ -1897,7 +1898,7 @@ const FLOOR_EASE_EPSILON_M = 0.02;
/**
* The floor to stand a grounded contact on while its own cell is unresolved.
*
* Product invariant (2026-08-21, after a Re:Earth outage buried a parked contact
* Owner directive (2026-08-21, after a Re:Earth outage buried a parked contact
* at a Texas field): "hold the last known altitude until a fresh one comes in.
* Never render otherwise." Two tiers, strongest first:
* own — a floor this contact's OWN cell resolved to while it stood there.
@@ -1976,7 +1977,7 @@ function _dropHeldFloor(state) {
/** @constant {number} How long a retired hold stays usable as a rehydration
* seed — three poll intervals.
*
* Deleting the state outright was the first cut, and a field observation found
* Deleting the state outright was the first cut, and an owner sighting found
* what that costs: VIR138M at JFK, 45 kt down the runway, "clearly on good
* ground, then suddenly popped below the ground, then popped back up".
* OpenSky's `on_ground` flag is not clean through a rotation — it flaps — and
@@ -2129,7 +2130,7 @@ function _floorGroundedDisplayPosition(icao24, info, pos, modelOwnsVisual, nowMs
next.heldActive = Number.isFinite(effective);
}
// The floor moved DOWN under a contact that was standing on a BORROWED one.
// Dropping it by that difference in a single tick is the snap product behavior requires
// Dropping it by that difference in a single tick is the snap the owner asked
// not to have, so approach it instead. Two ways in, and both need it:
// - the real floor arrives below the hold (releasing the hold);
// - a re-probe finds a LOWER neighbour than the one being held, which the
@@ -2309,12 +2310,6 @@ export function _clearDisplayFloorStateForTest() {
_displayFloorState.clear();
}
/** Test hook: drops cached model ground snaps so a browser-harness scenario
* cannot inherit another scenario's per-contact measurement. */
export function _clearGroundSnapStateForTest() {
_groundSnap.clear();
}
/** Spec identity for a LOADED model: URL and scale together (same-URL classes
* differ by scale — airliner vs quadjet both ship airplane.glb). */
const _specKeyFor = (klass) => {
@@ -2341,7 +2336,7 @@ function _syncModelToClass(icao24) {
}
}
/** IR hot-target mode (field test 2026-08-16): the NVG/FLIR post-styles
/** IR hot-target mode (owner playtest 2026-08-16): the NVG/FLIR post-styles
* map LUMINANCE, so mid-gray textured models read cold and vanish into
* terrain. While a boost style is active every model renders flat white
* (hottest); per-spec color/tint restores on style exit. Driven by ui.js
@@ -2679,7 +2674,7 @@ function _fleetTick() {
// not just at the handoff below, or accumulated conversions would starve
// ordinary contacts of 3D models. (The handoff guard stays as defence.)
if (isTr3b(icao)) continue;
// Ground planes compete for model slots like everyone else (product rule
// Ground planes compete for model slots like everyone else (owner decision
// 2026-07-03: "3D mode is respected regardless of whether a plane is on the
// ground or in the air — no distinction"). The cap + nearest-first ordering
// below already bound airport clusters; grounded placement is handled by the
@@ -2782,7 +2777,7 @@ function _fleetTick() {
cameraHeightM: camera.positionCartographic?.height,
});
_billboardLimbScale.set(bb, treatment.factors.scale);
// Two-tier glyph raster (field test 2026-08-16): the billboard atlas
// Two-tier glyph raster (owner playtest 2026-08-16): the billboard atlas
// has no mipmaps, so no single texture stays crisp across the ~25–150
// device-px range scaleByDistance produces. Swap between the 64 px fleet
// raster and the 192 px close raster on the billboard's ACTUAL on-screen
@@ -3079,7 +3074,7 @@ async function _backfillTrail(icao24, token, oldestFixEpochSec) {
// and floor every waypoint at it so low baro segments never dive below the
// mesh; a no-baro waypoint (predominantly taxi/ground segments in /tracks)
// sits ON the surface when the floor is known.
// Round-2 fix: the
// Round-2 fix (owner: "trails suddenly much shorter / not loading"): the
// resolve is BOUNDED (≤1.2 s), not a blocking await — a cold Re:Earth
// lookup across a long path could stall the paint for seconds-to-timeout.
// Paint with whatever cells are warm; the resolve keeps filling the cache
@@ -3891,7 +3886,6 @@ const flightsLayer = {
// Browser-harness seam: isolates synthetic display-floor scenarios without
// changing any production lifecycle or cache policy.
_clearDisplayFloorStateForTest,
_clearGroundSnapStateForTest,
/** @type {number} Polling interval (ms) between update() calls */
updateInterval: 30000,
@@ -4053,7 +4047,7 @@ const flightsLayer = {
* reconcile them with the billboard collection.
*
* Handles HTTP 429 (rate-limit), 401/403 (auth), and transient errors
* with exponential-ish backoff. On success, adds, updates, or removes
* with exponential-ish backoff. On success, adds/updates/removes
* billboards and position history, triggers lerp blending for the
* tracked aircraft, and updates its label text.
*
@@ -4247,7 +4241,7 @@ const flightsLayer = {
// on_ground surface prior: ONLY synchronous warm-cache reads here —
// never a per-aircraft network fetch inside the poll loop (see the
// batch resolve call below, which fills this cache for NEXT poll). A
// Round 5 SIMPLIFICATION (product invariant: one floor, evenly applied):
// Round 5 SIMPLIFICATION (owner directive: one floor, evenly applied):
// the grounded surface is the round-4 choke point and nothing else —
// rendered-mesh cell first, real (never fallback-poisoned) DEM cell
// second. The old exact-5-decimal warm chain is GONE: it minted a new
@@ -4553,7 +4547,7 @@ const flightsLayer = {
// is about to delete (billboard restore, DR cache reset), and we must
// never leave the camera mid-follow with stale tracking state. The
// camera is then RELEASED IN PLACE — it stays where the follow left
// it, fully free (product rule 2026-07-02: no overview flyTo).
// it, fully free (owner decision 2026-07-02: no overview flyTo).
if (icao24 === _trackedIcao) {
_clearTracking(false, { evicted: true });
}
+4 -4
View File
@@ -885,13 +885,13 @@ test('display floor: the cached output is dropped when the floor changes under i
// --- F8: hold the last known floor through a floor-data gap ----------------
//
// Field incident 2026-08-21: four `[terrain-heights-proxy] refresh incomplete`
// Owner incident 2026-08-21: four `[terrain-heights-proxy] refresh incomplete`
// events in a row (Re:Earth timing out), and a parked contact at a Texas field
// popped BELOW the photoreal mesh for a few seconds. A cold cell used to mean
// "no clamp", which is only safe if the un-clamped height is a real reading —
// and for a grounded contact reporting no altitude it is the geoid, tens of
// metres under the mesh inland. The product must hold the last known altitude
// until fresh floor evidence arrives.
// metres under the mesh inland. Owner: "hold the last known altitude until a
// fresh one comes in. Never render otherwise."
test('display floor: a cold cell HOLDS the last floor that resolved for this contact', () => {
_clearDisplayFloorStateForTest();
@@ -1052,7 +1052,7 @@ test('display floor: an on_ground FLAP mid-takeoff-roll never dips below the run
// from the resolved surface to baro + geoid N — which at a sea-level field IS
// the geoid. Deleting the hold on the airborne poll made that switch visible:
// the contact came back grounded with no prior, outrunning its own floor
// cells at 23 m/s, and sat under the runway (field observation, VIR138M).
// cells at 23 m/s, and sat under the runway (owner sighting, VIR138M).
const GROUND = -28.5, GEOID = -32.5, LON = -73.78;
reportMeshFloorCell(40.64, LON, GROUND); // only the cell it STARTED on is warm
let lat = 40.64;
+5 -4
View File
@@ -5,14 +5,15 @@
// Caltrans/TfL camera priors) give ORTHOMETRIC height (H, "height above mean
// sea level"). N is the local geoid undulation — the gap between the WGS84
// ellipsoid and the geoid (~mean sea level) surface, ranging roughly
// -106..+85 m worldwide. See docs/CURRENT-STATE.md.
// -106..+85 m worldwide. See docs/plans/2026-07-05-entity-height-datum-fix.md.
//
// The implementation uses `egm96-universal` (npm, MIT, embeds the NGA
// Decision rule (task brief): try `egm96-universal` (npm, MIT, embeds the NGA
// EGM96 15' grid) as a lazy dynamic import so its ~2.7 MB grid data-chunk
// never lands in the eager Vite bundle. Only fall back to vendoring the NGA
// grid ourselves if the package fails tests, isn't browser-safe, or bloats
// the eager bundle. It passed the browser-safety, accuracy, and bundle checks,
// so this file is a thin wrapper around it — no vendored fallback is needed.
// the eager bundle. `egm96-universal` passed all three checks (see the
// task report), so this file is a thin wrapper around it — no vendored
// fallback was needed.
//
// egm96-universal's `meanSeaLevel(lat, lon)` already returns exactly N in
// metres (relative to WGS84 ellipsoid) with internal longitude
+3 -3
View File
@@ -1,4 +1,4 @@
// src/data/geoid.test.mjs — EGM96 geoid-undulation lookup.
// src/data/geoid.test.mjs — EGM96 geoid-undulation lookup (docs/plans/2026-07-05-entity-height-datum-fix.md Task 1).
//
// Locks the module's public interface (later tasks — aircraft altitude
// correction, CCTV terrain fallback — call this verbatim):
@@ -7,7 +7,7 @@
// orthometricToEllipsoidal(hMslM, latDeg, lonDeg): number hMslM + N
//
// Tolerance is loose (±2.5 m) by design: the bundled grid is EGM96 while the
// reference values are Re:Earth's EGM2008 — the two
// plan's "Verified facts" reference values are Re:Earth's EGM2008 — the two
// models differ by up to ~1 m, and the brief's own tolerance absorbs that
// spread rather than asserting exact agreement.
import { test } from 'node:test';
@@ -100,7 +100,7 @@ test('the reported SFO cockpit OSD height turns into a small positive MSL number
await ensureGeoidReady();
const n = geoidHeight(SFO.lat, SFO.lon);
assert.ok(n < -25 && n > -40, `SFO undulation should be strongly negative, got ${n}`);
// The screenshot showed ALT: -15m ellipsoidal over the SFO deck.
// The owner's screenshot: ALT: -15m ellipsoidal over the SFO deck.
const displayed = ellipsoidalToMslDisplayM(-15, n);
assert.ok(
displayed > 10 && displayed < 25,
+3 -3
View File
@@ -1,7 +1,7 @@
// src/data/groundFloor.js — coarse ground-floor clamp for entity render
// heights (field-test round 2026-07-06).
//
// Two field findings drove this module:
// Two owner findings drove this module:
// - RS46 (military H60): baro-only low-altitude contacts near steep terrain
// render INSIDE the hillside (no alt_geom → baro+N is off by more than the
// local relief).
@@ -118,7 +118,7 @@ export const NEIGHBOR_FLOOR_MIN_SAMPLES = 2;
*
* An earlier cut leaned HIGH, reasoning from the locked "never below the
* visible surface" principle. That principle is about a contact's OWN measured
* ground; applied to a BORROWED cell it inverts, and a field test found
* ground; applied to a BORROWED cell it inverts, and an owner playtest found
* why — planes floating in midair at terminal gates. The two errors are not
* symmetric:
* - Too LOW is inert. `displayFloorHeightM` only ever RAISES a position, so a
@@ -376,7 +376,7 @@ export function allocateCorridorCells(
return out;
}
// --- Mesh-floor cells (round 4, validated design) ---------------------
// --- Mesh-floor cells (round 4, owner-approved design) ---------------------
// The Re:Earth DEM is BARE EARTH; the visible world in the google-3d regime
// is the photogrammetric MESH, which sits above it (measured ~17 m at the
// Austin airport apron). DEM-flooring therefore still buried sprites/trails
+1 -1
View File
@@ -597,7 +597,7 @@ test('neighborFloorM takes the apron, not the roof, at a structure edge', () =>
reportMeshFloorCell(30.201, -97.66, 120); // the apron
reportMeshFloorCell(30.199, -97.66, 205); // the terminal roof next door
// A parked contact is on the apron; it is never on the roof. Leaning high
// here is what put planes in midair at gates during the field test.
// here is what put planes in midair at gates during the owner playtest.
assert.equal(neighborFloorM({ lat: 30.2, lon: -97.66 }), 120);
});
+1 -1
View File
@@ -1,4 +1,4 @@
// Mapped-installation proxy persistence (field test 2026-08-18: "search
// Mapped-installation proxy persistence (owner playtest 2026-08-18: "search
// nearby sites" was slow because every look around paid a live Overpass round
// trip, and the 5-minute memory tier died with the dev server).
//
+1 -1
View File
@@ -183,7 +183,7 @@ function integerOption(key, token, defaultValue) {
const OPTION_GROUPS = Object.freeze({
flights: Object.freeze([
// Product invariant 2026-08-22: the fleet's 3D models are DEFAULT-ON in
// Owner directive 2026-08-22: the fleet's 3D models are DEFAULT-ON in
// PROXIMITY mode. Proximity is itself the altitude/count gate — models only
// materialize once the camera is close enough and only for the nearest
// contacts in view — so "on" costs nothing at globe scale, and an operator
+1 -1
View File
@@ -332,7 +332,7 @@ test('compact URL omits absent-meaning option state and still resolves to it', (
});
test('a fresh boot starts 3D aircraft ON in proximity — codec, both layers, and the rail agree', async () => {
// Product invariant 2026-08-22: the DISPLAY-rail 3D toggle defaults ON with mode
// Owner directive 2026-08-22: the DISPLAY-rail 3D toggle defaults ON with mode
// `proximity`, because proximity is itself the budget — models materialize only
// below the fleet altitude ceiling and only for the nearest MODEL_MAX in view,
// so "on" costs nothing at globe scale and `all` stays a deliberate opt-in.
+2 -2
View File
@@ -51,7 +51,7 @@ const DEFAULT_OVERLAY_HOST = Object.freeze({
});
/**
* Build the validated local-infrastructure card copy.
* Build the owner-approved local-infrastructure card copy.
* @param {object} properties Unwrapped GeoJSON feature properties.
* @param {string} layerId Local layer id.
* @returns {{title:string,details:string[]}}
@@ -317,7 +317,7 @@ export function createLocalGeoJsonLayer({
* camera is parked. One timer for the whole layer (not per record) — the
* retry pass walks every record anyway. (perf rebase 2026-08-17)
*
* Two gates keep this from becoming an idle leak (second review):
* Two gates keep this from becoming an idle leak (review round 2):
* - CAPABILITY: without `scene.sampleHeightSupported` the sample can never
* succeed, so a timer here would re-arm on every requested frame,
* forever. Records simply stay at ellipsoid height — exactly the
+2 -2
View File
@@ -149,7 +149,7 @@ async function createRealLocalLayerHarness({
};
}
test('local infrastructure card copy uses the validated source fields', () => {
test('local infrastructure card copy uses the owner-approved source fields', () => {
assert.deepEqual(localInfrastructureOverlayCopy({
tags: {
name: 'DFW-1',
@@ -740,7 +740,7 @@ test('disable cancels a pending ground-retry render', async (t) => {
);
});
// ── The retry must be able to STOP (second review) ───────────────────────────
// ── The retry must be able to STOP (review round 2) ───────────────────────────
//
// The retry above arms itself off its own requested frame, so anything that
// makes the sample permanently impossible turns it into a perpetual-motion
+4 -3
View File
@@ -29,7 +29,7 @@ const MAX_SAMPLES_PER_CALL = 40;
/** @constant {number} Only cells within this range of the viewer subpoint are
* sampled. Round-5 hardening: tightened 40 → 15 km — beyond the streamed
* fine-LOD area a probe returns COARSE-tile heights (not undefined), and the
* one-shot latch made those permanent (follow-up round 5: grounded planes stuck
* one-shot latch made those permanent (owner round 5: grounded planes stuck
* at a uniform wrong height). */
const MAX_SAMPLE_DIST_KM = 15;
/** @constant {number} No sampling when the camera is above this height — the
@@ -155,8 +155,9 @@ export function sampleMeshFloorCells(scene, points, { excludeObjects = [], viewe
// the sample point: walk the tileset tree to the tile containing the
// coordinate and require its `geometricError` to be below a threshold tied
// to the accuracy the caller needs, before trusting the sample. That is
// feasible and deliberately deferred until rendered-mesh sampling can be
// validated consistently across all consumers.
// feasible and deliberately deferred — see the P2 in
// the project roadmap, which bundles it with the fly_route probe so
// one post-launch pass settles rendered-mesh sampling everywhere.
reportValidatedMeshFloorCell(cell.lat, cell.lon, height);
}
}
+2 -2
View File
@@ -28,7 +28,7 @@ const DEFERRED_DEPENDENCIES = ['ais-live-vessels', 'military-installations'];
const DEPENDENCIES = [...AIRCRAFT_DEPENDENCIES, ...DEFERRED_DEPENDENCIES];
const AWARENESS_REFRESH_MS = 750;
/** @constant {number} Refresh cadence while the camera pose is CHANGING.
* Field test 2026-08-18: the Contacts direction arrows and card readouts
* Owner playtest 2026-08-18: the Contacts direction arrows and card readouts
* "feel sluggish when you look around" — at the parked 750 ms cadence the
* arrows lag the view by up to three quarters of a second. */
const AWARENESS_MOTION_REFRESH_MS = 175;
@@ -444,7 +444,7 @@ export function buildAwarenessContextSnapshot(results, navigation = {}, { subjec
* They used to be separate. The panel read live billboard positions through
* `getNearby` with a 20 000 cap; the analyst re-derived its own answer from
* last-fix coordinates over a 2 000-record slice. Same question, same centre,
* two numbers — and in the live trial the spoken answer (15) and the panel
* two numbers — and in the owner's trial the spoken answer (15) and the panel
* (111) disagreed badly enough that the model narrated the difference away.
* Routing both through here makes them the same number BY CONSTRUCTION, so
* they cannot drift again.
+2 -2
View File
@@ -1080,7 +1080,7 @@ test('awareness clears are scoped to the selected source layer', () => {
});
// ===========================================================================
// BEGIN Contact-readout presence block.
// BEGIN Contact-readout presence block — fix/context-panel-next-subjects.
// Integrators: this whole delimited block belongs to the Contact-panel
// CONTACT LOST work. Keep it intact and keep any concurrent branch's own
// additions at the END of the file, so the two never collide.
@@ -1483,7 +1483,7 @@ test('production eviction sites actually tag their clears', () => {
});
// ===========================================================================
// END Contact-readout presence block.
// END Contact-readout presence block — fix/context-panel-next-subjects.
// ===========================================================================
test('cockpit blocks only non-aircraft Context camera flights', () => {
+16 -16
View File
@@ -95,7 +95,7 @@ const MIL_ICON_COLOR = Cesium.Color.fromCssColorString('#FFB800');
/** @constant {Cesium.Color} Lighter amber tint applied to the actively tracked aircraft */
const TRACKED_ICON_COLOR = Cesium.Color.fromCssColorString('#FFD166');
// --- Ground traffic (product change 2026-07-03; mirror of flights.js) ---------------
// --- Ground traffic (owner reversal 2026-07-03; mirror of flights.js) ---------------
// adsb.lol/readsb flags ground traffic with alt_baro === "ground" (no separate
// boolean). Such aircraft are RENDERED instead of floating at the 3 km altitude
// fallback: same silhouette + rotation pipeline, clickable/trackable/detectable,
@@ -105,7 +105,7 @@ const TRACKED_ICON_COLOR = Cesium.Color.fromCssColorString('#FFD166');
// decision 2026-07-03 — no air/ground distinction), placed by the one-shot
// ground snap (see _modelDisplayPosition).
//
// TINT: full-strength amber, same as airborne (validated behavior 2026-07-03 field
// TINT: full-strength amber, same as airborne (owner verdict 2026-07-03 field
// test: the day-1 slate-gray 50%-alpha muted tint was unreadable — "in NYC I can
// barely see them"). "On the ground" reads from the ×0.8 scale + missing trail;
// "feed-dropped, coasting" stays the 45%-alpha stale fade.
@@ -113,7 +113,7 @@ const TRACKED_ICON_COLOR = Cesium.Color.fromCssColorString('#FFD166');
const GROUND_SCALE = 0.8;
/** Depth-test policy for aircraft billboards (mirror of flights.js — see the
* full rationale there). Round 5 (product invariant 2026-07-06): EVERY contact
* full rationale there). Round 5 (owner directive 2026-07-06): EVERY contact
* renders depth-test-free at every distance — a uniform always-visible rule;
* the fleet tick's horizon occluder still removes far-side contacts. */
function _groundDepthDistance() {
@@ -131,7 +131,7 @@ const JET_MODEL_URL = '/models/jet.glb';
const MODEL_ALT_CEIL_M = 800000; // m: below this camera altitude, draw 3D models (raised so it's easy to trigger)
const MODEL_MIN_PX = 24; // floor so distant models stay visible without ballooning into a min-pixel blob (mirror of flights.js, whose models now share this layer's ~30 m world size)
const TRACKED_MODEL_MIN_PX = 40; // keep the glTF silhouette comparable to the selected 2D glyph at handoff
export const TRACKED_MODEL_MAX_PX = 200; // selected close-range tracked-target feel
export const TRACKED_MODEL_MAX_PX = 200; // owner-selected close-range tracked-target feel
const MODEL_NATIVE_RADIUS_M = 29.83;
// jet.glb is transform-applied at real-world scale — native bounding radius
// 29.83 m at scale 1. ×1 → ~22–43 m aircraft across CLASS_SCALE_3D, matching
@@ -164,14 +164,14 @@ const PLANE_MODEL_URL = '/models/airplane.glb';
const PLANE_MODEL_SCALE = 1;
const PLANE_NATIVE_RADIUS_M = 34.41;
const PLANE_BELLY_OFFSET_NATIVE = 6.719;
/** Per-class model spec for THIS layer (2026-08-16, field test ask:
/** Per-class model spec for THIS layer (2026-08-16, owner playtest ask:
* military contacts should read as their WEIGHT CLASS, always in this layer's
* amber). Real Hangar GLBs serve the classes they cover (meters, nose −X →
* 180° offset); airliner/quadjet/glider get the shared 747 silhouette
* (airplane.glb — C-5M/RC-135-style heavies stop rendering as bizjets);
* fastjet and unknown keep jet.glb with the same 180° offset. The MIX tint stays
* dominant everywhere — military is amber, tracked is TRACKED_ICON_COLOR,
* and the tint must dominate any livery. Specs are
* and the tint must dominate any livery (owner: "stays yellow"). Specs are
* static per class — memoized (the fleet pass asks at 12 Hz per model). */
const _specCache = new Map();
function _modelSpec(klass) {
@@ -213,7 +213,7 @@ const _modelGen = new Map();
/** Lifecycle epoch; bumped on destroy so an in-flight load from a PREVIOUS init can't settle
* against a new lifecycle's globals (which destroy cleared). Captured by _ensureModel. */
let _modelEpoch = 0;
/** DEFAULT-ON in PROXIMITY (product invariant 2026-08-22). A fresh boot never runs
/** DEFAULT-ON in PROXIMITY (owner directive 2026-08-22). A fresh boot never runs
* layer-state restoration, so this initializer — not the codec — is what the app
* actually starts with; it must stay in lockstep with the `models3d` default in
* `layerState.js` and `this._models3dEnabled` in ui.js, or the DISPLAY rail would
@@ -384,7 +384,7 @@ const TRACKED_BILLBOARD_SCALE_BY_DISTANCE = new Cesium.NearFarScalar(
function _normalBillboardScaleByDistance() {
// Match commercial flights and preserve the established close-range 3×
// default. Smaller user-visible scaling must be proposed separately.
// default. Smaller owner-visible scaling must be proposed separately.
return new Cesium.NearFarScalar(1000, 3.0, 8000000, 0.5);
}
@@ -515,7 +515,7 @@ let _trailBackfillToken = 0;
const RENDER_DELAY_SEC = 15;
/** @constant {number} Polls an aircraft may miss before removal (transient adsb.lol dropouts). */
const MISSING_POLL_LIMIT = 3;
// --- Landed-plane fast cull (mirror of flights.js; field report
// --- Landed-plane fast cull (mirror of flights.js; owner field report
// 2026-07-02: "phantom" planes lingered ~2 min at airports after touchdown).
// The feed's ground flag lags the actual landing, so a landed plane's last
// airborne fixes show it low + slow on the runway; when it then drops out of
@@ -1357,11 +1357,11 @@ function _noteTrackedModelLoadFailure(url, err) {
/**
* The TRACKED aircraft's own model regime — DEFAULT-ON, camera-distance driven
* (product invariant 2026-08-19). Mirror of flights.js: this does NOT consult the
* (owner directive 2026-08-19). Mirror of flights.js: this does NOT consult the
* DISPLAY-rail `models3d` toggle, which keeps owning the FLEET
* (`_modelRegimeActive`) and its draw-call budget. Thresholds + hysteresis live
* in trackedModelRegime.js — enter at TRACKED_MODEL_ENTER_ALT_M (150_000 m, the
* playtested swap distance, deliberately NEARER than the fleet's 800 km
* owner's playtested swap distance, deliberately NEARER than the fleet's 800 km
* ceiling this used to inherit), hand back only above
* TRACKED_MODEL_EXIT_ALT_M so a boundary orbit cannot flap billboard↔model.
* First-person means your own airframe is not drawn in cockpit — the eye sits
@@ -1839,7 +1839,7 @@ function _fleetTick() {
// 3D-model eligibility: by DISTANCE (mode's add/keep band) with ON-SCREEN PRIORITY under the cap —
// mirror of flights.js. FOUR visible-first passes: (1) KEEP on-screen modeled; (2) ADD on-screen
// new in add radius; (3) KEEP off-screen modeled; (4) ADD off-screen new with leftover slots. KEEP
// is split by frustum so an off-screen retained model can't starve an on-screen plane (review).
// is split by frustum so an off-screen retained model can't starve an on-screen plane (review finding).
let modelEligible = null;
if (useModels) {
const cap = _modelCap();
@@ -1856,7 +1856,7 @@ function _fleetTick() {
// it must not occupy a CAP SLOT either (mirror of flights.js) — excluded
// at selection time, not just at the handoff below.
if (isTr3b(icao)) continue;
// Ground planes compete for model slots like everyone else (product rule
// Ground planes compete for model slots like everyone else (owner decision
// 2026-07-03, mirror of flights.js — no air/ground distinction; grounded
// placement is handled by the one-shot ground snap in _modelDisplayPosition).
const d2 = Cesium.Cartesian3.distanceSquared(camPos, bb.position);
@@ -2235,7 +2235,7 @@ async function _backfillTrail(icao24, token, oldestFixEpochSec) {
// 'ground'/null point sits ON the local surface — the old fixed 50 m
// sentinel rendered ~1.5 km underground at Kirtland AFB (field ~1590 m
// ellipsoidal) and dragged the whole pattern-work loop with it.
// Round-2 fix: the
// Round-2 fix (owner: "trails suddenly much shorter / not loading"): the
// resolve is BOUNDED (≤1.2 s), not a blocking await — a cold Re:Earth
// lookup across a long path could stall the paint for seconds-to-timeout.
// Paint with whatever cells are warm; the resolve keeps filling the cache
@@ -3193,7 +3193,7 @@ const militaryFlightsLayer = {
// is about to delete (billboard restore, DR cache reset), and we must
// never leave the camera mid-follow with stale tracking state. The
// camera is then RELEASED IN PLACE — it stays where the follow left
// it, fully free (product rule 2026-07-02: no overview flyTo).
// it, fully free (owner decision 2026-07-02: no overview flyTo).
if (icao24 === _trackedIcao) {
_clearTracking(false, { evicted: true });
}
@@ -3549,7 +3549,7 @@ const militaryFlightsLayer = {
// card, in the analyst's answer, and in the Contacts list. Matching only
// callsigns meant "follow 6606" — and the analyst → track_entity handoff
// the tool instructions prescribe — answered "nothing matched" for the
// very identity the app had just shown (field session 2026-08-21,
// very identity the app had just shown (owner field session 2026-08-21,
// 23:48: three failed track_entity retries before a fallback stuck).
// Ranking is shared with the flights layer (contactMatch.js) so the two
// cannot disagree, and it is strictly tiered so a registration can never
+1 -1
View File
@@ -449,7 +449,7 @@ test('real military track path creates no native label and publishes every cache
* and then hand that identity to track_entity. The analyst's `id` is a DISPLAY
* label — callsign, else registration, else hex — while the lookup matched
* callsigns and hex only, so a callsign-less contact came back as its tail
* number and "Nothing matched" (field session 2026-08-21, 23:48: three
* number and "Nothing matched" (owner field session 2026-08-21, 23:48: three
* failed retries before a fallback stuck).
*/
test('a callsign-less contact is findable by the tail number the app displays', () => {
+1 -1
View File
@@ -13,7 +13,7 @@ const CLASS_BY_MILITARY_TAG = {
};
/**
* How an UNNAMED feature reads on the map (field test 2026-08-18: the old
* How an UNNAMED feature reads on the map (owner playtest 2026-08-18: the old
* fallback surfaced "range (10981656305)" — an OSM primary key shown to a human
* as if it were a place name).
*
+1 -1
View File
@@ -309,7 +309,7 @@ function renderRecords() {
* `resolveGroundFloorCellsBounded` gives up after FLOOR_RESOLVE_DEADLINE_MS so
* a cold DEM can never hold the dots hostage — but the resolve keeps running
* and lands seconds later, and without this the records it covers stay pinned
* at ellipsoid height 0, sitting visibly under the 3D tiles (field test
* at ellipsoid height 0, sitting visibly under the 3D tiles (owner playtest
* 2026-08-18: "orange dots at the bottom").
*
* This is the render -> warm -> re-render chain FIRMS already uses, with one
+2 -2
View File
@@ -763,8 +763,8 @@ test('the trail head grows continuously across the envelope, never in one step',
// heading then rotated into world space, putting the trail out to one side and
// flipping which side as the course changed.
//
// A single-heading harness cannot catch this: at one heading a wrongly-framed
// offset can coincidentally point
// A single-heading harness cannot catch this, which is exactly how it reached
// the owner: at one heading a wrongly-framed offset can coincidentally point
// aft. So this sweeps headings and every shipped asset, and asserts the
// property directly — the anchor lies in the model's longitudinal/vertical
// plane, with NO lateral component.
+1 -1
View File
@@ -88,7 +88,7 @@ export function courseBetweenCartesians(from, to, minChordM = 25) {
* track only (chord weight 0). ≈30 kt. */
export const COURSE_TRACK_ONLY_MPS = 15.4;
/** At/above this displayed ground speed the course source is the chord only
* (weight 1) — the field-validated regime; behavior is unchanged there.
* (weight 1) — the regime the owner field-approved; behavior unchanged there.
* ≈50 kt. */
export const COURSE_CHORD_ONLY_MPS = 25.7;
/** Below this displayed speed neither chord nor reported track means anything
+1 -1
View File
@@ -3,7 +3,7 @@
* neighborhood boundaries that OSM tags as label-nodes-only (Chinatown, the Marina, the
* Mission, …). It sits AHEAD of the live-Overpass / synthesis path in the resolver, so
* covered neighborhoods resolve instantly to a REAL boundary with no network dependency
* because the live Overpass path is slow and inconsistent for neighborhoods.
* (the live-Overpass path is slow/flaky for neighborhoods — see docs/field-test-2-analysis.md).
*
* Source-agnostic: each city is a `{name, geometry}` GeoJSON file in
* `local_data/neighborhoods/`. Swap the file (e.g. to public-domain DataSF) without
+13
View File
@@ -4,6 +4,7 @@ import createViteConfig, {
adsbLolFallbackAnchor,
coalesceProxyRequest,
launchLibraryRequestHeaders,
keylessGooglePlacesResponse,
LL2_CACHE_TTL_MS,
readResponseJsonCapped,
regionalBriefHasAnySource,
@@ -11,6 +12,18 @@ import createViteConfig, {
validRegionalPoint,
} from '../../vite.config.js';
test('missing Google place context is a quiet keyless capability, not a 503', () => {
assert.deepEqual(keylessGooglePlacesResponse(undefined), {
statusCode: 200,
payload: { configured: false, error: null, places: [] },
});
assert.deepEqual(keylessGooglePlacesResponse(' '), {
statusCode: 200,
payload: { configured: false, error: null, places: [] },
});
assert.equal(keylessGooglePlacesResponse('configured-key'), null);
});
test('regional proxy rejects absent and blank coordinates instead of coercing them to zero', () => {
assert.equal(validRegionalPoint(new URLSearchParams('longitude=12.5')), null);
assert.equal(validRegionalPoint(new URLSearchParams('latitude=12.5')), null);
+2 -2
View File
@@ -1,5 +1,5 @@
// src/data/renderAltitude.js — pure priority-chain helper for aircraft render
// altitude.
// altitude (docs/plans/2026-07-05-entity-height-datum-fix.md Task 6).
//
// The globe needs ELLIPSOIDAL height (h = H + N). OpenSky's `geo_altitude`
// (state-vector index 13) is already WGS84 geometric/ellipsoidal — the
@@ -78,7 +78,7 @@ export function reuseGroundedSurfaceM(currentM, previousM) {
* contact's cells went cold, and this guess overwrote a height that had been
* sitting correctly on the mesh — dropping it through the ground until the
* proxy recovered. With `priorRenderM` present the caller's existing sticky
* fallback holds that height instead, which is what product behavior requires for:
* fallback holds that height instead, which is what the owner asked for:
* "hold the last known altitude until a fresh one comes in."
*
* @param {object} params
+2 -1
View File
@@ -1,4 +1,5 @@
// src/data/renderAltitude.test.mjs
// docs/plans/2026-07-05-entity-height-datum-fix.md Task 6.
//
// Locks pickRenderAltitudeM's priority chain — the SINGLE source of truth for
// where a flights-layer aircraft (and its dead-reckoned/tracked-camera
@@ -182,7 +183,7 @@ test('taxiing aircraft over nonzero terrain resolves every poll after the first
// ---------------------------------------------------------------------------
// geoidSurfaceLastResortM — the geoid guess must never outrank what the
// contact already knows (field incident 2026-08-21: a Re:Earth outage plus
// contact already knows (owner incident 2026-08-21: a Re:Earth outage plus
// this guess dropped a parked contact through the mesh at a Texas field).
// ---------------------------------------------------------------------------
+1 -1
View File
@@ -162,7 +162,7 @@ test('selected satellite params survive delayed arrival and yield to newer expli
test('a tracked docked cluster consolidates its companions onto one card', () => {
// ISS and everything berthed to it are separate real tracks at one position,
// so their ambient labels stack underneath the tracked card. Product decision:
// so their ambient labels stack underneath the tracked card. Owner ruling:
// consolidate them as secondary info on that card, and suppress only those
// members — never unrelated satellites that merely happen to be nearby.
const entity = { gevLabelModel: { title: 'OLD', details: ['? km'] } };
+1 -1
View File
@@ -30,7 +30,7 @@ test('the guard is an Earth-sized band, not just a non-zero check', () => {
// Earth actually occupies, because the values BETWEEN "zero" and "the globe"
// are the ones that fail quietly.
const belowTheFloor = [
// the regression probe: finite, non-zero, and 6,378 km underground. A bare
// The adversarial probe: finite, non-zero, and 6,378 km underground. A bare
// non-zero check accepts it, and the shipped path then reverse-geocodes
// 0°, 0° as if the operator were looking at the Gulf of Guinea.
new Cesium.Cartesian3(500, 0, 0),
+5 -3
View File
@@ -54,8 +54,10 @@ export const CABLE_SWEEP_MOTION_EPSILON_M = 250;
* layer alone cost ~9.5 ms/frame during camera motion. The depth cue this
* trades away (photoreal tiles occluding label TEXT at low, city-level
* cameras) matches the sibling dams/datacenters sources, which shipped
* host-composited under the same rule; the anchor points/stems remain
* Cesium-native and depth-tested.
* host-composited under the same ruling; the anchor points/stems remain
* Cesium-native and depth-tested. See
* the world-overlay consolidation design notes ("Depth-testing
* decision") for both dated decisions.
*/
export const CABLE_LABEL_DEPTH_DECISION = Object.freeze({
option: 2,
@@ -207,7 +209,7 @@ export function createCableOverlayPublisher({
* real `MAP_STACKS` so the omission is caught loudly.
*/
const CABLE_GLOBE_STACK_IDS = Object.freeze(
new Set(['bing-aerial', 'bing-labels', 'osm']),
new Set(['bing-aerial', 'bing-labels', 'esri-imagery', 'osm']),
);
/**
@@ -151,7 +151,7 @@ test('cable ground lines classify against exactly the active surface on every st
Cesium.ClassificationType.CESIUM_3D_TILE,
);
// Every globe stack renders imagery on the shown globe — terrain pass only.
for (const stackId of ['bing-aerial', 'bing-labels', 'osm']) {
for (const stackId of ['bing-aerial', 'bing-labels', 'esri-imagery', 'osm']) {
assert.equal(
cableClassificationTypeForStack(stackId),
Cesium.ClassificationType.TERRAIN,
+2 -1
View File
@@ -1,4 +1,5 @@
// src/data/terrainHeights.js — batched, cached client terrain-height resolver.
// src/data/terrainHeights.js — batched, cached client terrain-height resolver
// (docs/plans/2026-07-05-entity-height-datum-fix.md Task 3).
//
// Resolves ELLIPSOIDAL ground height per (lat, lon) via the server-side
// `/api/terrain/heights` proxy (Task 2 — Re:Earth `heights.json`, disk-cached,
+2 -2
View File
@@ -1,5 +1,5 @@
// src/data/terrainHeights.test.mjs — batched, cached client terrain-height
// resolver.
// resolver (docs/plans/2026-07-05-entity-height-datum-fix.md Task 3).
//
// Locks the module's public interface (Task 5/6 call it verbatim):
// resolveEllipsoidalGround(coords: [{lat, lon, sourceOrthometricM?}])
@@ -22,7 +22,7 @@ import {
} from './terrainHeights.js';
const AUSTIN = { lat: 30.2672, lon: -97.7431 };
const AUSTIN_GEOID_N = -26.9; // Cross-checked against Re:Earth reference data.
const AUSTIN_GEOID_N = -26.9; // docs/plans/2026-07-05-entity-height-datum-fix.md verified facts
/** Installs a fake fetch for the duration of `fn`, restoring the original after. */
async function withFakeFetch(fakeFetch, fn) {
+4 -4
View File
@@ -3,18 +3,18 @@
*
* The fleet's 3D models stay behind the DISPLAY-rail "3D" toggle (`models3d`)
* — hundreds of GLBs are a draw-call budget decision the operator owns. Since
* 2026-08-22 that toggle DEFAULTS ON in `proximity` (product invariant), so the
* 2026-08-22 that toggle DEFAULTS ON in `proximity` (owner directive), so the
* fleet is armed on a fresh boot; proximity is itself the budget, admitting only
* the nearest MODEL_MAX in view below the fleet ceiling. The contact you have
* SELECTED is still a separate case: it is exactly one model, it is what the
* camera is pointed at, and the operator's expectation is that zooming in on a
* target resolves it into an aircraft. So the tracked contact's handoff is
* DEFAULT behaviour, driven purely by camera distance and never consulting the
* toggle at all (product invariant, 2026-08-19).
* toggle at all (owner directive, 2026-08-19).
*
* Threshold history — the tracked contact used to inherit the fleet ceiling
* `MODEL_ALT_CEIL_M = 800_000` m, and a first pass raised it to 1_000_000 m to
* make the airframe arrive sooner. Field test 2026-08-20 rejected that: the
* make the airframe arrive sooner. Owner playtest 2026-08-20 rejected that: the
* model "pops to 3D far too early" — a 26 m airframe held at its minimum pixel
* size from ~1 Mm out reads as a floating toy, not an aircraft. The ruling is a
* MUCH closer swap: 2D is correct at ~600_000 m, and the handoff belongs at
@@ -49,7 +49,7 @@
export const FLEET_MODEL_ALT_CEIL_M = 800_000;
/** Camera altitude (m) at or below which the TRACKED contact takes its 3D model.
* Field test ruling 2026-08-20: 2D still reads correctly at ~600_000 m, and
* Owner playtest ruling 2026-08-20: 2D still reads correctly at ~600_000 m, and
* the swap belongs at ~150_000 m. */
export const TRACKED_MODEL_ENTER_ALT_M = 150_000;
+5 -5
View File
@@ -1,11 +1,11 @@
// src/data/trackedModelRegime.test.mjs
//
// Zoom-driven 2D↔3D for the TRACKED contact (product invariant 2026-08-19).
// Zoom-driven 2D↔3D for the TRACKED contact (owner directive 2026-08-19).
//
// Two things are pinned here, and both are behavioural rather than cosmetic:
//
// 1. The POLICY — thresholds and hysteresis — as pure math in
// trackedModelRegime.js. The enter ceiling is the playtested swap
// trackedModelRegime.js. The enter ceiling is the owner's playtested swap
// distance, deliberately much NEARER than the fleet ceiling, and the exit
// ceiling is deliberately higher than the enter ceiling. A regression that
// collapsed the two thresholds back into one would silently reintroduce
@@ -62,8 +62,8 @@ const WELL_INSIDE_M = TRACKED_MODEL_ENTER_ALT_M / 2;
// 1. Policy: thresholds + hysteresis math
// ---------------------------------------------------------------------------
test('the tracked 3D takeover sits at the playtested swap distance', () => {
// Field test 2026-08-20: an earlier 1_000_000 m ceiling "pops to 3D far
test('the tracked 3D takeover sits at the owner-playtested swap distance', () => {
// Owner playtest 2026-08-20: an earlier 1_000_000 m ceiling "pops to 3D far
// too early" — 2D still reads correctly at ~600_000 m and the swap belongs at
// ~150_000 m. These are the numbers the operator judged by eye, so they are
// pinned literally rather than derived from anything.
@@ -75,7 +75,7 @@ test('the tracked 3D takeover sits at the playtested swap distance', () => {
});
test('the tracked contact swaps NEARER than the fleet — a recorded inversion, not a bug', () => {
// Consequence of the selected threshold, spelled out so it cannot be "tidied
// Consequence of the owner's number, spelled out so it cannot be "tidied
// away": with the DISPLAY-rail 3D toggle ON, camera altitudes between the
// tracked ceiling and the fleet ceiling draw surrounding contacts as models
// while the SELECTED one is still a billboard. The fleet pass skips the
+1 -1
View File
@@ -124,7 +124,7 @@ test('civilian and military click handlers apply duration only at the deselect b
);
});
test('civilian and military tracked model caps both expose the selected 200 px feel', () => {
test('civilian and military tracked model caps both expose the owner-selected 200 px feel', () => {
assert.equal(CIVIL_TRACKED_MODEL_MAX_PX, 200);
assert.equal(MILITARY_TRACKED_MODEL_MAX_PX, 200);
});
+7 -7
View File
@@ -223,7 +223,7 @@ let _uncoveredMode = 'sim';
* + stop-and-go creep; 'heatline' = congestion corridor polylines; 'both';
* 'none' = shipped main behavior. Live mode only — the keyless simulation
* never has `road.flow`, so every jamViz path is unreachable there.
* Default 'density' — A/B verdict 2026-07-23 (heatline stays available
* Default 'density' — owner A/B verdict 2026-07-23 (heatline stays available
* via setParams).
* @type {'none'|'density'|'heatline'|'both'}
*/
@@ -270,7 +270,7 @@ let _activeBucketColors = { ...FLOW_BUCKET_COLORS };
/**
* @const {number} Minimum base pixel size for COLORED dots while a styled
* preset is active — residential-road dots spawn at 4 px and vanish into
* post-FX pixelation; presence is the dots' whole job there (follow-up round
* post-FX pixelation; presence is the dots' whole job there (owner round
* 2). Sim dots and the normal profile keep SIZE_BY_TYPE untouched.
*/
const STYLED_MIN_BASE_PX = 5;
@@ -1253,7 +1253,7 @@ export function trafficFeedPresentation({
const mode = liveMode ? 'live' : 'sim';
if (liveMode && flowError) {
// One string for both fields. The manager's meta line renders `error` and
// drops `loadingLabel` in its error branch, so the SIMULATED copy
// drops `loadingLabel` in its error branch, so the owner's SIMULATED copy
// has to BE the error text or the steady state reverts to a bare
// "TomTom daily budget reached" that never says what is on screen.
const degraded = `SIMULATED — ${flowError}`;
@@ -1269,7 +1269,7 @@ export function trafficFeedPresentation({
};
}
// Keyless simulation — one terse line that names the mode and the remedy
// (the copy shape). The chip's own progress text carries "working";
// (owner's copy shape). The chip's own progress text carries "working";
// this line must never imply a live feed.
return {
mode,
@@ -2351,13 +2351,13 @@ const trafficLayer = {
if (params.uncoveredRoads === 'sim' || params.uncoveredRoads === 'hide') {
_uncoveredMode = params.uncoveredRoads;
}
// Jam-viz prototype toggle (A/B): 'none' = shipped main behavior;
// Jam-viz prototype toggle (owner A/B): 'none' = shipped main behavior;
// live mode only, applies on the next camera-driven load like the
// uncoveredRoads param above.
if (['none', 'density', 'heatline', 'both'].includes(params.jamViz)) {
_jamViz = params.jamViz;
}
// Preset-aware dot styling kill switch (A/B): 'off' forces the
// Preset-aware dot styling kill switch (owner A/B): 'off' forces the
// shipped palette under every post-FX preset. Applies immediately via
// in-place restyle — no refetch — so A/B legs share identical dots.
if (params.presetDots === 'on' || params.presetDots === 'off') {
@@ -2415,7 +2415,7 @@ const trafficLayer = {
};
// Live mode: the detection bracket carries the congestion signal —
// its canvas sits ABOVE the post-FX chain, so tier colors survive
// every preset (follow-up round 2: "bounding boxes do the heavy
// every preset (owner round 2: "bounding boxes do the heavy
// lifting"). Keyless mode sets no tier: contacts keep the stock
// 'vehicle' bracket and the keyless experience stays untouched.
if (_liveMode) {
+1 -1
View File
@@ -10,7 +10,7 @@
* 0.72 / jam 0.49 — under NVG a jam renders DIMMER than free flow. CRT
* keeps hue but its 5-px pixelation + dithering shred 4–6 px dots.
*
* Encoding per profile (validated behavior 2026-07-23 round 2: "just bright
* Encoding per profile (owner verdict 2026-07-23 round 2: "just bright
* dots" — a luminance RAMP failed in the field because dim free-flow dots
* read as dark holes on NVG-bright roads; classification is the detection
* brackets' job via `trafficBucketTier`, presence is the dots' job):
+1 -1
View File
@@ -39,7 +39,7 @@ test('sim/uncovered dots (null bucket) are untouched under EVERY style — keyle
}
});
test('mono: EVERY colored dot is a bright white core (follow-up round 2: "just bright dots")', () => {
test('mono: EVERY colored dot is a bright white core (owner round 2: "just bright dots")', () => {
for (const s of MONO_STYLES) {
for (const b of BUCKETS) {
const rgba = presetDotRgba(s, b);
+5 -4
View File
@@ -4,7 +4,7 @@
* 2026-07-06).
*
* One trail = one ENTITY polyline. Round 6 replaced the faded per-vertex
* Primitive for two product invariants from the field:
* Primitive for two owner directives from the field:
* - "the line must ALWAYS be visible": the Primitive's depthFailAppearance
* did not reliably render segments below the photoreal mesh — entity
* polylines with `depthFailMaterial` DO (in-repo proof: CCTV's frustum
@@ -36,7 +36,7 @@ let _trailSeq = 0;
/** @constant {number} Alpha where the trail passes the depth test. */
const TRAIL_ALPHA = 0.85;
/** @constant {number} Alpha where the trail is behind/below scene geometry —
* still visible, but readable as occluded. */
* visible (owner: never vanish) but readable as occluded. */
const TRAIL_OCCLUDED_ALPHA = 0.4;
/** @constant {number} Squared distance (m^2) below which consecutive points are merged. */
const MIN_SEGMENT_DISTANCE_SQ = 0.01;
@@ -72,10 +72,11 @@ export function createTrail(viewer, { color, width = 2.5 }) {
positions: new Cesium.CallbackProperty(() => current, false),
width,
material: baseColor.withAlpha(TRAIL_ALPHA),
// The locked rule (round 6): a segment below the photoreal
// The owner-locked rule (round 6): a segment below the photoreal
// mesh renders dimmed — it must never disappear into the ground.
depthFailMaterial: baseColor.withAlpha(TRAIL_OCCLUDED_ALPHA),
// Round 8: NONE draws straight 3D chords between waypoints — over a
// Round 8 (owner: UAL1104's Pacific trail "cutting through the
// globe"): NONE draws straight 3D chords between waypoints — over a
// sparse trans-oceanic trace a single segment spans hundreds of km
// and tunnels through the planet. GEODESIC subdivides each segment
// along the curved surface (heights interpolated), so long legs hug