Files
worldmonitor/tests
Elie Habib 3d2dce3be1 feat(energy-atlas): promote Atlas map layers to FULL variant (§R #3 = B) (#3366)
* feat(energy-atlas): promote Atlas map layers to FULL variant (§R #3 = B)

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.

* fix(energy-atlas): restrict storageFacilities + fuelShortages to flat 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.

* fix(energy-atlas): gate layer:* CMD+K by current renderer + DeckGL state

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.

* fix(energy-atlas): filter CMD+K layer commands by variant too

Greptile P2 on commit 3f7a40036: `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.

* fix(energy-atlas): gate layers:* presets + add isLayerExecutable tests (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.

* fix(energy-atlas): add pipeline-status to finance + commodity panel sets (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.
2026-04-24 19:09:21 +04:00
..