mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* feat(simulation): MiroFish Phase 2 — theater-limited simulation runner Adds the simulation execution layer that consumes simulation-package.json and produces simulation-outcome.json for maritime chokepoint + energy/logistics theaters, closing the WorldMonitor → MiroFish handoff loop. Changes: - scripts/seed-forecasts.mjs: 2-round LLM simulation runner (prompt builders, JSON extractor, runTheaterSimulation, writeSimulationOutcome, task queue with NX dedup lock, runSimulationWorker poll loop) - scripts/process-simulation-tasks.mjs: standalone worker entry point - proto: GetSimulationOutcome RPC + make generate - server/worldmonitor/forecast/v1/get-simulation-outcome.ts: RPC handler - server/gateway.ts: slow tier for get-simulation-outcome - api/health.js: simulationOutcomeLatest in STANDALONE + ON_DEMAND keys - tests: 14 new tests for simulation runner functions * fix(simulation): address P1/P2 code review findings from PR #2220 Security (P1 #018): - sanitizeForPrompt() applied to all entity/seed fields interpolated into Round 1 prompt (entityId, class, stance, seedId, type, timing) - sanitizeForPrompt() applied to actorId and entityIds in Round 2 prompt - sanitizeForPrompt() + length caps applied to all LLM array fields written to R2 (dominantReactions, stabilizers, invalidators, keyActors, timingMarkers) Validation (P1 #019): - Added validateRunId() regex guard - Applied in enqueueSimulationTask() and processNextSimulationTask() loop Type safety (P1 #020): - Added isOutcomePointer() and isPackagePointer() type guards in TS handlers - Replaced unsafe as-casts with runtime-validated guards in both handlers Correctness (P2 #022): - Log warning when pkgPointer.runId does not match task runId Architecture (P2 #024): - isMaritimeChokeEnergyCandidate() accepts both flat and nested topBucketId - Call site simplified to pass theater directly Performance (P2 #025): - SIMULATION_ROUND1_MAX_TOKENS raised 1800 to 2200 - Added max 3 initialReactions instruction to Round 1 prompt Maintainability (P2 #026): - Simulation pointer keys exported from server/_shared/cache-keys.ts - Both TS handlers import from shared location Documentation (P2 #027): - Strengthened runId no-op description in proto and OpenAPI spec * fix(todos): add blank lines around lists in markdown todo files * style(api): reformat openapi yaml to match linter output * test(simulation): add flat-shape filter test + getSimulationOutcome handler coverage Two tests identified as missing during PR #2220 review: 1. isMaritimeChokeEnergyCandidate flat-shape tests — covers the || candidate.topBucketId normalization added in the P1/P2 review pass. The existing tests only used the nested marketContext.topBucketId shape; this adds the flat root-field shape that arrives from the simulation-package.json JSON (selectedTheaters entries have topBucketId at root). 2. getSimulationOutcome handler structural tests — verifies the isOutcomePointer guard, found:false NOT_FOUND return, found:true success path, note population on runId mismatch, and redis_unavailable error string. Follows the readSrc static-analysis pattern used elsewhere in server-handlers.test.mjs (handler imports Redis so full integration test would require a test Redis instance).
107 lines
4.9 KiB
TypeScript
107 lines
4.9 KiB
TypeScript
/**
|
|
* Shared Redis pointer keys for simulation artifacts.
|
|
* Defined here so TypeScript handlers and seed scripts agree on the exact string.
|
|
* The MJS seed script keeps its own copy (cannot import TS source directly).
|
|
*/
|
|
export const SIMULATION_OUTCOME_LATEST_KEY = 'forecast:simulation-outcome:latest';
|
|
export const SIMULATION_PACKAGE_LATEST_KEY = 'forecast:simulation-package:latest';
|
|
|
|
/**
|
|
* Static cache keys for the bootstrap endpoint.
|
|
* Only keys with NO request-varying suffixes are included.
|
|
*/
|
|
export const BOOTSTRAP_CACHE_KEYS: Record<string, string> = {
|
|
earthquakes: 'seismology:earthquakes:v1',
|
|
outages: 'infra:outages:v1',
|
|
serviceStatuses: 'infra:service-statuses:v1',
|
|
ddosAttacks: 'cf:radar:ddos:v1',
|
|
trafficAnomalies: 'cf:radar:traffic-anomalies:v1',
|
|
sectors: 'market:sectors:v1',
|
|
etfFlows: 'market:etf-flows:v1',
|
|
macroSignals: 'economic:macro-signals:v1',
|
|
bisPolicy: 'economic:bis:policy:v1',
|
|
bisExchange: 'economic:bis:eer:v1',
|
|
bisCredit: 'economic:bis:credit:v1',
|
|
shippingRates: 'supply_chain:shipping:v2',
|
|
chokepoints: 'supply_chain:chokepoints:v4',
|
|
chokepointTransits: 'supply_chain:chokepoint_transits:v1',
|
|
minerals: 'supply_chain:minerals:v2',
|
|
giving: 'giving:summary:v1',
|
|
climateAnomalies: 'climate:anomalies:v1',
|
|
radiationWatch: 'radiation:observations:v1',
|
|
thermalEscalation: 'thermal:escalation:v1',
|
|
crossSourceSignals: 'intelligence:cross-source-signals:v1',
|
|
wildfires: 'wildfire:fires:v1',
|
|
marketQuotes: 'market:stocks-bootstrap:v1',
|
|
commodityQuotes: 'market:commodities-bootstrap:v1',
|
|
cyberThreats: 'cyber:threats-bootstrap:v2',
|
|
techReadiness: 'economic:worldbank-techreadiness:v1',
|
|
progressData: 'economic:worldbank-progress:v1',
|
|
renewableEnergy: 'economic:worldbank-renewable:v1',
|
|
positiveGeoEvents: 'positive_events:geo-bootstrap:v1',
|
|
theaterPosture: 'theater_posture:sebuf:stale:v1',
|
|
riskScores: 'risk:scores:sebuf:stale:v1',
|
|
naturalEvents: 'natural:events:v1',
|
|
flightDelays: 'aviation:delays-bootstrap:v1',
|
|
insights: 'news:insights:v1',
|
|
predictions: 'prediction:markets-bootstrap:v1',
|
|
cryptoQuotes: 'market:crypto:v1',
|
|
gulfQuotes: 'market:gulf-quotes:v1',
|
|
stablecoinMarkets: 'market:stablecoins:v1',
|
|
unrestEvents: 'unrest:events:v1',
|
|
iranEvents: 'conflict:iran-events:v1',
|
|
ucdpEvents: 'conflict:ucdp-events:v1',
|
|
temporalAnomalies: 'temporal:anomalies:v1',
|
|
weatherAlerts: 'weather:alerts:v1',
|
|
spending: 'economic:spending:v1',
|
|
techEvents: 'research:tech-events-bootstrap:v1',
|
|
gdeltIntel: 'intelligence:gdelt-intel:v1',
|
|
correlationCards: 'correlation:cards-bootstrap:v1',
|
|
securityAdvisories: 'intelligence:advisories-bootstrap:v1',
|
|
forecasts: 'forecast:predictions:v2',
|
|
customsRevenue: 'trade:customs-revenue:v1',
|
|
sanctionsPressure: 'sanctions:pressure:v1',
|
|
groceryBasket: 'economic:grocery-basket:v1',
|
|
bigmac: 'economic:bigmac:v1',
|
|
fuelPrices: 'economic:fuel-prices:v1',
|
|
cryptoSectors: 'market:crypto-sectors:v1',
|
|
defiTokens: 'market:defi-tokens:v1',
|
|
aiTokens: 'market:ai-tokens:v1',
|
|
otherTokens: 'market:other-tokens:v1',
|
|
nationalDebt: 'economic:national-debt:v1',
|
|
marketImplications: 'intelligence:market-implications:v1',
|
|
fearGreedIndex: 'market:fear-greed:v1',
|
|
};
|
|
|
|
export const BOOTSTRAP_TIERS: Record<string, 'slow' | 'fast'> = {
|
|
bisPolicy: 'slow', bisExchange: 'slow', bisCredit: 'slow',
|
|
minerals: 'slow', giving: 'slow', sectors: 'slow',
|
|
progressData: 'slow', renewableEnergy: 'slow',
|
|
etfFlows: 'slow', shippingRates: 'fast', wildfires: 'slow',
|
|
climateAnomalies: 'slow', sanctionsPressure: 'slow', radiationWatch: 'slow', thermalEscalation: 'slow', crossSourceSignals: 'slow', cyberThreats: 'slow', techReadiness: 'slow',
|
|
theaterPosture: 'fast', naturalEvents: 'slow',
|
|
cryptoQuotes: 'slow', gulfQuotes: 'slow', stablecoinMarkets: 'slow',
|
|
unrestEvents: 'slow', ucdpEvents: 'slow', techEvents: 'slow',
|
|
earthquakes: 'fast', outages: 'fast', serviceStatuses: 'fast', ddosAttacks: 'fast', trafficAnomalies: 'fast',
|
|
macroSignals: 'fast', chokepoints: 'fast', chokepointTransits: 'fast', riskScores: 'fast',
|
|
marketQuotes: 'fast', commodityQuotes: 'fast', positiveGeoEvents: 'fast',
|
|
flightDelays: 'fast', insights: 'fast', predictions: 'fast',
|
|
iranEvents: 'fast', temporalAnomalies: 'fast', weatherAlerts: 'fast',
|
|
spending: 'fast', gdeltIntel: 'fast', correlationCards: 'fast',
|
|
securityAdvisories: 'slow',
|
|
forecasts: 'fast',
|
|
customsRevenue: 'slow',
|
|
consumerPricesOverview: 'slow', consumerPricesCategories: 'slow',
|
|
consumerPricesMovers: 'slow', consumerPricesSpread: 'slow',
|
|
groceryBasket: 'slow',
|
|
bigmac: 'slow',
|
|
fuelPrices: 'slow',
|
|
cryptoSectors: 'slow',
|
|
defiTokens: 'slow',
|
|
aiTokens: 'slow',
|
|
otherTokens: 'slow',
|
|
nationalDebt: 'slow',
|
|
marketImplications: 'slow',
|
|
fearGreedIndex: 'slow',
|
|
};
|