diff --git a/changelog.d/847-cyclone-shared-selection.md b/changelog.d/847-cyclone-shared-selection.md index 24edf2ebb..150295823 100644 --- a/changelog.d/847-cyclone-shared-selection.md +++ b/changelog.d/847-cyclone-shared-selection.md @@ -1,4 +1,3 @@ -- Explicit cyclone selections publish only the currently selected NHC/CPHC - storm into shared context. Switching storms removes the previous identity - without a false eviction event. Deliberate clears remain distinct from feed - disappearance (kvnloo, #847; reviewed by quanticsoul4772). +- Let the voice assistant read the storm you select in Cyclone advisories: + its official advisory, position, intensity and movement. Clearing the + selection and the storm leaving the feed are reported apart (kvnloo, #847). diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 6df75c27e..f3b3d3396 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -742,13 +742,12 @@ geographic seams and releases on disable; forecast animation is not implied. NHC movement speed is converted from miles per hour to rounded knots by the proxy; advisory wind intensity is already in knots and is passed through. Cyclone markers, labels, tracks and cones are hidden beyond the horizon on every map source. -Explicit cyclone selections publish one shared `weather-cyclones` record -with official advisory provenance, source position, the English classification -name and, while the proxy serves its cache, the cache fetch time. Switching -storms selects the replacement before pruning the old record; automatic row -selection never claims global context, and refreshes do not re-announce -selection. Deliberate clear/disable reports `deliberate`; losing the selected -storm from the feed reports `evicted` and assigns no fallback. +Explicitly selecting a storm publishes one shared `weather-cyclones` record: +advisory provenance, source position, the English classification name and, +while the proxy serves its cache, the cache fetch time. The automatic row +selection does not claim shared context; refreshes do not re-announce it. +Clear or disable reports `deliberate`; a storm leaving the feed reports +`evicted`, with no fallback. Consult the linked official advisory for safety decisions. Voice and HUD snapshots reuse the existing feedState classifier. Analyst follow-ups retain their original data provenance; current-view results append provenance without replacing legacy fields. HUD context and deterministic telemetry include non-nominal feed state. diff --git a/src/app/layers/cyclones.js b/src/app/layers/cyclones.js index d671a11eb..bed75c76e 100644 --- a/src/app/layers/cyclones.js +++ b/src/app/layers/cyclones.js @@ -1,7 +1,7 @@ import { createCyclonesLayer } from '../../layers/cyclones/index.js'; import * as context from '../../data/contextStore.js'; -/** Wire cyclone selection into the application shared-context owner. */ +/** Wire cyclone selection to the application shared context. */ export function createApplicationCyclones(options) { const browserContext = typeof window !== 'undefined' ? { context } : {}; return createCyclonesLayer({ diff --git a/src/layers/cyclones/index.js b/src/layers/cyclones/index.js index 8741ad8a3..95655d5d0 100644 --- a/src/layers/cyclones/index.js +++ b/src/layers/cyclones/index.js @@ -116,14 +116,12 @@ export function createCyclonesLayer({ layerId: 'weather-cyclones', layerName: 'Cyclone advisories', source: 'NOAA NHC / CPHC', - // Keep official storm names verbatim; layerName identifies the layer. label: storm.name, latitude: storm.position.latitude, longitude: storm.position.longitude, // At most 12 properties reach the voice summary; keep the list short. properties: { classification: storm.classification, - // English source meaning; unknown codes stay unexpanded. classificationName: Object.hasOwn( CLASSIFICATION_NAMES, storm.classification, @@ -140,7 +138,7 @@ export function createCyclonesLayer({ movementDirectionDegrees: storm.movement?.directionDegrees, movementSpeedKt: storm.movement?.speedKt, advisoryUrl: storm.advisoryUrl, - // Present only while the server serves its cache with NHC unreachable. + // Only while the proxy serves its cache because NHC is unreachable. staleCacheFetchedAt: snapshot.stale && Number.isFinite(snapshot.fetchedAt) ? new Date(snapshot.fetchedAt).toISOString() diff --git a/src/layers/cyclones/index.test.mjs b/src/layers/cyclones/index.test.mjs index a3e40fc95..41ecaf3d1 100644 --- a/src/layers/cyclones/index.test.mjs +++ b/src/layers/cyclones/index.test.mjs @@ -405,8 +405,7 @@ function harness( }, }; } -// Runs a test body against the real shared-context store on a stand-in window, -// with the voice runner reading the same store. +// Real shared-context store on a stand-in window; the voice runner reads it. async function withSharedContext(t, body) { const previousWindow = Object.getOwnPropertyDescriptor(globalThis, 'window'); const host = new EventTarget(); @@ -657,8 +656,7 @@ test('cyclone clears leave another layer selection and its records alone', async assert.equal(store.selectedEntityId, 'flight:abc123'); assert.equal(events.length, before); - // A foreign record removed without clearing its selection: a cyclone - // feed failure must not report that selection as evicted by cyclones. + // A cyclone feed failure does not evict a dangling foreign selection. store.entities.delete('flight:abc123'); let fail = false; const other = open({