Skip to content

fix(resilience): tighten debtSustainabilityGap inflation cap 25% → 10%#3838

Merged
koala73 merged 1 commit into
mainfrom
feat/tighten-inflation-cap-10pct
May 19, 2026
Merged

fix(resilience): tighten debtSustainabilityGap inflation cap 25% → 10%#3838
koala73 merged 1 commit into
mainfrom
feat/tighten-inflation-cap-10pct

Conversation

@koala73

@koala73 koala73 commented May 19, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to PR #3669 (debtSustainabilityGap indicator). Tightens the inflation cap on the gap formula from 25% → 10%.

Why

Post-deploy verification on PR #3669 surfaced Lebanon scoring #1 globally on this indicator at 14.6% CPI. The formula was working correctly: Lebanon's 18.6% nominal GDP growth mechanically erodes its 139% debt-to-GDP ratio, producing a +25.8pt gap. But that interpretation only holds when paired with capital controls, financial repression, deep domestic bond markets, and stable institutions — none of which apply to Lebanon's actual situation.

The original 25% cap was a guess (round number above Turkey's ~28% CPI). 10% has a principled basis: above ~10% CPI, inflation expectations un-anchor and the inflation-tax mechanism becomes part of a debt CRISIS rather than a debt RESOLUTION. The IMF DSA framework's own implicit scope is sub-10% inflation regimes; this PR aligns our cap with that.

Impact

Countries that drop from debtSustainabilityGap (15 of 176)

Listed by current inflation (descending):

Country CPI 2026 Current gap
Malawi 24.4% +7.14
Haiti 23.5% +1.24
Bolivia 20.7% +5.89
Myanmar 19.0% +4.98
Nigeria 16.0% +1.64
Lebanon 14.6% +25.79 ← motivating case
Burundi 14.5% +3.51
South Sudan 14.0% +10.76
Zambia 13.9% +10.46
Egypt 13.2% +3.35
Angola 12.9% +4.97
Suriname 11.8% +8.10
Ethiopia 11.8% +5.77
Kazakhstan 10.7% +2.11
Kyrgyzstan 10.6% +2.07

Coverage: 176 → 161 with the cap (still well above the 100-country validate floor).

Fiscal-3 indicators (revenue / fiscal balance / debt-to-GDP) continue to score every dropped country. Only the fourth sub-score (gap) goes null, and weightedBlend in scoreFiscalSpace redistributes weight to the remaining three.

Expected new top/bottom 5 (verified post-deploy)

Country Gap Plausibility
Best Norway +8.82 ✅ Low debt, strong primary surplus
Greece +7.69 ✅ Post-bailout stabilization
Oman +6.87 ✅ Oil surplus, debt declining
UAE +6.63 ✅ Diversification + surplus
Cyprus +5.40 ✅ Post-crisis recovery
Worst Ukraine −8.77 ✅ Wartime fiscal pressure
Bahrain −7.67 ✅ Oil revenue declining vs debt
Algeria −6.42 ✅ Hydrocarbon dependency
Botswana −6.15 ✅ Mining-cycle pressure
Qatar −5.84 ✅ Gas-revenue commitments

All defensible — these are countries where the gap genuinely surfaces a real debt-trajectory concern, not a formula artifact.

What this PR does NOT address

Post-deploy analysis revealed three failure modes in the gap indicator's extremes, not one. This PR addresses only the inflation-tax distortion (Lebanon-class). The other two:

  • Small-economy noise: Timor-Leste gap=−51, Kiribati gap=−16. IMF data for tiny economies (<$20B GDP) is sparse and the gap formula amplifies that noise.
  • Low-debt instability: Brunei gap=−10.6 at debt=1% GDP. When d approaches 0, the r = interest/debt term becomes numerically unstable.

These are real but separable. Addressing them would mean either a GDP-size filter (would drop ~50 countries; significant scope reduction) or rebuilding the formula's tail behavior. Out of scope for this PR.

Test plan

  • npx tsc --noEmit clean
  • npx biome lint clean on touched files
  • 149/149 affected resilience tests pass (formula unit, seeder contract, scorer integration, tiering invariants, baseline assertions)
  • No fixture baseline shifts — NO/US fiscal-space fixtures use inflation 2.5% / 3% (well under both caps); same numbers
  • Indicator-registry tier=enrichment, coverage 150 → 140 (closer to realistic post-cap)
  • Manual: re-run scripts/compare-resilience-current-vs-proposed.mjs after next seeder cron tick

Files

File Change
scripts/seed-recovery-fiscal-space.mjs INFLATION_GAP_CAP_PCT = 25 → 10; expanded comment with WWII analog + post-PR-#3669 motivation
server/worldmonitor/resilience/v1/_indicator-registry.ts Description updated; tier comment expanded; coverage: 150 → 140
docs/methodology/country-resilience-index.mdx Prose update (cap value + reasoning)
docs/methodology/indicator-sources.yaml reviewNotes update
tests/seed-recovery-fiscal-space.test.mts Cap-pin assertion 25 → 10 with provenance comment

Post-Deploy Monitoring & Validation

Pre-merge

  • All affected tests green
  • Scope changes documented at all 5 touch points

Post-merge (1-2 cron ticks, ~30-60 days for monthly seeder)

  • Direct Redis GET on resilience:recovery:fiscal-space:v1, verify ≥100 countries with debtSustainabilityGapPct (expected ~160-165)
  • Verify the 15 listed countries above all now have debtSustainabilityGapPct: null while fiscalBalancePct / govRevenuePct / debtToGdpPct remain populated
  • Eyeball top-5 / bottom-5 — should match the predicted lists above

Rollback

  • Revert the PR. Next seeder tick repopulates with the 25% cap. Dropped countries reappear in the gap indicator. Zero data loss.

Risk

Low. Single-parameter tuning, no formula change, no schema change, no validation-floor change. The schemaVersion stays at 2 because the field shape is unchanged — only the fraction of countries with non-null values shifts.

Related

Follow-up to PR #3669 motivated by post-deploy verification: with the
original 25% cap, Lebanon scored #1 globally on this indicator at 14.6%
CPI — the formula's nominal-growth term (18.6% nominal GDP growth in
Lebanon's 2026 forecast) mechanically erodes its 139% debt-to-GDP ratio,
producing a +25.8pt gap that masks the country's actual fiscal/banking
fragility.

The 25% cap was a guess (round number above Turkey's 28%). 10% has a
principled basis: above ~10% CPI, inflation expectations un-anchor and
the inflation-tax mechanism becomes part of a debt CRISIS rather than a
debt RESOLUTION. Post-WWII US/UK/France did successfully inflate away
war debts at 10-20% — but only with capital controls, financial
repression, deep domestic bond markets, and stable institutions. None
of those apply to Lebanon. The IMF DSA framework's own implicit scope
is sub-10% inflation regimes; this PR aligns our cap with that.

Impact:
- 15 countries currently with gap drop to null: Lebanon, Egypt, Nigeria,
  Kazakhstan, Bolivia, Ethiopia, Angola, Myanmar, Suriname, Haiti,
  Malawi, Burundi, South Sudan, Zambia, Kyrgyzstan.
- Coverage: 176 → 161 (still well above the 100-country validate floor).
- New best-5 (anticipated, will verify post-deploy): Norway, Greece,
  Oman, UAE, Cyprus — all defensible.
- New worst-5: Ukraine, Bahrain, Algeria, Botswana, Qatar — also
  defensible (wartime, oil-dependent, etc.).
- Fiscal-3 indicators still score every dropped country; only the
  fourth sub-score (gap) goes null. weightedBlend redistributes weight.

Diagnostic context (NOT addressed in this PR — separate concerns):
The same post-deploy analysis revealed two other failure modes that
this PR does NOT fix: small-economy noise (Timor-Leste gap=-50,
Kiribati gap=-16 — IMF data sparse for tiny economies amplifies into
extreme gap values) and low-debt instability (Brunei gap=-10.6 at
debt=1%). Those are arguably separate issues; this PR scope is just
the inflation-tax distortion (Lebanon-class), which is what users
would most obviously misinterpret.

Files:
- scripts/seed-recovery-fiscal-space.mjs: INFLATION_GAP_CAP_PCT 25 → 10,
  expanded comment with the rationale + WWII analog
- server/worldmonitor/resilience/v1/_indicator-registry.ts: updated
  description + tier comment; coverage 150 → 140 (realistic post-cap)
- docs/methodology/country-resilience-index.mdx: prose update
- docs/methodology/indicator-sources.yaml: reviewNotes update
- tests/seed-recovery-fiscal-space.test.mts: cap-pin assertion 25 → 10
  with provenance comment

Tests: 149/149 affected resilience tests pass.
Lint + tsc clean.

No change to: formula, goalposts, weights, validation floors, schemaVersion.
Backward-compatible — the next seeder run repopulates the canonical blob
with the new cap; old scorer code reads the new blob fine.
@mintlify

mintlify Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
WorldMonitor 🟢 Ready View Preview May 19, 2026, 10:33 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@vercel

vercel Bot commented May 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview, Comment May 19, 2026 10:34am

Request Review

@greptile-apps

greptile-apps Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR tightens the inflation cap on the debtSustainabilityGap formula from 25% to 10%, motivated by Lebanon (14.6% CPI) incorrectly ranking #1 globally on the indicator after the gap was introduced in PR #3669.

  • INFLATION_GAP_CAP_PCT is updated from 25 to 10 in the seeder, the pin assertion in the test is updated to match, and the coverage field in the indicator registry is reduced from 150 to 140 to reflect the additional ~15 countries newly dropped.
  • All five touch points (seeder, registry, two doc files, one test file) are updated consistently, the gap validation floor (gap: 100) is left unchanged and remains well below the expected ~140-country post-cap coverage, and the schemaVersion is correctly left at 2 because the field shape is unchanged.

Confidence Score: 4/5

Safe to merge — a single constant changes from 25 to 10 with no schema, formula, or validation-floor modifications; the only observable effect is 15 additional countries producing gap=null, and fiscal-3 scoring continues for all of them.

The change is minimal and all five touch points are updated consistently. The one gap is that the test suite exercises the inflation cap only at 200% (Argentina), which was above both the old and new threshold — the motivating 14.6%-CPI Lebanon case has no dedicated regression test, so a future reader cannot immediately tell from the tests whether the 10% boundary is doing its job.

tests/seed-recovery-fiscal-space.test.mts — the inflation > cap test case does not exercise the newly-excluded 10–25% CPI range.

Important Files Changed

Filename Overview
scripts/seed-recovery-fiscal-space.mjs Core constant changed from 25 to 10; comment expanded with principled rationale; formula logic is unchanged and correctly uses the exported constant.
tests/seed-recovery-fiscal-space.test.mts Pin assertion updated 25→10; the Argentina (200% CPI) test still covers the above-cap path but no test exercises the newly-excluded 10–25% range that motivated this change.
server/worldmonitor/resilience/v1/_indicator-registry.ts Description and tier comment updated; coverage reduced 150→140; all changes are consistent with the seeder and documentation.
docs/methodology/country-resilience-index.mdx Prose updated to reflect 10% cap and narrowed country list; coverage estimate updated 150→140; consistent with other files.
docs/methodology/indicator-sources.yaml reviewNotes updated with new threshold and motivation; date and PR reference are hard-coded but intentional as audit trail.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Country fiscal data\ndebt, pb, fb, realG, infl] --> B{debt ≤ 0\nor null?}
    B -->|yes| N[gap = null\nfiscal-3 still scored]
    B -->|no| C{infl >\nINFLATION_GAP_CAP_PCT\n10%}
    C -->|yes – inflation-tax regime\nArgentina / Lebanon / Egypt …| N
    C -->|no| D{pb / fb / realG\nnull?}
    D -->|yes| N
    D -->|no| E[Compute gap\ng = 1+realG/100 × 1+infl/100 − 1\nr = max0, pb−fb ÷ debt\npb* = r−g ÷ 1+g × debt\ngap = pb − pb*]
    E --> F[gap emitted as\ndebtSustainabilityGapPct]
    F --> G[Scorer normalises\ngoalposts −5 / +3\nweight 0.35]
    N --> H[weightedBlend redistributes\n0.35 weight across\nfiscal-3 sub-scores]
Loading

Comments Outside Diff (1)

  1. tests/seed-recovery-fiscal-space.test.mts, line 159-174 (link)

    P2 Missing regression test for the motivating case

    The existing inflation > cap test uses Argentina at 200% CPI, which was above both the old 25% cap and the new 10% cap. It passes regardless of the threshold change, so it does not actually verify the new boundary behavior. A country at, say, 14.6% CPI (Lebanon's figure) or 12% CPI would produce gap=null under the new 10% cap but gap≠null under the old 25% cap — that's the exact behavioral change this PR introduces, and it has no dedicated test. The pin assertion (INFLATION_GAP_CAP_PCT === 10) guarantees the constant is right, but a companion case like inflation: { LBN: { [Y]: 14.6 } } asserting debtSustainabilityGapPct === null would lock in the motivating regression and make the intent self-documenting.

Reviews (1): Last reviewed commit: "fix(resilience): tighten debtSustainabil..." | Re-trigger Greptile

@koala73
koala73 merged commit 85220ae into main May 19, 2026
12 checks passed
@koala73
koala73 deleted the feat/tighten-inflation-cap-10pct branch June 25, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant