Skip to content

fix(energy-atlas): wire 4 panels into App.ts primeTask (panels stuck on 'Loading…')#3386

Merged
koala73 merged 1 commit into
mainfrom
fix/energy-atlas-panels-prime
Apr 24, 2026
Merged

fix(energy-atlas): wire 4 panels into App.ts primeTask (panels stuck on 'Loading…')#3386
koala73 merged 1 commit into
mainfrom
fix/energy-atlas-panels-prime

Conversation

@koala73

@koala73 koala73 commented Apr 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • All 4 Energy Atlas panels (`pipeline-status`, `storage-facility-map`, `fuel-shortages`, `energy-disruptions`) ship rendering their headers and the spinner but never leave `showLoading()` — their `fetchData()` is never called.
  • Root cause: `App.ts::primeDataForVisiblePanels` is the sole near-viewport kickoff for panel fetches; all 4 entries were missing from it.
  • Fix adds the 4 missing `if (shouldPrime(...)) primeTask(...)` entries, mirroring the pattern already used for `energy-crisis`, `hormuz-tracker`, `oil-inventories`, etc.

Evidence the data is fine; it's pure wiring

All 5 backing Redis keys report OK via `/api/health` with expected record counts:
```
pipelinesGas OK 75
pipelinesOil OK 75
storageFacilities OK 200
fuelShortages OK 29
energyDisruptions OK 52
```

All 4 RPCs return real payloads (probed directly):
```
GET /api/supply-chain/v1/list-pipelines?commodityType= → 200 (100 KB)
GET /api/supply-chain/v1/list-storage-facilities → 200 (115 KB)
GET /api/supply-chain/v1/list-fuel-shortages → 200 (21 KB)
GET /api/supply-chain/v1/list-energy-disruptions → 200 (36 KB)
```

Why the panels self-fetch already

Each panel already implements the full data path: bootstrap-cache-first lookup, RPC fallback, `setCached…` back-propagation into the shared store so map layers pick up the same data, and error states. The panels are not missing any logic; they were simply never triggered because App.ts doesn't know about them.

Test plan

  • `npm run typecheck` passes.
  • Visual check: pull the branch, load a variant that includes the panels (FULL / ENERGY / FINANCE / COMMODITY), confirm all 4 leave "Loading…" and render data.

…ly fetch

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.
@vercel

vercel Bot commented Apr 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
worldmonitor Ignored Ignored Apr 24, 2026 7:52pm

Request Review

@greptile-apps

greptile-apps Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires four existing Energy Atlas panels (pipeline-status, storage-facility-map, fuel-shortages, energy-disruptions) into App.ts::primeDataForVisiblePanels so their fetchData() is triggered on near-viewport entry. The fix follows the identical pattern used for every other self-fetching panel in the file, and the backing bootstrap hydration, Redis seed keys, and panel instantiation were all already in place.

Confidence Score: 5/5

Safe to merge — purely additive wiring that follows the established pattern with no logic or security concerns.

The change adds four well-established primeTask blocks with no novel logic. All prerequisites (bootstrap hydration, panel instantiation in panel-layout.ts, config registration) were already in place. No existing behavior is modified, typecheck passes, and the pattern is identical to dozens of existing entries.

No files require special attention.

Important Files Changed

Filename Overview
src/App.ts Adds 4 type imports and 4 shouldPrime/primeTask blocks for Energy Atlas panels, exactly mirroring every other panel entry; bootstrap wiring, panel instantiation, and config registration were already present.

Sequence Diagram

sequenceDiagram
    participant VP as Viewport Observer
    participant App as App.ts
    participant PT as primeTask
    participant P as EnergyAtlasPanel
    participant BS as Bootstrap Cache
    participant RPC as Supply-Chain RPC

    VP->>App: panel enters near-viewport
    App->>App: primeDataForVisiblePanels()
    Note over App: shouldPrime('pipeline-status') etc.<br/>(added by this PR)
    App->>PT: primeTask('pipeline-status', () => panel.fetchData())
    PT->>P: fetchData()
    P->>BS: getHydratedData() — bootstrap cache lookup
    alt cache hit
        BS-->>P: raw registry data
        P->>P: render()
    else cache miss
        P->>RPC: list-pipelines / list-storage-facilities / etc.
        RPC-->>P: 200 payload
        P->>BS: setCached…() — back-propagate to shared store
        P->>P: render()
    end
Loading

Reviews (1): Last reviewed commit: "fix(energy-atlas): wire 4 panels into Ap..." | Re-trigger Greptile

