Files
worldmonitor/tests/swf-classification-manifest.test.mjs
Elie Habib abdcdb581f feat(resilience): SWF manifest expansion + KIA split + new schema fields (#3391)
* feat(resilience): SWF manifest expansion + KIA split + new schema fields

Phase 1 of plan 2026-04-25-001 (Codex-approved round 5). Manifest-only
data correction; no construct change, no cache prefix bump.

Schema additions (loader-validated, misplacement-rejected):
- top-level: aum_usd, aum_year, aum_verified (primary-source AUM)
- under classification: aum_pct_of_audited (fraction multiplier),
  excluded_overlaps_with_reserves (boolean; documentation-only)

Manifest expansion (13 → 21 funds, 6 → 13 countries):
- UAE: +ICD ($320B verified), +ADQ ($199B verified), +EIA (unverified —
  loaded for documentation, excluded from scoring per data-integrity rule)
- KW: kia split into kia-grf (5%, access=0.9) + kia-fgf (95%,
  access=0.20). Corrects ~18× over-statement of crisis-deployable
  Kuwait sovereign wealth (audit found combined-AUM × 0.7 access
  applied $750B as "deployable" against ~$15B actual GRF stabilization
  capacity).
- CN: +CIC ($1.35T), +NSSF ($400B, statutorily-gated 0.20 tier),
  +SAFE-IC ($417B, excluded — overlaps SAFE FX reserves)
- HK: +HKMA-EF ($498B, excluded — overlaps HKMA reserves)
- KR: +KIC ($182B, IFSWF full member)
- AU: +Future Fund ($192B, pension-locked)
- OM: +OIA ($50B, IFSWF member)
- BH: +Mumtalakat ($19B)
- TL: +Petroleum Fund ($22B, GPFG-style high-transparency)

Re-audits (Phase 1E):
- ADIA access 0.3 → 0.4 (rubric flagged; ruler-discretionary deployment
  empirically demonstrated)
- Mubadala access 0.4 → 0.5 (rubric flagged); transparency 0.6 → 0.7
  (LM=10 + IFSWF full member alignment)

Rubric (docs/methodology/swf-classification-rubric.md):
- New "Statutorily-gated long-horizon" 0.20 access tier added between
  0.1 (sanctions/frozen) and 0.3 (intergenerational/ruler-discretionary).
  Anchored by KIA-FGF (Decree 106 of 1976; Council-of-Ministers + Emir
  decree gate; crossed once in extremis during COVID).

Seeder:
- Two new pure helpers: shouldSkipFundForBuffer (excluded/unverified
  decision) and applyAumPctOfAudited (sleeve fraction multiplier)
- Manifest-AUM bypass: if aum_verified=true AND aum_usd present,
  use that value directly (skip Wikipedia)
- Skip funds with excluded_overlaps_with_reserves=true (no
  double-counting against reserveAdequacy / liquidReserveAdequacy)
- Skip funds with aum_verified=false (load for documentation only)

Tests (+25 net):
- 15 schema-extension tests (misplacement rejection, value-range gates,
  rationale-pairing coherence, backward-compat with pre-PR entries)
- 10 helper tests (shouldSkipFundForBuffer + applyAumPctOfAudited
  predicates and arithmetic; KIA-GRF + KIA-FGF sum equals combined AUM)
- Existing manifest test updated for the kia → kia-grf+kia-fgf split

Full suite: 6,940 tests pass (+50 net), typecheck clean, no new lint.

Predicted ranking deltas (informational, NOT acceptance criteria per
plan §"Hard non-goals"):
- AE sovFiscBuf likely 39 → 47-49 (Phase 1A + 1E)
- KW sovFiscBuf likely 98 → 53-57 (Phase 1B)
- CN, HK (excluded), KR, AU acquire newly-defined sovFiscBuf scores
- GCC ordering shifts toward QA > KW > AE; AE-KW gap likely 6 → ~3-4

Real outcome will be measured post-deploy via cohort audit per plan
§Phase 4.

* fix(resilience): completeness denominator excludes documentation-only funds

PR-3391 review (P1 catch): the per-country `expectedFunds` denominator
counted ALL manifest entries (`funds.length`) including those skipped
from buffer scoring by design — `excluded_overlaps_with_reserves: true`
(SAFE-IC, HKMA-EF) and `aum_verified: false` (EIA). Result: countries
with mixed scorable + non-scorable rosters showed `completeness < 1.0`
even when every scorable fund matched. UAE (4 scorable + EIA) would
show 0.8; CN (CIC + NSSF + SAFE-IC excluded) would show 0.67. The
downstream scorer then derated those countries' coverage based on a
fake-partial signal.

Three call sites all carried the same bug:
- per-country `expectedFunds` in fetchSovereignWealth main loop
- `expectedFundsTotal` + `expectedCountries` in buildCoverageSummary
- `countManifestFundsForCountry` (missing-country path)

All three now filter via `shouldSkipFundForBuffer` to count only
scorable manifest entries. Documentation-only funds neither expected
nor matched — they don't appear in the ratio at all.

Tests added (+4):
- AE complete with all 4 scorable matched (EIA documented but excluded)
- CN complete with CIC + NSSF matched (SAFE-IC documented but excluded)
- Missing-country path returns scorable count not raw manifest count
- Country with ONLY documentation-only entries excluded from expectedCountries

Full suite: 6,944 tests pass (+4 net), typecheck clean.

* fix(resilience): address Greptile P2s on PR #3391 manifest

Three review findings, all in the manifest YAML:

1. **KIA-GRF access 0.9 → 0.7** (rubric alignment): GRF deployment
   requires active Council-of-Ministers authorization (2020 COVID
   precedent demonstrates this), not rule-triggered automatic
   deployment. The rubric's 0.9 tier ("Pure automatic stabilization")
   reserved for funds where political authorization is post-hoc /
   symbolic (Chile ESSF candidate). KIA-GRF correctly fits 0.7
   ("Explicit stabilization with rule") — the same tier the
   pre-split combined-KIA was assigned. Updated rationale clarifies
   the tier choice. Rubric's 0.7 precedent column already lists
   "KIA General Reserve Fund" — now consistent with the manifest.

2. **Duplicate `# ── Australia ──` header before Oman** (copy-paste
   artifact): removed the orphaned header at the Oman section;
   added proper `# ── Australia ──` header above the Future Fund
   entry where it actually belongs (after Timor-Leste).

3. **NSSF `aum_pct_of_audited: 1.0` removed** (no-op): a multiplier
   of 1.0 is identity. The schema field is OPTIONAL and only meant
   for fund-of-funds split entries (e.g. KIA-GRF/FGF). Setting it
   to 1.0 forced the loader to require an `aum_pct_of_audited`
   rationale paragraph with no computational benefit. Both the
   field and the paragraph are now removed; NSSF remains a single-
   sleeve entry that scores its full audited AUM.

Full suite: 6,944 tests pass, typecheck clean.
2026-04-25 12:02:48 +04:00

176 lines
7.1 KiB
JavaScript

import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import {
groupFundsByCountry,
loadSwfManifest,
validateManifest,
} from '../scripts/shared/swf-manifest-loader.mjs';
// Validate the shipped SWF classification manifest (PR 2 §3.4). This
// test is the only CI gate on the YAML: any schema violation (missing
// rationale, out-of-range score, duplicate fund identifier, missing
// source citation) fails the build before a malformed manifest can
// reach the seeder. Adding a new fund or adjusting a score must run
// this test locally.
//
// The manifest is reviewer-approved metadata, not auto-generated, so
// the test intentionally prefers loud assertion failures over silent
// coercion. Downstream consumers (seeder, future scorer, methodology
// lint) can rely on the returned object shape without re-validating.
describe('SWF classification manifest — shipped YAML', () => {
const manifest = loadSwfManifest();
it('parses with a recognized schema version', () => {
assert.equal(manifest.manifestVersion, 1, 'bump both YAML manifest_version AND this assertion when evolving the schema');
});
it('records an external-review status (PENDING until sign-off)', () => {
assert.ok(
manifest.externalReviewStatus === 'PENDING' || manifest.externalReviewStatus === 'REVIEWED',
`external_review_status must be PENDING or REVIEWED, got ${manifest.externalReviewStatus}`,
);
});
it('lists the first-release set of funds from plan §3.4 (KIA split per Phase 1B)', () => {
// Phase 1B (Plan 2026-04-25-001) split the original `KW:kia` entry
// into `KW:kia-grf` and `KW:kia-fgf` to correctly attribute GRF's
// 0.9 stabilization access to its ~5% sleeve and FGF's 0.20
// statutorily-gated access to the remaining ~95%. Both identifiers
// are now required.
const expected = new Set([
'NO:gpfg',
'AE:adia',
'AE:mubadala',
'SA:pif',
'KW:kia-grf',
'KW:kia-fgf',
'QA:qia',
'SG:gic',
'SG:temasek',
]);
const actual = new Set(manifest.funds.map((f) => `${f.country}:${f.fund}`));
for (const required of expected) {
assert.ok(actual.has(required), `plan §3.4 + Phase 1B required fund missing from manifest: ${required}`);
}
});
it('Phase 1 (Plan 2026-04-25-001) expansion adds 12 new funds across 7 new + extended countries', () => {
// Phase 1 expansion: UAE adds ICD/ADQ/EIA (3); KW splits kia → kia-grf+kia-fgf
// (1 net since kia is dropped); CN adds CIC/NSSF/SAFE-IC (3); HK adds HKMA-EF
// (1); KR adds KIC (1); AU adds Future Fund (1); OM adds OIA (1); BH adds
// Mumtalakat (1); TL adds Petroleum Fund (1). Net new identifiers: 12 over
// the original 8 + 1 from KIA split. Manifest total ≥ 20.
const required = new Set([
'AE:icd', 'AE:adq', 'AE:eia',
'CN:cic', 'CN:nssf', 'CN:safe-ic',
'HK:hkma-ef',
'KR:kic',
'AU:future-fund',
'OM:oia',
'BH:mumtalakat',
'TL:petroleum-fund',
]);
const actual = new Set(manifest.funds.map((f) => `${f.country}:${f.fund}`));
for (const r of required) {
assert.ok(actual.has(r), `Phase 1 expansion fund missing from manifest: ${r}`);
}
});
it('classification components are all in [0, 1]', () => {
for (const fund of manifest.funds) {
const { access, liquidity, transparency } = fund.classification;
assert.ok(access >= 0 && access <= 1, `${fund.country}:${fund.fund} access out of range: ${access}`);
assert.ok(liquidity >= 0 && liquidity <= 1, `${fund.country}:${fund.fund} liquidity out of range: ${liquidity}`);
assert.ok(transparency >= 0 && transparency <= 1, `${fund.country}:${fund.fund} transparency out of range: ${transparency}`);
}
});
it('every fund carries non-empty rationale strings and source citations', () => {
for (const fund of manifest.funds) {
assert.ok(fund.rationale.access.length > 20, `${fund.country}:${fund.fund} rationale.access too short`);
assert.ok(fund.rationale.liquidity.length > 20, `${fund.country}:${fund.fund} rationale.liquidity too short`);
assert.ok(fund.rationale.transparency.length > 20, `${fund.country}:${fund.fund} rationale.transparency too short`);
assert.ok(fund.sources.length > 0, `${fund.country}:${fund.fund} has no sources cited`);
}
});
it('groupFundsByCountry handles multi-fund countries (AE, SG)', () => {
const byCountry = groupFundsByCountry(manifest);
assert.ok((byCountry.get('AE') ?? []).length >= 2, 'AE should have ADIA + Mubadala at minimum');
assert.ok((byCountry.get('SG') ?? []).length >= 2, 'SG should have GIC + Temasek at minimum');
assert.ok((byCountry.get('NO') ?? []).length >= 1, 'NO should have GPFG');
});
});
describe('validateManifest — schema enforcement', () => {
const minimalValid = () => ({
manifest_version: 1,
last_reviewed: '2026-04-23',
external_review_status: 'PENDING',
funds: [
{
country: 'NO',
fund: 'gpfg',
display_name: 'Government Pension Fund Global',
classification: { access: 0.6, liquidity: 1.0, transparency: 1.0 },
rationale: {
access: 'Norwegian fiscal rule caps annual withdrawal at expected real return.',
liquidity: '100% publicly listed equities + fixed income per NBIM 2025 report.',
transparency: 'Full audited AUM, daily returns disclosed. IFSWF full compliance.',
},
sources: ['https://www.nbim.no/en/the-fund/'],
},
],
});
it('accepts a minimal-valid manifest', () => {
const out = validateManifest(minimalValid());
assert.equal(out.funds.length, 1);
assert.equal(out.funds[0].country, 'NO');
});
it('rejects out-of-range classification scores', () => {
const bad = minimalValid();
bad.funds[0].classification.access = 1.5;
assert.throws(() => validateManifest(bad), /access.*expected number in \[0, 1\]/);
});
it('rejects non-ISO2 country codes', () => {
const bad = minimalValid();
bad.funds[0].country = 'NOR';
assert.throws(() => validateManifest(bad), /expected ISO-3166-1 alpha-2/);
});
it('rejects missing rationale strings', () => {
const bad = minimalValid();
bad.funds[0].rationale.access = '';
assert.throws(() => validateManifest(bad), /rationale.access.*expected non-empty string/);
});
it('rejects empty sources list', () => {
const bad = minimalValid();
bad.funds[0].sources = [];
assert.throws(() => validateManifest(bad), /sources.*expected non-empty array/);
});
it('rejects duplicate country:fund identifiers', () => {
const bad = minimalValid();
bad.funds.push({ ...bad.funds[0] });
assert.throws(() => validateManifest(bad), /duplicate fund identifier NO:gpfg/);
});
it('rejects wrong schema version (forces explicit bump)', () => {
const bad = minimalValid();
bad.manifest_version = 2;
assert.throws(() => validateManifest(bad), /manifest_version: expected 1/);
});
it('rejects invalid external_review_status', () => {
const bad = minimalValid();
bad.external_review_status = 'APPROVED';
assert.throws(() => validateManifest(bad), /external_review_status.*expected 'PENDING' or 'REVIEWED'/);
});
});