Files
worldmonitor/scripts/seed-bundle-resilience-recovery.mjs
Elie Habib 21331b9a1e feat(resilience): recovery seeder bundle + de-stub import-HHI and fuel-stocks (#2999)
* feat(resilience): recovery seeder bundle + de-stub import-HHI and fuel-stocks

- Add seed-bundle-resilience-recovery.mjs orchestrating all 5 recovery seeders
  (fiscal-space, reserve-adequacy, external-debt, import-HHI, fuel-stocks)
  with 30-day intervals and freshness-gated skipping via _bundle-runner
- Replace import-HHI stub with real Comtrade HS2 HHI computation: fetches
  per-country bilateral import data, computes Herfindahl-Hirschman Index,
  flags concentrated importers (HHI > 0.25), 90-day TTL (3x interval)
- Replace fuel-stocks stub with IEA-derived implementation: reads existing
  energy:iea-oil-stocks:v1:index Redis key, computes fuel-stock-days per
  country, flags 90-day IEA obligation compliance, avoids duplicate API calls
- Add tests: HHI computation (8 cases), fuel-stock-days derivation (6 cases),
  bundle smoke test (13 cases verifying entries, labels, scripts on disk)

* fix(resilience): remove duplicate RESILIENCE_SCHEMA_V2_ENABLED declaration

Pre-existing typecheck:api error from duplicate const export in _shared.ts.
Keep the v2-enabled-by-default variant.

* fix(resilience): Comtrade HHI seeder P1 fixes (#2999 review)

1. Removed fallback to public preview API (sends partnerCode='' which
   returns zero rows). COMTRADE_API_KEY is now required. Logs an error
   and returns empty if missing. Pinned period to previous year to
   avoid multi-year row duplication.

2. computeHhi() now aggregates values by partner (Map) before computing
   shares. Previously it computed per-row shares, so a partner appearing
   in N commodity rows was counted N times, understating concentration.
   Returns { hhi, partnerCount } instead of a bare number.

Added 2 new test cases covering multi-row and multi-year aggregation.

* fix(resilience): use COMTRADE_API_KEYS (plural) with key rotation

* fix(resilience): omit partnerCode to fetch all bilateral partners (not world aggregate)

* fix(resilience): HHI scale conversion + fuel-stocks field name (#2999 P1)

1. HHI seeder writes 0..1 scale but scorer expected 0..5000. Added
   *10000 conversion before normalizeLowerBetter so a concentrated
   importer at hhi=0.55 maps to 5500 (above the 5000 goalpost, scores
   low) instead of scoring near-perfect.

2. Fuel-stocks seeder writes fuelStockDays but scorer read stockDays.
   Updated the RecoveryFuelStocksCountry interface and both read sites
   to match the seeder field name.

* fix(resilience): retry warning + fuel-stocks flag consistency (#2999 P2)
2026-04-12 11:39:48 +04:00

11 lines
937 B
JavaScript

#!/usr/bin/env node
import { runBundle, DAY } from './_bundle-runner.mjs';
await runBundle('resilience-recovery', [
{ label: 'Fiscal-Space', script: 'seed-recovery-fiscal-space.mjs', seedMetaKey: 'resilience:recovery:fiscal-space', intervalMs: 30 * DAY, timeoutMs: 300_000 },
{ label: 'Reserve-Adequacy', script: 'seed-recovery-reserve-adequacy.mjs', seedMetaKey: 'resilience:recovery:reserve-adequacy', intervalMs: 30 * DAY, timeoutMs: 300_000 },
{ label: 'External-Debt', script: 'seed-recovery-external-debt.mjs', seedMetaKey: 'resilience:recovery:external-debt', intervalMs: 30 * DAY, timeoutMs: 300_000 },
{ label: 'Import-HHI', script: 'seed-recovery-import-hhi.mjs', seedMetaKey: 'resilience:recovery:import-hhi', intervalMs: 30 * DAY, timeoutMs: 600_000 },
{ label: 'Fuel-Stocks', script: 'seed-recovery-fuel-stocks.mjs', seedMetaKey: 'resilience:recovery:fuel-stocks', intervalMs: 30 * DAY, timeoutMs: 300_000 },
]);