* 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)
3.4 KiB
status, priority, issue_id, tags
| status | priority | issue_id | tags | ||||
|---|---|---|---|---|---|---|---|
| pending | p1 | 012 |
|
actorRegistry always [] in production — priorWorldState not threaded to writeSimulationPackage call site
Problem Statement
buildSimulationPackageFromDeepSnapshot accepts priorWorldState as its second argument and uses priorWorldState?.actorRegistry || [] for the highest-fidelity entity extraction path (actorRegistry entries whose forecastIds overlap the selected theaters). But the call site in the main seed path never passes priorWorldState:
writeSimulationPackage(snapshotPayload, { storageConfig: snapshotWrite.storageConfig })
priorWorldState is available earlier in scope (resolved via readPreviousForecastWorldState during snapshot building) but writeDeepForecastSnapshot does not return it, so it cannot be passed through. The result: actorRegistry is always [] in all production runs, the registry-based entity extraction branch is silently dead, and entities degrade to stateUnit actors and evidence table only.
Findings
scripts/seed-forecasts.mjs— fire-and-forget call site in seed path:const snapshotWrite = await writeDeepForecastSnapshot(snapshotPayload, { runId }); if (snapshotWrite?.storageConfig && ...) { writeSimulationPackage(snapshotPayload, { storageConfig: snapshotWrite.storageConfig })priorWorldStateis in scope earlier but not accessible at this point- Entity extraction priority (per gap doc): actorRegistry FIRST, then stateUnit actors, then evidence table, then fallback anchors
- The most specific extraction path (forecastId overlap with registry) never runs in production
Proposed Solutions
Option A: Return priorWorldState from writeDeepForecastSnapshot (Recommended)
// In writeDeepForecastSnapshot return:
return { storageConfig, snapshotKey, priorWorldState };
// At call site:
writeSimulationPackage(snapshotPayload, {
storageConfig: snapshotWrite.storageConfig,
priorWorldState: snapshotWrite.priorWorldState,
})
Effort: Small | Risk: Low
Option B: Call writeSimulationPackage before writeDeepForecastSnapshot, where priorWorldState is still in scope
Requires restructuring the call order slightly. writeSimulationPackage can also accept the already-built snapshot payload.
Effort: Small | Risk: Low
Option C: Pass priorWorldState into the snapshot payload itself
Add priorWorldState to snapshotPayload and read it in buildSimulationPackageFromDeepSnapshot.
Effort: Tiny | Risk: Medium (grows snapshot payload)
Acceptance Criteria
- In production runs,
entities[]contains entries sourced fromactorRegistrywhen the registry has relevant actors priorWorldState.actorRegistryis passed through tobuildSimulationPackageFromDeepSnapshot- Test:
buildSimulationPackageFromDeepSnapshot(snapshot, { actorRegistry: [{ id: 'actor-1', name: 'Iran', forecastIds: [...], ... }] })produces entity withentityId: 'actor-1'andrelevanceToTheater: 'actor_registry'
Technical Details
- File:
scripts/seed-forecasts.mjs—writeSimulationPackagecall site in_isDirectRunseed path - Functions:
writeSimulationPackage,writeDeepForecastSnapshot,buildSimulationPackageFromDeepSnapshot
Work Log
- 2026-03-24: Found by compound-engineering:research:learnings-researcher in PR #2204 review