* fix(panels): always fire background RPC refresh after bootstrap render Bootstrap hydration (getHydratedData) is one-shot — once rendered from it, panels never refresh and can show stale or partial data indefinitely. Affected panels: MacroSignals, ETFFlows, Stablecoins, FuelPrices, GulfEconomies, GroceryBasket, BigMac. Pattern: render from bootstrap immediately for fast first paint, then fire a background RPC call that silently updates the panel with live data. Errors during background refresh are suppressed when bootstrap data is already visible (no error flash over valid data). * fix(panels): guard background RPC refresh against empty response overwriting bootstrap Empty RPC responses (200 + empty array) no longer overwrite valid bootstrap data with error/unavailable state across all 7 affected panels: - ETFFlowsPanel, StablecoinPanel: wrap this.data assignment in `if (fresh.xxx?.length || !this.data)` guard - FuelPricesPanel, GulfEconomiesPanel, GroceryBasketPanel, BigMacPanel: add `!data.xxx?.length` check in background .then() before calling render - MacroSignalsPanel: return false early when error suppressed to skip redundant renderPanel() call * fix(hormuz): fix noUncheckedIndexedAccess TypeScript errors * fix(todos): add blank lines around headings (markdownlint MD022) * fix(hormuz): add missing hormuz-tracker service + fix implicit any in HormuzPanel * revert: remove HormuzPanel.ts from this branch (belongs in PR #2210)
2.6 KiB
status, priority, issue_id, tags
| status | priority | issue_id | tags | ||||
|---|---|---|---|---|---|---|---|
| pending | p1 | 011 |
|
Entity key collision: same dominantRegion across multiple candidates silently drops entities + wrong relevanceToTheater
Problem Statement
buildSimulationPackageEntities uses su:${actorName}:${candidate.dominantRegion} and ev:${name}:${candidate.dominantRegion} as Map dedup keys. When two selected theater candidates share the same dominantRegion (e.g., Bab el-Mandeb and Suez Canal both map to "Red Sea"), an actor appearing in both candidates (e.g., "US Navy Fifth Fleet") produces identical keys. The addEntity guard silently drops the second entry, and the kept entity has relevanceToTheater pointing to whichever candidate was processed first — not the one it is most relevant to.
Findings
scripts/seed-forecasts.mjs—buildSimulationPackageEntities,su:andev:key construction:const key = `su:${actorName}:${candidate.dominantRegion}`; // and const key = `ev:${name}:${candidate.dominantRegion}`;- Red Sea / Bab el-Mandeb / Suez Canal all share
dominantRegion: 'Red Sea'inCHOKEPOINT_MARKET_REGIONS - The dropped entity causes incorrect
relevanceToTheaterassignment with no log - This is a data correctness issue in the exported schema — downstream simulators/LLMs get wrong theater attribution
Proposed Solutions
Option A: Include candidateStateId in the dedup key (Recommended)
const key = `su:${actorName}:${candidate.candidateStateId}`;
// and
const key = `ev:${name}:${candidate.candidateStateId}`;
Effort: Tiny | Risk: Low — candidateStateId is always unique per candidate
Option B: Use actor name only as dedup key, merge relevanceToTheater as array
Allow the same actor to appear once with relevanceToTheater: ['theater-1', 'theater-2']. This is semantically richer for Phase 2 but changes the schema contract.
Effort: Small | Risk: Medium (schema change)
Acceptance Criteria
- Two candidates with same
dominantRegionand overlapping actors produce separate entities per candidate in the output - Each entity's
relevanceToTheatercorrectly references its source candidate [...seen.values()].slice(0, 20)cap still applies- Test: two theater candidates sharing
dominantRegion: 'Red Sea', same actor instateSummary.actors— assert both entities present with correctrelevanceToTheater
Technical Details
- File:
scripts/seed-forecasts.mjs—buildSimulationPackageEntities
Work Log
- 2026-03-24: Found by compound-engineering:review:kieran-typescript-reviewer in PR #2204 review