Data audit · public & verifiable

Data audit

Every number on Wisdom traces to a public source. Here are our methods, our coverage gaps, our known weaknesses, and our formulas — published the same way the World Bank and IMF publish theirs.

Indicators wired
8/22
with live country-level data
AU goals covered
9/20
11 of 20 lack public data
AU aspirations scored
4/7
composite is unweighted mean of these
Countries covered
55/55
all AU member states (with gaps per indicator)
Data architecture
How every number on Wisdom gets from a public API to your screen. Four layers, one weekly refresh cycle.
1 · Authoritative public sources
  • World Bank Open Data API
  • WHO Global Health Observatory
  • Mo Ibrahim IIAG
  • UN World Population Prospects
  • UNESCO UIS · FAO · IPCC · UNCTAD
  • ISS African Futures (forecasts)
  • AU / AUDA-NEPAD (comparison only)
HTTPS · weekly cron
2 · Ingestion (Node scripts)
  • scripts/ingest/world-bank.ts — pulls API
  • scripts/ingest/mo-ibrahim-iiag.ts — CSV
  • scripts/ingest/indicators-registry.ts — maps 20 AU goals → source codes
  • scripts/ingest/african-countries.ts — 55 ISO codes
  • scripts/ingest/african-population.ts — UN WPP weights
writes JSON · commits to git
3 · Storage (versioned JSON)
  • data/ingested/world-bank.json (21 indicators)
  • data/ingested/mo-ibrahim-iiag.json (1 indicator)
  • data/ingested/manifest.json (timestamps)
  • pan-african-literature-database.json (561 works)
imported by lib/
4 · Read + aggregate layer (TypeScript)
  • lib/agenda-2063-live.ts — continentalAggregate() · calculateProgress() · getCoverageStats()
  • lib/futures-data.ts — three-scenario forecasts (Failure / Current / Possible)
  • lib/literature-data.ts — search · filter · get-by-id
rendered as Server Components
5 · Display (Next.js routes)
  • /africa-2050 — Our reality + Our goals (live Agenda 2063 scoring)
  • /futures — Three paths to 2043
  • /audit — This page (methodology disclosure)
  • /browse · /work/[id] · /themes — Literature archive
  • /ask — AI assistant (calls MCP literature tools)

Sources are external and authoritative. We deliberately don't trust AU self-reported numbers — they're widely seen as politically optimistic (Goal 8 "United Africa" jumped 12% → 98% in two years per their 2022 report). Instead we pull from the World Bank, WHO GHO, Mo Ibrahim IIAG, UN WPP, UNESCO UIS, FAO, and ISS African Futures. AU's published scores appear only as comparison benchmarks.

The indicator registry is the contract. scripts/ingest/indicators-registry.ts declares each indicator with its goal, aspiration, source URL, baseline, target, and direction. Ingestion scripts know how to fetch each sourceCode from each source. Aggregation scripts know how to compute progress from baseline → current → target. One file, one source of truth.

Storage is plain JSON committed to git. The GitHub Action runs npx tsx scripts/ingest/world-bank.ts every Sunday at 03:00 UTC, fetches every indicator × every country, computes continental aggregates, writes JSON, commits to the branch, Vercel rebuilds. No live API calls at request time — pages render fast and survive when source APIs are slow.

Methodology lives in the read layer. lib/agenda-2063-live.ts exposes continentalAggregate() (both simple-mean and population-weighted), calculateProgress() (refuses to invent confidence when target===baseline or current is null), and getCoverageStats() (surfaces missingness so the composite score isn't misread).

Forecasts are a separate layer. lib/futures-data.ts holds the three-scenario data (Failure / Current Path / Possible Africa). Forecasting is fundamentally different from measurement — you need a simulation model (we use ISS's IFs platform via Pardee Institute), not linear extrapolation.

Aspiration coverage matrix
AU has a 7-aspiration / 20-goal framework. We do not yet have public-data indicators for every goal. The composite score is computed only from aspirations with at least one indicator; missing aspirations are excluded, not assumed at zero.
AspirationGoals totalGoals with dataMissing goalsOur scoreAU 2021
#1Prosperous Africa76Goals 623%37%
#2Integrated Africa31Goals 8, 932%84%
#3Good Governance21Goals 121%42%
#4Peaceful Africa30Goals 13, 14, 15no data63%
#5Cultural Identity10Goals 16no data45%
#6People-driven21Goals 1810%67%
#7Global Partner20Goals 19, 20no data58%
Continental aggregation: simple mean vs population-weighted
Every "continental average" on Wisdom is a population-weighted mean (matching World Bank / WHO / IMF methodology). If you naïvely averaged the 54 country values, smaller high-income states would dominate — making Africa look better off than the average African actually is.

Indicators where the two methods diverge by more than 5% — i.e. where the choice matters:

IndicatorSimple meanPop-weightedΔ %
GDP per capita (current US$)2824.1 USD2019.3 USD+39.9%
Under-5 mortality rate55.9 per 1,00061.4 per 1,000-9.0%
Individuals using the internet42.1 %39.5 %+6.6%

Population weights from UN World Population Prospects 2024, 2023 medium-variant estimates. Total African population covered: 1.46B.

Scoring formulas
Every score on Wisdom is one of three calculations. Source code lives at lib/agenda-2063-live.ts.
Indicator progress (0–100%)
// when higherIsBetter = true (e.g. life expectancy)
progress = clamp((current − baseline_2013) / (target_2063 − baseline_2013), 0, 100) × 100
// when higherIsBetter = false (e.g. maternal mortality)
progress = clamp((baseline_2013 − current) / (baseline_2013 − target_2063), 0, 100) × 100

Worked example: maternal mortality. Baseline 480/100k (2013) → target 70/100k (2063). Current: 394/100k. Progress = (480−394) / (480−70) = 86 / 410 = 21%.

Goal score
goal_score = mean(progress for each indicator in goal)
Aspiration score
aspiration_score = mean(goal_score for each goal in aspiration)

Caveat: goals with zero indicators are skipped, not zeroed. So Aspiration 1 (6 of 7 goals covered) is comparable to other aspirations, but Aspirations 4, 5, 7 (zero coverage) return null, not zero.

Composite score
composite = mean(aspiration_score for each aspiration with score ≠ null)

Caveat: currently averaged across 4 of 7 aspirations. AU's published methodology uses all 7 with their own indicator weights — not directly comparable, intentionally.

Status thresholds
expected_progress_by_2026 = (2026 − 2013) / 50 × 100 = 26%
on-track if progress ≥ 26%
at-risk if 18% ≤ progress < 26% (= 70% of expected)
behind if progress < 18%
Known issues & weaknesses
What we'd want a peer reviewer to push on. Published openly so users can judge.
mediumCoverage gap: 11 of 20 AU goals lack indicators

Aspirations 4 (Peaceful Africa), 5 (Cultural Identity), and 7 (Global Partner) currently have zero indicators wired into the live data layer. Goals 6, 8, 9, 12, 13, 14, 15, 16, 18, 19, 20 are empty. Most are intentionally hard to quantify (cultural renaissance, federal unification) or politically contested (continental peace metrics).

Mitigation: Disclosed in the dashboard's coverage banner. Ingestion roadmap targets ACLED (peace), AfCFTA Secretariat (integration), Afrobarometer (governance perception).

mediumBootstrap seed data, not yet live-ingested

The data file ships with hand-curated values from publicly known World Bank and IIAG figures rather than fetched live from APIs. The GitHub Action runs ingestion but the dev sandbox blocked outbound HTTPS during initial setup.

Mitigation: GitHub Actions workflow at .github/workflows/ingest-data.yml runs on Sundays 03:00 UTC and on-demand. First production run will replace seed values with API-fetched data.

lowLinear extrapolation in Our Goals view, not real simulation

The 'projected completion year' calculation in Our Goals uses simple linear extrapolation from the 2013 baseline. Real forecasting (capturing inter-sector dynamics, demographic transitions, capital accumulation) requires a simulation model.

Mitigation: Our Paths page uses ISS African Futures values, which are computed from the Pardee Institute's IFs simulation platform. Linear extrapolation is clearly labeled in the methodology footer of Our Goals.

lowDifferent data providers can disagree by ~5%

World Bank, WHO, UNESCO, and national statistical offices may publish different values for the same conceptual indicator (different reference years, survey vs administrative, different aggregation rules). For example, World Bank life expectancy and WHO life expectancy can differ by 1-2 years for the same country-year.

Mitigation: Each indicator clearly cites its specific source. We don't blend across providers within a single indicator.

lowMo Ibrahim IIAG continental average: we compute 48.0; they publish 49.3

A 1.3-point gap between our hand-curated continental mean and the published IIAG 2024 figure. Likely explained by 1-2 country values being slightly off from the underlying scoreboard, plus the IIAG report including a richer set of category-weighted aggregations.

Mitigation: Once IIAG ingestion runs against an official CSV download (data/external/iiag-2024.csv), this gap closes. CSV parser stub is in place at scripts/ingest/mo-ibrahim-iiag.ts.

infoFailure scenario is our construction, not ISS

The Failure scenario on the Our Paths page is not from ISS African Futures. We assembled it from documented historical worst-case patterns: 1980s-1995 lost decade (SAP era), Zimbabwe 2000-2008 collapse, Sahel 2020-2024 coup contagion, +3°C climate trajectory.

Mitigation: Each Failure value cites its specific historical anchor in the indicator card's expandable methodology section.

medium/api/ask LLM endpoint has no rate limit and no auth

The /api/ask route forwards user prompts to OpenRouter using a Wisdom-owned API key. It does not require authentication and has no per-IP or per-session rate limiting. A malicious or buggy client can hit this endpoint indefinitely and drain credits. Discovered in audit pass XII (2026-05-15).

Mitigation: Two reasonable fixes: (1) require Supabase auth on the route (re-use the same getCurrentUser pattern as the rest of the app), or (2) add an in-memory token bucket per IP and a daily cap per key. We've intentionally NOT shipped either fix without explicit product direction — both change runtime behaviour and may break dev. Documented openly so anyone deploying this can see the gap before going public.

infoAudit pass XII (2026-05-15) — bundle/security/orphan sweep

Twelfth pass found and fixed: (1) /app/(app)/africa-2050/page.tsx imported ScoreDashboard but never rendered it (dead import). Removed. The component file itself stays until the deprecated lib/agenda-2063-data.ts is fully migrated to live data. (2) System prompt for /api/ask said '370+ works' but database is now 561. Updated to '500+' as a forward-looking floor. (3) 56/561 works (10%) had no accessLinks, dead-ending the user. Added a fallback card on /work/[id] with 3 search links (Internet Archive, Open Library, WorldCat) so every work has a way to look further. (4) 3 browse-filter pages (/browse/era/[era], /browse/genre/[genre], /browse/region/[region]) have no pagination — render up to 170 cards at once for popular categories. Flagged but not fixed; not critical at 170 items. (5) Two orphan exports: WisdomIcon in wisdom-logo.tsx and ScoreDashboard. Left in place for future use; the WisdomIcon may be needed once the marketing site refactors.

Mitigation: All fixes in commit applied today.

infoAudit pass XI (2026-05-15) — geography + completeness + 3 DB duplicates

Eleventh pass: (1) verified tile map geography — 0 of 55 country positions mismatch their expected latitude band; north coast west-to-east ordering correct. (2) verified DB field completeness — language/region/country/genre/era/description/significance all 100% present across 561 works; only accessLinks is 90% (gap fixed via fallback). (3) removed 3 confirmed duplicate works: 'We Need New Names' (id 310), 'Homegoing' (id 256), 'Facing Mount Kenya' (id 260). 'Paradise' (×2) retained — Toni Morrison's 1997 and Abdulrazak Gurnah's 1994 are legitimately different works that share a title. (4) verified all 9 futures categories have ≥1 indicator. (5) verified AU framework mapping in ASPIRATION_GOALS still matches 7+3+2+3+1+2+2=20.

Mitigation: 3 duplicates removed, work count 564 → 561.

infoAudit pass IX-X (2026-05-15) — formula consistency + feature gaps

Pass IX-X: (1) calculateProgress in lib/agenda-2063-live returned 100 or 0 at range==0, inconsistent with the static layer's null. Made live match static — both now return null when baseline === target (progress is mathematically undefined). (2) AfricaTileMap rendered all tiles muted when only one country had data (range==0); now mid-scale amber so the data point is visible. (3) MCP server exposes 7 literature tools but no Agenda 2063 tools — feature gap, not bug. (4) /api/ask provides no Agenda 2063 context to the LLM — feature gap.

Mitigation: Live/static formula consistency restored; MCP+ask Agenda 2063 integration logged for future iteration.

infoAudit pass VIII (2026-05-15) — 8 verification checks

Pass VIII verified clean (no issues found): (1) ScenarioHero indicator references all exist in FUTURE_INDICATORS. (2) ScenarioHero displayed numbers (2.0/3.3/6.0 GDP share, 38/18/6 poverty, 320/188/80 ag imports) match ISS exactly. (3) scenario-chart 2043 endpoints match futures-data gdp-per-capita card. (4) forecast-summary requiredPace 2063 = exactly 100 (algebraic). (5) trendIcon logic correct for negative deltas (governance-effectiveness). (6) 1458M population sum matches UN WPP 1460M within rounding. (7) /audit dynamic StatBlocks use live coverage stats. (8) Tile-map ESH position row 0 col 2 — geographically correct for north-western Africa.

Mitigation: No fixes needed; documented as verified-clean.

infoAudit pass VII (2026-05-15) — aspiration-radar null-collapse fix

Pass VII: AspirationRadar was doing ours: d.ours ?? 0 to feed recharts. When an aspiration had no live indicators (4, 5, 7 currently), the polygon collapsed to that vertex at 0% AND the tooltip showed '0%' — visually claiming Africa scored zero when truth was 'we don't measure this'. Now: passes undefined (not 0) so recharts breaks the polygon at the null vertex; tooltip formatter displays 'no data' for null values.

Mitigation: Radar now honestly shows missing data instead of misleading zero.

infoAudit pass VI (2026-05-15) — wellbeing radar + dead links + chart drift

Sixth deep audit pass: (1) /futures ImaginedFuture cards linked to /work/lagoon, /rosewater, /who-fears-death — but the route is /work/[id] (numeric), not [slug]. Three dead links. Fixed by switching to numeric workIds (495, 454, 73 — looked up in the literature database). (2) Wellbeing radar in charts.tsx drifted from live data: 'Women Parl. 27%' was the simple country mean; pop-weighted continental is 24%. 'Governance 48.8' was stale IIAG; updated to 49 (IIAG 2023 published). 'Education 52%' had unclear definition; relabeled 'Secondary Completion' so the value is defined. (3) Happiness data in charts.tsx had no methodology — the World Happiness Report ranks individual countries, not regions. Added explicit comment that 'rank' is the unweighted mean of African countries' WHR global ranks, and the forward values are linear extrapolations only. (4) charts.tsx employmentData.laborForce 2013 = 330M doesn't cleanly match ILO whole-Africa (~470M) or SSA-only (~370M); scope ambiguous — flagged in code for migration to ILO modelled estimates when ingestion expands.

Mitigation: Three dead links fixed. Wellbeing radar values reconciled with live data layer. Happiness methodology now documented in code comment.

infoAudit pass V (2026-05-15) — external reviewer findings addressed

External audit (Codex 2026-05-15) found bugs the previous internal passes missed. All real findings fixed: (1) getStatus() hardcoded the 2026 expected-progress threshold (26%) regardless of each indicator's currentYear. An indicator measured in 2023 should be benchmarked against 2023's expected progress (20%), not 2026's. Function now accepts currentYear and computes year-specific threshold; all callers updated to pass indicator.currentYear. (2) calculateProgress_static returned 100 whenever target===baseline (range=0), even if current differed materially; and silently coerced null/NaN current to 0 or 100. Now returns null in both cases; callers must handle explicitly. Score aggregation functions filter nulls instead of treating them as zero. (3) ProgressBar component hardcoded the expected marker at 26%; now accepts markerYear and computes proportionally. (4) charts.tsx had 'pressFreeedom' typo (5 places). Renamed to pressFreedom. (5) charts.tsx energyData electricityAccess trajectory was too slow — 47% by 2025 vs WB's actual ~58% by 2022. Series rebuilt with 2022=58 (WB actual) waypoint and accelerated forward projection. The external auditor also flagged simple-mean vs population-weighted discrepancy (our 58% whole-Africa vs their 53% SSA-only weighted) — different scopes, not an error; disclosure improved. Stale-extraction findings (missing files, IIAG 47.5) were already fixed in earlier passes; auditor was looking at older branch state.

Mitigation: All fixes pushed in commit applied today. The original audit report is preserved for traceability.

infoAudit pass IV (2026-05-15) — currency + ISS source fidelity fixes

Fourth deep audit pass found and fixed: (1) GDP per capita baseline2013 was $1,880 — that's sub-Saharan Africa only; whole continent was $2,210. Skewed all progress math. Corrected. (2) Population 2043 Current Path was 2.14B citing UN WPP, but ISS African Futures Current Path uses 2.3B. We're a Current-Path-driven page, so we should match ISS for internal coherence. Switched to 2.3B; failure and possible scenarios rescaled. (3) Cereal yield was citing FAOSTAT cereal-only baseline (1.6 t/ha) while scenario forecast was ISS all-crops (5.9 t/ha) — unit mismatch. Renamed to 'Crop yield (all crops)' with consistent baseline 4.3 → currentPath 5.9 per ISS Agriculture theme. (4) IIAG Cabo Verde was 67.6 (off by 4pts); Kenya was 56.6 (off by 3.4pts). Both corrected to published 71.6 and 60.0. (5) Three duplicate MWI keys in IIAG 2014 baseline — only one canonical 56.2 retained. (6) Population indicator was marked higherIsBetter=true but Possible Africa scenario has LOWER population (demographic transition). Reversed: more population in failure path, lower in possible path. (7) AU report URL fabricated path /20220207/ replaced with the documents index https://au.int/en/documents.

Mitigation: All Current Path 2043 values now align with ISS African Futures to ≤5% error. Scenario ordering passes monotonicity check for all 16 indicators.

infoAudit pass III (2026-05-15) fixes applied

Third deep audit pass found and fixed: (1) charts.tsx healthData infantMortality showed 92/1000 — that's the under-5 rate, not infant. Corrected to ~60/1000 per WHO/UN IGME. (2) charts.tsx digitalData mobile field was 280 in 2013 — wrong by 3x; GSMA reports ~750M SIM connections. Corrected. (3) Dual composite-score rendering removed: page.tsx no longer renders ScoreDashboard alongside LiveDataSection. (4) Function name collisions resolved: the deprecated lib/agenda-2063-data.ts now exports calculateProgress_static, calculateGoalScore_static, calculateAspirationScore_static, calculateOverallScore_static to prevent silent wrong-import bugs. (5) AU Continental Report URL changed from a fabricated /20220207/ path to https://au.int/en/documents (the documents index) where users can search reliably. (6) formatValue() in both live-data-section.tsx and futures/page.tsx expanded to handle all 13 declared units.

Mitigation: All fixes shipped in commit applied today. Verifiable in git history.

Authoritative sources
Every URL we use. If you can verify a Wisdom number on the source page below, the number is right.
World Bank Open Data
GDP, poverty, electricity, health, education, mortality, governance
Auth: None
WHO Global Health Observatory
Life expectancy, mortality, disease burden
Auth: None
UNESCO Institute for Statistics
Literacy, school completion, gender parity in education
Auth: Free key
UN World Population Prospects
Population estimates and projections, by country and year
Auth: None
FAOSTAT
Cereal yield, food prices, agricultural trade
Auth: None
IEA Africa Energy Outlook
Electricity access, renewable share, energy demand
Auth: Some restricted
UNICEF / WHO IGME
Under-5 and infant mortality
Auth: None
Mo Ibrahim IIAG
Governance, rule of law, security, participation
Auth: Free CSV download
Transparency International CPI
Corruption Perceptions Index
Auth: Free CSV
Reporters Without Borders
Press Freedom Index
Auth: Free
ACLED
Armed conflict events, fatalities, by country
Auth: Free academic
Inter-Parliamentary Union
Women in parliament, by country, monthly
Auth: Free CSV
Afrobarometer
Public opinion surveys, governance perception, democracy
Auth: Free with registration
UNCTAD
Trade, FDI, intra-African trade share
Auth: None
Our World in Data
Cross-source aggregations, validated datasets
Auth: None
ISS African Futures
Forecast scenarios using IFs simulation model (Pardee Institute)
Auth: None
IPCC AR6
Climate vulnerability, regional projections
Auth: None
African Union
Agenda 2063 framework, biennial Continental Reports
Auth: None (PDF)
How to cite Wisdom

For academic and journalistic use, please cite the original primary sources (the URLs above) for any specific numerical claim. Wisdom is an aggregator and visualiser, not an originator of statistical estimates.

For dashboard methodology and the Failure scenario construction, cite: Wisdom Pan-African Library, Agenda 2063 dashboard methodology,with a link to wisdom.example/audit.

For the IFs-based Current Path and Possible Africa forecasts, cite Cilliers, J. (2026). Africa Current Path. ISS African Futures and Innovation Programme.