Files
worldmonitor/todos/012-pending-p1-actorregistry-dead-at-callsite.md
Elie Habib 092efd4fe9 fix(panels): always fire background RPC refresh after bootstrap render (#2208)
* 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)
2026-03-24 20:23:40 +04:00

3.4 KiB

status, priority, issue_id, tags
status priority issue_id tags
pending p1 012
code-review
deep-forecast
simulation-package
correctness

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 })
    
  • priorWorldState is 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

// 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 from actorRegistry when the registry has relevant actors
  • priorWorldState.actorRegistry is passed through to buildSimulationPackageFromDeepSnapshot
  • Test: buildSimulationPackageFromDeepSnapshot(snapshot, { actorRegistry: [{ id: 'actor-1', name: 'Iran', forecastIds: [...], ... }] }) produces entity with entityId: 'actor-1' and relevanceToTheater: 'actor_registry'

Technical Details

  • File: scripts/seed-forecasts.mjswriteSimulationPackage call site in _isDirectRun seed path
  • Functions: writeSimulationPackage, writeDeepForecastSnapshot, buildSimulationPackageFromDeepSnapshot

Work Log

  • 2026-03-24: Found by compound-engineering:research:learnings-researcher in PR #2204 review