feat(energy-atlas): promote Atlas map layers to FULL variant (§R #3 = B)#3366
Conversation
Per plan §R/#3 decision B: the Redis-backed evidence registries (75 gas + 75 oil pipelines, 200 storage facilities, 29 fuel shortages) are now toggleable on the main worldmonitor.app map. Previously they were hardcoded energy-variant-only, and FULL users who toggled `pipelines: true` got the ~20-entry legacy static PIPELINES list. Changes: - `src/components/DeckGLMap.ts`: drop the `SITE_VARIANT === 'energy'` gates at :1511-1541. The pipelines layer now always uses `createEnergyPipelinesLayer()` (Redis-backed evidence registry); `createPipelinesLayer` (legacy static) is left in the file as dead code pending a separate cleanup PR that also retires `src/config/pipelines.ts`. Storage and fuel-shortage layers are now gated only on the variant's `mapLayers.storageFacilities` / `mapLayers.fuelShortages` booleans. - `src/config/panels.ts`: add `storageFacilities: false` + `fuelShortages: false` to FULL_MAP_LAYERS (desktop + mobile) so the keys exist for toggle dispatch; default off so users opt in. - `src/config/map-layer-definitions.ts`: extend the `full` variant's VARIANT_LAYER_ORDER to include `storageFacilities` and `fuelShortages`, so `getAllowedLayerKeys('full')` admits them and the layer picker surfaces them. - `src/config/commands.ts`: add CMD+K toggles `layer:storageFacilities` and `layer:fuelShortages` next to the existing `layer:pipelines`. Finance + commodity variants already had `pipelines: true`; they now render the more comprehensive Redis-backed 150-entry dataset instead of the ~20-entry legacy list. If a variant doesn't want this, they set `pipelines: false` in their MAP_LAYERS config. Part of docs/internal/energy-atlas-registry-expansion.md §R.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR promotes the Redis-backed Atlas map layers (pipelines, storage facilities, fuel shortage pins) from energy-variant-only to the FULL variant by removing Confidence Score: 5/5Safe to merge — no P0/P1 issues; the single P2 finding is a pre-existing UX pattern, not a regression. All four changed files are logically consistent. Guard removal in DeckGLMap.ts is correct, graceful fallback to static data is preserved, variant layer ordering is updated correctly, and CMD+K execution is already gated by getAllowedLayerKeys. The only finding is a P2 UX note about commands appearing in non-applicable variant CMD+K menus, which mirrors the existing behaviour of every other variant-specific layer command. No files require special attention beyond the minor P2 in commands.ts. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[mapLayers.pipelines === true] -->|always| B[createEnergyPipelinesLayer]
B --> C{getCachedPipelineRegistries\nreturns data?}
C -->|yes – Redis hydrated| D[Render 150 Redis-backed pipelines]
C -->|no – bootstrap pending| E[createPipelinesLayer static fallback ~20 entries]
F[mapLayers.storageFacilities === true] --> G[createEnergyStorageLayer]
H[mapLayers.fuelShortages === true] --> I[createEnergyShortagePinsLayer]
J[CMD+K layer:storageFacilities / layer:fuelShortages] --> K{getAllowedLayerKeys\ncontains key?}
K -->|full / energy variant| L[Toggle layer, persist to storage]
K -->|other variants| M[return – silent no-op]
Reviews (1): Last reviewed commit: "feat(energy-atlas): promote Atlas map la..." | Re-trigger Greptile |
| { id: 'layer:storageFacilities', keywords: ['storage layer', 'storage map', 'spr map', 'lng map', 'ugs map', 'tank farm map', 'storage markers'], label: 'Toggle storage facility markers', icon: '\u{1F3ED}', category: 'layers' }, | ||
| { id: 'layer:fuelShortages', keywords: ['shortage pins', 'fuel shortage map', 'shortage markers', 'shortage layer'], label: 'Toggle fuel shortage pins', icon: '\u26FD', category: 'layers' }, |
There was a problem hiding this comment.
CMD+K commands visible but silently no-op on non-full/non-energy variants
layer:storageFacilities and layer:fuelShortages are added to the global COMMANDS array unconditionally, so they surface in CMD+K on every variant (tech, finance, commodity, happy). When invoked, handleCommand in search-manager.ts will hit the !variantAllowed.has(layerKey) guard and return silently — no feedback to the user. This is consistent with how other variant-specific layer commands (e.g. layer:nuclear on tech variant) already behave today, but the pattern could mislead users on finance/commodity who search for "storage layer" in CMD+K and see a command that does nothing.
… renderer Reviewer (Codex) found two gaps in PR #3366: 1. GlobeMap 3D toggles did nothing. LAYER_REGISTRY declared both new layers with the default ['flat', 'globe'] renderers, so the toggle showed up in globe mode. But GlobeMap.ts has no rendering support: ensureStaticDataForLayer (:2160) only handles cables/pipelines/etc., and the layer-channel map (:2484) has no entries for either. Users in globe mode saw the toggle and got silent no-ops. 2. SVG/mobile fallback (Map.ts fullLayers at :381) also has no render path for these data types. The existing cyberThreats precedent at :387 documents this as an intentional DeckGL-only pattern. Fix: - Restrict both LAYER_REGISTRY entries to ['flat'] explicitly. The layer picker hides the toggle in globe mode instead of exposing a no-op. Comment points to the GlobeMap gap so a future globe-rendering PR knows what to undo. - Extend the existing cyberThreats note in Map.ts:387 to cover storageFacilities + fuelShortages too, noting they're already hidden from globe mode via the LAYER_REGISTRY restriction. This is the smallest possible fix consistent with the pre-existing pattern. Full globe-mode rendering for these layers is out of scope — tracked separately as a follow-up.
Reviewer follow-up on PR #3366: the previous fix restricted LAYER_REGISTRY renderers to ['flat'] so the globe-mode layer picker hides storageFacilities / fuelShortages toggles. But CMD+K was still callable — SearchModal.matchCommands didn't filter `layer:*` commands by renderer, so a user could CMD+K "storage layer" in globe or SVG mode and trigger a silent no-op. Fix — centralize "can this layer render right now?" in one helper: - Add `deckGLOnly?: boolean` to LayerDefinition. `renderers: ['flat']` is not enough because `'flat'` covers both DeckGL-flat and SVG-flat, and the SVG/mobile fallback has no render path for either layer. Mark both as `deckGLOnly: true`. - New `isLayerExecutable(key, renderer, isDeckGLActive)` helper in map-layer-definitions.ts. Returns true iff renderers include the current renderer AND (if deckGLOnly) DeckGL is active. - `SearchModal.setLayerExecutableFn(fn)`: caller-supplied predicate used in both `matchCommands` (search results) and `renderAllCommandsList` (full picker). - `search-manager` wires the predicate using `ctx.map.isGlobeMode()` + `ctx.map.isDeckGLActive()`, and also adds a symmetric guard in the `layer:` dispatch case so direct activations (keyboard accelerator, programmatic invocation) bail the same way. Pre-existing resilienceScore DeckGL gate at search-manager:494 kept as a belt-and-suspenders — the new isLayerExecutable check already covers it since resilienceScore has `renderers: ['flat']` (though it lacks deckGLOnly). Left the specific check in place to avoid scope creep on a working guard. Typecheck clean, 6694/6694 tests pass.
Greptile P2 on commit 3f7a400: `layer:storageFacilities` and `layer:fuelShortages` still surface in CMD+K on tech / finance / commodity / happy variants (where they're not in VARIANT_LAYER_ORDER). Renderer + DeckGL filter was passing because those variants run flat DeckGL. Dispatch silently failed at the `variantAllowed` guard in handleCommand (:491), producing an invisible no-op from the user's POV. Fix: extend `setLayerExecutableFn` predicate to also check `getAllowedLayerKeys(SITE_VARIANT).has(key)` before the renderer checks. SearchModal now hides these commands on non-full/non-energy variants where they can't execute. This also cleans up the pre-existing pattern for other variant-specific layer commands flagged by Greptile as "consistent with how other variant-specific layer commands (e.g. layer:nuclear on tech variant) already behave today" — they now all route through the same predicate.
…s (review P2)
Two Codex P2 findings on this PR:
1. `layers:*` presets bypassed the renderer/DeckGL gate.
`search-manager.ts:481` checked only `allowed.has(layer)` before
flipping a preset layer on. A user in globe mode or on SVG
fallback who ran `layers:all` or `layers:infra` would silently
set `deckGLOnly` layers (storageFacilities, fuelShortages) to
true — toggles with no rendered output, and since the picker
hides those layers under the current renderer the user had no
way to toggle them back off without switching modes.
Fix: funnel presets through the same `isLayerExecutable`
predicate per-layer CMD+K already uses. `executable(k)` combines
the existing `allowed.has` variant check with the renderer + DeckGL
gate, so presets now match the per-layer dispatch behavior exactly.
2. No regression tests for the `deckGLOnly` / `isLayerExecutable`
contract, despite it being behavior-critical renderer gating.
Fix: added `tests/map-layer-executable.test.mts` — 16 cases:
- Flag assertions: storageFacilities + fuelShortages carry
`deckGLOnly: true` and renderers: ['flat']. Layers without the
flag (pipelines, conflicts, cables) have it `undefined`, not
accidentally `false`.
- Renderer-gate cases: deckGLOnly layers pass only on flat + DeckGL
active, not on SVG fallback, not on globe. Flat-only non-deckGLOnly
layers (ciiChoropleth) pass on flat regardless of DeckGL status.
Dual-renderer layers (pipelines) pass on both flat and globe.
Unknown layer keys return false.
- Exhaustive 2×2×2 matrix across (renderer, isDeckGL, deckGLOnly)
using representative layer keys for each shape.
All 16 new tests pass. Full test:data suite still green. Typecheck clean.
…ets (review P1) Codex P1: FINANCE_MAP_LAYERS and COMMODITY_MAP_LAYERS both carry `pipelines: true`, and PR #3366 unified all variants on `createEnergyPipelinesLayer` which dispatches `energy:open-pipeline-detail` on row click. The listener for that event lives in PipelineStatusPanel. `PanelLayoutManager.createPanel()` only instantiates panels whose keys are present in `panelSettings`, which derives from FULL_PANELS / FINANCE_PANELS / etc. — so on finance and commodity variants the listener never existed, and pipeline clicks were a silent no-op. Fix: add `pipeline-status` to both FINANCE_PANELS and COMMODITY_PANELS with `enabled: false` (panel slot not auto-opened; users invoke it by clicking a pipeline on the map or via CMD+K). The panel now instantiates on both variants and the click-through works end to end. FULL_PANELS + ENERGY_PANELS already had the key from earlier PRs; no change there. Typecheck clean, test:data 6696/6696 pass.
…ly fetch (#3386) Root cause: `App.ts::primeDataForVisiblePanels` is the sole near-viewport kickoff path for panel `fetchData()` calls. Panel's constructor only calls `showLoading()`; nothing else in the app triggers fetchData on attach. Every panel that does real work has a corresponding `if (shouldPrime('<key>')) primeTask(...)` entry in that table. All 4 Energy Atlas panels were missing their entries: - pipeline-status → PipelineStatusPanel - storage-facility-map → StorageFacilityMapPanel - fuel-shortages → FuelShortagePanel - energy-disruptions → EnergyDisruptionsPanel User-visible symptom: the 4 panels shipped as part of #3366 / #3378 / the Energy Atlas PR chain rendered their headers + spinner but never left "Loading…". Verified all four upstream RPCs return data: - /api/supply-chain/v1/list-pipelines?commodityType= → 100 KB - /api/supply-chain/v1/list-storage-facilities → 115 KB - /api/supply-chain/v1/list-fuel-shortages → 21 KB - /api/supply-chain/v1/list-energy-disruptions → 36 KB and /api/health reports all 5 backing Redis keys as OK with the expected record counts (75 gas + 75 oil + 200 storage + 29 shortages + 52 disruptions). Fix: 4 primeTask entries mirroring the pattern already used for energy-crisis, hormuz-tracker, oil-inventories, etc. Each panel already implements the full fetch path (bootstrap-cache-first, RPC fallback, setCached... back-propagation, error states); the entries just give App.ts a reason to call it. Placement: immediately after oil-inventories, grouping the energy surface together.
Summary
Per the Codex-approved plan at `docs/internal/energy-atlas-registry-expansion.md` §R/#3, product picked option B: the Redis-backed Atlas map layers (75 gas + 75 oil pipelines, 200 storage facilities, 29 fuel shortage pins) are now toggleable on `worldmonitor.app`. Previously they were hardcoded energy-variant-only, and FULL users who enabled `pipelines: true` got the legacy ~20-entry static `src/config/pipelines.ts` list.
Changes
Behavioural impact on other variants
Test plan
Context
Part of docs/internal/energy-atlas-registry-expansion.md §L (gaps #3-12) execution. Stack: this is PR A of 4; PR B (gap #5B seed-side `countries[]` denorm), PR C (gap #4 EnergyDisruptionsPanel), PR D (gap data-sensitivity redaction) follow.
Related: #3364 merged (FULL_PANELS + CMD+K exposure), #3365 open (Greptile P2 followups).