@koala73
koala73 merged commit 4efd286 into main Apr 24, 2026
11 checks passed
@koala73
koala73 deleted the fix/energy-atlas-panels-prime branch April 24, 2026 19:58
koala73 added a commit that referenced this pull request Apr 25, 2026
… (parity PR 2, plan U5-U6) (#3398)

* feat(energy-atlas): EnergyRiskOverviewPanel — executive overview tile (PR 2, plan U5-U6)

Lands the consolidated "first fold" surface that peer reference energy-intel
sites use as their executive overview. Six tiles in a single panel:
1. Strait of Hormuz status (closed/disrupted/restricted/open)
2. EU gas storage fill % (red <30, amber 30-49, green ≥50)
3. Brent crude price + 1-day change (importer-leaning: up=red, down=green)
4. Active disruption count (filtered to endAt === null)
5. Data freshness ("X min ago" from youngest fetchedAt)
6. Hormuz crisis day counter (default 2026-02-23 start, env-overridable)

Per docs/plans/2026-04-25-003-feat-energy-parity-pushup-plan.md PR 2.

U5 — Component (src/components/EnergyRiskOverviewPanel.ts):
- Composes 5 existing services via Promise.allSettled — never .all. One slow
  or failing source CANNOT freeze the panel; failed tiles render '—' and
  carry data-degraded="true" for QA inspection. Single most important
  behavior — guards against the recurrence of the #3386 panel-stuck bug.
- Uses the actual Hormuz status enum 'closed'|'disrupted'|'restricted'|'open'
  (NOT 'normal'/'reduced'/'critical' — that triplet was a misread in earlier
  drafts). Test suite explicitly rejects the wrong triplet via the gray
  sentinel fallback.
- Brent color inverted from a default market panel: oil price UP = red
  (bad for energy importers, the dominant Atlas reader); DOWN = green.
- Crisis-day counter sourced from VITE_HORMUZ_CRISIS_START_DATE env
  (default 2026-02-23). NaN/future-dated values handled with explicit
  '—' / 'pending' sentinels so the tile never renders 'Day NaN'.
- 60s setInterval re-renders the freshness tile only — no new RPCs fire
  on tick. setInterval cleared in destroy() so panel teardown is clean.
- Tests: 24 in tests/energy-risk-overview-panel.test.mts cover Hormuz
  color enum (including the wrong-triplet rejection), EU gas thresholds,
  Brent inversion, active-disruption color bands, freshness label
  formatting, crisis-day counter (today/5-days/NaN/future), and the
  degraded-mode contract (all-fail still renders 6 tiles with 4 marked
  data-degraded).

U6 — Wiring (5 sites per skill panel-stuck-loading-means-missing-primetask):
- src/components/index.ts: barrel export
- src/app/panel-layout.ts: import + createPanel('energy-risk-overview', ...)
- src/config/panels.ts: priority-1 entry in ENERGY_PANELS (top-of-grid),
  priority-2 entry in FULL_PANELS (CMD+K-discoverable, default disabled),
  panelKey added to PANEL_CATEGORY_MAP marketsFinance category
- src/App.ts: import type + primeTask kickoff (between energy-disruptions
  and climate-news in the existing ordering convention)
- src/config/commands.ts: panel:energy-risk-overview command with keywords
  for 'risk overview', 'executive overview', 'hormuz status', 'crisis day'

No new RPCs (preserves agent-native parity — every metric the panel shows
is already exposed via existing Connect-RPC handlers and bootstrap-cache
keys; agents can answer the same questions through the same surface).

Tests: typecheck clean; 24 unit tests pass on the panel's pure helpers.
Manual visual QA pending PR merge + deploy.

Plan section §M effort estimate: ~1.5d. Codex-approved through 8 review
rounds against origin/main @ 0500733.

* fix(energy-atlas): extract Risk Overview state-builder + real component test (PR2 review)

P2 — tests duplicated helper logic instead of testing the real panel
(energy-risk-overview-panel.test.mts:10):
- The original tests pinned color/threshold helpers but didn't import
  the panel's actual state-building logic, so the panel could ship
  with a broken Promise.allSettled wiring while the tests stayed green.

Refactor:
- Extract the state-building logic into a NEW Vite-free module:
  src/components/_energy-risk-overview-state.ts. Exports
  buildOverviewState(hormuz, euGas, brent, disruptions, now) and a
  countDegradedTiles() helper for tests.
- The panel now imports and calls buildOverviewState() directly inside
  fetchData(); no logic duplication. The Hormuz tile renderer narrows
  status with an explicit cast at use site.
- Why a new module: the panel transitively imports `import.meta.glob`
  via the i18n service, which doesn't resolve under node:test even
  with tsx loader. Extracting the testable logic into a
  Vite-dependency-free module is the cleanest way to exercise the
  production code from tests, per skill panel-stuck-loading-means-
  missing-primetask's emphasis on "test the actual production logic,
  not a copy-paste of it".

Tests added (11 real-component cases via the new module):
- All four sources fulfilled → 0 degraded.
- All four sources rejected → 4 degraded, no throw, no cascade.
- Mixed (1 fulfilled, 3 rejected) → only one tile populated.
- euGas with `unavailable: true` sentinel → degraded.
- euGas with fillPct=0 → degraded (treats as no-data, not "0% red").
- brent empty data array → degraded.
- brent first-quote price=null → degraded.
- disruptions upstreamUnavailable=true → degraded.
- disruptions ongoing filter: counts only endAt-falsy events.
- Malformed hormuz response (missing status field) → degraded sentinel.
- One rejected source MUST NOT cascade to fulfilled siblings (the
  core degraded-mode contract — pinned explicitly).

Total: 35 tests in this file (was 24; +11 real-component cases).
typecheck clean.

* fix(energy-atlas): server-side disruptions filter + once-only style + panel name parity (PR2 review)

Three Greptile P2 findings on PR #3398:

- listEnergyDisruptions called with ongoingOnly:true so the server filters
  the historical 52-event payload server-side. The state builder still
  re-filters as defense-in-depth.
- RISK_OVERVIEW_CSS injected once into <head> via injectRiskOverviewStylesOnce
  instead of being emitted into setContent on every render. The 60s freshness
  setInterval was tearing out and re-inserting the style tag every minute.
- FULL_PANELS entry renamed from "Energy Risk Overview" to
  "Global Energy Risk Overview" to match ENERGY_PANELS and the CMD+K command.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant