1- # Economic Data Contract — Agentic Workflows (v1 .0)
1+ # Economic Data Contract — Agentic Workflows (v2 .0)
22
3- > ** Single source of truth** for live World Bank / SCB data, Chart.js
3+ > ** Single source of truth** for live IMF / World Bank / SCB data, Chart.js
44> visualisations, and AI commentary in every news article.
55> Consumed by ` scripts/validate-economic-context.ts ` and referenced
66> (by link) from every ` news-*.md ` agentic workflow.
77
8+ > ** Schema v2.0 (2026-04-20)** — additive. Adds IMF (accessed via the
9+ > repo's pure-TypeScript ` scripts/imf-client.ts ` + ` scripts/imf-fetch.ts `
10+ > CLI, no Python MCP) as a first-class primary source for macro, fiscal,
11+ > monetary, and external-sector indicators. World Bank remains
12+ > authoritative for governance (WGI), environment, and long-horizon
13+ > social/education residue. SCB remains the Swedish primary source.
14+ > v1 artefacts remain valid; the validator accepts both shapes during
15+ > the 2026-04-20 → 2026-05-31 grace window.
16+
817---
918
1019## Why this contract exists
@@ -51,22 +60,28 @@ where `{analysisSubfolder}` maps from the kebab article-type slug via
5160
5261** Schema** : ` analysis/schemas/economic-data.schema.json ` .
5362
54- ** Shape** :
63+ ** Shape** (v2.0 — additive over v1) :
5564
5665``` jsonc
5766{
58- " version" : " 1 .0" ,
67+ " version" : " 2 .0" ,
5968 " articleType" : " committee-reports" ,
60- " date" : " 2026-04-17 " ,
69+ " date" : " 2026-04-20 " ,
6170 " policyDomains" : [" fiscal policy" , " labor market" ],
6271 " dataPoints" : [
63- { " countryCode" : " SWE" , " countryName" : " Sweden" , " indicatorId" : " NY.GDP.MKTP.KD.ZG" , " date" : " 2024" , " value" : 0.82 },
64- { " countryCode" : " DNK" , " countryName" : " Denmark" , " indicatorId" : " NY.GDP.MKTP.KD.ZG" , " date" : " 2024" , " value" : 1.75 }
72+ // IMF (WEO projections permitted for look-ahead article types)
73+ { " countryCode" : " SWE" , " countryName" : " Sweden" , " indicatorId" : " NGDP_RPCH" , " date" : " 2025" , " value" : 1.9 , " provider" : " imf" , " projection" : false },
74+ { " countryCode" : " SWE" , " countryName" : " Sweden" , " indicatorId" : " GGXWDG_NGDP" , " date" : " 2027" , " value" : 32.4 , " provider" : " imf" , " projection" : true , " projectionVintage" : " WEO-2026-04" },
75+ // World Bank (governance / environment / social residue)
76+ { " countryCode" : " SWE" , " countryName" : " Sweden" , " indicatorId" : " CC.EST" , " date" : " 2023" , " value" : 2.1 , " provider" : " worldBank" , " projection" : false },
77+ // SCB (Swedish primary source)
78+ { " countryCode" : " SWE" , " countryName" : " Sweden" , " indicatorId" : " TAB1291" , " date" : " 2025-02" , " value" : 405.2 , " provider" : " scb" , " projection" : false }
6579 ],
66- " commentary" : " Sweden's 0.82% 2024 GDP growth lags Denmark (1.75%) and Norway (1.1% ), framing the committee's fiscal debate against a prolonged slowdown. Health expenditure at 11.2% of GDP (2022, top 3 Nordic) keeps SoU-2024/25:1 in focus ." ,
80+ " commentary" : " IMF projects Sweden's general government gross debt at 32.4 % of GDP in 2027 (WEO Apr-2026, GGXWDG_NGDP ), giving SkU-2025/26:12 fiscal headroom absent in 2022 (38 %). Control-of-corruption remains strong at 2.1 (WGI 2023) ." ,
6781 " source" : {
68- " worldBank" : [" NY.GDP.MKTP.KD.ZG" , " FP.CPI.TOTL.ZG" , " SL.UEM.TOTL.ZS" ],
69- " scb" : [" TAB1291" ]
82+ " worldBank" : [" CC.EST" ],
83+ " scb" : [" TAB1291" ],
84+ " imf" : [" WEO:NGDP_RPCH" , " WEO:GGXWDG_NGDP" ]
7085 }
7186}
7287```
@@ -78,65 +93,103 @@ where `{analysisSubfolder}` maps from the kebab article-type slug via
7893- ` commentary ` MUST cite 2–3 concrete numeric values that appear in
7994 ` dataPoints ` (the validator enforces only a word count; the human
8095 review + multi-dim quality score enforces the citation).
81- - ` source.worldBank ` / ` source.scb ` MUST list the IDs actually queried.
96+ - At least one of ` source.worldBank ` / ` source.scb ` / ` source.imf ` MUST
97+ be non-empty (previously only ` worldBank ` /` scb ` counted).
98+ - When any ` dataPoint.projection === true ` , it MUST carry a
99+ ` projectionVintage ` tag (e.g. ` "WEO-2026-04" ` , ` "FM-2026-04" ` ).
100+ Projection values MAY only be cited in article commentary for the
101+ ** look-ahead allow-list** : ` week-ahead ` , ` month-ahead ` ,
102+ ` weekly-review ` , ` monthly-review ` . Daily types (` committee-reports ` ,
103+ ` propositions ` , ` motions ` , ` interpellations ` , ` evening-analysis ` )
104+ MAY include projection values in ` dataPoints ` for context but MUST
105+ NOT quote them as definitive forward-looking claims in commentary.
82106- File MUST validate against
83107 ` analysis/schemas/economic-data.schema.json ` .
84108
109+ ### v1 back-compat
110+
111+ A v1 artefact (no ` source.imf[] ` , no ` provider ` /` projection ` on data
112+ points) is still accepted. The loader fills defaults
113+ (` provider: "worldBank" ` , ` projection: false ` ). New workflows SHOULD
114+ emit v2.
115+
85116---
86117
87118## MCP tool contract
88119
89120Step 2.6 of every ` news-*.md ` workflow MUST perform these MCP calls
90- ** before** writing ` economic-data.json ` :
121+ ** before** writing ` economic-data.json ` . The order of provider
122+ preference is:
123+
124+ 1 . ** IMF** — macro (GDP, inflation, unemployment), fiscal (debt, deficit,
125+ revenue, expenditure), monetary, external sector. WEO projections
126+ extend to T+5 and MUST be used for look-ahead article types.
127+ 2 . ** SCB** — Swedish-specific ground truth that IMF does not carry
128+ (household consumption patterns, regional data, high-frequency
129+ monthly tables).
130+ 3 . ** World Bank** — governance (WGI: CC.EST, RL.EST, VA.EST, GE.EST,
131+ RQ.EST, PV.EST), environment (CO2, forest area, renewables), and
132+ long-horizon social/education indicators IMF does not cover.
91133
92134### 1. Map documents → policy domains → indicators
93135
94136```
95- view analysis/worldbank/ indicators-inventory.json
137+ view analysis/economic- indicators-inventory.json
96138```
97139
98- Select every indicator that has a ` mcpTool ` field and whose
99- ` committees ` /` policyAreas ` match the day's source documents. Prioritise
100- the committee-specific matrix in ` SHARED_PROMPT_PATTERNS.md ` §"World
101- Bank Indicator Reference".
140+ Each indicator entry carries a ` provider ` field (` imf ` | ` worldBank ` |
141+ ` scb ` ), plus IMF-specific fields (` imfDatabase ` , ` imfIndicatorCode ` ,
142+ ` imfDimensionFilters ` , ` projectionHorizon ` ) when the primary provider
143+ is IMF. Select every indicator whose ` committees ` / ` policyAreas `
144+ match the day's source documents. The legacy inventory at
145+ ` analysis/worldbank/indicators-inventory.json ` is retained as an
146+ append-only reference during the migration window.
102147
103- ### 2. Query World Bank (Sweden + Nordic peers)
148+ ### 2. Query IMF (Sweden + Nordic peers) — PRIMARY for macro/fiscal/monetary
104149
105- Required calls, retried 3× on failure (see Risks):
150+ Via the repository's pure-TypeScript client ` scripts/imf-client.ts ` ,
151+ exposed to agentic workflows through the thin ` scripts/imf-fetch.ts `
152+ CLI. No Python MCP / ` uvx ` runtime is involved; all IMF traffic goes
153+ directly to ` data.imf.org ` , ` api.imf.org ` , and ` www.imf.org ` on the
154+ firewall allowlist.
106155
107- ```
108- # Sweden — full 10-year series for the primary domains
109- get-economic-data(countryCode="SE", indicator="GDP_GROWTH", years=10)
110- get-economic-data(countryCode="SE", indicator="INFLATION", years=10)
111- get-economic-data(countryCode="SE", indicator="UNEMPLOYMENT", years=10)
112-
113- # Nordic + Germany comparison — top 3 domain indicators, 5-year series
114- for country in [DK, NO, FI, DE]:
115- get-economic-data(countryCode=country, indicator=<top-3>, years=5)
116-
117- # Domain-specific extras (only when article touches the domain)
118- get-health-data(countryCode="SE", indicator="HEALTH_EXPENDITURE", years=5)
119- get-education-data(countryCode="SE", indicator="EDUCATION_EXPENDITURE", years=5)
120- get-social-data(countryCode="SE", indicator="POPULATION", years=10)
156+ ``` bash
157+ # 1. WEO time series for one country (default 10 years; --persist caches
158+ # the JSON under analysis/data/imf/{indicator}/{country}.json with
159+ # projectionVintage provenance).
160+ tsx scripts/imf-fetch.ts weo \
161+ --country SWE --indicator NGDP_RPCH --years 15 --persist
162+
163+ # 2. Compare the latest WEO value across Sweden + Nordic peers in one call.
164+ tsx scripts/imf-fetch.ts compare \
165+ --indicator GGXWDG_NGDP \
166+ --countries SWE,DNK,NOR,FIN,DEU --persist
167+
168+ # 3. SDMX 3.0 passthrough for IFS / BOP / FM / GFS / DOTS.
169+ tsx scripts/imf-fetch.ts sdmx \
170+ --path " /data/IMF.STA,CPI,4.0.0/M.SE.PCPI_IX?startPeriod=2024-01" \
171+ --indicator PCPI_IX --country SWE --persist
172+
173+ # 4. Inspect the built-in WEO + FM indicator catalog (no network call).
174+ tsx scripts/imf-fetch.ts list-indicators
121175```
122176
123- Committee → minimum indicator mapping (shortened — full matrix in the
124- inventory JSON):
177+ ** Rate-limit discipline** (IMF ~ 10 req / 5 s): prefer the ` compare `
178+ subcommand (one batched call across countries), insert a 1 s sleep
179+ between separate ` imf-fetch.ts ` invocations, and rely on the client's
180+ built-in 3× retry with exponential back-off (1 s → 2 s → 4 s) for 429 /
181+ 5xx. Pre-warm 1 request at workflow start.
125182
126- | Committee | MUST query |
127- | -----------| -----------------------------------------------------------------|
128- | FiU | GDP_GROWTH, INFLATION, GDP_PER_CAPITA, GOVERNMENT_DEBT |
129- | AU | UNEMPLOYMENT, LABOR_FORCE, YOUTH_UNEMPLOYMENT |
130- | SkU | TAX_REVENUE, GDP_GROWTH |
131- | SoU | HEALTH_EXPENDITURE, PHYSICIANS, HOSPITAL_BEDS, LIFE_EXPECTANCY |
132- | UbU | EDUCATION_EXPENDITURE, LITERACY_RATE, SCHOOL_ENROLLMENT |
133- | FöU | MILITARY_EXPENDITURE, MILITARY_EXPENDITURE_GDP |
134- | MJU | CO2_EMISSIONS, RENEWABLE_ENERGY, FOREST_AREA |
135- | JuU | HOMICIDE_RATE, PRISON_POPULATION |
136- | CU | HOUSING_EXPENDITURE, MORTGAGE_RATE |
137- | TU | TRANSPORT_INFRASTRUCTURE_SPENDING |
183+ ### 3. Query World Bank (governance / env / social residue)
184+
185+ ```
186+ # Kept for indicators IMF does not cover
187+ get-economic-data(countryCode="SE", indicator="CC.EST", years=5) # Control of Corruption (WGI, source=75)
188+ get-economic-data(countryCode="SE", indicator="EN.ATM.CO2E.PC", years=10) # CO2 emissions
189+ get-social-data(countryCode="SE", indicator="POPULATION", years=10)
190+ ```
138191
139- ### 3 . Query SCB (Statistics Sweden)
192+ ### 4 . Query SCB (Statistics Sweden)
140193
141194```
142195# CRITICAL: language MUST be "sv" or "en". NEVER "no" — SCB returns
@@ -150,7 +203,22 @@ search_tables(query="<committee-topic>", language="en")
150203query_table(table_id="<TAB>", value_codes={"Tid": "top(10)", ...})
151204```
152205
153- ### 4. (High-level reviews only) D3 coalition-flow dataset
206+ ### Committee → primary provider & indicator matrix (v2)
207+
208+ | Committee | Primary provider(s) | MUST query (provider: code ) |
209+ | -----------| ---------------------| -----------------------------------------------------------------------------------------------|
210+ | FiU | IMF + WB | ` imf:WEO:NGDP_RPCH ` , ` imf:WEO:PCPIPCH ` , ` imf:WEO:NGDPDPC ` , ` imf:WEO:GGXWDG_NGDP ` |
211+ | SkU | IMF + WB | ` imf:WEO:GGR_NGDP ` , ` imf:WEO:GGX_NGDP ` , ` imf:WEO:GGXCNL_NGDP ` , ` imf:FM:GGXONLB_NGDP ` |
212+ | AU | IMF + WB | ` imf:WEO:LUR ` , ` wb:SL.UEM.1524.ZS ` , ` wb:SL.TLF.CACT.ZS ` |
213+ | NU / UU | IMF | ` imf:WEO:BCA_NGDPD ` , ` imf:WEO:TX_RPCH ` |
214+ | SoU | WB | ` wb:SH.XPD.CHEX.GD.ZS ` , ` wb:SH.MED.PHYS.ZS ` , ` wb:SH.MED.BEDS.ZS ` , ` wb:SP.DYN.LE00.IN ` |
215+ | UbU | WB | ` wb:SE.XPD.TOTL.GD.ZS ` , ` wb:SE.ADT.LITR.ZS ` , ` wb:SE.PRM.ENRR ` |
216+ | FöU | WB | ` wb:MS.MIL.XPND.GD.ZS ` , ` wb:MS.MIL.XPND.CD ` |
217+ | MJU | WB | ` wb:EN.ATM.CO2E.PC ` , ` wb:EG.FEC.RNEW.ZS ` , ` wb:AG.LND.FRST.ZS ` |
218+ | KU | WB | ` wb:CC.EST ` , ` wb:RL.EST ` , ` wb:VA.EST ` (WGI, source=75) |
219+ | JuU | WB | ` wb:VC.IHR.PSRC.P5 ` , ` wb:CC.EST ` |
220+
221+ ### 5. (High-level reviews only) D3 coalition-flow dataset
154222
155223Article types: ` week-ahead ` , ` month-ahead ` , ` weekly-review ` ,
156224` monthly-review ` . In addition to ` economic-data.json ` , produce a
@@ -236,9 +304,12 @@ cat > "$ANALYSIS_DIR/economic-data.json" <<EOF
236304EOF
237305```
238306
239- Cache raw MCP responses under
240- ` analysis/data/worldbank/$(date +%Y)/$indicator-$country.json ` so
241- subsequent article types in the same daily run reuse data (rate-limit
307+ Cache raw MCP responses alongside the legacy WB path:
308+ - IMF: ` analysis/data/imf/$(date +%Y)/$indicator-$country.json `
309+ - World Bank: ` analysis/data/worldbank/$(date +%Y)/$indicator-$country.json `
310+ - SCB: ` analysis/data/scb/$(date +%Y)/$table.json `
311+
312+ Reuse across article types in the same daily run (rate-limit
242313mitigation).
243314
244315---
@@ -259,6 +330,22 @@ Banned phrasings (all detected by `multi-dim quality score`):
259330- "Touches on X policy…"
260331- "Analysis of N documents…"
261332- Pure definitions of indicators (e.g. "GDP is the total output of…").
333+ - Un-sourced forecasts — phrases like "Sweden will…" / "The economy is
334+ expected to…" without a cited IMF WEO or Fiscal Monitor value from
335+ ` dataPoints ` where ` projection: true ` . Use the explicit form "IMF
336+ projects Sweden's debt/GDP at 32.4 % in 2027 (WEO Apr-2026,
337+ GGXWDG_NGDP)" instead.
338+
339+ ### Projections — allowed usage
340+
341+ From schema v2 onwards, ` dataPoint.projection === true ` values may be
342+ cited in commentary ** only** for these article types:
343+
344+ - ` week-ahead ` , ` month-ahead ` — explicit forward-looking narrative
345+ - ` weekly-review ` , ` monthly-review ` — retrospective + 6-12 month outlook
346+
347+ Any projection citation MUST include the ` projectionVintage ` tag
348+ (e.g. ` (WEO-2026-04) ` ) to make stale vintages visible to the audit.
262349
263350---
264351
@@ -274,7 +361,9 @@ commits violations:
2743613 . Missing / empty / malformed ` economic-data.json ` .
2753624 . ` dataPoints ` empty (without a valid ` skip: true ` ).
2763635 . Commentary below ` minCommentaryWords ` .
277- 6 . Missing footer attribution "Data by World Bank / SCB".
364+ 6 . Missing footer attribution "Data by IMF / World Bank / SCB"
365+ (historical variants "Data by World Bank / SCB" still accepted
366+ during the v1→v2 grace window).
278367
279368### Contract effective date
280369
@@ -313,3 +402,23 @@ mirror the change in the version history below.
313402 exemption to the validator so the daily audit stops re-reporting
314403 pre-contract articles for 7 days after every rollout. No change to
315404 the schema or enforcement for new articles.
405+ - ** 2.0 (2026-04-20)** — Add IMF as a first-class primary source via
406+ the repository's pure-TypeScript ` scripts/imf-client.ts ` +
407+ ` scripts/imf-fetch.ts ` CLI (Datamapper JSON for WEO, SDMX 3.0
408+ passthrough for IFS / BOP / FM / GFS / DOTS). ** No Python MCP /
409+ ` uvx ` ** — IMF access is under the same npm / SBOM governance as
410+ ` world-bank-client.ts ` and ` scb-client.ts ` . Additive schema changes:
411+ - ` source.imf: string[] ` (optional in v1, recommended in v2)
412+ - ` dataPoints[].provider ` (` imf ` | ` worldBank ` | ` scb ` )
413+ - ` dataPoints[].projection ` (boolean)
414+ - ` dataPoints[].projectionVintage ` (string; required when
415+ ` projection: true ` )
416+ Validator now accepts IMF in the footer attribution and treats any
417+ non-empty ` source.{imf,worldBank,scb} ` as satisfying citation.
418+ Projections are allowed in commentary for look-ahead article types
419+ (` week-ahead ` , ` month-ahead ` , ` weekly-review ` , ` monthly-review ` )
420+ only, and MUST carry the ` projectionVintage ` tag. Un-sourced
421+ forecast phrasing added to the banned list. World Bank remains
422+ authoritative for WGI governance, environment, and long-horizon
423+ social/education residue; SCB unchanged. v1 artefacts remain valid
424+ through the 2026-04-20 → 2026-05-31 grace window.
0 commit comments