Context
Today's EDH 3dfc7fb ("strategic envelope wrap v2.2") wrapped energy_price_forecast.json and wind_forecast.json under a {metadata, data: {...}} envelope. Two dashboard consumers had not been migrated, so the Prices-tab wholesale lines and the Forecast-tab wind chart silently rendered zero traces — fix shipped in 4a557c8 (defensive obj.data ?? obj shim).
User-interaction reviewer flagged that the fix is correct for today's regression but the underlying silent-failure pattern remains. Five findings, ranked by RPN, none individually blocker (no FAIL), but together they form a hardening backlog so the next schema bump doesn't repeat today's "stale dashboard, no signal" experience.
Findings
1. (RPN 288) No zero-trace guard in Prices chart → next schema bump silently shows partial data
updateChart in static/js/dashboard.js:251-274 calls renderChart with however many traces survive processEnergyDataForChart. If a future schema rename breaks the per-source loop (entsoe/epex/elspot), Plotly draws an EZ live line + Augur forecast band only — which looks partially inhabited and is more confusing than empty. Fix: after processEnergyDataForChart, count traces matching forecast sources; if zero and energyData is non-null, call showErrorNotification (already imported pattern in api-client.js:217).
2. (RPN 252) Solar/weather/grid processors lack the same defensive ?? shim
tab-charts.js getSolarLocations, processSolar, getWeatherLocations, processWeatherTemp, processWeatherCloud, processImbalance, processFlows, processLoad, processNedProduction, processNuclear, buildMarketCards, processGasChart all read .data directly. EDH says the other 14 feeds were already wrapped pre-v2.2 — so this is hypothetical-future hardening, not a current bug. May or may not be worth adding given the project's "don't design for hypothetical requirements" stance.
3. (RPN 210) DataLoader.loadFile silently returns null on fetch failure
static/js/modules/data-loader.js:27-36 does console.warn and returns null on non-200 or network error. No showErrorNotification call (unlike ApiClient.loadEnergyData at line 217). Tab-charts receive null silently → blank charts, no message. Fix: import showErrorNotification from error-handler.js, call it on the warn path. Alternatively consolidate at the onTabChange level.
4. (RPN 168) Empty <select> after a load failure has no placeholder
populateSelect in dashboard.js:381-405 does select.innerHTML = '' and iterates an empty array → completely empty dropdown widget. Combined with the blank chart div (since renderPlotlyChart is never called when locations are empty), it looks like a layout bug or perpetual loading state. Fix: append a disabled placeholder option when locations is empty; inject the "No data available." paragraph into the chart div mirroring renderPlotlyChart:455.
5. (RPN 120) lastUpdate shows wall-clock time when entsoe absent
dashboard.js:264 falls back to new Date().toISOString() if the optional chain to entsoe.metadata.start_time resolves undefined. For an energy price tool where freshness informs decisions (EV charging, heat pump timing), claiming "Last updated: now" when the data is actually stale is a trust issue. Pre-existing — the misleading fallback predates today's edit; today's commit only extended the optional-chain path to handle v2.2 envelope. Fix: replace || new Date().toISOString() with || null; render "Last updated: unknown" in chart-renderer when null.
Summary
| # |
Title |
RPN |
Type |
| 1 |
Zero-trace guard on Prices chart |
288 |
New |
| 2 |
Defensive shim on remaining processors |
252 |
Hypothetical |
| 3 |
DataLoader silent null → notification |
210 |
Pre-existing |
| 4 |
Empty select placeholder + chart fallback |
168 |
Pre-existing |
| 5 |
lastUpdate new Date() fallback lies |
120 |
Pre-existing |
Recommend tackling #1, #3, #4, #5 (real existing silent-failure modes). #2 is the only one that's purely defensive against a hypothetical future schema bump and the project's CLAUDE.md guidance pushes against that kind of work.
🤖 Generated with Claude Code
Context
Today's EDH
3dfc7fb("strategic envelope wrap v2.2") wrappedenergy_price_forecast.jsonandwind_forecast.jsonunder a{metadata, data: {...}}envelope. Two dashboard consumers had not been migrated, so the Prices-tab wholesale lines and the Forecast-tab wind chart silently rendered zero traces — fix shipped in 4a557c8 (defensiveobj.data ?? objshim).User-interaction reviewer flagged that the fix is correct for today's regression but the underlying silent-failure pattern remains. Five findings, ranked by RPN, none individually blocker (no FAIL), but together they form a hardening backlog so the next schema bump doesn't repeat today's "stale dashboard, no signal" experience.
Findings
1. (RPN 288) No zero-trace guard in Prices chart → next schema bump silently shows partial data
updateChartinstatic/js/dashboard.js:251-274callsrenderChartwith however many traces surviveprocessEnergyDataForChart. If a future schema rename breaks the per-source loop (entsoe/epex/elspot), Plotly draws an EZ live line + Augur forecast band only — which looks partially inhabited and is more confusing than empty. Fix: afterprocessEnergyDataForChart, count traces matching forecast sources; if zero andenergyDatais non-null, callshowErrorNotification(already imported pattern inapi-client.js:217).2. (RPN 252) Solar/weather/grid processors lack the same defensive
??shimtab-charts.jsgetSolarLocations,processSolar,getWeatherLocations,processWeatherTemp,processWeatherCloud,processImbalance,processFlows,processLoad,processNedProduction,processNuclear,buildMarketCards,processGasChartall read.datadirectly. EDH says the other 14 feeds were already wrapped pre-v2.2 — so this is hypothetical-future hardening, not a current bug. May or may not be worth adding given the project's "don't design for hypothetical requirements" stance.3. (RPN 210)
DataLoader.loadFilesilently returns null on fetch failurestatic/js/modules/data-loader.js:27-36doesconsole.warnand returnsnullon non-200 or network error. NoshowErrorNotificationcall (unlikeApiClient.loadEnergyDataat line 217). Tab-charts receive null silently → blank charts, no message. Fix: importshowErrorNotificationfromerror-handler.js, call it on the warn path. Alternatively consolidate at theonTabChangelevel.4. (RPN 168) Empty
<select>after a load failure has no placeholderpopulateSelectindashboard.js:381-405doesselect.innerHTML = ''and iterates an empty array → completely empty dropdown widget. Combined with the blank chart div (sincerenderPlotlyChartis never called when locations are empty), it looks like a layout bug or perpetual loading state. Fix: append a disabled placeholder option when locations is empty; inject the"No data available."paragraph into the chart div mirroringrenderPlotlyChart:455.5. (RPN 120)
lastUpdateshows wall-clock time when entsoe absentdashboard.js:264falls back tonew Date().toISOString()if the optional chain toentsoe.metadata.start_timeresolves undefined. For an energy price tool where freshness informs decisions (EV charging, heat pump timing), claiming "Last updated: now" when the data is actually stale is a trust issue. Pre-existing — the misleading fallback predates today's edit; today's commit only extended the optional-chain path to handle v2.2 envelope. Fix: replace|| new Date().toISOString()with|| null; render "Last updated: unknown" in chart-renderer when null.Summary
new Date()fallback liesRecommend tackling #1, #3, #4, #5 (real existing silent-failure modes). #2 is the only one that's purely defensive against a hypothetical future schema bump and the project's CLAUDE.md guidance pushes against that kind of work.
🤖 Generated with Claude Code