fix(resilience): tighten debtSustainabilityGap inflation cap 25% → 10%#3838
Conversation
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.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR tightens the inflation cap on the
Confidence Score: 4/5Safe 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 Important Files Changed
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]
|
Summary
Follow-up to PR #3669 (
debtSustainabilityGapindicator). 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):
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
weightedBlendinscoreFiscalSpaceredistributes weight to the remaining three.Expected new top/bottom 5 (verified post-deploy)
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:
dapproaches 0, ther = interest/debtterm 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 --noEmitcleannpx biome lintclean on touched filesscripts/compare-resilience-current-vs-proposed.mjsafter next seeder cron tickFiles
scripts/seed-recovery-fiscal-space.mjsINFLATION_GAP_CAP_PCT = 25 → 10; expanded comment with WWII analog + post-PR-#3669 motivationserver/worldmonitor/resilience/v1/_indicator-registry.tscoverage: 150 → 140docs/methodology/country-resilience-index.mdxdocs/methodology/indicator-sources.yamltests/seed-recovery-fiscal-space.test.mts25 → 10with provenance commentPost-Deploy Monitoring & Validation
Pre-merge
Post-merge (1-2 cron ticks, ~30-60 days for monthly seeder)
resilience:recovery:fiscal-space:v1, verify ≥100 countries withdebtSustainabilityGapPct(expected ~160-165)debtSustainabilityGapPct: nullwhilefiscalBalancePct/govRevenuePct/debtToGdpPctremain populatedRollback
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
realGdpfield in economic:imf:growth:v1 #3837 (realGdpambiguous-unit cleanup) — independent, no dependency