mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* feat(resilience): PR 0 diagnostic freeze + fairness-audit harness
Lands the before-state and measurement apparatus every subsequent
resilience-scorer PR validates against. Zero scoring changes. Per the
v3 plan at docs/plans/2026-04-22-001-fix-resilience-scorer-structural-
bias-plan.md this is tranche 0 of five.
What lands:
- Construct contract published in the methodology doc: absolute
resilience not development-adjusted, mechanism test for every
indicator, peer-relative views published separately from the core.
- Known construct limitations section: six construct errors scheduled
for PR 1-3 repair with explicit mapping to plan tranches.
- Indicator-source manifest at docs/methodology/indicator-sources.yaml
with source, seriesId, seriesUrl, coveragePct, lastObservedYear,
license, mechanismTestRationale, and a constructStatus classification.
- Pre-repair ranking snapshot at
docs/snapshots/resilience-ranking-live-pre-repair-2026-04-22.json
(217 items + 5 greyedOut, captured 2026-04-22 08:38 UTC at commit
425507d15).
- Cohort configuration at tests/helpers/resilience-cohorts.mts: six
cohorts covering 87 countries (net-fuel-exporters, net-energy-
importers-oecd, nuclear-heavy-generation, coal-heavy-domestic,
small-island-importers, fragile-states).
- Matched-pair sanity panel at tests/helpers/resilience-matched-pairs.mts:
six pairs (FR/DE, NO/CA, UAE/BH, JP/KR, IN/ZA, SG/CH) with expected-
direction rationale and minGap for acceptance gate 7.
- scripts/compare-resilience-current-vs-proposed.mjs extended to emit
cohortSummary and matchedPairSummary alongside the existing output
shape (backward compatible).
- tests/resilience-cohort-config.test.mts: 11 validations ensuring the
cohort + matched-pair configs stay well-formed.
Deferred to PR 0.5 (before PR 1 lands):
- Monotonicity test harness for all 19 dimension scorers pinning the
sign of every indicator.
- Pearson-derivative variable-influence baseline inside the sensitivity
script producing the nominal-weight-vs-effective-influence table that
plan acceptance gate 8 requires.
Verification: typecheck:all clean, 430/430 resilience tests pass,
11/11 new cohort-config tests pass, snapshot auto-discovered and
validated by the existing snapshot-test harness.
* feat(resilience): PR 0 follow-ups — monotonicity harness, variable-influence baseline, cross-consumer formula gate
Completes the PR 0 scope per the v3 plan §5 deliverables. Three adds:
1. Monotonicity test harness
tests/resilience-dimension-monotonicity.test.mts pins the direction
of movement for 14 indicators across 7 dimensions (reserve adequacy,
fiscal space 3x, external debt coverage, import concentration,
governance WGI, food/water 2x, energy 5x). Each test builds two
synthetic ResilienceSeedReader fixtures differing only in the target
indicator and asserts the dimension score moves in the documented
direction. The scoreEnergy tests explicitly flag three indicators
(gasShare, coalShare, electricityConsumption) that PR 1 §3.1-3.2
overturns so future readers understand which directional claims the
plan intentionally replaces.
2. Variable-influence baseline
scripts/compare-resilience-current-vs-proposed.mjs now computes
per-dimension Pearson correlation against the current overallScore
scaled by the dimension's nominal domain weight (a Pearson-derivative
approximation of Sobol indices). The output carries a
variableInfluence[] array sorted by abs(effectiveInfluence) desc.
Acceptance gate 8 from the plan compares post-change effective
influence against assigned nominal weight; divergences flag a
wealth-proxy or saturated-signal construct problem.
3. Cross-consumer formula gate
Five external consumers of resilience:score:v10:* now filter stale-
formula entries so a flag flip does not serve mixed-formula data
downstream:
- server/worldmonitor/supply-chain/v1/get-route-impact.ts —
readResilienceScore() checks _formula via the new
getCurrentCacheFormula export and returns 0 on mismatch.
- scripts/validate-resilience-correlation.mjs,
scripts/validate-resilience-backtest.mjs,
scripts/backtest-resilience-outcomes.mjs,
scripts/benchmark-resilience-external.mjs — each inlines a
currentCacheFormulaLocal() helper that mirrors the server's
formula derivation from env, skips parsed entries whose
_formula disagrees, and logs the skip count so operators can
notice a mismatch during the flip window.
A mixed-formula cohort (some countries d6-tagged, others pc-tagged)
would confound every correlation, AUC, and Spearman this repair plan
depends on for its acceptance gates. These guards close that gap.
Verification: typecheck:all clean, 444/444 resilience tests pass
(+14 from the new monotonicity harness).
* fix(resilience): PR 0 review follow-ups — sample-union + doc tense
Two review-driven fixes on top of PR 0.
1. scripts/compare-resilience-current-vs-proposed.mjs — the cohort and
matched-pair summaries were computed against the historical
52-country sensitivity seed, which silently excluded the
small-island-importers cohort (zero members in the seed) and the
sg-vs-ch matched pair (Singapore not in the seed). With the current
script those acceptance gates are partially measured at best.
SAMPLE now = union(historical 52 seed, every cohort member, every
matched-pair endpoint). The imports for RESILIENCE_COHORTS and
MATCHED_PAIRS moved from inside main() to module scope so the union
can be computed before the script runs.
Net sample size grows from 52 to ~95 countries. Still fast enough
for an interactive pass; makes the acceptance gates honest.
2. docs/methodology/country-resilience-index.mdx — the construct
contract wording read as present-tense compliance ("Every indicator
in the scorer passes a single mechanism test"), which contradicted
the immediately-following passage about indicators that currently
fail the test. Reworded to "is being evaluated against" and added
an explicit PR-0-does-not-change-scoring paragraph that names the
known-failing indicators (electricityConsumption, gas/coal flat
penalties, WHO per-capita health spend) and points at the repair
plan for the replacement schedule.
Verification: typecheck:all clean, 444/444 resilience tests pass.
* fix(resilience): compare-script loads frozen baseline + emits per-indicator influence
Addresses two P1 review findings on PR #3284:
1. Script previously compared current-6d vs proposed-pillar-combined
from the SAME checkout; never loaded the frozen pre-PR-0 baseline,
so acceptance gates 2/6/7 ("no country moved >15pts vs baseline",
cohort median shift vs baseline, matched-pair gap change vs
baseline) could not be enforced for later scorer PRs.
Now auto-discovers the most recent
resilience-ranking-live-pre-repair-<date>.json (or post-<pr>-<date>)
in docs/snapshots/ and emits a baselineComparison block with:
spearmanVsBaseline, maxCountryAbsDelta, biggestDriftsVsBaseline,
cohortShiftVsBaseline, matchedPairGapChange. If no baseline is
found, the block is emitted with status 'unavailable' so callers
distinguish missing-baseline from passed-baseline.
2. variableInfluence was emitted only at the dimension level, which
hid the exact sub-indicators the repair plan targets
(electricityConsumption, gasShare, coalShare, etc.) inside their
parent dimension. Added extractIndicatorValues() which pulls twelve
construct-risk indicators per country from the shared memoized
reader, then computes per-indicator Pearson correlation against
the current overall score. Emitted as perIndicatorInfluence[],
sorted by absolute effective influence.
Acceptance gate 8 ("effective influence agrees in sign and rank-order
with assigned nominal weights") is now computable at the indicator
level, not only at the dimension level.
No production code touched; diagnostic-harness only.
* fix(resilience): baseline-snapshot selection by structured parse, not filename sort
Addresses P1 review on compare-resilience-current-vs-proposed.mjs:118-130.
Plain filename sort breaks the "immediate-prior state" contract two ways:
1. Lexical ordering: `pre-repair` sorts after `post-*`
(`pr...` to 'r' > 'o'), so the PR-0 freeze would keep winning even
after post-PR snapshots exist. Later scorer PRs would then report
acceptance-gate deltas against the original pre-repair freeze
instead of the immediately-prior post-PR-(N-1) snapshot — the gate
would appear valid while measuring against the wrong baseline.
2. Lexical ordering: `pr10` < `pr9` (digit-by-digit), so PR-10 would
lose the selection to PR-9.
Fix: parseBaselineSnapshotMeta() extracts (kind, prNumber, date) from
the filename. Sort keys are (kindRank desc, prNumber desc, date desc):
- post always beats pre-repair (kindRank 1 vs 0)
- among posts, prNumber compared numerically (10 beats 9)
- date breaks ties (same-PR re-snapshots, later capture wins)
- unlabeled post tags get prNumber 0 so they sort between
pre-repair and any numbered PR snapshot
Surfaced in output: baselineKind / baselinePrNumber / baselineDate
alongside baselineFile so the operator can verify which snapshot was
selected without having to reopen the file.
Module now isMain-guarded per feedback_seed_isMain_guard memory so
tests can import parseBaselineSnapshotMeta without firing the
scoring run.
Added tests/resilience-baseline-snapshot-ordering.test.mjs (9 tests)
pinning the ordering contract for every known failure mode.
Diagnostic-harness change only. No production code touched.
* fix(resilience): full scorable universe + registry-driven per-indicator influence
Addresses two fresh P1 review findings on the PR 0 compare harness.
Finding 1 — acceptance math ran on a curated ~95-country sample,
so plan gate 2 could miss large regressions in excluded countries.
- Main scoring loop now iterates the FULL scorable universe
(listScorableCountries()), not the 52-country seed + cohort union.
- Removed SAMPLE / HISTORICAL_SENSITIVITY_SEED constants.
- Added scorableUniverseSize + cohortMissingFromScorable to output
so operators see universe size and any cohort/pair endpoint that
listScorable refuses to score (fail-loud, not silent drop).
Finding 3 — per-indicator influence was a hand-picked 12-indicator
subset, hiding most registry indicators from the baseline that
later scorer PRs need.
- Extraction is now driven by INDICATOR_REGISTRY. Every Core +
Enrichment indicator gets a row with explicit extractionStatus:
implemented | not-implemented (with reason) | unregistered-in-harness
- EXTRACTION_RULES covers 40/59 indicators across 11 shape families
(static-path, static-wb-infrastructure, static-wgi, static-wgi-mean,
static-who, energy-mix-field, gas-storage-field, recovery-country-
field, imf-macro/labor-country-field, national-debt, sanctions-count).
- Remaining 19 indicators need either a scorer trace hook (PR 0.5)
or a safe aggregation duplicate; each carries a reason string.
- extractionCoverage summary (totalIndicators / implemented /
notImplemented / unregisteredInHarness / coreImplemented / coreTotal)
exposed in output so PR 0.5 progress is measurable.
Added tests/resilience-indicator-extraction-plan.test.mjs (11 tests)
pinning: every registry entry has an extraction row; not-implemented
rows carry a reason; all 12 plan-named construct-risk indicators stay
extractable; Core-tier coverage floor of 45%; shape-family unit tests.
Diagnostic-harness change only. No production code touched.
* fix(resilience): wire event-aggregate per-indicator influence via exported scorer helpers
Addresses P1 review on PR 0 compare harness. Previous commit marked 16
Core-tier indicators as 'not-implemented' because they needed scorer
event-window/severity-weighting math; that left the gate-9 acceptance
apparatus incomplete for a large part of the shipped score.
Fix: export the scorer-internal aggregation helpers so the harness
calls them directly. Zero aggregation math duplicated in the harness,
harness and scorer cannot drift.
Exported from _dimension-scorers.ts (purely additive):
summarizeCyber, summarizeOutages, summarizeGps,
summarizeUcdp, summarizeUnrest, summarizeSocialVelocity,
getCountryDisplacement, getThreatSummaryScore,
countTradeRestrictions, countTradeBarriers.
13 extraction rules moved from not-implemented to implemented:
cyberThreats, internetOutages, infraOutages, gpsJamming,
ucdpConflict, unrestEvents, socialVelocity, newsThreatScore,
displacementTotal, displacementHosted, tradeRestrictions,
tradeBarriers, recoveryConflictPressure, recoveryDisplacementVelocity.
Coverage:
52/59 total (88%), 46/50 Core-tier (92%).
Four Core indicators remain not-implemented for STRUCTURAL reasons,
NOT missing code. Scorer inputs are genuinely global scalars with
zero per-country variance, so Pearson(indicator, overall) is 0 or
NaN by construction:
shippingStress, transitDisruption, energyPriceStress — scorer
reads a global scalar applied to every country; a per-country
effective signal would need re-expression as (global x per-country
exposure), which is a derived signal in a different entry.
aquastatWaterAvailability — needs a distinct sub-indicator path
resolver; enrichment follow-up.
New test asserts the three no-per-country-variance indicators STAY
not-implemented with a matching reason, so any future extraction
that appears to cover them without fixing the underlying construct
fails.
Dispatcher split into STATIC / SIMPLE / AGGREGATE extractor tables
to stay under biome complexity limit. Core-tier floor test raised
from 45% to 80%.
89 resilience tests pass, typecheck clean, biome clean. No production
behaviour changes.
* fix(resilience): tag-gated AQUASTAT extractor closes the last fixable Core gap
Reviewer flagged aquastatWaterAvailability as the only remaining Core
indicator where the not-implemented status was structurally fixable
rather than conceptually impossible.
Both aquastatWaterStress and aquastatWaterAvailability share a single
.aquastat.value field; the scorer's scoreAquastatValue splits them
by the sibling .aquastat.indicator tag keyword (stress/withdrawal/
dependency to stress family; availability/renewable/access to
availability family). The harness now mirrors this branching:
- classifyAquastatFamily implements the scorer's priority order
(stress-family match wins even if the tag also contains an
availability keyword, matching the sequential if-check at
_dimension-scorers.ts L770-776).
- static-aquastat-stress / static-aquastat-availability extractors
return the value only when the family matches, so stress-family
readings never corrupt the availability Pearson and vice versa.
Core-tier coverage: 46/50 to 47/50 (94%). The 3 remaining Core
not-implemented indicators (shippingStress, transitDisruption,
energyPriceStress) are all structural impossibilities: scorer inputs
are global scalars with zero per-country variance.
New contract test pins both directions of the tag gate plus the
priority-order edge case (a tag containing both families' keywords
routes to stress).
90 resilience tests pass, typecheck clean, biome clean.
687 lines
26 KiB
YAML
687 lines
26 KiB
YAML
# Resilience scorer indicator-source manifest (PR 0 scaffold, 2026-04-22).
|
||
#
|
||
# One entry per sub-indicator used inside a dimension scorer. Each entry
|
||
# answers the mechanism test from docs/plans/2026-04-22-001-fix-resilience-
|
||
# scorer-structural-bias-plan.md §1.1: what direct shock channel does this
|
||
# measure?
|
||
#
|
||
# Fields:
|
||
# indicator — scorer variable name (matches the weighted-blend entry)
|
||
# dimension — parent dimension id (matches RESILIENCE_DIMENSION_ORDER)
|
||
# domain — parent domain id
|
||
# weight — current nominal weight inside the dimension blend
|
||
# direction — higher-better | lower-better | composite
|
||
# source — authority that publishes the series
|
||
# seriesId — canonical series id where applicable (e.g. EG.IMP.CONS.ZS)
|
||
# seriesUrl — direct link to source documentation
|
||
# coveragePct — observed-data coverage across the 222-country static index (first-pass estimate; authoritative value lives in the matching seeder's seed-meta.coverage field)
|
||
# lastObservedYear — most-recent year with global data in the source
|
||
# license — reuse license (CC-BY, CC0, OGL, Proprietary-with-fair-use, etc.)
|
||
# mechanismTestRationale — one-sentence answer to "what direct shock channel does this measure?"
|
||
# constructStatus — observed-mechanism | wealth-proxy | imputed-floor | regional-only | dead-signal
|
||
#
|
||
# constructStatus is the v3-plan classification:
|
||
# observed-mechanism — passes the mechanism test; kept as-is pending goalpost review
|
||
# wealth-proxy — fails the mechanism test; slated for removal or threshold-transform
|
||
# imputed-floor — data source not wired; producing only the imputed midpoint
|
||
# regional-only — data source covers <50% of scorable countries
|
||
# dead-signal — saturated or compressed; signal collapsed across the ranking
|
||
|
||
# ECONOMIC DOMAIN (weight 0.17) -------------------------------------------
|
||
|
||
- indicator: govRevenuePct
|
||
dimension: macroFiscal
|
||
domain: economic
|
||
weight: 0.50
|
||
direction: higher-better
|
||
source: IMF
|
||
seriesId: GGR_G01_GDP_PT
|
||
seriesUrl: https://www.imf.org/external/datamapper/GGR_G01_GDP_PT@FM/
|
||
coveragePct: 0.90
|
||
lastObservedYear: 2024
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: Government revenue % GDP is the policy-response headroom a state can deploy during a fiscal shock; higher = more ability to absorb.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Goalpost 5-45 is probably too wide at the top; Nordic revenue/GDP ≥ 50% saturates. Review in PR 4 goalpost pass.
|
||
|
||
- indicator: debtGrowthRate
|
||
dimension: macroFiscal
|
||
domain: economic
|
||
weight: 0.20
|
||
direction: lower-better
|
||
source: National debt databases (IMF + WB cross-source)
|
||
seriesId: TBD
|
||
seriesUrl: TBD
|
||
coveragePct: 0.80
|
||
lastObservedYear: 2024
|
||
license: Mixed (per-source)
|
||
mechanismTestRationale: Rising debt growth indicates deteriorating fiscal trajectory and reduced capacity to finance a shock response.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: currentAccountPct
|
||
dimension: macroFiscal
|
||
domain: economic
|
||
weight: 0.30
|
||
direction: higher-better
|
||
source: IMF
|
||
seriesId: BCA_NGDPD
|
||
seriesUrl: https://www.imf.org/external/datamapper/BCA_NGDPD@WEO/
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2024
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: Current account surplus indicates external-payments resilience; deficit indicates vulnerability to external-finance shocks.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: fxVolatility
|
||
dimension: currencyExternal
|
||
domain: economic
|
||
weight: 0.60
|
||
direction: lower-better
|
||
source: BIS Data Portal
|
||
seriesId: Broad Effective Exchange Rate
|
||
seriesUrl: https://data.bis.org/topics/EER
|
||
coveragePct: 0.30 # BIS covers 64 economies
|
||
lastObservedYear: 2024
|
||
license: CC-BY
|
||
mechanismTestRationale: FX volatility measures monetary-shock transmission risk.
|
||
constructStatus: regional-only
|
||
reviewNotes: BIS EER covers only 64 economies. Replace with FR.INR.RINR (real interest rate) or IMF inflation volatility in PR 3.
|
||
|
||
- indicator: fxDeviation
|
||
dimension: currencyExternal
|
||
domain: economic
|
||
weight: 0.25
|
||
direction: lower-better
|
||
source: BIS Data Portal
|
||
seriesId: EER deviation from equilibrium
|
||
seriesUrl: https://data.bis.org/topics/EER
|
||
coveragePct: 0.30
|
||
lastObservedYear: 2024
|
||
license: CC-BY
|
||
mechanismTestRationale: EER deviation from equilibrium proxies mis-aligned exchange rates that create abrupt-correction risk.
|
||
constructStatus: regional-only
|
||
reviewNotes: Same 64-economy limitation. Retire in PR 3.
|
||
|
||
- indicator: fxReservesAdequacy
|
||
dimension: currencyExternal
|
||
domain: economic
|
||
weight: 0.15
|
||
direction: higher-better
|
||
source: World Bank
|
||
seriesId: FI.RES.TOTL.MO
|
||
seriesUrl: https://data.worldbank.org/indicator/FI.RES.TOTL.MO
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2023
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: Reserves in months of imports directly measures immediate external-finance cushion.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Also enters reserveAdequacy at 1.0 weight. Double-counting risk across dimensions; review in PR 2.
|
||
|
||
- indicator: sanctionCount
|
||
dimension: tradeSanctions
|
||
domain: economic
|
||
weight: 0.45
|
||
direction: lower-better
|
||
source: OFAC
|
||
seriesId: Consolidated Sanctions List (count per country)
|
||
seriesUrl: https://sanctionslist.ofac.treas.gov/
|
||
coveragePct: 1.00
|
||
lastObservedYear: 2026
|
||
license: US Government public domain
|
||
mechanismTestRationale: Active sanctions restrict trade/finance channels; higher count = more channels restricted.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: OFAC-only. PR 4 adds EU/UK/CN sanctions for directional completeness.
|
||
|
||
- indicator: tradeRestrictions
|
||
dimension: tradeSanctions
|
||
domain: economic
|
||
weight: 0.15
|
||
direction: lower-better
|
||
source: WTO
|
||
seriesId: Trade Monitoring Database
|
||
seriesUrl: https://www.wto.org/english/tratop_e/tpr_e/trade_monitoring_e.htm
|
||
coveragePct: 0.75
|
||
lastObservedYear: 2025
|
||
license: Open
|
||
mechanismTestRationale: Active trade restrictions (in-force, weighted 3×) directly measure market-access loss.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: tradeBarriers
|
||
dimension: tradeSanctions
|
||
domain: economic
|
||
weight: 0.15
|
||
direction: lower-better
|
||
source: WTO
|
||
seriesId: Trade Barriers Notifications
|
||
seriesUrl: https://tradebarriers.wto.org/
|
||
coveragePct: 0.70
|
||
lastObservedYear: 2025
|
||
license: Open
|
||
mechanismTestRationale: Notified trade barriers (not yet in force) indicate near-term market-access risk.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: appliedTariffRate
|
||
dimension: tradeSanctions
|
||
domain: economic
|
||
weight: 0.25
|
||
direction: lower-better
|
||
source: World Bank / WITS
|
||
seriesId: TM.TAX.MRCH.WM.AR.ZS
|
||
seriesUrl: https://data.worldbank.org/indicator/TM.TAX.MRCH.WM.AR.ZS
|
||
coveragePct: 0.90
|
||
lastObservedYear: 2023
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: Applied tariff rates measure cost of trade restriction on imports.
|
||
constructStatus: observed-mechanism
|
||
|
||
# INFRASTRUCTURE DOMAIN (weight 0.15) -------------------------------------
|
||
|
||
- indicator: cyberThreats
|
||
dimension: cyberDigital
|
||
domain: infrastructure
|
||
weight: 0.45
|
||
direction: lower-better
|
||
source: Cyber threat feeds (mixed Western-origin)
|
||
seriesId: severity-weighted count (critical 3×, high 2×, medium 1×, low 0.5×)
|
||
seriesUrl: Internal seed
|
||
coveragePct: 0.70
|
||
lastObservedYear: 2026
|
||
license: Proprietary feeds (aggregated)
|
||
mechanismTestRationale: Severity-weighted cyber threat count directly measures ongoing cyber-attack pressure on national digital infrastructure.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Western-feed bias; non-English cyber activity under-represented. PR 4 §4.8 tracks this.
|
||
|
||
- indicator: internetOutages
|
||
dimension: cyberDigital
|
||
domain: infrastructure
|
||
weight: 0.35
|
||
direction: lower-better
|
||
source: Cloudflare Radar + internal monitoring
|
||
seriesId: Outage penalty (total 4×, major 2×, partial 1×)
|
||
seriesUrl: https://radar.cloudflare.com/
|
||
coveragePct: 0.95
|
||
lastObservedYear: 2026
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: Internet outages directly measure digital-infrastructure availability under current stress.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: gpsJamming
|
||
dimension: cyberDigital
|
||
domain: infrastructure
|
||
weight: 0.20
|
||
direction: lower-better
|
||
source: GPSJam
|
||
seriesId: Hex penalty (high 3×, medium 1×)
|
||
seriesUrl: https://gpsjam.org/
|
||
coveragePct: 0.95
|
||
lastObservedYear: 2026
|
||
license: Open data
|
||
mechanismTestRationale: GPS jamming intensity measures electronic-warfare / navigation-disruption exposure.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: logisticsPerformanceIndex
|
||
dimension: logisticsSupply
|
||
domain: infrastructure
|
||
weight: TBD
|
||
direction: higher-better
|
||
source: World Bank LPI
|
||
seriesId: LP.LPI.OVRL.XQ
|
||
seriesUrl: https://lpi.worldbank.org/
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2023
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: Logistics Performance Index measures functional capacity to move goods; directly shocks during supply-chain disruptions.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Goalpost anchors OECD-centric; PR 4 review.
|
||
|
||
- indicator: infrastructureSubcomponents
|
||
dimension: infrastructure
|
||
domain: infrastructure
|
||
weight: TBD
|
||
direction: higher-better
|
||
source: World Bank + WEF Global Competitiveness
|
||
seriesId: Composite
|
||
seriesUrl: TBD
|
||
coveragePct: 0.80
|
||
lastObservedYear: 2024
|
||
license: Mixed
|
||
mechanismTestRationale: Physical infrastructure quality is the baseline capacity for delivering services during normal and crisis periods.
|
||
constructStatus: observed-mechanism
|
||
|
||
# ENERGY DOMAIN (weight 0.11) --------------------------------------------
|
||
|
||
- indicator: dependency
|
||
dimension: energy
|
||
domain: energy
|
||
weight: 0.25
|
||
direction: lower-better
|
||
source: IEA (via static seed)
|
||
seriesId: Energy import dependency (%)
|
||
seriesUrl: https://www.iea.org/data-and-statistics/data-browser
|
||
coveragePct: 0.50 # IEA detail covers OECD + major non-OECD
|
||
lastObservedYear: 2023
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: Share of energy consumption that is imported; direct supply-shock exposure.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: PR 1 §3.2 replaces with World Bank EG.IMP.CONS.ZS (better coverage) as part of importedFossilDependence composite.
|
||
|
||
- indicator: gasShare
|
||
dimension: energy
|
||
domain: energy
|
||
weight: 0.12
|
||
direction: lower-better
|
||
source: IEA World Energy Balances via static seed
|
||
seriesId: Natural gas share of primary energy
|
||
seriesUrl: https://www.iea.org/data-and-statistics/data-browser
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2023
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: CURRENT SCORER applies this as a vulnerability (lower-better) but it CONFLATES fossil-dominance with fossil-import-dependence. Domestic gas is a resilience asset, not a vulnerability.
|
||
constructStatus: wealth-proxy
|
||
reviewNotes: PR 1 §3.2 removes as standalone input; folds into importedFossilDependence under power-system framing.
|
||
|
||
- indicator: coalShare
|
||
dimension: energy
|
||
domain: energy
|
||
weight: 0.08
|
||
direction: lower-better
|
||
source: IEA World Energy Balances via static seed
|
||
seriesId: Coal share of primary energy
|
||
seriesUrl: https://www.iea.org/data-and-statistics/data-browser
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2023
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: Same concern as gasShare — penalty is climate-frame, not resilience-frame. Fails mechanism test under absolute-resilience contract.
|
||
constructStatus: wealth-proxy
|
||
reviewNotes: PR 1 §3.2 removes.
|
||
|
||
- indicator: renewShare
|
||
dimension: energy
|
||
domain: energy
|
||
weight: 0.05
|
||
direction: higher-better
|
||
source: IEA / World Bank
|
||
seriesId: EG.ELC.RNEW.ZS
|
||
seriesUrl: https://data.worldbank.org/indicator/EG.ELC.RNEW.ZS
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2023
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: Share of electricity from renewables, proxies low-carbon-firm-generation capacity (for hydro/geothermal) and diversity-of-supply (for wind/solar).
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: PR 1 §3.3 collapses with nuclearShare (currently missing) into one lowCarbonGenerationShare indicator.
|
||
|
||
- indicator: storageStress
|
||
dimension: energy
|
||
domain: energy
|
||
weight: 0.10
|
||
direction: lower-better
|
||
source: GIE AGSI+
|
||
seriesId: EU gas storage fill % (per country)
|
||
seriesUrl: https://agsi.gie.eu/
|
||
coveragePct: 0.15 # EU + UK + a handful
|
||
lastObservedYear: 2026
|
||
license: Open
|
||
mechanismTestRationale: EU gas storage fill directly measures winter-heating-shock buffer. European-only platform.
|
||
constructStatus: regional-only
|
||
reviewNotes: PR 1 §3.5 renames to euGasStorageStress and scopes to EU-only (weight 0 for non-EU).
|
||
|
||
- indicator: exposedEnergyStress
|
||
dimension: energy
|
||
domain: energy
|
||
weight: 0.10
|
||
direction: composite
|
||
source: Internal composite (energy-price-stress × import-exposure)
|
||
seriesId: Derived
|
||
seriesUrl: Internal seed
|
||
coveragePct: 0.70
|
||
lastObservedYear: 2026
|
||
license: Internal
|
||
mechanismTestRationale: Combines energy-price shocks with import-exposure to measure price-shock transmission.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: PR 1 may simplify given importedFossilDependence covers import-exposure directly.
|
||
|
||
- indicator: electricityConsumption
|
||
dimension: energy
|
||
domain: energy
|
||
weight: 0.30
|
||
direction: higher-better
|
||
source: World Bank
|
||
seriesId: EG.USE.ELEC.KH.PC
|
||
seriesUrl: https://data.worldbank.org/indicator/EG.USE.ELEC.KH.PC
|
||
coveragePct: 0.90
|
||
lastObservedYear: 2022
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: FAILS the mechanism test. Per-capita electricity consumption tracks GDP per capita; it is a level-of-load measure not a resilience mechanism. IEA energy-security framing treats EFFICIENCY (lower load for same output) as resilience, which this indicator inversely rewards.
|
||
constructStatus: wealth-proxy
|
||
reviewNotes: PR 1 §3.1 removes. Replaced with powerLossesPct (EG.ELC.LOSS.ZS), reserveMarginPct (IEA), and accessToElectricityPct (EG.ELC.ACCS.ZS) moved to infrastructure domain.
|
||
|
||
# SOCIAL-GOVERNANCE DOMAIN (weight 0.19) ---------------------------------
|
||
|
||
- indicator: wgiComposite
|
||
dimension: governanceInstitutional
|
||
domain: social-governance
|
||
weight: 1.0
|
||
direction: higher-better
|
||
source: World Bank WGI
|
||
seriesId: Voice/Accountability, Political Stability, Government Effectiveness, Regulatory Quality, Rule of Law, Control of Corruption
|
||
seriesUrl: https://info.worldbank.org/governance/wgi/
|
||
coveragePct: 0.98
|
||
lastObservedYear: 2023
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: WGI subscores measure state capacity to design and enforce policy response to shocks. Passes the mechanism test conditionally — the composite is a direct policy-response-capacity signal.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Weights review in PR 4. Individual WGI subscores may need separate weighting vs equal-blend.
|
||
|
||
- indicator: gpiScore
|
||
dimension: socialCohesion
|
||
domain: social-governance
|
||
weight: 0.40 # approximate
|
||
direction: lower-better
|
||
source: Institute for Economics and Peace
|
||
seriesId: Global Peace Index
|
||
seriesUrl: https://www.visionofhumanity.org/
|
||
coveragePct: 0.75
|
||
lastObservedYear: 2024
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: GPI measures internal conflict, militarization, and external conflict intensity — direct social-cohesion-shock exposure.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Known Western-democracy bias in GPI methodology; PR 4 review.
|
||
|
||
- indicator: displacementMetric
|
||
dimension: socialCohesion
|
||
domain: social-governance
|
||
weight: 0.30 # approximate
|
||
direction: lower-better
|
||
source: UNHCR
|
||
seriesId: totalDisplaced
|
||
seriesUrl: https://data.unhcr.org/
|
||
coveragePct: 0.95
|
||
lastObservedYear: 2025
|
||
license: Open
|
||
mechanismTestRationale: Total displaced persons directly measures ongoing forced-migration pressure. BIAS: currently blends origin + host; penalizes Jordan/Turkey/Germany for HOSTING.
|
||
constructStatus: wealth-proxy # classified as biased — bias label
|
||
reviewNotes: PR 4 §4.2 splits origin (negative signal) from host (mixed signal).
|
||
|
||
- indicator: unrestMetric
|
||
dimension: socialCohesion
|
||
domain: social-governance
|
||
weight: 0.30 # approximate
|
||
direction: lower-better
|
||
source: Internal unrest seed (cross-source signals + UCDP)
|
||
seriesId: unrestCount + sqrt(fatalities)
|
||
seriesUrl: Internal seed
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2026
|
||
license: Mixed
|
||
mechanismTestRationale: Active unrest events measure current social-cohesion stress.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: borderSecuritySubs
|
||
dimension: borderSecurity
|
||
domain: social-governance
|
||
weight: TBD
|
||
direction: composite
|
||
source: Composite (UNHCR displacement + UCDP conflict + governance)
|
||
seriesId: Derived
|
||
seriesUrl: Internal seed
|
||
coveragePct: 0.80
|
||
lastObservedYear: 2026
|
||
license: Mixed
|
||
mechanismTestRationale: Border-security composite captures cross-border shock transmission exposure.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Inherits displacement host-vs-sending bias from socialCohesion. PR 4 fix.
|
||
|
||
- indicator: rsfPressFreedom
|
||
dimension: informationCognitive
|
||
domain: social-governance
|
||
weight: TBD
|
||
direction: higher-better
|
||
source: Reporters Sans Frontieres
|
||
seriesId: Press Freedom Index
|
||
seriesUrl: https://rsf.org/en/index
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2024
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: Press freedom proxies quality of information-shock response and independent verification capacity.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: languageNormalizedSocialVelocity
|
||
dimension: informationCognitive
|
||
domain: social-governance
|
||
weight: TBD
|
||
direction: composite
|
||
source: Reddit + cross-source + internal language-coverage-weighting
|
||
seriesId: Internal
|
||
seriesUrl: Internal seed
|
||
coveragePct: 0.95
|
||
lastObservedYear: 2026
|
||
license: Mixed
|
||
mechanismTestRationale: Language-normalized social-information velocity measures information-shock propagation speed adjusted for source-density bias.
|
||
constructStatus: observed-mechanism
|
||
|
||
# HEALTH-FOOD DOMAIN (weight 0.13) ---------------------------------------
|
||
|
||
- indicator: whoHealthExpenditure
|
||
dimension: healthPublicService
|
||
domain: health-food
|
||
weight: TBD
|
||
direction: higher-better
|
||
source: WHO Global Health Observatory
|
||
seriesId: Current health expenditure per capita, PPP
|
||
seriesUrl: https://www.who.int/data/gho
|
||
coveragePct: 0.95
|
||
lastObservedYear: 2022
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: Health expenditure per capita proxies health-system capacity. FAILS the strict mechanism test — it measures SPEND, not CAPACITY. Should be replaced with surge-capacity / bed-density / ICU-density threshold signal.
|
||
constructStatus: wealth-proxy
|
||
reviewNotes: PR 4 §4.9 replacement.
|
||
|
||
- indicator: ipcPhase
|
||
dimension: foodWater
|
||
domain: health-food
|
||
weight: 0.15
|
||
direction: lower-better
|
||
source: FAO IPC (Integrated Food Security Phase Classification)
|
||
seriesId: IPC Phase (1-5)
|
||
seriesUrl: https://www.ipcinfo.org/
|
||
coveragePct: 0.40 # IPC covers acutely-affected countries
|
||
lastObservedYear: 2025
|
||
license: Open
|
||
mechanismTestRationale: IPC phase directly measures current food-security-crisis severity.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Coverage is inherently partial — IPC only tracks countries with current/imminent food crises. Imputed to a resilient-default for non-tracked countries.
|
||
|
||
- indicator: aquastatWaterStress
|
||
dimension: foodWater
|
||
domain: health-food
|
||
weight: 0.25
|
||
direction: lower-better
|
||
source: FAO AQUASTAT
|
||
seriesId: Water stress (withdrawal / renewable resources)
|
||
seriesUrl: https://www.fao.org/aquastat/
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2020
|
||
license: Open
|
||
mechanismTestRationale: Water stress directly measures water-supply-shock exposure.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: aquastatWaterAvailability
|
||
dimension: foodWater
|
||
domain: health-food
|
||
weight: 0.15
|
||
direction: higher-better
|
||
source: FAO AQUASTAT
|
||
seriesId: Water availability (m³/capita)
|
||
seriesUrl: https://www.fao.org/aquastat/
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2020
|
||
license: Open
|
||
mechanismTestRationale: Water availability per capita proxies baseline water-security-shock buffer.
|
||
constructStatus: observed-mechanism
|
||
|
||
# RECOVERY DOMAIN (weight 0.25) ------------------------------------------
|
||
|
||
- indicator: recoveryGovRevenue
|
||
dimension: fiscalSpace
|
||
domain: recovery
|
||
weight: 0.40
|
||
direction: higher-better
|
||
source: IMF
|
||
seriesId: GGR_G01_GDP_PT
|
||
seriesUrl: https://www.imf.org/external/datamapper/GGR_G01_GDP_PT@FM/
|
||
coveragePct: 0.90
|
||
lastObservedYear: 2024
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: Government revenue % GDP for recovery scenarios — policy-response fiscal headroom.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Duplicate with macroFiscal.govRevenuePct. PR 4 may de-duplicate.
|
||
|
||
- indicator: recoveryFiscalBalance
|
||
dimension: fiscalSpace
|
||
domain: recovery
|
||
weight: 0.30
|
||
direction: higher-better
|
||
source: IMF
|
||
seriesId: GGXCNL_G01_GDP_PT
|
||
seriesUrl: https://www.imf.org/external/datamapper/GGXCNL_G01_GDP_PT@FM/
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2024
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: General government net lending/borrowing as % of GDP — direct fiscal-response-capacity signal.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: recoveryDebtToGdp
|
||
dimension: fiscalSpace
|
||
domain: recovery
|
||
weight: 0.30
|
||
direction: lower-better
|
||
source: IMF
|
||
seriesId: GGXWDG_NGDP_PT
|
||
seriesUrl: https://www.imf.org/external/datamapper/GGXWDG_NGDP_PT@FM/
|
||
coveragePct: 0.90
|
||
lastObservedYear: 2024
|
||
license: Proprietary-with-fair-use
|
||
mechanismTestRationale: General government gross debt to GDP — fiscal-stress cushion.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Goalpost 0-150 is too linear; Japan at 260% (mostly domestic, yen-denominated) scores 0 despite weak real fiscal-stress risk. PR 4 §4.4 adds holder-composition modifier.
|
||
|
||
- indicator: recoveryReserveMonths
|
||
dimension: reserveAdequacy
|
||
domain: recovery
|
||
weight: 1.00
|
||
direction: higher-better
|
||
source: World Bank
|
||
seriesId: FI.RES.TOTL.MO
|
||
seriesUrl: https://data.worldbank.org/indicator/FI.RES.TOTL.MO
|
||
coveragePct: 0.85
|
||
lastObservedYear: 2023
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: Central-bank reserves in months of imports — immediate external-liquidity cushion.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: PR 2 §3.4 renames to liquidReserveAdequacy; new dimension sovereignFiscalBuffer added.
|
||
|
||
- indicator: recoveryDebtToReserves
|
||
dimension: externalDebtCoverage
|
||
domain: recovery
|
||
weight: 1.00
|
||
direction: lower-better
|
||
source: World Bank
|
||
seriesId: DT.DOD.DSTC.CD / FI.RES.TOTL.CD
|
||
seriesUrl: https://data.worldbank.org/indicator/DT.DOD.DSTC.CD
|
||
coveragePct: 0.75
|
||
lastObservedYear: 2023
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: Short-term external debt to reserves ratio — rollover-shock exposure.
|
||
constructStatus: dead-signal
|
||
reviewNotes: Saturates at 100 for every country in the 9-country probe (goalpost 0-5 is too generous). PR 3 re-goalpost.
|
||
|
||
- indicator: recoveryImportHhi
|
||
dimension: importConcentration
|
||
domain: recovery
|
||
weight: 1.00
|
||
direction: lower-better
|
||
source: UN Comtrade
|
||
seriesId: HS2 bilateral Herfindahl-Hirschman Index
|
||
seriesUrl: https://comtrade.un.org/
|
||
coveragePct: 0.70
|
||
lastObservedYear: 2023
|
||
license: Open
|
||
mechanismTestRationale: Import-partner concentration (HHI) — supplier-shock exposure.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Coverage gap for UAE and small-island states; PR 1+ audit.
|
||
|
||
- indicator: recoveryWgiContinuity
|
||
dimension: stateContinuity
|
||
domain: recovery
|
||
weight: 0.50
|
||
direction: higher-better
|
||
source: World Bank WGI
|
||
seriesId: Mean of WGI subscores
|
||
seriesUrl: https://info.worldbank.org/governance/wgi/
|
||
coveragePct: 0.98
|
||
lastObservedYear: 2023
|
||
license: CC-BY-4.0
|
||
mechanismTestRationale: WGI composite as state-continuity proxy — institutional durability through shocks.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Duplicate with governanceInstitutional.wgiComposite. PR 4 de-duplicate.
|
||
|
||
- indicator: recoveryConflictPressure
|
||
dimension: stateContinuity
|
||
domain: recovery
|
||
weight: 0.30
|
||
direction: lower-better
|
||
source: UCDP
|
||
seriesId: Armed conflict events / fatalities
|
||
seriesUrl: https://ucdp.uu.se/
|
||
coveragePct: 0.95
|
||
lastObservedYear: 2026
|
||
license: Open
|
||
mechanismTestRationale: UCDP conflict intensity — direct state-continuity-shock metric.
|
||
constructStatus: observed-mechanism
|
||
|
||
- indicator: recoveryDisplacementVelocity
|
||
dimension: stateContinuity
|
||
domain: recovery
|
||
weight: 0.20
|
||
direction: lower-better
|
||
source: UNHCR
|
||
seriesId: Displacement as share of population
|
||
seriesUrl: https://data.unhcr.org/
|
||
coveragePct: 0.95
|
||
lastObservedYear: 2025
|
||
license: Open
|
||
mechanismTestRationale: Displacement velocity — population-scale state-continuity stress.
|
||
constructStatus: observed-mechanism
|
||
reviewNotes: Inherits host-vs-sending bias. PR 4 §4.2 fix.
|
||
|
||
- indicator: recoveryFuelStockDays
|
||
dimension: fuelStockDays
|
||
domain: recovery
|
||
weight: 1.00
|
||
direction: higher-better
|
||
source: IEA / EIA
|
||
seriesId: Days of fuel stock cover
|
||
seriesUrl: https://www.iea.org/data-and-statistics/data-tools/oil-stocks-of-iea-countries
|
||
coveragePct: 0.30 # imputed for every country
|
||
lastObservedYear: null
|
||
license: Proprietary
|
||
mechanismTestRationale: Days of fuel stock for import-shock coverage. IEA rules bind only net importers; net exporters get no observed value.
|
||
constructStatus: imputed-floor
|
||
reviewNotes: PR 3 §3.5 retires from core score (permanent). Enrichment-only if IEA/EIA connector ever wires.
|
||
|
||
# PENDING ADDITIONS FOR PR 1+ --------------------------------------------
|
||
|
||
# PR 1 additions (not yet in the scorer):
|
||
# - powerLossesPct → EG.ELC.LOSS.ZS (transmission+distribution losses, lower-better)
|
||
# - reserveMarginPct → IEA electricity balance (generation reserve margin, higher-better)
|
||
# - accessToElectricityPct → EG.ELC.ACCS.ZS (threshold/saturating, moved to infrastructure domain)
|
||
# - importedFossilDependence → EG.IMP.CONS.ZS × fossil-generation-share (Option B power-system framing)
|
||
# - lowCarbonGenerationShare → EG.ELC.NUCL.ZS + EG.ELC.RNEW.ZS (higher-better)
|
||
|
||
# PR 2 additions (not yet in the scorer):
|
||
# - liquidReserveAdequacy → FI.RES.TOTL.MO (rename of current reserveMonths)
|
||
# - sovereignFiscalBuffer → IFSWF + official disclosures × access × liquidity × transparency
|
||
|
||
# PR 3 replacements (not yet in the scorer):
|
||
# - realInterestRate → FR.INR.RINR (replaces currencyExternal for non-BIS countries)
|