We read 14,577 paragraphs with an AI model, checked it against our own hand coding, corrected for its mistakes, and then looked for lasting shifts. This page explains each step, the numbers behind it, and what we had to assume.
Every step was fixed in writing before we saw its results: the plan, the scoring rule and the random seeds.
We wanted what EU institutions say and what the wider press says. Everything is in English and runs from Russia’s invasion of Ukraine (24 Feb 2022) to 9 Oct 2026.
EU institutions covers anything EU bodies or their members publish or say: Commission press material, Parliament speeches, questions and resolutions, and formal Commission documents. Parliamentary questions react within weeks, which makes them useful for timing.
The press is the English-language international press: the Guardian through its free API, and Reuters, the Financial Times and Euractiv exported by hand from Factiva through the Sciences Po library. Factiva’s licence bans bulk downloading, so no script touched it.
We dropped think tanks and academic work because their publication delays make dates unreliable. The Council could not be collected: its site blocks automated access, and we did not get around that.
We split documents into paragraphs and kept those that mention graphite, battery, lithium, cobalt, critical / strategic raw materials, or export controls together with China. We dropped paragraphs under 15 words and boilerplate, and removed exact and near duplicates (paragraphs sharing 80% of their five-word sequences) within each source.
Very long EU documents were capped at 30 paragraphs each so that one impact assessment could not swamp a month.
We hand-checked a random sample of kept and dropped paragraphs. 76–82% of kept paragraphs are on topic (precision). The filter finds 76–85% of the relevant paragraphs (recall); the worst case consistent with our sample is 37%. Off-topic paragraphs that slip through are mostly other uses of “battery”, and the coder marks them as “no claim”.
| Source | Stream | What | Access | Paragraphs |
|---|---|---|---|---|
| Commission press corner | EU institutions | Press releases, speeches, statements | Open dataset (Zenodo) + Press Corner API | 2,869 |
| EP plenary speeches | EU institutions | Debate speeches (67% machine-translated) | PlenaryEP corpus (to Apr 2026) | 1,519 |
| EP questions and answers | EU institutions | Written and oral questions, Commission answers | EP Open Data API + Word files | 814 |
| EP adopted texts | EU institutions | Resolutions and reports (max 30 paragraphs each) | EP Open Data API | 428 |
| Commission documents | EU institutions | COM communications and staff working documents | EUR-Lex (CELLAR) | 205 |
| The Guardian | The press | Articles naming Europe and China | Guardian Open Platform API | 2,968 |
| Reuters | The press | News wire | Factiva, exported by hand | 2,935 |
| Financial Times | The press | Articles | Factiva, exported by hand | 1,798 |
| Euractiv | The press | Brussels policy news | Factiva, exported by hand | 1,041 |
Plenary speeches given in other languages use the corpus’s English machine translation (1,021 paragraphs, 7% of the total); every one carries a flag. Months with fewer than 30 EU paragraphs (9 of 57) are marked as thin in the monthly charts.
The unit is one paragraph. The coder answers in three steps, using a written codebook (version 2, frozen 16 Oct 2026).
We also coded which stages a paragraph mentions at all. The gap between the two is a result: packs and vehicles are mentioned in 31% of paragraphs but are the place of dependency in only 5% of those.
| Code | Means | Illustrative example |
|---|---|---|
| Mining (Tier I) | Extraction, ore, mines, deposits, concentrates. | “Europe imports almost all its graphite ore from China.” |
| Refining (Tier II) | Turning a raw material into a purer form of itself: refining, processing capacity, battery-grade chemicals, spherical graphite. Also a country producing a material. | “China refines 90% of the world’s graphite.” |
| Components (Tier III) | The material becomes a battery part: active materials, precursors, electrodes, cells, gigafactories, battery production. | “European cell makers rely on Chinese anode material.” |
| Packs & vehicles | Finished products: battery packs, modules, EVs, batteries bought as a product. | “Europe depends on imported EV batteries.” |
| No stage named | A dependency is claimed but no stage is named. Generic “critical raw materials” counts here. | “We must cut our reliance on China for critical raw materials.” |
| No claim | The paragraph does not say Europe depends on anyone. It must stand alone. | “Lithium demand will triple by 2030.” |
A paragraph can carry several stage codes. “No claim” must stand alone. The full codebook, with every edge-case rule and its history, is in the data folder.
We never checked the model’s answers: we coded first, then compared. No check paragraph was ever shown to the model as an example.
A separate set of 100 paragraphs (a 40-paragraph pilot and 60 more) was used to write the rules and to pick the worked examples in the prompt. It never touched the scoring.
A second coder coded 40 of the 150 blind. Agreement is measured with Krippendorff’s alpha (0 = chance, 1 = perfect). It is moderate: 0.56 on the full set of codes and 0.61 on the main question. We then settled disagreements together, which changed 13 of 150 codes. This is the ceiling against which the model should be read.
| What the two coders agree on | Alpha |
|---|---|
| Is there a claim, and does it name a stage? | 0.61 |
| Full set of codes | 0.56 |
| No claim | 0.70 |
| No stage named | 0.65 |
| Packs & vehicles | 0.61 |
| Refining | 0.29 |
| Mining, components | about 0 (1–3 positives; not informative) |
Mining 12 · Refining 12 · Components 8 · Packs & vehicles 16 · No stage named 34 · No claim 81.
About ±6–7 points on overall accuracy, ±11–12 points per stage.
The rule was fixed on 9 Oct, before scoring: the highest average F1 wins; within 0.03, the more transparent method wins.
A word list. Terms for each stage, extended with frequent word pairs from the corpus (“needle coke”, “battery grade”). A stage only counts as the place of dependency if it sits in the same sentence as a dependency word. Free and fully transparent.
Sentence similarity. A free sentence-embedding model (all-mpnet-base-v2) compares each paragraph with short model sentences such as “The EU relies on Chinese refineries to process graphite.” It catches synonyms but measures what a text is about, not what it claims.
Claude. Claude Sonnet 5.5 through the Batch API, given the full codebook, the edge-case rules, worked examples from the pilot set and a strict JSON answer format, 25 paragraphs per call. It also returns a short quote as evidence and an “unsure” flag.
F1 combines how many of the true cases a method finds (recall) with how many of its hits are right (precision); 0 is useless, 1 is perfect. We averaged it over the six codes and got 95% ranges by resampling the check set within its sampling groups (2,000 times).
| Method | Average F1 (95% range) | Exact match | Role |
|---|---|---|---|
| Claude Sonnet 5.5 with the codebook | 0.71 (0.63–0.77) | 74% | Chosen |
| Word list (dictionary) | 0.53 (0.45–0.60) | 52% | Robustness check |
| Sentence similarity (embeddings) | 0.33 (0.25–0.41) | 45% | Robustness check |
Claude leads the word list by 0.18 (paired range 0.10–0.26). With paragraphs weighted to the corpus the lead is 0.10 (−0.01 to 0.21); the ranking does not change. Per-code scores are charted on the “more charts” page.
If a coder over-claims by a known amount, you can work backwards to the true share. We did that for every month, stream and period.
From the check set we estimate how often Claude puts a paragraph of each true kind (no claim, no stage named, names a stage) into each box. That gives a 3 × 3 table of error rates, weighted to the corpus. Inverting it turns observed shares into corrected ones. This is the matrix version of the Rogan–Gladen correction used for imperfect medical tests. The corrected stage share is then split across stages in the proportions Claude used.
Uncertainty. We redraw the check set and the paragraphs 1,000 times and repeat the correction each time. The 95% ranges on the site come from these runs. Changes between periods are computed within each run, so the shared uncertainty about error rates cancels out; that is why changes are much tighter than levels.
For document-level figures, each paragraph gets a simulated “true” code drawn from the same error rates, and a document counts as a claim if any paragraph is one (1,000 runs, 5,489 documents). If errors were fully linked within a document, levels would be 1–3 points lower and changes almost the same.
Mentioned stages were scored against a second hand-coded set of 150 paragraphs (agreement 0.74–0.90 by stage) and corrected one stage at a time, with the same 1,000-run ranges.
Share of all 14,577 paragraphs, whole period
Claude-coded paragraphs · 3 × 3 error table from the check set
| Claude’s error rates (weighted to the corpus) | Rate |
|---|---|
| A paragraph with no claim is coded as a claim | 12% |
| …of which, coded “no stage named” | 9.2% |
| A no-stage claim is coded as naming a stage | 5.6% |
| A stage-naming claim is coded as “no stage named” | 8.2% |
| A real claim is missed | 0% |
| Stage mentioned | Agreement (kappa) | As coded | Adjusted | Change after Oct 2025, points |
|---|---|---|---|---|
| Mining | 0.79 | 16.2% | 13.0% | −3.9 (−5.5 to −2.3) |
| Refining | 0.76 | 12.7% | 17.0% | +0.3 (−2.2 to +2.6) |
| Components | 0.90 | 13.1% | 22.0% | −9.3 (−21.7 to −5.2) |
| Packs & vehicles | 0.74 | 31.2% | 44.0% | −9.9 (−16.6 to −6.5) |
We looked for months when a line settles at a new level and stays there for at least six months, with no dates given in advance.
We ran this on monthly shares (no claim, no stage named, names a stage, and each stage) for all texts, EU institutions and the press, and quarterly for lithium and cobalt. Because the correction is linear, break dates are the same on adjusted and as-coded series.
As a cross-check we grouped neighbouring months with similar mixes of stages (Ward clustering, where only adjacent months may merge). It produced no clear eras.
For every break we built a file of the paragraphs either side, the topics that moved and the events nearby. Claude drafted a cause for each of 31 breaks in 9 episodes; we reviewed each one against the event dates and the paragraphs before it went on the site.
Claims naming no stage rise by 8.7 points (date range Nov 2024 – Jan 2026).
Claims naming no stage rise by 8.8 points.
Up, down, up.
Press talk on packs & vehicles, during the UK–EU rules-of-origin dispute.
None of these is an outcome. They help read the coded series.
A topic model on all 14,577 paragraphs (word weights, single words and pairs). The number of topics was set by a rule fixed in advance that balances how coherent and how stable topics are, over 8 to 25. Topic names are ours, written from top words and example paragraphs.
Relevant documents divided by everything a source published that month. This separates “more talk about stages” from “everything else fell”. Reuters, the FT and Euractiv are left out (no monthly totals).
400 key words (lemmatised with spaCy). Two words are linked when they share paragraphs more than chance (NPMI above 0, at least five paragraphs). A filter keeps each word’s strongest links (disparity filter, α 0.05). Clusters by Louvain, 50 runs. Changes tested by shuffling documents between periods 1,000 times, with a false-discovery correction (q 0.10).
Each paragraph carries material tags. Graphite has 448 paragraphs (71 after Oct 2025), so it is read by period, not by month. Lithium is read by quarter.
Built only from the 3,893 paragraphs coded as a claim. Singular and plural are merged and procedural words removed. The map of Europe behind it is decoration.
Every analysis had a written plan, saved with a hash before the results existed. Seeds are fixed (e.g. 20261020, 20261021, 20261030). Notebooks are numbered in run order in the data folder.
Levels are approximate; changes over time are more reliable. That is the single most important caveat.