Skip to content

fetch-chart-data silently returns the wrong indicator for explorer and multi-dim chart URLs #21

Description

@lucasrodes

Summary

search-charts returns three record types, and roughly a third to all of the hits for a given query are not plain charts. explorerView and multiDimView hits carry a queryParams field that selects which indicator the view shows. fetch-chart-data documents only the plain case ($CHARTURL.metadata.json / $CHARTURL.csv), so an agent that drops or mishandles those params gets a different indicator back, with HTTP 200 and no error of any kind.

For a data skill this is the worst failure class: plausible-looking numbers, silently wrong, no signal.

Evidence

Non-chart share of search hits (hitsPerPage=100, measured 2026-08-18):

Query Composition
life expectancy chart=58, explorerView=30, multiDimView=12
poverty chart=64, explorerView=36
co2 emissions chart=86, explorerView=14
energy mix multiDimView=100 — not a single plain chart

An explorer view, with and without its queryParams:

E="https://ourworldindata.org/explorers/population-and-demography"

# without params — what you get if they are stripped
curl -s "$E.csv?csvType=filtered&country=USA&time=2015"
# Entity,Code,Year,all years
# United States,USA,2015,326126496        <- population

# with the params the search result actually returned
curl -s "$E.csv?indicator=Life+expectancy&Sex=Both+sexes&Age=At+birth&Projection+scenario=None&csvType=filtered&country=USA&time=2015"
# Entity,Code,Year,Life expectancy at birth
# United States,USA,2015,78.722           <- life expectancy

Population versus life expectancy from the same URL. Both HTTP 200.

Same for a multi-dim chart — the column changes from Both genders to Primary education:

M="https://ourworldindata.org/grapher/years-of-schooling"
curl -s "$M.csv?csvType=filtered&time=2015" | head -1
# Entity,Code,Year,Both genders,Both genders (Original Year)
curl -s "$M.csv?level=primary&metric_type=expected_years_schooling&sex=both&csvType=filtered&time=2015" | head -1
# Entity,Code,Year,Primary education,Primary education (Original Year)

Why the skills allow it

search-charts is right, and says so (SKILL.md:112):

To fetch the data behind a chart, use the url property as is verbatim, including all query params.

But fetch-chart-data (SKILL.md:10) only describes:

Once you have identified a chart url like https://ourworldindata.org/grapher/literate-and-illiterate-world-population … The urls for both are $CHARTURL.metadata.json and $CHARTURL.csv respectively.

Two problems follow:

  1. The mechanics are undocumented. When the URL already has a query string, the .csv suffix goes before the ?, and the view's params must be merged with csvType=filtered etc. Naively appending .csv to a URL ending in &sex=both does not produce a valid data URL. Nothing says this.
  2. explorers/ URLs are never mentioned. The skill only ever shows grapher/ paths, so there is no signal that ourworldindata.org/explorers/<slug> is even a fetchable thing.

Why the existing test layers miss it

  • Layer 1 (contract tests) cannot catch it as written: both URL forms return HTTP 200 with a well-formed CSV. The bug is that they return different data, which no current check compares.
  • Layer 3 (output evals) would catch it, but no case exercises an explorer or multi-dim hit. All 12 cases use plain grapher charts.

Proposed fix

  1. fetch-chart-data: document the three record types, that queryParams selects the indicator and must be carried through, that .csv/.metadata.json is inserted before the query string, and that explorers/<slug> works the same way as grapher/<slug>.
  2. Contract test: assert the two URL forms return different column names for a known multi-dim chart and a known explorer. That converts an invisible failure into a red test, and guards against regression if OWID changes the defaults.
  3. Layer 3 case: a prompt that starts from a search result which happens to be an explorer view, asserting the returned indicator matches the one requested.
  4. Layer 2 case: no trigger set currently mentions explorers or multi-dim views; worth one positive so routing is exercised for that phrasing too.

Secondary observation

In the multi-dim example, passing country=USA without the dimension params returned Afghanistan rather than the USA, so entity filtering may also behave differently for these views. Not investigated — worth checking while fixing the above, and possibly its own test.


Found while building the eval layers (#17–#20). Layer 1 and layer 2 are working; this is the first defect that needs layer 3 to catch it, which is a useful argument for building that runner.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions